mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-09-29 16:50:02 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38501cebf1 | ||
|
|
ee45d0217e | ||
|
|
558f362919 | ||
|
|
fb9db2f50c | ||
|
|
c1862e9c68 | ||
|
|
731bde8e28 | ||
|
|
47c87f8694 | ||
|
|
5558cdacc3 | ||
|
|
4650d244a5 | ||
|
|
c65b8aae07 | ||
|
|
7ba2fb9584 | ||
|
|
9a2cfd0c9b | ||
|
|
a7d61b1f2b | ||
|
|
7ab185f7d7 | ||
|
|
724541d691 | ||
|
|
baa6cda9c2 | ||
|
|
9c987e9aad | ||
|
|
134974ea9c | ||
|
|
2d946958e3 | ||
|
|
ffe010e9ce | ||
|
|
1c67252b0c | ||
|
|
60c1be25d4 | ||
|
|
18323a0916 | ||
|
|
9d98abded9 | ||
|
|
b06a742b8c | ||
|
|
87fe44f188 | ||
|
|
96f6ef1693 | ||
|
|
23c4141e55 | ||
|
|
c21200c2a9 | ||
|
|
4a8b94259d | ||
|
|
28a58df27b | ||
|
|
caaf486bbe | ||
|
|
0a972a399e | ||
|
|
245dad1239 | ||
|
|
a6b6a2fbff | ||
|
|
53000a949b | ||
|
|
eae1dd59a7 | ||
|
|
e6347a76a8 | ||
|
|
13e0fb3df7 | ||
|
|
3f1b23ef8a | ||
|
|
acae7f0658 | ||
|
|
a9456b1bd6 | ||
|
|
e4d5a230d2 | ||
|
|
fedf44b9fa | ||
|
|
451db104e8 | ||
|
|
9ac4c7f4e3 | ||
|
|
7cf1c6f016 | ||
|
|
6dd87ee301 | ||
|
|
96837fa8db | ||
|
|
d816e76962 | ||
|
|
310945d9cd | ||
|
|
1aba480495 | ||
|
|
07407749fe | ||
|
|
dfab70dd90 | ||
|
|
3b15469c98 | ||
|
|
e4590800c2 | ||
|
|
c63d4b8ad0 | ||
|
|
1fb913e2ed | ||
|
|
72ace2dab7 | ||
|
|
3bcef18629 | ||
|
|
c91758519c | ||
|
|
9c8f2e7aa0 | ||
|
|
5f377af6d5 | ||
|
|
570b5b1766 | ||
|
|
c4d53d58eb | ||
|
|
ed65033cf1 | ||
|
|
41836bde26 | ||
|
|
8381858742 | ||
|
|
1040bd0d58 | ||
|
|
404e47332e | ||
|
|
3db128785e | ||
|
|
2189ea11df | ||
|
|
49b9bd65b0 | ||
|
|
b3fdb1ff58 | ||
|
|
c79e3fdcf0 | ||
|
|
d7509f7030 | ||
|
|
5c99b55502 | ||
|
|
fd255f022b | ||
|
|
41642589ae | ||
|
|
619fef3bff | ||
|
|
38c0da0cf8 | ||
|
|
b241a58e84 | ||
|
|
35c9585b22 | ||
|
|
d2fe95cc05 | ||
|
|
92e60ba141 | ||
|
|
2356527791 | ||
|
|
cab0f7b522 | ||
|
|
8d3f101a68 | ||
|
|
c8ac2b6281 | ||
|
|
d7d8efd305 | ||
|
|
a4bc04c958 | ||
|
|
5fc208c95a | ||
|
|
02743d5955 | ||
|
|
a7ffebea35 | ||
|
|
c511ef50a2 | ||
|
|
82902e0cde | ||
|
|
1de5ae09bc | ||
|
|
8b735f1cb5 | ||
|
|
33d1334174 | ||
|
|
324b58c21b | ||
|
|
b7b1651709 | ||
|
|
b5a4a6a9ff | ||
|
|
fe2c82b03c | ||
|
|
81e4bca9ef | ||
|
|
c0d055b825 | ||
|
|
188ae38525 | ||
|
|
86a8fe7096 | ||
|
|
0bb2b45f5c | ||
|
|
a17d8731c8 | ||
|
|
9cdd2326c8 | ||
|
|
d75653631d | ||
|
|
e234c8906e | ||
|
|
57baea18b5 | ||
|
|
0f3efe29c4 | ||
|
|
89b87a38b3 | ||
|
|
c52b7ca2e9 | ||
|
|
371b55dc9e | ||
|
|
53e572353c | ||
|
|
3ae71c74e9 | ||
|
|
cb643c04f7 | ||
|
|
faf9ee159b | ||
|
|
ffb3b2d73e | ||
|
|
ffcc327089 | ||
|
|
dfd5a6a86a | ||
|
|
d43f89b067 | ||
|
|
f15c3ab6a2 | ||
|
|
e27809298e | ||
|
|
4b3e28e14b | ||
|
|
71ce9153ed | ||
|
|
25d63966de | ||
|
|
98afbb54b6 | ||
|
|
03a51ca5dd | ||
|
|
428481b2d5 | ||
|
|
f87c60e6db | ||
|
|
6d824e38bc | ||
|
|
f0fd5bfb9e | ||
|
|
33374df671 | ||
|
|
c035b1b19c | ||
|
|
b3b09ed666 | ||
|
|
8029c00a02 | ||
|
|
a2c0921013 | ||
|
|
31cdabc229 | ||
|
|
1059a0aae0 | ||
|
|
e6576e7f40 | ||
|
|
5837144095 | ||
|
|
d30a5c4207 | ||
|
|
7d169b0e5e | ||
|
|
1f54226673 | ||
|
|
901a90ff13 | ||
|
|
56573d1d34 |
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: Issue template
|
||||
about: " issue template's purpose here."
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### 系统版本:Ubuntu 20.04
|
||||
### 真寻版本:0.1.5.3
|
||||
|
||||
### 错误截图
|
||||
|
||||
[img]
|
||||
|
||||
### 日志截图
|
||||
|
||||
[img]
|
||||
|
||||
### 错误说明
|
||||
|
||||
发生了xx错误...
|
||||
@@ -0,0 +1,38 @@
|
||||
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 }}
|
||||
@@ -0,0 +1,9 @@
|
||||
### 系统版本:Ubuntu 20.04
|
||||
### 真寻版本:0.1.5.3
|
||||
|
||||
### 错误截图
|
||||
|
||||
[img]
|
||||
|
||||
### 错误说明
|
||||
发生了xx错误...
|
||||
@@ -142,6 +142,5 @@ test.py
|
||||
server_ip.py
|
||||
member_activity_handle.py
|
||||
Yu-Gi-Oh/
|
||||
black_word/
|
||||
csgo/
|
||||
fantasy_card/
|
||||
|
||||
@@ -18,9 +18,10 @@
|
||||
## 声明
|
||||
此项目仅用于学习交流,请勿用于非法用途
|
||||
|
||||
## Gitee
|
||||
# Nonebot2
|
||||
<img style="height: 200px;width: 200px;" src="https://camo.githubusercontent.com/0ef71e86056da694c540790aa4a4e314396884d6c4fdb95362a7538b27a1b034/68747470733a2f2f76322e6e6f6e65626f742e6465762f6c6f676f2e706e67">
|
||||
|
||||
# [Gitee](https://gitee.com/two_Dimension/zhenxun_bot)
|
||||
非常 [ **[NICE](https://github.com/nonebot/nonebot2)** ] 的OneBot框架
|
||||
|
||||
## 未完成的文档
|
||||
|
||||
@@ -113,7 +114,7 @@
|
||||
- [x] 获取b站视频封面
|
||||
- [x] 通过PID获取图片
|
||||
- [x] 功能统计可视化
|
||||
|
||||
- [x] 词云
|
||||
- [x] 关于
|
||||
|
||||
### 已实现的管理员功能
|
||||
@@ -235,13 +236,139 @@ python bot.py
|
||||
|
||||
## 使用Docker
|
||||
__Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
|
||||
#### GitHub:[Sakuracio/zxenv](https://github.com/Sakuracio/zxenv)
|
||||
#### GitHub:[Sakuracio/zhenxun_bot_docker](https://github.com/Sakuracio/zhenxun_bot_docker)
|
||||
#### DockerHub:[hibikier/zhenxun_bot](https://hub.docker.com/r/hibikier/zhenxun_bot)
|
||||
|
||||
|
||||
|
||||
## 更新
|
||||
|
||||
### 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阻拦
|
||||
* 修复webui中plugins2setting修改时会改变plugins2setting.cmd为字符串
|
||||
* 修复微博热搜报错,发红包小bug [@pull/688](https://github.com/HibiKier/zhenxun_bot/pull/688)
|
||||
* 更多的中文提示
|
||||
|
||||
### 2022/5/26
|
||||
|
||||
* 修复\[滴滴滴]会被转义成[滴滴滴]导致无法触发的问题
|
||||
* 将一些错误以中文提示输出
|
||||
* 更新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
|
||||
|
||||
* 修复了群白名单无法正确添加
|
||||
@@ -477,4 +604,4 @@ __..... 更多更新信息请查看文档__
|
||||
[NothAmor / nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says) :基于nonebot2机器人框架的鲁迅说插件
|
||||
[Kyomotoi / AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus) :一个~~特二刺螈~~(文爱)的适用于任何bot的词库
|
||||
[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
@@ -1 +1 @@
|
||||
__version__: v0.1.5.0
|
||||
__version__: v0.1.5.8
|
||||
@@ -232,10 +232,7 @@ async def update_member_info(group_id: int, remind_superuser: bool = False) -> b
|
||||
_exist_member_list = []
|
||||
# try:
|
||||
for user_info in _group_user_list:
|
||||
if user_info["card"] == "":
|
||||
nickname = user_info["nickname"]
|
||||
else:
|
||||
nickname = user_info["card"]
|
||||
nickname = user_info["card"] or user_info["nickname"]
|
||||
async with db.transaction():
|
||||
# 更新权限
|
||||
if (
|
||||
@@ -321,3 +318,4 @@ def set_group_bot_status(group_id: int, status: bool) -> str:
|
||||
# for x in group_manager.get_task_data():
|
||||
# group_manager.close_group_task(group_id, x)
|
||||
return "那我先睡觉了..."
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ from nonebot import on_command, on_notice
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, GROUP, GroupIncreaseNoticeEvent
|
||||
from ._data_source import update_member_info
|
||||
|
||||
|
||||
__zx_plugin_name__ = "更新群组成员列表 [Admin]"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
|
||||
@@ -125,11 +125,13 @@ async def _(
|
||||
msg = arg.extract_plain_text().strip()
|
||||
if msg:
|
||||
if str(event.user_id) in bot.config.superusers:
|
||||
if is_number(arg.extract_plain_text().strip().split()[0]):
|
||||
qq = int(msg[0])
|
||||
msg = msg[1:]
|
||||
msg_splt = msg.split()
|
||||
if is_number(msg_splt[0]):
|
||||
|
||||
qq = int(msg_splt[0])
|
||||
msg = msg_splt[1:]
|
||||
if cmd in [".ban", "/ban"]:
|
||||
time = parse_ban_time(msg)
|
||||
time = parse_ban_time(" ".join(msg))
|
||||
if isinstance(time, str):
|
||||
await ban.finish(time)
|
||||
result = await a_ban(qq, time, str(qq), event, 9)
|
||||
|
||||
@@ -25,7 +25,9 @@ def parse_ban_time(msg: str) -> Union[int, str]:
|
||||
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:
|
||||
async def a_ban(
|
||||
qq: int, time: int, user_name: str, event: MessageEvent, ban_level: int = None
|
||||
) -> str:
|
||||
"""
|
||||
ban
|
||||
:param qq: qq
|
||||
@@ -38,7 +40,8 @@ async def a_ban(qq: int, time: int, user_name: str, event: MessageEvent, ban_lev
|
||||
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 {event.group_id} 将 USER {qq} 封禁 时长 {time / 60} 分钟"
|
||||
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:
|
||||
@@ -57,13 +60,3 @@ async def a_ban(qq: int, time: int, user_name: str, event: MessageEvent, ban_lev
|
||||
time += " 分钟"
|
||||
result = f"{user_name} 已在黑名单!预计 {time}后解封"
|
||||
return result
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ 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
|
||||
|
||||
@@ -22,10 +23,10 @@ chat_history = on_message(rule=rule, priority=1, block=False)
|
||||
async def _(event: MessageEvent):
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
await ChatHistory.add_chat_msg(
|
||||
event.user_id, event.group_id, str(event.get_message())
|
||||
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()))
|
||||
await ChatHistory.add_chat_msg(event.user_id, None, str(event.get_message()), get_message_text(event.json()))
|
||||
|
||||
|
||||
# @test.handle()
|
||||
|
||||
@@ -42,7 +42,7 @@ __plugin_settings__ = {
|
||||
}
|
||||
|
||||
|
||||
msg_handler = on_regex(r"(周|月)?消息统计(des|DES)?(n=[0-9]{1,2})?", priority=5, block=True)
|
||||
msg_handler = on_regex(r"^(周|月)?消息统计(des|DES)?(n=[0-9]{1,2})?$", priority=5, block=True)
|
||||
|
||||
|
||||
@msg_handler.handle()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from nonebot import on_notice, on_request
|
||||
from configs.path_config import IMAGE_PATH, DATA_PATH
|
||||
from models.level_user import LevelUser
|
||||
from utils.message_builder import image
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from datetime import datetime
|
||||
@@ -96,6 +97,23 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
|
||||
for plugin in data.keys():
|
||||
if not data[plugin]["default_status"]:
|
||||
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:
|
||||
join_time = datetime.now()
|
||||
user_info = await bot.get_group_member_info(
|
||||
@@ -128,9 +146,7 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
|
||||
msg = msg.replace("[at]", "")
|
||||
at_flag = True
|
||||
if (DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg").exists():
|
||||
img = image(
|
||||
DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg"
|
||||
)
|
||||
img = image(DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg")
|
||||
if msg or img:
|
||||
msg = msg.strip() + img
|
||||
msg = "\n" + msg if at_flag else msg
|
||||
|
||||
@@ -56,6 +56,9 @@ def _create_help_img(
|
||||
for matcher in _matchers:
|
||||
plugin_name = None
|
||||
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
|
||||
if not _plugin:
|
||||
logger.warning(f"获取 功能:{matcher.plugin_name} 失败...")
|
||||
continue
|
||||
_module = _plugin.module
|
||||
try:
|
||||
plugin_name = _module.__getattribute__("__zx_plugin_name__")
|
||||
@@ -277,7 +280,7 @@ def _create_help_img(
|
||||
0,
|
||||
plain_text=msg,
|
||||
font_size=24,
|
||||
font="yuanshen.ttf",
|
||||
font="HYWenHei-85W.ttf",
|
||||
)
|
||||
B.paste(text, (w, h), True)
|
||||
h += 50
|
||||
@@ -289,7 +292,7 @@ def _create_help_img(
|
||||
0,
|
||||
plain_text="注: 红字代表功能被群管理员禁用,红线代表功能正在维护",
|
||||
font_size=24,
|
||||
font="yuanshen.ttf",
|
||||
font="HYWenHei-85W.ttf",
|
||||
font_color=(231, 74, 57)
|
||||
),
|
||||
(300, 10),
|
||||
|
||||
@@ -91,7 +91,10 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
except AttributeError:
|
||||
pass
|
||||
# 群黑名单检测 群总开关检测
|
||||
if isinstance(event, GroupMessageEvent) or matcher.plugin_name == other_limit_plugins:
|
||||
if (
|
||||
isinstance(event, GroupMessageEvent)
|
||||
or matcher.plugin_name in other_limit_plugins
|
||||
):
|
||||
try:
|
||||
if (
|
||||
group_manager.get_group_level(event.group_id) < 0
|
||||
@@ -106,7 +109,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
raise IgnoredException("功能总开关关闭状态")
|
||||
except AttributeError:
|
||||
pass
|
||||
if module in admin_manager.keys() and matcher.priority not in [1, 9]:
|
||||
if module in admin_manager.keys() and matcher.priority not in [1, 999]:
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
# 个人权限
|
||||
if (
|
||||
@@ -146,9 +149,13 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
if event.is_tome():
|
||||
status_message_manager.add(event.user_id)
|
||||
raise IgnoredException("权限不足")
|
||||
if module in plugins2info_dict.keys() and matcher.priority not in [1, 9]:
|
||||
if module in plugins2info_dict.keys() and matcher.priority not in [1, 999]:
|
||||
# 戳一戳单独判断
|
||||
if isinstance(event, GroupMessageEvent) or isinstance(event, PokeNotifyEvent) or matcher.plugin_name in other_limit_plugins:
|
||||
if (
|
||||
isinstance(event, GroupMessageEvent)
|
||||
or isinstance(event, PokeNotifyEvent)
|
||||
or matcher.plugin_name in other_limit_plugins
|
||||
):
|
||||
if status_message_manager.get(event.group_id) is None:
|
||||
status_message_manager.delete(event.group_id)
|
||||
if plugins2info_dict[module]["level"] > group_manager.get_group_level(
|
||||
|
||||
@@ -31,7 +31,7 @@ _flmt = FreqLimiter(300)
|
||||
async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
if (
|
||||
(isinstance(event, MessageEvent) or isinstance(event, PokeNotifyEvent))
|
||||
and matcher.priority not in [1, 9]
|
||||
and matcher.priority not in [1, 999]
|
||||
) or matcher.plugin_name in other_limit_plugins:
|
||||
try:
|
||||
if (
|
||||
@@ -58,7 +58,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
if not static_flmt.check(event.user_id):
|
||||
raise IgnoredException("用户处于黑名单中")
|
||||
static_flmt.start_cd(event.user_id)
|
||||
if matcher.priority != 9:
|
||||
if matcher.priority != 999:
|
||||
try:
|
||||
ban_result = Config.get_config("hook", "BAN_RESULT")
|
||||
if (
|
||||
@@ -79,7 +79,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
if not static_flmt.check(event.user_id):
|
||||
raise IgnoredException("用户处于黑名单中")
|
||||
static_flmt.start_cd(event.user_id)
|
||||
if matcher.priority != 9:
|
||||
if matcher.priority != 999:
|
||||
try:
|
||||
ban_result = Config.get_config("hook", "BAN_RESULT")
|
||||
if ban_result and matcher.plugin_name not in ignore_rst_module:
|
||||
|
||||
@@ -25,7 +25,7 @@ _blmt = BanCheckLimiter(
|
||||
async def _(matcher: Matcher, bot: Bot, event: GroupMessageEvent, state: T_State):
|
||||
if not isinstance(event, MessageEvent):
|
||||
return
|
||||
if matcher.type == "message" and matcher.priority not in [1, 9]:
|
||||
if matcher.type == "message" and matcher.priority not in [1, 999]:
|
||||
if state["_prefix"]["raw_command"]:
|
||||
if _blmt.check(f'{event.user_id}{state["_prefix"]["raw_command"]}'):
|
||||
if await BanUser.ban(
|
||||
|
||||
@@ -20,26 +20,36 @@ def init_none_plugin_count_manager():
|
||||
"""
|
||||
清除已删除插件数据
|
||||
"""
|
||||
modules = [x.module for x in get_matchers()]
|
||||
for module in none_plugin_count_manager.keys():
|
||||
if module not in modules:
|
||||
none_plugin_count_manager.add_count(module)
|
||||
else:
|
||||
none_plugin_count_manager.reset(module)
|
||||
if none_plugin_count_manager.check(module):
|
||||
modules = [x.plugin_name for x in get_matchers()]
|
||||
plugins_manager_list = list(plugins_manager.keys())
|
||||
for module in plugins_manager_list:
|
||||
if module not in modules or none_plugin_count_manager.check(module):
|
||||
try:
|
||||
plugin_name = plugins_manager.get(module)["plugin_name"]
|
||||
except (AttributeError, KeyError):
|
||||
plugin_name = ""
|
||||
try:
|
||||
plugins2settings_manager.delete(module)
|
||||
plugins2count_manager.delete(module)
|
||||
plugins2cd_manager.delete(module)
|
||||
plugins2block_manager.delete(module)
|
||||
plugins_manager.delete(module)
|
||||
resources_manager.remove_resource(module)
|
||||
logger.info(f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据...")
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据失败...{type(e)}:{e}"
|
||||
)
|
||||
if none_plugin_count_manager.check(module):
|
||||
try:
|
||||
plugins2settings_manager.delete(module)
|
||||
plugins2settings_manager.save()
|
||||
plugins2count_manager.delete(module)
|
||||
plugins2count_manager.save()
|
||||
plugins2cd_manager.delete(module)
|
||||
plugins2cd_manager.save()
|
||||
plugins2block_manager.delete(module)
|
||||
plugins2block_manager.save()
|
||||
plugins_manager.delete(module)
|
||||
plugins_manager.save()
|
||||
# resources_manager.remove_resource(module)
|
||||
none_plugin_count_manager.delete(module)
|
||||
logger.info(f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据...")
|
||||
except Exception as e:
|
||||
logger.exception(
|
||||
f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据失败...{type(e)}:{e}")
|
||||
else:
|
||||
none_plugin_count_manager.add_count(module)
|
||||
logger.info(
|
||||
f"{module}:{plugin_name} 插件疑似已删除,加载{none_plugin_count_manager._max_count}次失败后将清除对应插件数据,当前次数:{none_plugin_count_manager._data[module]}")
|
||||
else:
|
||||
none_plugin_count_manager.reset(module)
|
||||
none_plugin_count_manager.save()
|
||||
|
||||
@@ -3,6 +3,7 @@ from ruamel.yaml import round_trip_load, round_trip_dump, YAML
|
||||
from utils.manager import admin_manager, plugins_manager
|
||||
from configs.config import Config
|
||||
from services.log import logger
|
||||
from utils.text_utils import prompt2cn
|
||||
from utils.utils import get_matchers
|
||||
from ruamel import yaml
|
||||
import nonebot
|
||||
@@ -56,7 +57,9 @@ def init_plugins_config(data_path):
|
||||
_override=True,
|
||||
)
|
||||
else:
|
||||
Config.add_plugin_config(matcher.plugin_name, key, plugin_configs[key])
|
||||
Config.add_plugin_config(
|
||||
matcher.plugin_name, key, plugin_configs[key]
|
||||
)
|
||||
else:
|
||||
plugin_configs = _data[matcher.plugin_name]
|
||||
for key in plugin_configs:
|
||||
@@ -79,7 +82,10 @@ def init_plugins_config(data_path):
|
||||
_data[plugin].yaml_set_start_comment(plugin_name, indent=2)
|
||||
# 初始化未设置的管理员权限等级
|
||||
for k, v in Config.get_admin_level_data():
|
||||
admin_manager.set_admin_level(k, v)
|
||||
try:
|
||||
admin_manager.set_admin_level(k, v)
|
||||
except KeyError as e:
|
||||
raise KeyError(f"{e} ****** 请检查是否有插件加载失败 ******")
|
||||
# 存完插件基本设置
|
||||
with open(plugins2config_file, "w", encoding="utf8") as wf:
|
||||
round_trip_dump(
|
||||
@@ -96,14 +102,23 @@ def init_plugins_config(data_path):
|
||||
for plugin in Config.keys():
|
||||
_tmp_data[plugin] = {}
|
||||
for k in Config[plugin].keys():
|
||||
if _data.get(plugin) and k in _data[plugin].keys():
|
||||
Config.set_config(plugin, k, _data[plugin][k])
|
||||
if level2module := Config.get_level2module(plugin, k):
|
||||
try:
|
||||
admin_manager.set_admin_level(level2module, _data[plugin][k])
|
||||
except KeyError:
|
||||
logger.warning(f"{level2module} 设置权限等级失败:{_data[plugin][k]}")
|
||||
_tmp_data[plugin][k] = Config.get_config(plugin, k)
|
||||
try:
|
||||
if _data.get(plugin) and k in _data[plugin].keys():
|
||||
Config.set_config(plugin, k, _data[plugin][k])
|
||||
if level2module := Config.get_level2module(plugin, k):
|
||||
try:
|
||||
admin_manager.set_admin_level(
|
||||
level2module, _data[plugin][k]
|
||||
)
|
||||
except KeyError:
|
||||
logger.warning(
|
||||
f"{level2module} 设置权限等级失败:{_data[plugin][k]}"
|
||||
)
|
||||
_tmp_data[plugin][k] = Config.get_config(plugin, k)
|
||||
except AttributeError as e:
|
||||
raise AttributeError(
|
||||
f"{e}\n" + prompt2cn("可能为config.yaml配置文件填写不规范", 46)
|
||||
)
|
||||
Config.save()
|
||||
temp_file = Path() / "configs" / "temp_config.yaml"
|
||||
try:
|
||||
|
||||
@@ -28,7 +28,7 @@ def init_plugins_cd_limit(data_path):
|
||||
_module = _plugin.module
|
||||
plugin_cd_limit = _module.__getattribute__("__plugin_cd_limit__")
|
||||
plugins2cd_manager.add_cd_limit(
|
||||
matcher.plugin_name, data_dict=plugin_cd_limit
|
||||
matcher.plugin_name, **plugin_cd_limit
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
@@ -76,7 +76,7 @@ def init_plugins_block_limit(data_path):
|
||||
_module = _plugin.module
|
||||
plugin_block_limit = _module.__getattribute__("__plugin_block_limit__")
|
||||
plugins2block_manager.add_block_limit(
|
||||
matcher.plugin_name, data_dict=plugin_block_limit
|
||||
matcher.plugin_name, **plugin_block_limit
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
@@ -124,7 +124,7 @@ def init_plugins_count_limit(data_path):
|
||||
_module = _plugin.module
|
||||
plugin_count_limit = _module.__getattribute__("__plugin_count_limit__")
|
||||
plugins2count_manager.add_count_limit(
|
||||
matcher.plugin_name, data_dict=plugin_count_limit
|
||||
matcher.plugin_name, **plugin_count_limit
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
@@ -25,7 +25,7 @@ __plugin_configs__ = {
|
||||
|
||||
friend_req = on_request(priority=5, block=True)
|
||||
group_req = on_request(priority=5, block=True)
|
||||
x = on_message(priority=9, block=False)
|
||||
x = on_message(priority=999, block=False)
|
||||
|
||||
exists_data = {"private": {}, "group": {}}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, PrivateMessageEvent, Message
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, PrivateMessageEvent, Message, MessageEvent
|
||||
from nonebot import on_command
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.rule import to_me
|
||||
@@ -24,7 +24,7 @@ __plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_configs__ = {
|
||||
"BLACK_WORD": {
|
||||
"value": ["爸", "爹", "爷"],
|
||||
"value": ["爸", "爹", "爷", "父亲"],
|
||||
"help": "昵称所屏蔽的关键词,会被替换为 *",
|
||||
"default_value": None
|
||||
}
|
||||
@@ -47,7 +47,7 @@ cancel_nickname = on_command("取消昵称", rule=to_me(), priority=5, block=Tru
|
||||
|
||||
|
||||
@nickname.handle()
|
||||
async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
msg = arg.extract_plain_text().strip()
|
||||
if not msg:
|
||||
await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True)
|
||||
@@ -73,7 +73,7 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
|
||||
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
|
||||
f"好突然,突然要叫你昵称什么的...{msg}..",
|
||||
f"{NICKNAME}会好好记住的{msg}的,放心吧",
|
||||
f"{NICKNAME}会好好记住{msg}的,放心吧",
|
||||
f"好..好.,那窝以后就叫你{msg}了.",
|
||||
]
|
||||
)
|
||||
@@ -90,7 +90,7 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
|
||||
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
|
||||
f"好突然,突然要叫你昵称什么的...{msg}..",
|
||||
f"{NICKNAME}会好好记住的{msg}的,放心吧",
|
||||
f"{NICKNAME}会好好记住{msg}的,放心吧",
|
||||
f"好..好.,那窝以后就叫你{msg}了.",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
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__ = [""]
|
||||
__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" 查看插件仓库"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
import os
|
||||
import shutil
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
from typing import Union, Tuple
|
||||
|
||||
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()}"
|
||||
)
|
||||
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},请重启bot!"
|
||||
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"))
|
||||
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"],
|
||||
}
|
||||
s = (
|
||||
f'id:{i+1}\n名称:{plugin_info[key]["plugin_name"]}\n'
|
||||
f'模块:{key}\n'
|
||||
f'作者:{plugin_info[key]["author"]}\n'
|
||||
f'版本:{plugin_info[key]["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
|
||||
@@ -108,6 +108,10 @@ async def _():
|
||||
"ALTER TABLE genshin ADD bind_group Integer;",
|
||||
"genshin"
|
||||
), # 新增原神群号绑定字段
|
||||
(
|
||||
"ALTER TABLE chat_history ADD plain_text Text;",
|
||||
"chat_history"
|
||||
), # 新增纯文本
|
||||
]
|
||||
for sql in sql_str:
|
||||
try:
|
||||
|
||||
@@ -7,11 +7,13 @@ from typing import Optional, Union
|
||||
from configs.config import Config
|
||||
from nonebot import Driver
|
||||
from nonebot.plugin import require
|
||||
from utils.decorator.shop import shop_register
|
||||
import nonebot
|
||||
import time
|
||||
|
||||
driver: Driver = nonebot.get_driver()
|
||||
|
||||
|
||||
use = require("use")
|
||||
|
||||
|
||||
@@ -20,24 +22,26 @@ async def init_default_shop_goods():
|
||||
"""
|
||||
导入内置的三个商品
|
||||
"""
|
||||
async def sign_card(**kwargs):
|
||||
user_id = kwargs['user_id']
|
||||
group_id = kwargs['group_id']
|
||||
prob = kwargs["prob"]
|
||||
|
||||
@shop_register(
|
||||
name=("好感度双倍加持卡Ⅰ", "好感度双倍加持卡Ⅱ", "好感度双倍加持卡Ⅲ"),
|
||||
price=(30, 150, 250),
|
||||
des=(
|
||||
"下次签到双倍好感度概率 + 10%(谁才是真命天子?)(同类商品将覆盖)",
|
||||
"下次签到双倍好感度概率 + 20%(平平庸庸)(同类商品将覆盖)",
|
||||
"下次签到双倍好感度概率 + 30%(金币才是真命天子!)(同类商品将覆盖)",
|
||||
),
|
||||
load_status=Config.get_config("shop", "IMPORT_DEFAULT_SHOP_GOODS"),
|
||||
** {"好感度双倍加持卡Ⅰ_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)
|
||||
await user.update(add_probability=prob).apply()
|
||||
|
||||
if Config.get_config("shop", "IMPORT_DEFAULT_SHOP_GOODS"):
|
||||
await register_goods(
|
||||
"好感度双倍加持卡Ⅰ", 30, "下次签到双倍好感度概率 + 10%(谁才是真命天子?)(同类商品将覆盖)"
|
||||
)
|
||||
use.register_use("好感度双倍加持卡Ⅰ", sign_card, **{"prob": 0.1})
|
||||
await register_goods("好感度双倍加持卡Ⅱ", 150, "下次签到双倍好感度概率 + 20%(平平庸庸)(同类商品将覆盖)")
|
||||
use.register_use("好感度双倍加持卡Ⅱ", sign_card, **{"prob": 0.2})
|
||||
await register_goods(
|
||||
"好感度双倍加持卡Ⅲ", 250, "下次签到双倍好感度概率 + 30%(金币才是真命天子!)(同类商品将覆盖)"
|
||||
)
|
||||
use.register_use("好感度双倍加持卡Ⅲ", sign_card, **{"prob": 0.3})
|
||||
|
||||
@driver.on_bot_connect
|
||||
async def _():
|
||||
await shop_register.load_register()
|
||||
|
||||
|
||||
# 创建商店界面
|
||||
@@ -59,9 +63,7 @@ async def create_shop_help() -> str:
|
||||
A = BuildImage(1000, h, color="#f9f6f2")
|
||||
current_h = 0
|
||||
for goods in _list:
|
||||
bk = BuildImage(
|
||||
700, 80, font_size=15, color="#f9f6f2", font="CJGaoDeGuo.otf"
|
||||
)
|
||||
bk = BuildImage(700, 80, font_size=15, color="#f9f6f2", font="CJGaoDeGuo.otf")
|
||||
goods_image = BuildImage(
|
||||
600, 80, font_size=20, color="#a29ad6", font="CJGaoDeGuo.otf"
|
||||
)
|
||||
@@ -92,10 +94,18 @@ async def create_shop_help() -> str:
|
||||
await bk.apaste(goods_image, alpha=True)
|
||||
# 添加限时图标和时间
|
||||
if goods.goods_limit_time > 0:
|
||||
_limit_time_logo = BuildImage(40, 40, background=f"{IMAGE_PATH}/other/time.png")
|
||||
_limit_time_logo = BuildImage(
|
||||
40, 40, background=f"{IMAGE_PATH}/other/time.png"
|
||||
)
|
||||
await bk.apaste(_limit_time_logo, (600, 0), True)
|
||||
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(
|
||||
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()
|
||||
y_m_d = limit_time[0]
|
||||
_h_m = limit_time[1].split(":")
|
||||
h_m = _h_m[0] + "时 " + _h_m[1] + "分"
|
||||
@@ -125,13 +135,12 @@ async def create_shop_help() -> str:
|
||||
|
||||
|
||||
async def register_goods(
|
||||
name: str,
|
||||
price: int,
|
||||
des: str,
|
||||
discount: Optional[float] = 1,
|
||||
limit_time: Optional[int] = 0,
|
||||
**kwargs,
|
||||
):
|
||||
name: str,
|
||||
price: int,
|
||||
des: str,
|
||||
discount: Optional[float] = 1,
|
||||
limit_time: Optional[int] = 0,
|
||||
) -> bool:
|
||||
"""
|
||||
添加商品
|
||||
例如: 折扣:可选参数↓ 限时时间:可选,单位为小时
|
||||
@@ -142,21 +151,20 @@ async def register_goods(
|
||||
:param des: 商品简介
|
||||
:param discount: 商品折扣
|
||||
:param limit_time: 商品限时销售时间,单位为小时
|
||||
:param kwargs: kwargs
|
||||
:return: 是否添加成功
|
||||
"""
|
||||
if kwargs:
|
||||
name = kwargs.get("name")
|
||||
price = kwargs.get("price")
|
||||
des = kwargs.get("des")
|
||||
discount = kwargs.get("discount")
|
||||
limit_time = kwargs.get("time_limit")
|
||||
limit_time = float(limit_time) if limit_time else limit_time
|
||||
discount = discount if discount is None else 1
|
||||
limit_time = int(time.time() + limit_time * 60 * 60) if limit_time is not None and limit_time != 0 else 0
|
||||
return await GoodsInfo.add_goods(
|
||||
name, int(price), des, float(discount), limit_time
|
||||
)
|
||||
if not await GoodsInfo.get_goods_info(name):
|
||||
limit_time = float(limit_time) if limit_time else limit_time
|
||||
discount = discount if discount is not None else 1
|
||||
limit_time = (
|
||||
int(time.time() + limit_time * 60 * 60)
|
||||
if limit_time is not None and limit_time != 0
|
||||
else 0
|
||||
)
|
||||
return await GoodsInfo.add_goods(
|
||||
name, int(price), des, float(discount), limit_time
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
# 删除商品
|
||||
@@ -230,7 +238,11 @@ async def update_goods(**kwargs) -> "str, str, int":
|
||||
int(price),
|
||||
des,
|
||||
float(discount),
|
||||
int(time.time() + limit_time * 60 * 60 if limit_time != 0 and new_time else 0),
|
||||
int(
|
||||
time.time() + limit_time * 60 * 60
|
||||
if limit_time != 0 and new_time
|
||||
else 0
|
||||
),
|
||||
),
|
||||
name,
|
||||
tmp[:-1],
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
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
|
||||
|
||||
|
||||
@@ -30,24 +34,52 @@ class GoodsUseFuncManager:
|
||||
:param goods_name: 商品名称
|
||||
"""
|
||||
if self.exists(goods_name):
|
||||
return self._data[goods_name]["kwargs"]["_max_num_limit"]
|
||||
return self._data[goods_name]["kwargs"]["max_num_limit"]
|
||||
return 1
|
||||
|
||||
async def use(self, **kwargs) -> Optional[Union[str, MessageSegment]]:
|
||||
async def use(
|
||||
self, param: ShopParam, **kwargs
|
||||
) -> Optional[Union[str, MessageSegment]]:
|
||||
"""
|
||||
使用道具
|
||||
:param param: BaseModel
|
||||
:param kwargs: kwargs
|
||||
"""
|
||||
goods_name = kwargs.get("goods_name")
|
||||
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):
|
||||
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
|
||||
return await self._data[goods_name]["func"](
|
||||
**kwargs,
|
||||
)
|
||||
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:
|
||||
return self._data[goods_name]["func"](
|
||||
**kwargs,
|
||||
)
|
||||
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:
|
||||
"""
|
||||
@@ -67,6 +99,18 @@ class GoodsUseFuncManager:
|
||||
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()
|
||||
|
||||
@@ -83,22 +127,23 @@ async def effect(
|
||||
:return: 使用是否成功
|
||||
"""
|
||||
# 优先使用注册的商品插件
|
||||
try:
|
||||
if func_manager.exists(goods_name):
|
||||
_kwargs = func_manager.get_kwargs(goods_name)
|
||||
return await func_manager.use(
|
||||
**{
|
||||
**_kwargs,
|
||||
"_bot": bot,
|
||||
"event": event,
|
||||
"group_id": event.group_id,
|
||||
"user_id": event.user_id,
|
||||
"num": num,
|
||||
"goods_name": goods_name,
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"use 商品生效函数effect 发生错误 {type(e)}:{e}")
|
||||
# try:
|
||||
if func_manager.exists(goods_name):
|
||||
_kwargs = func_manager.get_kwargs(goods_name)
|
||||
return await func_manager.use(
|
||||
func_manager.init_model(goods_name, bot, event, num),
|
||||
**{
|
||||
**_kwargs,
|
||||
"_bot": bot,
|
||||
"event": event,
|
||||
"group_id": event.group_id,
|
||||
"user_id": event.user_id,
|
||||
"num": num,
|
||||
"goods_name": goods_name,
|
||||
},
|
||||
)
|
||||
# except Exception as e:
|
||||
# logger.error(f"use 商品生效函数effect 发生错误 {type(e)}:{e}")
|
||||
return None
|
||||
|
||||
|
||||
@@ -112,10 +157,14 @@ def register_use(goods_name: str, func, **kwargs):
|
||||
if func_manager.exists(goods_name):
|
||||
raise ValueError("该商品使用函数已被注册!")
|
||||
# 发送使用成功信息
|
||||
if kwargs.get("send_success_msg") is None:
|
||||
kwargs["send_success_msg"] = True
|
||||
kwargs["_max_num_limit"] = (
|
||||
kwargs.get("_max_num_limit") if kwargs.get("_max_num_limit") else 1
|
||||
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,
|
||||
},
|
||||
)
|
||||
func_manager.register_use(goods_name, **{"func": func, "kwargs": kwargs})
|
||||
logger.info(f"register_use 成功注册商品:{goods_name} 的使用函数")
|
||||
|
||||
@@ -92,7 +92,7 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
level = 0
|
||||
if not msg:
|
||||
await add_group_level.finish("用法:修改群权限 [group] [level]")
|
||||
msg = msg.split(" ")
|
||||
msg = msg.split()
|
||||
if len(msg) < 2:
|
||||
await add_group_level.finish("参数不完全..[group] [level]")
|
||||
if is_number(msg[0]) and is_number(msg[1]):
|
||||
|
||||
@@ -45,6 +45,7 @@ async def _():
|
||||
group_info["group_name"],
|
||||
group_info["max_member_count"],
|
||||
group_info["member_count"],
|
||||
1
|
||||
):
|
||||
num += 1
|
||||
logger.info(f"自动更新群组 {g} 信息成功")
|
||||
|
||||
@@ -12,6 +12,7 @@ driver.on_shutdown(disconnect)
|
||||
nonebot.load_plugin("nonebot_plugin_apscheduler")
|
||||
nonebot.load_plugins("basic_plugins")
|
||||
nonebot.load_plugins("plugins")
|
||||
nonebot.load_plugins("extensive_plugin")
|
||||
# 最后加载权限控制
|
||||
nonebot.load_plugins("basic_plugins/hooks")
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ from typing import Optional, Any, Union
|
||||
from pathlib import Path
|
||||
from ruamel.yaml import YAML
|
||||
from ruamel import yaml
|
||||
from ruamel.yaml.scanner import ScannerError
|
||||
|
||||
|
||||
class ConfigsManager:
|
||||
@@ -21,9 +22,24 @@ class ConfigsManager:
|
||||
if file.exists():
|
||||
with open(file, "r", encoding="utf8") as 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():
|
||||
with open(self._simple_file, "r", encoding="utf8") as f:
|
||||
self._simple_data = _yaml.load(f)
|
||||
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(
|
||||
self,
|
||||
@@ -86,7 +102,10 @@ class ConfigsManager:
|
||||
:param value: 值
|
||||
"""
|
||||
if module in self._data.keys():
|
||||
if self._data[module].get(key) is not None and self._data[module][key] != value:
|
||||
if (
|
||||
self._data[module].get(key) is not None
|
||||
and self._data[module][key] != value
|
||||
):
|
||||
self._data[module][key]["value"] = value
|
||||
self._simple_data[module][key] = value
|
||||
self.save()
|
||||
@@ -115,7 +134,9 @@ class ConfigsManager:
|
||||
self._data[module][key]["default_value"] = value
|
||||
self.save()
|
||||
|
||||
def get_config(self, module: str, key: str, default: Optional[Any] = None) -> Optional[Any]:
|
||||
def get_config(
|
||||
self, module: str, key: str, default: Optional[Any] = None
|
||||
) -> Optional[Any]:
|
||||
"""
|
||||
获取指定配置值
|
||||
:param module: 模块名
|
||||
@@ -161,7 +182,11 @@ class ConfigsManager:
|
||||
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
|
||||
self._simple_data,
|
||||
f,
|
||||
indent=2,
|
||||
Dumper=yaml.RoundTripDumper,
|
||||
allow_unicode=True,
|
||||
)
|
||||
path = path if path else self.file
|
||||
with open(path, "w", encoding="utf8") as f:
|
||||
|
||||
+1450
-155
File diff suppressed because it is too large
Load Diff
@@ -11,12 +11,13 @@ class ChatHistory(db.Model):
|
||||
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):
|
||||
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, create_time=datetime.now()
|
||||
user_qq=user_qq, group_id=group_id, text=text, plain_text=plain_text, create_time=datetime.now()
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -97,7 +98,6 @@ class ChatHistory(db.Model):
|
||||
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}"
|
||||
print(sql)
|
||||
return await db.all(db.text(sql))
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -114,8 +114,7 @@ class GoodsInfo(db.Model):
|
||||
参数:
|
||||
:param goods_name: 商品名称
|
||||
"""
|
||||
query = await cls.query.where(cls.goods_name == goods_name).gino.first()
|
||||
return query
|
||||
return await cls.query.where(cls.goods_name == goods_name).gino.first()
|
||||
|
||||
@classmethod
|
||||
async def get_all_goods(cls) -> List["GoodsInfo"]:
|
||||
|
||||
+15
-10
@@ -1,3 +1,5 @@
|
||||
from asyncpg import UniqueViolationError
|
||||
|
||||
from services.db_context import db
|
||||
|
||||
|
||||
@@ -44,16 +46,19 @@ class LevelUser(db.Model):
|
||||
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
if user is None:
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
group_id=group_id,
|
||||
user_level=level,
|
||||
group_flag=group_flag,
|
||||
)
|
||||
return True
|
||||
else:
|
||||
await user.update(user_level=level, group_flag=group_flag).apply()
|
||||
try:
|
||||
if not user:
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
group_id=group_id,
|
||||
user_level=level,
|
||||
group_flag=group_flag,
|
||||
)
|
||||
return True
|
||||
else:
|
||||
await user.update(user_level=level, group_flag=group_flag).apply()
|
||||
return False
|
||||
except UniqueViolationError:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -37,7 +37,7 @@ Config.add_plugin_config(
|
||||
"alapi", "ALAPI_TOKEN", None, help_="在 https://admin.alapi.cn/user/login 登录后获取token"
|
||||
)
|
||||
|
||||
ai = on_message(rule=to_me(), priority=8)
|
||||
ai = on_message(rule=to_me(), priority=998)
|
||||
|
||||
|
||||
@ai.handle()
|
||||
|
||||
+33
-29
@@ -126,35 +126,39 @@ async def xie_ai(text: str) -> str:
|
||||
"""
|
||||
res = await AsyncHttpx.get(f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}")
|
||||
content = ""
|
||||
data = json.loads(res.text)
|
||||
if data["result"] == 0:
|
||||
content = data["content"]
|
||||
if "菲菲" in content:
|
||||
content = content.replace("菲菲", NICKNAME)
|
||||
if "艳儿" in content:
|
||||
content = content.replace("艳儿", NICKNAME)
|
||||
if "公众号" in content:
|
||||
content = ""
|
||||
if "{br}" in content:
|
||||
content = content.replace("{br}", "\n")
|
||||
if "提示" in content:
|
||||
content = content[: content.find("提示")]
|
||||
if "淘宝" in content or "taobao.com" in content:
|
||||
return ""
|
||||
while True:
|
||||
r = re.search("{face:(.*)}", content)
|
||||
if r:
|
||||
id_ = r.group(1)
|
||||
content = content.replace(
|
||||
"{" + f"face:{id_}" + "}", str(face(int(id_)))
|
||||
)
|
||||
else:
|
||||
break
|
||||
return (
|
||||
content
|
||||
if not content and not Config.get_config("ai", "ALAPI_AI_CHECK")
|
||||
else await check_text(content)
|
||||
)
|
||||
try:
|
||||
data = json.loads(res.text)
|
||||
if data["result"] == 0:
|
||||
content = data["content"]
|
||||
if "菲菲" in content:
|
||||
content = content.replace("菲菲", NICKNAME)
|
||||
if "艳儿" in content:
|
||||
content = content.replace("艳儿", NICKNAME)
|
||||
if "公众号" in content:
|
||||
content = ""
|
||||
if "{br}" in content:
|
||||
content = content.replace("{br}", "\n")
|
||||
if "提示" in content:
|
||||
content = content[: content.find("提示")]
|
||||
if "淘宝" in content or "taobao.com" in content:
|
||||
return ""
|
||||
while True:
|
||||
r = re.search("{face:(.*)}", content)
|
||||
if r:
|
||||
id_ = r.group(1)
|
||||
content = content.replace(
|
||||
"{" + f"face:{id_}" + "}", str(face(int(id_)))
|
||||
)
|
||||
else:
|
||||
break
|
||||
return (
|
||||
content
|
||||
if not content and not Config.get_config("ai", "ALAPI_AI_CHECK")
|
||||
else await check_text(content)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Ai xie_ai 发生错误 {type(e)}:{e}")
|
||||
return ""
|
||||
|
||||
|
||||
def hello() -> str:
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
from nonebot.adapters.onebot.v11 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, Tuple, Union
|
||||
from configs.config import Config
|
||||
from utils.http_utils import AsyncHttpx
|
||||
@@ -23,27 +19,8 @@ async def get_data(url: str, params: Optional[dict] = None) -> Tuple[Union[dict,
|
||||
return "没有搜索到...", 997
|
||||
return data, 200
|
||||
else:
|
||||
if data["code"] == 101:
|
||||
return "缺失ALAPI TOKEN,请在配置文件中填写!", 999
|
||||
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())
|
||||
|
||||
@@ -38,7 +38,7 @@ usage:
|
||||
__plugin_des__ = "非常便利的B站订阅通知"
|
||||
__plugin_cmd__ = ["添加订阅 [主播/UP/番剧] [id/链接/番名]", "删除订阅 [id]", "查看订阅"]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_author__ = "HibiKier & NumberSir"
|
||||
__plugin_settings__ = {
|
||||
"level": 5,
|
||||
"default_status": True,
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
from bilibili_api.exceptions.ResponseCodeException import ResponseCodeException
|
||||
from bilireq.exceptions import ResponseCodeError
|
||||
from utils.manager import resources_manager
|
||||
from asyncio.exceptions import TimeoutError
|
||||
from .model import BilibiliSub
|
||||
from bilibili_api.live import LiveRoom
|
||||
from bilibili_api import bangumi
|
||||
from bilireq.live import get_room_info_by_id
|
||||
from .utils import get_meta
|
||||
from utils.message_builder import image
|
||||
from bilibili_api.user import User
|
||||
from bilibili_api import user
|
||||
from bilireq.user import get_user_info
|
||||
from bilireq import dynamic
|
||||
from .utils import get_videos
|
||||
from typing import Optional
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from datetime import datetime
|
||||
@@ -36,9 +37,9 @@ async def add_live_sub(live_id: int, sub_user: str) -> str:
|
||||
try:
|
||||
async with db.transaction():
|
||||
try:
|
||||
live = LiveRoom(live_id)
|
||||
live_info = (await live.get_room_info())["room_info"]
|
||||
except ResponseCodeException:
|
||||
"""bilibili_api.live库的LiveRoom类中get_room_info改为bilireq.live库的get_room_info_by_id方法"""
|
||||
live_info = await get_room_info_by_id(live_id)
|
||||
except ResponseCodeError:
|
||||
return f"未找到房间号Id:{live_id} 的信息,请检查Id是否正确"
|
||||
uid = live_info["uid"]
|
||||
room_id = live_info["room_id"]
|
||||
@@ -78,16 +79,18 @@ async def add_up_sub(uid: int, sub_user: str) -> str:
|
||||
try:
|
||||
async with db.transaction():
|
||||
try:
|
||||
u = user.User(uid)
|
||||
user_info = await u.get_user_info()
|
||||
except ResponseCodeException:
|
||||
"""bilibili_api.user库中User类的get_user_info改为bilireq.user库的get_user_info方法"""
|
||||
user_info = await get_user_info(uid)
|
||||
except ResponseCodeError:
|
||||
return f"未找到UpId:{uid} 的信息,请检查Id是否正确"
|
||||
uname = user_info["name"]
|
||||
dynamic_info = await u.get_dynamics(0)
|
||||
"""bilibili_api.user库中User类的get_dynamics改为bilireq.dynamic库的get_user_dynamics方法"""
|
||||
dynamic_info = await dynamic.get_user_dynamics(uid)
|
||||
dynamic_upload_time = 0
|
||||
if dynamic_info.get("cards"):
|
||||
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
|
||||
video_info = await u.get_videos()
|
||||
"""bilibili_api.user库中User类的get_videos改为bilireq.user库的get_videos方法"""
|
||||
video_info = await get_videos(uid)
|
||||
latest_video_created = 0
|
||||
if video_info["list"].get("vlist"):
|
||||
latest_video_created = video_info["list"]["vlist"][0]["created"]
|
||||
@@ -117,8 +120,9 @@ async def add_season_sub(media_id: int, sub_user: str) -> str:
|
||||
try:
|
||||
async with db.transaction():
|
||||
try:
|
||||
season_info = await bangumi.get_meta(media_id)
|
||||
except ResponseCodeException:
|
||||
"""bilibili_api.bangumi库中get_meta改为bilireq.bangumi库的get_meta方法"""
|
||||
season_info = await get_meta(media_id)
|
||||
except ResponseCodeError:
|
||||
return f"未找到media_id:{media_id} 的信息,请检查Id是否正确"
|
||||
season_id = season_info["media"]["season_id"]
|
||||
season_current_episode = season_info["media"]["new_ep"]["index"]
|
||||
@@ -200,7 +204,7 @@ async def get_sub_status(id_: int, sub_type: str) -> Optional[str]:
|
||||
return await _get_up_status(id_)
|
||||
elif sub_type == "season":
|
||||
return await _get_season_status(id_)
|
||||
except ResponseCodeException:
|
||||
except ResponseCodeError:
|
||||
return f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试..."
|
||||
# except Exception as e:
|
||||
# logger.error(f"获取订阅状态发生预料之外的错误 id_:{id_} {type(e)}:{e}")
|
||||
@@ -212,12 +216,12 @@ async def _get_live_status(id_: int) -> Optional[str]:
|
||||
获取直播订阅状态
|
||||
:param id_: 直播间 id
|
||||
"""
|
||||
live = LiveRoom(id_)
|
||||
live_info = (await live.get_room_info())["room_info"]
|
||||
"""bilibili_api.live库的LiveRoom类中get_room_info改为bilireq.live库的get_room_info_by_id方法"""
|
||||
live_info = await get_room_info_by_id(id_)
|
||||
title = live_info["title"]
|
||||
room_id = live_info["room_id"]
|
||||
live_status = live_info["live_status"]
|
||||
cover = live_info["cover"]
|
||||
cover = live_info["user_cover"]
|
||||
sub = await BilibiliSub.get_sub(id_)
|
||||
if sub.live_status != live_status:
|
||||
await BilibiliSub.update_sub_info(id_, live_status=live_status)
|
||||
@@ -238,15 +242,16 @@ async def _get_up_status(id_: int) -> Optional[str]:
|
||||
:return:
|
||||
"""
|
||||
_user = await BilibiliSub.get_sub(id_)
|
||||
u = user.User(_user.uid)
|
||||
user_info = await u.get_user_info()
|
||||
"""bilibili_api.user库中User类的get_user_info改为bilireq.user库的get_user_info方法"""
|
||||
user_info = await get_user_info(_user.uid)
|
||||
uname = user_info["name"]
|
||||
video_info = await u.get_videos()
|
||||
"""bilibili_api.user库中User类的get_videos改为bilireq.user库的get_videos方法"""
|
||||
video_info = await get_videos(id_)
|
||||
latest_video_created = 0
|
||||
video = None
|
||||
if _user.uname != uname:
|
||||
await BilibiliSub.update_sub_info(id_, uname=uname)
|
||||
dynamic_img, dynamic_upload_time = await get_user_dynamic(u, _user)
|
||||
dynamic_img, dynamic_upload_time = await get_user_dynamic(id_, _user)
|
||||
if video_info["list"].get("vlist"):
|
||||
video = video_info["list"]["vlist"][0]
|
||||
latest_video_created = video["created"]
|
||||
@@ -280,7 +285,8 @@ async def _get_season_status(id_) -> Optional[str]:
|
||||
获取 番剧 更新状态
|
||||
:param id_: 番剧 id
|
||||
"""
|
||||
season_info = await bangumi.get_meta(id_)
|
||||
"""bilibili_api.bangumi库中get_meta改为bilireq.bangumi库的get_meta方法"""
|
||||
season_info = await get_meta(id_)
|
||||
title = season_info["media"]["title"]
|
||||
_idx = (await BilibiliSub.get_sub(id_)).season_current_episode
|
||||
new_ep = season_info["media"]["new_ep"]["index"]
|
||||
@@ -297,15 +303,16 @@ async def _get_season_status(id_) -> Optional[str]:
|
||||
|
||||
|
||||
async def get_user_dynamic(
|
||||
u: User, local_user: BilibiliSub
|
||||
uid: int, local_user: BilibiliSub
|
||||
) -> "Optional[MessageSegment], int":
|
||||
"""
|
||||
获取用户动态
|
||||
:param u: 用户类
|
||||
:param uid: 用户uid
|
||||
:param local_user: 数据库存储的用户数据
|
||||
:return: 最新动态截图与时间
|
||||
"""
|
||||
dynamic_info = await u.get_dynamics(0)
|
||||
"""bilibili_api.user库中User类的get_dynamics改为bilireq.dynamic库的get_user_dynamics方法"""
|
||||
dynamic_info = await dynamic.get_user_dynamics(uid)
|
||||
browser = await get_browser()
|
||||
if dynamic_info.get("cards") and browser:
|
||||
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
|
||||
@@ -317,21 +324,21 @@ async def get_user_dynamic(
|
||||
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(
|
||||
"""
|
||||
xs = document.getElementsByClassName('first-card-with-title');
|
||||
xs = document.getElementsByClassName('bili-dyn-item__tag');
|
||||
for (x of xs) {
|
||||
x.remove();
|
||||
x.parentNode.parentNode.remove();
|
||||
}
|
||||
"""
|
||||
)
|
||||
card = await page.query_selector(".card")
|
||||
card = page.locator(".bili-dyn-list__item").first
|
||||
await card.wait_for()
|
||||
# 截图并保存
|
||||
await card.screenshot(
|
||||
path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
|
||||
timeout=100000,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"B站订阅:获取用户动态 发送错误 {type(e)}:{e}")
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from utils.image_utils import BuildImage
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from bilibili_api import user
|
||||
# from bilibili_api import user
|
||||
from bilireq.user import get_user_info
|
||||
from io import BytesIO
|
||||
|
||||
|
||||
@@ -28,8 +29,8 @@ async def create_live_des_image(uid: int, title: str, cover: str, tags: str, des
|
||||
:param des: 直播简介
|
||||
:return:
|
||||
"""
|
||||
u = user.User(uid)
|
||||
user_info = await u.get_user_info()
|
||||
|
||||
user_info = await get_user_info(uid)
|
||||
name = user_info["name"]
|
||||
sex = user_info["sex"]
|
||||
face = user_info["face"]
|
||||
@@ -37,7 +38,6 @@ 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.circle()
|
||||
cover = BuildImage(470, 265, background=BytesIO(await get_pic(cover)))
|
||||
print()
|
||||
|
||||
|
||||
def _create_live_des_image(
|
||||
@@ -68,3 +68,47 @@ def _create_live_des_image(
|
||||
border_img = BuildImage(1772, 2657, background=border)
|
||||
bk = BuildImage(1772, 2657, font_size=30)
|
||||
bk.paste(cover, (0, 100), center_type="by_width")
|
||||
|
||||
|
||||
async def get_meta(media_id: 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)
|
||||
@@ -0,0 +1,253 @@
|
||||
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}"
|
||||
)
|
||||
@@ -0,0 +1,118 @@
|
||||
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或敏感词"
|
||||
@@ -0,0 +1,148 @@
|
||||
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()
|
||||
@@ -0,0 +1,322 @@
|
||||
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.exception 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")
|
||||
@@ -3,13 +3,13 @@ from configs.config import Config
|
||||
from bs4 import BeautifulSoup
|
||||
import platform
|
||||
|
||||
if platform.system() == "Windows":
|
||||
import asyncio
|
||||
|
||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
# if platform.system() == "Windows":
|
||||
# import asyncio
|
||||
#
|
||||
# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
|
||||
|
||||
url = "http://www.eclzz.mobi"
|
||||
url = "http://www.eclzz.win"
|
||||
|
||||
|
||||
async def get_bt_info(keyword: str, page: int):
|
||||
@@ -19,7 +19,7 @@ async def get_bt_info(keyword: str, page: int):
|
||||
:param page: 页数
|
||||
"""
|
||||
text = (await AsyncHttpx.get(f"{url}/s/{keyword}_rel_{page}.html", timeout=5)).text
|
||||
if text.find("大约0条结果") != -1:
|
||||
if "大约0条结果" in text:
|
||||
return
|
||||
soup = BeautifulSoup(text, "lxml")
|
||||
item_lst = soup.find_all("div", {"class": "search-item"})
|
||||
|
||||
@@ -25,6 +25,11 @@ __plugin_cmd__ = ["检查更新真寻", "重启"]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_configs__ = {
|
||||
"UPDATE_REMIND": {
|
||||
"value": True,
|
||||
"help": "真寻是否检测版本状态",
|
||||
"default": True,
|
||||
},
|
||||
"AUTO_UPDATE_ZHENXUN": {
|
||||
"value": False,
|
||||
"help": "真寻是否自动检查更新",
|
||||
@@ -72,7 +77,7 @@ async def _():
|
||||
await restart.finish("暂无windows重启脚本...")
|
||||
|
||||
|
||||
@restart.got("flag", prompt="确定是否重启真寻?(重启失败咱们将失去联系,请谨慎!)")
|
||||
@restart.got("flag", prompt="确定是否重启真寻?确定请回复[是|好|确定](重启失败咱们将失去联系,请谨慎!)")
|
||||
async def _(flag: str = ArgStr("flag")):
|
||||
if flag.lower() in ["true", "是", "好", "确定", "确定是"]:
|
||||
await restart.send("开始重启真寻..请稍等...")
|
||||
@@ -88,7 +93,7 @@ async def _(flag: str = ArgStr("flag")):
|
||||
minute=0,
|
||||
)
|
||||
async def _():
|
||||
if Config.get_config("check_zhenxun_update", "AUTO_UPDATE_ZHENXUN"):
|
||||
if Config.get_config("check_zhenxun_update", "UPDATE_REMIND"):
|
||||
_version = "v0.0.0"
|
||||
_version_file = Path() / "__version__"
|
||||
if _version_file.exists():
|
||||
@@ -108,17 +113,18 @@ async def _():
|
||||
message=f"检测到真寻版本更新\n"
|
||||
f"当前版本:{_version},最新版本:{latest_version}",
|
||||
)
|
||||
# try:
|
||||
# code = await check_update(bot)
|
||||
# except Exception as e:
|
||||
# logger.error(f"更新真寻未知错误 {type(e)}:{e}")
|
||||
# await bot.send_private_msg(
|
||||
# user_id=int(list(bot.config.superusers)[0]),
|
||||
# message=f"更新真寻未知错误 {type(e)}:{e}\n",
|
||||
# )
|
||||
# else:
|
||||
# if code == 200:
|
||||
# await bot.send_private_msg(
|
||||
# user_id=int(list(bot.config.superusers)[0]),
|
||||
# message=f"更新完毕,请重启真寻....",
|
||||
# )
|
||||
if Config.get_config("check_zhenxun_update", "AUTO_UPDATE_ZHENXUN"):
|
||||
try:
|
||||
code = await check_update(bot)
|
||||
except Exception as e:
|
||||
logger.error(f"更新真寻未知错误 {type(e)}:{e}")
|
||||
await bot.send_private_msg(
|
||||
user_id=int(list(bot.config.superusers)[0]),
|
||||
message=f"更新真寻未知错误 {type(e)}:{e}\n",
|
||||
)
|
||||
else:
|
||||
if code == 200:
|
||||
await bot.send_private_msg(
|
||||
user_id=int(list(bot.config.superusers)[0]),
|
||||
message=f"更新完毕,请重启真寻....",
|
||||
)
|
||||
|
||||
@@ -14,9 +14,9 @@ import tarfile
|
||||
import shutil
|
||||
import os
|
||||
|
||||
if str(platform.system()).lower() == "windows":
|
||||
policy = asyncio.WindowsSelectorEventLoopPolicy()
|
||||
asyncio.set_event_loop_policy(policy)
|
||||
# if str(platform.system()).lower() == "windows":
|
||||
# policy = asyncio.WindowsSelectorEventLoopPolicy()
|
||||
# asyncio.set_event_loop_policy(policy)
|
||||
|
||||
|
||||
driver = nonebot.get_driver()
|
||||
|
||||
@@ -53,7 +53,7 @@ __plugin_settings__ = {
|
||||
dialogue_data = {}
|
||||
|
||||
|
||||
dialogue = on_command("[滴滴滴]", aliases={"滴滴滴-"}, priority=5, block=True)
|
||||
dialogue = on_command("[滴滴滴]", aliases={"滴滴滴-"}, priority=5, block=True)
|
||||
reply = on_command("/t", priority=1, permission=SUPERUSER, block=True)
|
||||
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ from .handles.onmyoji_handle import OnmyojiHandle
|
||||
from .handles.pcr_handle import PcrHandle
|
||||
from .handles.pretty_handle import PrettyHandle
|
||||
from .handles.prts_handle import PrtsHandle
|
||||
from .handles.ba_handle import BaHandle
|
||||
|
||||
from .config import draw_config
|
||||
|
||||
@@ -30,7 +31,7 @@ from .config import draw_config
|
||||
__zx_plugin_name__ = "游戏抽卡"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
模拟赛马娘,原神,明日方舟,坎公骑冠剑,公主连结(国/台),碧蓝航线,FGO,阴阳师进行抽卡
|
||||
模拟赛马娘,原神,明日方舟,坎公骑冠剑,公主连结(国/台),碧蓝航线,FGO,阴阳师,碧蓝档案进行抽卡
|
||||
指令:
|
||||
原神[1-180]抽: 原神常驻池
|
||||
原神角色[1-180]抽: 原神角色UP池子
|
||||
@@ -43,6 +44,7 @@ usage:
|
||||
碧蓝航线/碧蓝[重型/轻型/特型/活动][1-300]抽: 碧蓝航线重型/轻型/特型/活动卡池
|
||||
fgo[1-300]抽: fgo卡池
|
||||
阴阳师[1-300]抽: 阴阳师卡池
|
||||
ba/碧蓝档案[1-200]抽:碧蓝档案卡池
|
||||
* 以上指令可以通过 XX一井 来指定最大抽取数量 *
|
||||
* 示例:原神一井 *
|
||||
""".strip()
|
||||
@@ -73,6 +75,7 @@ __plugin_cmd__ = [
|
||||
"pcr/公主连接[1-300]抽",
|
||||
"fgo[1-300]抽",
|
||||
"阴阳师[1-300]抽",
|
||||
"碧蓝档案[1-200]抽",
|
||||
"更新方舟信息 [_superuser]",
|
||||
"重载方舟卡池 [_superuser]",
|
||||
"更新原神信息 [_superuser]",
|
||||
@@ -83,6 +86,7 @@ __plugin_cmd__ = [
|
||||
"更新碧蓝航线信息 [_superuser]",
|
||||
"更新fgo信息 [_superuser]",
|
||||
"更新阴阳师信息 [_superuser]",
|
||||
"更新碧蓝档案信息 [_superuser]",
|
||||
]
|
||||
__plugin_type__ = ("抽卡相关", 1)
|
||||
__plugin_version__ = 0.1
|
||||
@@ -105,7 +109,7 @@ class Game:
|
||||
|
||||
|
||||
games = (
|
||||
Game({"azur", "碧蓝", "碧蓝航线"}, AzurHandle(), draw_config.AZUR_FLAG),
|
||||
Game({"azur", "碧蓝航线"}, AzurHandle(), draw_config.AZUR_FLAG),
|
||||
Game({"fgo", "命运冠位指定"}, FgoHandle(), draw_config.FGO_FLAG),
|
||||
Game(
|
||||
{"genshin", "原神"},
|
||||
@@ -130,6 +134,11 @@ games = (
|
||||
reload_time=4,
|
||||
),
|
||||
Game({"prts", "方舟", "明日方舟"}, PrtsHandle(), draw_config.PRTS_FLAG, reload_time=4),
|
||||
Game(
|
||||
{"ba","碧蓝档案"},BaHandle(),
|
||||
draw_config.BA_FLAG,
|
||||
max_count=200,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -87,6 +87,12 @@ class OnmyojiConfig(BaseModel, extra=Extra.ignore):
|
||||
ONMYOJI_SR: float = 0.2
|
||||
ONMYOJI_R: float = 0.7875
|
||||
|
||||
#碧蓝档案
|
||||
class BaConfig(BaseModel, extra=Extra.ignore):
|
||||
BA_THREE_P: float = 0.025
|
||||
BA_TWO_P: float = 0.185
|
||||
BA_ONE_P: float = 0.79
|
||||
BA_G_TWO_P: float = 0.975
|
||||
|
||||
class Config(BaseModel, extra=Extra.ignore):
|
||||
# 开关
|
||||
@@ -98,6 +104,7 @@ class Config(BaseModel, extra=Extra.ignore):
|
||||
AZUR_FLAG: bool = AConfig.get_config("draw_card", "AZUR_FLAG")
|
||||
FGO_FLAG: bool = AConfig.get_config("draw_card", "FGO_FLAG")
|
||||
ONMYOJI_FLAG: bool = AConfig.get_config("draw_card", "ONMYOJI_FLAG")
|
||||
BA_FLAG: bool = AConfig.get_config("draw_card", "BA_FLAG")
|
||||
|
||||
# 其他配置
|
||||
PCR_TAI: bool = AConfig.get_config("draw_card", "PCR_TAI")
|
||||
@@ -112,6 +119,7 @@ class Config(BaseModel, extra=Extra.ignore):
|
||||
azur: AzurConfig = AzurConfig()
|
||||
fgo: FgoConfig = FgoConfig()
|
||||
onmyoji: OnmyojiConfig = OnmyojiConfig()
|
||||
ba: BaConfig = BaConfig()
|
||||
|
||||
|
||||
driver = nonebot.get_driver()
|
||||
@@ -133,8 +141,9 @@ for game_flag, game_name in zip(
|
||||
"FGO_FLAG",
|
||||
"ONMYOJI_FLAG",
|
||||
"PCR_TAI",
|
||||
"BA_FLAG"
|
||||
],
|
||||
["明日方舟", "原神", "赛马娘", "坎公骑冠剑", "公主连结", "碧蓝航线", "命运-冠位指定(FGO)", "阴阳师", "pcr台服卡池"],
|
||||
["明日方舟", "原神", "赛马娘", "坎公骑冠剑", "公主连结", "碧蓝航线", "命运-冠位指定(FGO)", "阴阳师", "pcr台服卡池","碧蓝档案"],
|
||||
):
|
||||
AConfig.add_plugin_config(
|
||||
"draw_card",
|
||||
|
||||
@@ -293,7 +293,7 @@ class AzurHandle(BaseHandle[AzurChar]):
|
||||
type_ = ship.xpath("./tbody/tr/td[2]/p/small/text()")[0] # 舰船类型
|
||||
try:
|
||||
p = float(str(ship.xpath(".//sup/text()")[0]).strip("%"))
|
||||
except IndexError:
|
||||
except (IndexError, ValueError):
|
||||
p = 0
|
||||
star = self.parse_star(
|
||||
ship.xpath("./tbody/tr/td[1]/div/div/div/a/img/@alt")[0]
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
import random
|
||||
from lxml import etree
|
||||
from typing import List, Tuple
|
||||
from PIL import ImageDraw
|
||||
from urllib.parse import unquote
|
||||
from nonebot.log import logger
|
||||
|
||||
from .base_handle import BaseHandle, BaseData
|
||||
from ..config import draw_config
|
||||
from ..util import remove_prohibited_str, cn2py, load_font
|
||||
from utils.image_utils import BuildImage
|
||||
|
||||
|
||||
class BaChar(BaseData):
|
||||
pass
|
||||
|
||||
|
||||
class BaHandle(BaseHandle[BaChar]):
|
||||
def __init__(self):
|
||||
super().__init__("ba", "碧蓝档案")
|
||||
self.max_star = 3
|
||||
self.config = draw_config.ba
|
||||
self.ALL_CHAR: List[BaChar] = []
|
||||
|
||||
def get_card(self, mode: int = 1) -> BaChar:
|
||||
if mode == 2:
|
||||
star = self.get_star(
|
||||
[3, 2], [self.config.BA_THREE_P, self.config.BA_G_TWO_P]
|
||||
)
|
||||
else:
|
||||
star = self.get_star(
|
||||
[3, 2, 1],
|
||||
[self.config.BA_THREE_P, self.config.BA_TWO_P, self.config.BA_ONE_P],
|
||||
)
|
||||
chars = [x for x in self.ALL_CHAR if x.star == star and not x.limited]
|
||||
return random.choice(chars)
|
||||
|
||||
def get_cards(self, count: int, **kwargs) -> List[Tuple[BaChar, int]]:
|
||||
card_list = []
|
||||
card_count = 0 # 保底计算
|
||||
for i in range(count):
|
||||
card_count += 1
|
||||
# 十连保底
|
||||
if card_count == 10:
|
||||
card = self.get_card(2)
|
||||
card_count = 0
|
||||
else:
|
||||
card = self.get_card(1)
|
||||
if card.star > self.max_star - 2:
|
||||
card_count = 0
|
||||
card_list.append((card, i + 1))
|
||||
return card_list
|
||||
|
||||
def generate_card_img(self, card: BaChar) -> BuildImage:
|
||||
sep_w = 5
|
||||
sep_h = 5
|
||||
star_h = 15
|
||||
img_w = 90
|
||||
img_h = 100
|
||||
font_h = 20
|
||||
bar_h = 20
|
||||
bar_w = 90
|
||||
bg = BuildImage(img_w + sep_w * 2, img_h + font_h + sep_h * 2, color="#EFF2F5")
|
||||
img_path = str(self.img_path / f"{cn2py(card.name)}.png")
|
||||
img = BuildImage(img_w, img_h, background=img_path)
|
||||
bar = BuildImage(bar_w, bar_h, color="#6495ED")
|
||||
bg.paste(img, (sep_w, sep_h), alpha=True)
|
||||
bg.paste(bar, (sep_w, img_h - bar_h + sep_h), alpha=True)
|
||||
if (card.star == 1):
|
||||
star_path = str(self.img_path / "star-1.png")
|
||||
star_w = 15
|
||||
elif (card.star == 2):
|
||||
star_path = str(self.img_path / "star-2.png")
|
||||
star_w = 30
|
||||
else:
|
||||
star_path = str(self.img_path / "star-3.png")
|
||||
star_w = 45
|
||||
star = BuildImage(star_w, star_h, background=star_path)
|
||||
bg.paste(star, (img_w // 2 - 15 * (card.star - 1) // 2, img_h - star_h), alpha=True)
|
||||
text = card.name[:5] + "..." if len(card.name) > 6 else card.name
|
||||
font = load_font(fontsize=14)
|
||||
text_w, text_h = font.getsize(text)
|
||||
draw = ImageDraw.Draw(bg.markImg)
|
||||
draw.text(
|
||||
(sep_w + (img_w - text_w) / 2, sep_h + img_h + (font_h - text_h) / 2),
|
||||
text,
|
||||
font=font,
|
||||
fill="gray",
|
||||
)
|
||||
return bg
|
||||
|
||||
def _init_data(self):
|
||||
self.ALL_CHAR = [
|
||||
BaChar(
|
||||
name=value["名称"],
|
||||
star=int(value["星级"]),
|
||||
limited=True if "(" in key else False,
|
||||
)
|
||||
for key, value in self.load_data().items()
|
||||
]
|
||||
|
||||
def title2star(self, title: int):
|
||||
if title == 'Star-3.png':
|
||||
return 3
|
||||
elif title == 'Star-2.png':
|
||||
return 2
|
||||
else:
|
||||
return 1
|
||||
|
||||
async def _update_info(self):
|
||||
info = {}
|
||||
url = "https://wiki.biligame.com/bluearchive/学生筛选"
|
||||
result = await self.get_url(url)
|
||||
if not result:
|
||||
logger.warning(f"更新 {self.game_name_cn} 出错")
|
||||
return
|
||||
else:
|
||||
dom = etree.HTML(result, etree.HTMLParser())
|
||||
char_list = dom.xpath("//div[@class='filters']/table[2]/tbody/tr")
|
||||
for char in char_list:
|
||||
try:
|
||||
name = char.xpath("./td[2]/a/div/text()")[0]
|
||||
avatar = char.xpath("./td[1]/div/div/a/img/@data-src")[0]
|
||||
star_pic = char.xpath("./td[4]/img/@alt")[0]
|
||||
except IndexError:
|
||||
continue
|
||||
member_dict = {
|
||||
"头像": unquote(str(avatar)),
|
||||
"名称": remove_prohibited_str(name),
|
||||
"星级": self.title2star(star_pic),
|
||||
}
|
||||
info[member_dict["名称"]] = member_dict
|
||||
self.dump_data(info)
|
||||
logger.info(f"{self.game_name_cn} 更新成功")
|
||||
# 下载头像
|
||||
for value in info.values():
|
||||
await self.download_img(value["头像"], value["名称"])
|
||||
# 下载星星
|
||||
await self.download_img(
|
||||
"https://patchwiki.biligame.com/images/bluearchive/thumb/e/e0/82nj2x9sxko473g7782r14fztd4zyky.png/15px-Star-1.png",
|
||||
"star-1",
|
||||
)
|
||||
await self.download_img(
|
||||
"https://patchwiki.biligame.com/images/bluearchive/thumb/0/0b/msaff2g0zk6nlyl1rrn7n1ri4yobcqc.png/30px-Star-2.png",
|
||||
"star-2",
|
||||
)
|
||||
await self.download_img(
|
||||
"https://patchwiki.biligame.com/images/bluearchive/thumb/8/8a/577yv79x1rwxk8efdccpblo0lozl158.png/46px-Star-3.png",
|
||||
"star-3"
|
||||
)
|
||||
@@ -3,7 +3,6 @@ import random
|
||||
import dateparser
|
||||
from lxml import etree
|
||||
from PIL import ImageDraw
|
||||
from bs4 import BeautifulSoup
|
||||
from datetime import datetime
|
||||
from urllib.parse import unquote
|
||||
from typing import List, Optional, Tuple
|
||||
@@ -32,8 +31,9 @@ class Operator(BaseData):
|
||||
|
||||
class PrtsHandle(BaseHandle[Operator]):
|
||||
def __init__(self):
|
||||
super().__init__("prts", "明日方舟", "#eff2f5")
|
||||
super().__init__(game_name="prts", game_name_cn="明日方舟")
|
||||
self.max_star = 6
|
||||
self.game_card_color = "#eff2f5"
|
||||
self.config = draw_config.prts
|
||||
|
||||
self.ALL_OPERATOR: List[Operator] = []
|
||||
@@ -41,8 +41,8 @@ class PrtsHandle(BaseHandle[Operator]):
|
||||
|
||||
def get_card(self, add: float) -> Operator:
|
||||
star = self.get_star(
|
||||
[6, 5, 4, 3],
|
||||
[
|
||||
star_list=[6, 5, 4, 3],
|
||||
probability_list=[
|
||||
self.config.PRTS_SIX_P + add,
|
||||
self.config.PRTS_FIVE_P,
|
||||
self.config.PRTS_FOUR_P,
|
||||
@@ -53,7 +53,7 @@ class PrtsHandle(BaseHandle[Operator]):
|
||||
all_operators = [
|
||||
x
|
||||
for x in self.ALL_OPERATOR
|
||||
if x.star == star and not any([x.limited, x.event_only, x.recruit_only])
|
||||
if x.star == star and not any([x.limited, x.recruit_only, x.event_only])
|
||||
]
|
||||
acquire_operator = None
|
||||
|
||||
@@ -74,7 +74,7 @@ class PrtsHandle(BaseHandle[Operator]):
|
||||
acquire_operator = random.choice(all_operators)
|
||||
return acquire_operator
|
||||
|
||||
def get_cards(self, count: int) -> List[Tuple[Operator, int]]:
|
||||
def get_cards(self, count: int, **kwargs) -> List[Tuple[Operator, int]]:
|
||||
card_list = [] # 获取所有角色
|
||||
add = 0.0
|
||||
count_idx = 0
|
||||
@@ -109,7 +109,8 @@ class PrtsHandle(BaseHandle[Operator]):
|
||||
|
||||
def _draw(self, count: int, **kwargs) -> Message:
|
||||
index2card = self.get_cards(count)
|
||||
cards = [card[0] for card in self.get_cards(count)]
|
||||
"""这里cards修复了抽卡图文不符的bug"""
|
||||
cards = [card[0] for card in index2card]
|
||||
up_list = [x.name for x in self.UP_EVENT.up_char] if self.UP_EVENT else []
|
||||
result = self.format_result(index2card, up_list=up_list)
|
||||
pool_info = self.format_pool_info()
|
||||
@@ -162,7 +163,11 @@ class PrtsHandle(BaseHandle[Operator]):
|
||||
def load_up_char(self):
|
||||
try:
|
||||
data = self.load_data(f"draw_card_up/{self.game_name}_up_char.json")
|
||||
self.UP_EVENT = UpEvent.parse_obj(data.get("char", {}))
|
||||
"""这里的 waring 有点模糊,更新游戏信息时没有up池的情况下也会报错,所以细分了一下"""
|
||||
if not data:
|
||||
logger.warning(f"当前无UP池或 {self.game_name}_up_char.json 文件不存在")
|
||||
else:
|
||||
self.UP_EVENT = UpEvent.parse_obj(data.get("char", {}))
|
||||
except ValidationError:
|
||||
logger.warning(f"{self.game_name}_up_char 解析出错")
|
||||
|
||||
@@ -185,14 +190,15 @@ class PrtsHandle(BaseHandle[Operator]):
|
||||
avatar = char.xpath("./td[1]/div/div/div/a/img/@srcset")[0]
|
||||
name = char.xpath("./td[2]/a/text()")[0]
|
||||
star = char.xpath("./td[5]/text()")[0]
|
||||
sources = str(char.xpath("./td[8]/text()")[0]).split("\n")
|
||||
"""这里sources修好了干员获取标签有问题的bug,如三星只能抽到卡缇就是这个原因"""
|
||||
sources = [_.strip('\n') for _ in char.xpath("./td[8]/text()")]
|
||||
except IndexError:
|
||||
continue
|
||||
member_dict = {
|
||||
"头像": unquote(str(avatar).split(" ")[-2]),
|
||||
"名称": remove_prohibited_str(str(name).strip()),
|
||||
"星级": int(str(star).strip()),
|
||||
"获取途径": [s for s in sources if s],
|
||||
"获取途径": sources,
|
||||
}
|
||||
info[member_dict["名称"]] = member_dict
|
||||
self.dump_data(info)
|
||||
@@ -208,6 +214,7 @@ class PrtsHandle(BaseHandle[Operator]):
|
||||
await self.update_up_char()
|
||||
|
||||
async def update_up_char(self):
|
||||
"""重载卡池"""
|
||||
announcement_url = "https://ak.hypergryph.com/news.html"
|
||||
result = await self.get_url(announcement_url)
|
||||
if not result:
|
||||
@@ -221,41 +228,53 @@ class PrtsHandle(BaseHandle[Operator]):
|
||||
end_time = None
|
||||
up_chars = []
|
||||
pool_img = ""
|
||||
title = ""
|
||||
for activity_url in activity_urls:
|
||||
for activity_url in activity_urls[:10]: # 减少响应时间, 10个就够了
|
||||
activity_url = f"https://ak.hypergryph.com{activity_url}"
|
||||
result = await self.get_url(activity_url)
|
||||
if not result:
|
||||
logger.warning(f"{self.game_name_cn}获取公告 {activity_url} 出错")
|
||||
continue
|
||||
soup = BeautifulSoup(result, "lxml")
|
||||
contents = soup.find_all("p")
|
||||
|
||||
"""因为鹰角的前端太自由了,这里重写了匹配规则以尽可能避免因为前端乱七八糟而导致的重载失败"""
|
||||
dom = etree.HTML(result, etree.HTMLParser())
|
||||
contents = dom.xpath(
|
||||
"//div[@class='article-content']/p/text() | //div[@class='article-content']/p/span/text() | //div[@class='article-content']/div[@class='media-wrap image-wrap']/img/@src"
|
||||
)
|
||||
title = ""
|
||||
time = ""
|
||||
chars: List[str] = []
|
||||
for index, content in enumerate(contents):
|
||||
if re.search("(.*)(寻访|复刻).*?开启", content.text):
|
||||
title = content.text
|
||||
if "【" in title and "】" in title:
|
||||
title = re.split(r"[【】]", title)[1]
|
||||
lines = [str(contents[index + i + 1].text) for i in range(5)]
|
||||
time = ""
|
||||
chars: List[str] = []
|
||||
for line in lines:
|
||||
if re.search("(.*)(寻访|复刻).*?开启", content):
|
||||
title = re.split(r"[【】]", content)
|
||||
title = "".join(title[1:-1]) if "-" in title else title[1]
|
||||
lines = [contents[index-2+_] for _ in range(8)] # 从 -2 开始是因为xpath获取的时间有的会在寻访开启这一句之前
|
||||
lines.append("") # 防止IndexError,加个空字符串
|
||||
for idx, line in enumerate(lines):
|
||||
match = re.search(
|
||||
r"(\d{1,2}月\d{1,2}日.*?-.*?\d{1,2}月\d{1,2}日.*?$)", line
|
||||
)
|
||||
if match:
|
||||
time = match.group(1)
|
||||
if "★" in line:
|
||||
chars.append(line)
|
||||
"""因为 <p> 的诡异排版,所以有了下面的一段"""
|
||||
if ("★★" in line and "%" in line) or ("★★" in line and "%" in lines[idx + 1]):
|
||||
chars.append(line) if ("★★" in line and "%" in line) else chars.append(line + lines[idx + 1])
|
||||
if not time:
|
||||
continue
|
||||
start, end = time.replace("月", "/").replace("日", "").split("-")[:2]
|
||||
start, end = time.replace("月", "/").replace("日", " ").split("-")[:2] # 日替换为空格是因为有日后面不接空格的情况,导致 split 出问题
|
||||
start_time = dateparser.parse(start)
|
||||
end_time = dateparser.parse(end)
|
||||
pool_img = content.find_previous("img")["src"]
|
||||
pool_img = contents[index-2]
|
||||
r"""两类格式:用/分割,用\分割;★+(概率)+名字,★+名字+(概率)"""
|
||||
for char in chars:
|
||||
star = char.split("(")[0].count("★")
|
||||
name = re.split(r"[:(]", char)[1]
|
||||
names = name.split("/") if "/" in name else [name]
|
||||
name = re.split(r"[:(]", char)[1] if "★(" not in char else re.split("):", char)[1] # 有的括号在前面有的在后面
|
||||
if "\\" in name:
|
||||
names = name.split("\\")
|
||||
elif "/" in name:
|
||||
names = name.split("/")
|
||||
else:
|
||||
names = [name] # 既有用/分割的,又有用\分割的
|
||||
|
||||
names = [name.replace("[限定]", "").strip() for name in names]
|
||||
if "权值" in char:
|
||||
match = re.search(r"(在.*?以.*?(\d+).*?倍权值.*?)", char)
|
||||
@@ -269,7 +288,7 @@ class PrtsHandle(BaseHandle[Operator]):
|
||||
up_chars.append(
|
||||
UpChar(name=name, star=star, limited=False, zoom=zoom)
|
||||
)
|
||||
break
|
||||
break # 这里break会导致个问题:如果一个公告里有两个池子,会漏掉下面的池子,比如 5.19 的定向寻访。但目前我也没啥好想法解决
|
||||
if title and start_time and end_time:
|
||||
if start_time <= datetime.now() <= end_time:
|
||||
self.UP_EVENT = UpEvent(
|
||||
|
||||
+24
-37
@@ -1,58 +1,34 @@
|
||||
from httpx import AsyncClient
|
||||
from datetime import datetime
|
||||
from nonebot.log import logger
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from configs.config import NICKNAME
|
||||
from utils.http_utils import AsyncHttpx
|
||||
|
||||
|
||||
# 获取所有 Epic Game Store 促销游戏
|
||||
# 方法参考:RSSHub /epicgames 路由
|
||||
# https://github.com/DIYgod/RSSHub/blob/master/lib/routes/epicgames/index.js
|
||||
async def get_epic_game():
|
||||
# 现在没用 graphql 辣
|
||||
"""prv_graphql Code
|
||||
epic_url = "https://www.epicgames.com/store/backend/graphql-proxy"
|
||||
headers = {
|
||||
"Referer": "https://www.epicgames.com/store/zh-CN/",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
}
|
||||
data = {
|
||||
"query": "query searchStoreQuery($allowCountries: String, $category: String, $count: Int, $country: String!, $keywords: String, $locale: String, $namespace: String, $sortBy: String, $sortDir: String, $start: Int, $tag: String, $withPrice: Boolean = false, $withPromotions: Boolean = false) {\n Catalog {\n searchStore(allowCountries: $allowCountries, category: $category, count: $count, country: $country, keywords: $keywords, locale: $locale, namespace: $namespace, sortBy: $sortBy, sortDir: $sortDir, start: $start, tag: $tag) {\n elements {\n title\n id\n namespace\n description\n effectiveDate\n keyImages {\n type\n url\n }\n seller {\n id\n name\n }\n productSlug\n urlSlug\n url\n items {\n id\n namespace\n }\n customAttributes {\n key\n value\n }\n categories {\n path\n }\n price(country: $country) @include(if: $withPrice) {\n totalPrice {\n discountPrice\n originalPrice\n voucherDiscount\n discount\n currencyCode\n currencyInfo {\n decimals\n }\n fmtPrice(locale: $locale) {\n originalPrice\n discountPrice\n intermediatePrice\n }\n }\n lineOffers {\n appliedRules {\n id\n endDate\n discountSetting {\n discountType\n }\n }\n }\n }\n promotions(category: $category) @include(if: $withPromotions) {\n promotionalOffers {\n promotionalOffers {\n startDate\n endDate\n discountSetting {\n discountType\n discountPercentage\n }\n }\n }\n upcomingPromotionalOffers {\n promotionalOffers {\n startDate\n endDate\n discountSetting {\n discountType\n discountPercentage\n }\n }\n }\n }\n }\n paging {\n count\n total\n }\n }\n }\n}\n",
|
||||
"variables": {
|
||||
"allowCountries": "CN",
|
||||
"category": "freegames",
|
||||
"count": 1000,
|
||||
"country": "CN",
|
||||
"locale": "zh-CN",
|
||||
"sortBy": "effectiveDate",
|
||||
"sortDir": "asc",
|
||||
"withPrice": True,
|
||||
"withPromotions": True,
|
||||
},
|
||||
}
|
||||
"""
|
||||
|
||||
epic_url = "https://store-site-backend-static-ipv4.ak.epicgames.com/freeGamesPromotions?locale=zh-CN&country=CN&allowCountries=CN"
|
||||
headers = {
|
||||
"Referer": "https://www.epicgames.com/store/zh-CN/",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36",
|
||||
}
|
||||
async with AsyncClient(headers=headers) as client:
|
||||
try:
|
||||
res = await client.get(epic_url, timeout=10.0)
|
||||
res_json = res.json()
|
||||
games = res_json["data"]["Catalog"]["searchStore"]["elements"]
|
||||
return games
|
||||
except Exception as e:
|
||||
logger.error(str(e))
|
||||
return None
|
||||
try:
|
||||
res = await AsyncHttpx.get(epic_url, headers=headers, timeout=10)
|
||||
res_json = res.json()
|
||||
games = res_json["data"]["Catalog"]["searchStore"]["elements"]
|
||||
return games
|
||||
except Exception as e:
|
||||
logger.error(f"Epic 访问接口错误 {type(e)}:{e}")
|
||||
return None
|
||||
|
||||
|
||||
# 获取 Epic Game Store 免费游戏信息
|
||||
# 处理免费游戏的信息方法借鉴 pip 包 epicstore_api 示例
|
||||
# https://github.com/SD4RK/epicstore_api/blob/master/examples/free_games_example.py
|
||||
async def get_epic_free(bot: Bot, Type_Event: str):
|
||||
async def get_epic_free(bot: Bot, type_event: str):
|
||||
games = await get_epic_game()
|
||||
if not games:
|
||||
return "Epic 可能又抽风啦,请稍后再试(", 404
|
||||
@@ -81,7 +57,7 @@ async def get_epic_free(bot: Bot, Type_Event: str):
|
||||
end_date = datetime.fromisoformat(end_date_iso).strftime(
|
||||
"%b.%d %H:%M"
|
||||
)
|
||||
if Type_Event == "Group":
|
||||
if type_event == "Group":
|
||||
_message = "\n由 {} 公司发行的游戏 {} ({}) 在 UTC 时间 {} 即将推出免费游玩,预计截至 {}。".format(
|
||||
game_corp, game_name, game_price, start_date, end_date
|
||||
)
|
||||
@@ -101,8 +77,19 @@ async def get_epic_free(bot: Bot, Type_Event: str):
|
||||
msg_list.append(msg)
|
||||
else:
|
||||
for image in game["keyImages"]:
|
||||
if image["type"] == "Thumbnail":
|
||||
if (
|
||||
image.get("url")
|
||||
and not game_thumbnail
|
||||
and image["type"]
|
||||
in [
|
||||
"Thumbnail",
|
||||
"VaultOpened",
|
||||
"DieselStoreFrontWide",
|
||||
"OfferImageWide",
|
||||
]
|
||||
):
|
||||
game_thumbnail = image["url"]
|
||||
break
|
||||
for pair in game["customAttributes"]:
|
||||
if pair["key"] == "developerName":
|
||||
game_dev = pair["value"]
|
||||
@@ -127,7 +114,7 @@ async def get_epic_free(bot: Bot, Type_Event: str):
|
||||
game_url = "https://www.epicgames.com/store/zh-CN/p/{}".format(
|
||||
game_url_part
|
||||
)
|
||||
if Type_Event == "Group":
|
||||
if type_event == "Group":
|
||||
_message = "[CQ:image,file={}]\n\nFREE now :: {} ({})\n{}\n此游戏由 {} 开发、{} 发行,将在 UTC 时间 {} 结束免费游玩,戳链接速度加入你的游戏库吧~\n{}\n".format(
|
||||
game_thumbnail,
|
||||
game_name,
|
||||
|
||||
+11
-9
@@ -9,6 +9,7 @@ from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||
from configs.config import Config
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from services.log import logger
|
||||
from configs.config import NICKNAME
|
||||
|
||||
|
||||
__zx_plugin_name__ = "复读"
|
||||
@@ -74,15 +75,15 @@ class Fudu:
|
||||
_fudu_list = Fudu()
|
||||
|
||||
|
||||
fudu = on_message(permission=GROUP, priority=9)
|
||||
fudu = on_message(permission=GROUP, priority=999)
|
||||
|
||||
|
||||
@fudu.handle()
|
||||
async def _(event: GroupMessageEvent):
|
||||
if event.is_tome():
|
||||
return
|
||||
if get_message_text(event.json()):
|
||||
if get_message_text(event.json()).find("@可爱的小真寻") != -1:
|
||||
if msg := get_message_text(event.json()):
|
||||
if msg.startswith(f"@可爱的{NICKNAME}"):
|
||||
await fudu.finish("复制粘贴的虚空艾特?", at_sender=True)
|
||||
img = get_message_img(event.json())
|
||||
msg = get_message_text(event.json())
|
||||
@@ -105,20 +106,21 @@ async def _(event: GroupMessageEvent):
|
||||
"fudu", "FUDU_PROBABILITY"
|
||||
) and not _fudu_list.is_repeater(event.group_id):
|
||||
if random.random() < 0.2:
|
||||
await fudu.finish("[[_task|fudu]]打断施法!")
|
||||
if msg.endswith("打断施法!"):
|
||||
await fudu.finish("[[_task|fudu]]打断" + msg)
|
||||
else:
|
||||
await fudu.finish("[[_task|fudu]]打断施法!")
|
||||
_fudu_list.set_repeater(event.group_id)
|
||||
if img and msg:
|
||||
rst = msg + image(f"compare_{event.group_id}_img.jpg", "temp")
|
||||
rst = msg + image(TEMP_PATH / f"compare_{event.group_id}_img.jpg")
|
||||
elif img:
|
||||
rst = image(f"compare_{event.group_id}_img.jpg", "temp")
|
||||
rst = image(TEMP_PATH / f"compare_{event.group_id}_img.jpg")
|
||||
elif msg:
|
||||
rst = msg
|
||||
else:
|
||||
rst = ""
|
||||
if rst:
|
||||
if rst.endswith("打断施法!"):
|
||||
rst = "打断" + rst
|
||||
await fudu.send("[[_task|fudu]]" + rst)
|
||||
await fudu.finish("[[_task|fudu]]" + rst)
|
||||
|
||||
|
||||
async def get_fudu_img_hash(url, group_id):
|
||||
|
||||
@@ -35,7 +35,7 @@ Config.add_plugin_config(
|
||||
default_value=True,
|
||||
)
|
||||
|
||||
almanac = on_command("原神黄历", priority=5, block=True)
|
||||
almanac = on_command("原神黄历", priority=15, block=True)
|
||||
|
||||
|
||||
ALC_PATH = IMAGE_PATH / "genshin" / "alc"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from pathlib import Path
|
||||
from typing import Tuple, Optional, List
|
||||
from configs.path_config import IMAGE_PATH, TEXT_PATH
|
||||
from PIL.Image import UnidentifiedImageError
|
||||
from utils.message_builder import image
|
||||
from services.log import logger
|
||||
from utils.image_utils import BuildImage
|
||||
@@ -137,7 +137,7 @@ async def download_resource_data(semaphore: Semaphore):
|
||||
asyncio.ensure_future(
|
||||
download_image(
|
||||
img_url,
|
||||
f"{icon_path}/{id_}.png",
|
||||
icon_path / f"{id_}.png",
|
||||
semaphore,
|
||||
True,
|
||||
)
|
||||
@@ -179,7 +179,7 @@ async def download_map_init(semaphore: Semaphore, flag: bool = False):
|
||||
map_url = _map_data["url"]
|
||||
await download_image(
|
||||
map_url,
|
||||
f"{map_path}/{idx}.png",
|
||||
map_path / f"{idx}.png",
|
||||
semaphore,
|
||||
force_flag=flag,
|
||||
)
|
||||
@@ -228,15 +228,14 @@ async def download_resource_type():
|
||||
except (TimeoutError, ConnectTimeout):
|
||||
logger.warning("下载原神资源类型数据超时....")
|
||||
except Exception as e:
|
||||
logger.error(f"载原神资源类型数据超时 {type(e)}:{e}")
|
||||
logger.error(f"载原神资源类型数据错误 {type(e)}:{e}")
|
||||
|
||||
|
||||
# 初始化资源图标
|
||||
def gen_icon(icon: str):
|
||||
def gen_icon(icon: Path):
|
||||
A = BuildImage(0, 0, background=f"{icon_path}/box.png")
|
||||
B = BuildImage(0, 0, background=f"{icon_path}/box_alpha.png")
|
||||
icon_ = icon_path / f"{icon}"
|
||||
icon_img = BuildImage(115, 115, background=icon_)
|
||||
icon_img = BuildImage(115, 115, background=icon)
|
||||
icon_img.circle()
|
||||
B.paste(icon_img, (17, 10), True)
|
||||
B.paste(A, alpha=True)
|
||||
@@ -247,22 +246,22 @@ def gen_icon(icon: str):
|
||||
# 下载图片
|
||||
async def download_image(
|
||||
img_url: str,
|
||||
path: str,
|
||||
path: Path,
|
||||
semaphore: Semaphore,
|
||||
gen_flag: bool = False,
|
||||
force_flag: bool = False,
|
||||
):
|
||||
async with semaphore:
|
||||
try:
|
||||
if not os.path.exists(path) or not is_valid or force_flag:
|
||||
if not path.exists() or not is_valid(path) or force_flag:
|
||||
if await AsyncHttpx.download_file(img_url, path, timeout=10):
|
||||
logger.info(f"下载原神资源图标:{img_url}")
|
||||
if gen_flag:
|
||||
gen_icon(path)
|
||||
else:
|
||||
logger.info(f"下载原神资源图标:{img_url} 失败,等待下次更新...")
|
||||
except UnidentifiedImageError:
|
||||
logger.warning(f"原神图片打开错误..已删除,等待下次更新... file: {path}")
|
||||
except Exception as e:
|
||||
logger.warning(f"原神图片错误..已删除,等待下次更新... file: {path} {type(e)}:{e}")
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ async def get_character(
|
||||
) -> Optional[dict]:
|
||||
try:
|
||||
req = await AsyncHttpx.post(
|
||||
url="https://api-takumi.mihoyo.com/game_record/app/genshin/api/character",
|
||||
url="https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/character",
|
||||
headers={
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"DS": get_ds(
|
||||
@@ -232,7 +232,7 @@ async def get_mys_data(uid: str, mys_id: Optional[str]) -> Optional[List[Dict]]:
|
||||
if mys_id:
|
||||
try:
|
||||
req = await AsyncHttpx.get(
|
||||
url=f"https://api-takumi.mihoyo.com/game_record/card/wapi/getGameRecordCard?uid={mys_id}",
|
||||
url=f"https://api-takumi-record.mihoyo.com/game_record/card/wapi/getGameRecordCard?uid={mys_id}",
|
||||
headers={
|
||||
"DS": get_ds(f"uid={mys_id}"),
|
||||
"x-rpc-app_version": Config.get_config("genshin", "mhyVersion"),
|
||||
|
||||
@@ -300,6 +300,8 @@ def get_country_data_image(world_data_dict: Dict) -> BuildImage:
|
||||
region = BuildImage(790, 267 * (len(world_data_dict) - 1), color="#F9F6F2")
|
||||
height = 0
|
||||
for country in ["蒙德", "龙脊雪山", "璃月", "层岩巨渊", "稻妻", "渊下宫"]:
|
||||
if not world_data_dict.get(country):
|
||||
continue
|
||||
x = BuildImage(790, 250, color="#3A4467")
|
||||
logo = BuildImage(180, 180, background=image_path / "logo" / f"{country}.png")
|
||||
tmp_bk = BuildImage(770, 230, color="#606779")
|
||||
@@ -328,18 +330,19 @@ def get_country_data_image(world_data_dict: Dict) -> BuildImage:
|
||||
f"{world_data_dict['层岩巨渊']['exploration_percentage'] / 10}%",
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
content_bk.text((300, 85), "地下矿区探索", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(570, 85),
|
||||
f"{world_data_dict['层岩巨渊·地下矿区']['exploration_percentage'] / 10}%",
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
content_bk.text((300, 150), "流明石触媒", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(570, 150),
|
||||
f"LV.{world_data_dict['层岩巨渊·地下矿区']['offerings'][0]['level']}",
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
if world_data_dict.get('层岩巨渊·地下矿区'):
|
||||
content_bk.text((300, 85), "地下矿区探索", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(570, 85),
|
||||
f"{world_data_dict['层岩巨渊·地下矿区']['exploration_percentage'] / 10}%",
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
content_bk.text((300, 150), "流明石触媒", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(570, 150),
|
||||
f"LV.{world_data_dict['层岩巨渊·地下矿区']['offerings'][0]['level']}",
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
elif country in ["龙脊雪山"]:
|
||||
content_bk.text((300, 40), "探索", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from nonebot.adapters.onebot.v11 import ActionFailed
|
||||
|
||||
from utils.utils import get_bot, scheduler
|
||||
from utils.message_builder import at
|
||||
from models.group_member_info import GroupInfoUser
|
||||
@@ -178,10 +180,13 @@ async def _remind(user_id: int, uid: str):
|
||||
group_list = await GroupInfoUser.get_user_all_group(user_id)
|
||||
if group_list:
|
||||
group_id = group_list[0]
|
||||
await bot.send_group_msg(
|
||||
group_id=group_id,
|
||||
message=at(user_id) + msg
|
||||
)
|
||||
try:
|
||||
await bot.send_group_msg(
|
||||
group_id=group_id,
|
||||
message=at(user_id) + msg
|
||||
)
|
||||
except ActionFailed as e:
|
||||
logger.error(f"树脂提醒推送发生错误 {type(e)}:{e}")
|
||||
if not next_time:
|
||||
if user_manager.check(uid) and Config.get_config("resin_remind", "AUTO_CLOSE_QUERY_FAIL_RESIN_REMIND"):
|
||||
await Genshin.set_resin_remind(int(uid), False)
|
||||
|
||||
@@ -64,8 +64,8 @@ async def _generate_open_redbag_pic(user_id: int, send_user_nickname: str, amoun
|
||||
head = BuildImage(1000, 980, font_size=30, background=f'{IMAGE_PATH}/prts/redbag_12.png')
|
||||
size = BuildImage(0, 0, font_size=50).getsize(send_user_nickname)
|
||||
# QQ头像
|
||||
ava_bk = BuildImage(100 + size[0], 66, color='white', font_size=50)
|
||||
ava = BuildImage(66, 66, background=BytesIO(await get_user_avatar(user_id)))
|
||||
ava_bk = BuildImage(100 + size[0], 66, is_alpha=True, font_size=50)
|
||||
ava = BuildImage(66, 66, is_alpha=True, background=BytesIO(await get_user_avatar(user_id)))
|
||||
ava_bk.paste(ava)
|
||||
ava_bk.text((100, 7), send_user_nickname)
|
||||
# ava_bk.show()
|
||||
@@ -73,7 +73,7 @@ async def _generate_open_redbag_pic(user_id: int, send_user_nickname: str, amoun
|
||||
head.paste(ava_bk, (int((1000 - ava_bk_w) / 2), 300))
|
||||
# 金额
|
||||
size = BuildImage(0, 0, font_size=150).getsize(amount)
|
||||
price = BuildImage(size[0], size[1], font_size=150)
|
||||
price = BuildImage(size[0], size[1], is_alpha=True, font_size=150)
|
||||
price.text((0, 0), amount, fill=(209, 171, 108))
|
||||
# 金币中文
|
||||
head.paste(price, (int((1000 - size[0]) / 2) - 50, 460))
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
from nonebot import on_message
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||
from .data_source import cancel_all_notice, save_data, get_data, set_data_value
|
||||
from services.log import logger
|
||||
import time
|
||||
|
||||
|
||||
__zx_plugin_name__ = "群聊最后聊天时间记录 [Hidden]"
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
|
||||
|
||||
last_chat = on_message(priority=1, block=False, permission=GROUP)
|
||||
|
||||
|
||||
@last_chat.handle()
|
||||
async def _(event: GroupMessageEvent):
|
||||
time_data = await get_data()
|
||||
set_data_value(event.group_id, time.time())
|
||||
if event.group_id in time_data["_group"]:
|
||||
time_data["_group"].remove(event.group_id)
|
||||
set_data_value("_group", time_data["_group"])
|
||||
for key in time_data.keys():
|
||||
if key not in ["check_time", "_group"]:
|
||||
if key not in time_data["_group"]:
|
||||
if time.time() - time_data[key] > 60 * 60 * 36:
|
||||
await cancel_all_notice(key)
|
||||
time_data["_group"].append(key)
|
||||
set_data_value("_group", time_data["_group"])
|
||||
logger.info(f"GROUP {event.group_id} 因群内发言时间大于36小时被取消全部通知")
|
||||
if time.time() - time_data["check_time"] > 60 * 60 * 1:
|
||||
set_data_value("check_time", time.time())
|
||||
save_data()
|
||||
@@ -1,67 +0,0 @@
|
||||
from configs.path_config import DATA_PATH
|
||||
from utils.utils import get_bot
|
||||
from datetime import datetime
|
||||
import time
|
||||
from services.log import logger
|
||||
from utils.manager import group_manager
|
||||
try:
|
||||
import ujson as json
|
||||
except ModuleNotFoundError:
|
||||
import json
|
||||
|
||||
|
||||
time_data = {}
|
||||
|
||||
|
||||
async def init():
|
||||
global time_data
|
||||
bot = get_bot()
|
||||
gl = await bot.get_group_list()
|
||||
gl = [g["group_id"] for g in gl]
|
||||
data = read_data("group_last_chat_time.json")
|
||||
for g in gl:
|
||||
if not data.get(g):
|
||||
time_data[g] = time.time()
|
||||
if not time_data.get("check_time"):
|
||||
time_data["check_time"] = time.time()
|
||||
if not time_data.get("_group"):
|
||||
time_data["_group"] = []
|
||||
save_data()
|
||||
return time_data
|
||||
|
||||
|
||||
def read_data(file_name: str):
|
||||
try:
|
||||
with open(DATA_PATH / file_name, "r", encoding="utf8") as f:
|
||||
return json.load(f)
|
||||
except (ValueError, FileNotFoundError):
|
||||
return {}
|
||||
|
||||
|
||||
def save_data():
|
||||
with open(DATA_PATH / "group_last_chat_time.json", "w") as f:
|
||||
json.dump(time_data, f, indent=4)
|
||||
logger.info(
|
||||
f'自动存储 group_last_chat_time.json 时间:{str(datetime.now()).split(".")[0]}'
|
||||
)
|
||||
|
||||
|
||||
# 取消全部通知
|
||||
async def cancel_all_notice(group_id):
|
||||
group_id = int(group_id)
|
||||
for command in group_manager.get_task_data():
|
||||
if await group_manager.check_group_task_status(group_id, command):
|
||||
await group_manager.close_group_task(group_id, command)
|
||||
logger.info(f"关闭了 {group_id} 群的全部通知")
|
||||
|
||||
|
||||
async def get_data():
|
||||
global time_data
|
||||
if not time_data:
|
||||
time_data = await init()
|
||||
return time_data
|
||||
|
||||
|
||||
def set_data_value(key, value):
|
||||
global time_data
|
||||
time_data[key] = value
|
||||
@@ -36,7 +36,7 @@ __plugin_settings__ = {
|
||||
"limit_superuser": False,
|
||||
"cmd": ["发送图片"] + Config.get_config("image_management", "IMAGE_DIR_LIST"),
|
||||
}
|
||||
__plugin_resources__ = {"pa": IMAGE_PATH}
|
||||
__plugin_resources__ = {"pa": IMAGE_PATH / "pa"}
|
||||
|
||||
Config.add_plugin_config(
|
||||
"_task",
|
||||
|
||||
+14
-16
@@ -1,8 +1,7 @@
|
||||
from nonebot import on_message, on_command
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from utils.utils import is_number, get_message_img
|
||||
from nonebot.typing import T_State
|
||||
from utils.utils import is_number, get_message_img, get_message_text
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from configs.path_config import DATA_PATH, TEMP_PATH
|
||||
from utils.image_utils import get_img_hash
|
||||
@@ -10,7 +9,7 @@ from services.log import logger
|
||||
from configs.config import NICKNAME, Config
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from nonebot.params import CommandArg, Command
|
||||
from typing import Tuple
|
||||
from typing import Tuple, Dict, Any
|
||||
import time
|
||||
|
||||
try:
|
||||
@@ -57,7 +56,7 @@ mute_setting = on_command(
|
||||
)
|
||||
|
||||
|
||||
def get_data():
|
||||
def get_data() -> Dict[Any, Any]:
|
||||
try:
|
||||
with open(DATA_PATH / "group_mute_data.json", "r", encoding="utf8") as f:
|
||||
data = json.load(f)
|
||||
@@ -72,7 +71,7 @@ def save_data():
|
||||
json.dump(mute_data, f, indent=4)
|
||||
|
||||
|
||||
async def download_img_and_hash(url, group_id):
|
||||
async def download_img_and_hash(url, group_id) -> str:
|
||||
if await AsyncHttpx.download_file(
|
||||
url, TEMP_PATH / f"mute_{group_id}_img.jpg"
|
||||
):
|
||||
@@ -85,9 +84,9 @@ mute_data = get_data()
|
||||
|
||||
|
||||
@mute.handle()
|
||||
async def _(bot: Bot, event: GroupMessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
|
||||
async def _(bot: Bot, event: GroupMessageEvent):
|
||||
group_id = str(event.group_id)
|
||||
msg = arg.extract_plain_text().strip()
|
||||
msg = get_message_text(event.json())
|
||||
img_list = get_message_img(event.json())
|
||||
img_hash = ""
|
||||
for img in img_list:
|
||||
@@ -102,8 +101,6 @@ async def _(bot: Bot, event: GroupMessageEvent, cmd: Tuple[str, ...] = Command()
|
||||
if not mute_dict.get(event.user_id):
|
||||
mute_dict[event.user_id] = {"time": time.time(), "count": 1, "msg": msg}
|
||||
else:
|
||||
if cmd or not msg:
|
||||
return
|
||||
if msg and msg.find(mute_dict[event.user_id]["msg"]) != -1:
|
||||
mute_dict[event.user_id]["count"] += 1
|
||||
else:
|
||||
@@ -123,7 +120,7 @@ async def _(bot: Bot, event: GroupMessageEvent, cmd: Tuple[str, ...] = Command()
|
||||
await bot.set_group_ban(
|
||||
group_id=event.group_id,
|
||||
user_id=event.user_id,
|
||||
duration=mute_data[group_id]["duration"],
|
||||
duration=mute_data[group_id]["duration"] * 60,
|
||||
)
|
||||
await mute.send(f"检测到恶意刷屏,{NICKNAME}要把你关进小黑屋!", at_sender=True)
|
||||
mute_dict[event.user_id]["count"] = 0
|
||||
@@ -137,27 +134,28 @@ async def _(bot: Bot, event: GroupMessageEvent, cmd: Tuple[str, ...] = Command()
|
||||
|
||||
@mute_setting.handle()
|
||||
async def _(event: GroupMessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
|
||||
global mute_data
|
||||
group_id = str(event.group_id)
|
||||
if not mute_data.get(group_id):
|
||||
mute_data[group_id] = {"count": 10, "time": 7, "duration": 0}
|
||||
mute_data[group_id] = {"count": Config.get_config("mute", "MUTE_DEFAULT_COUNT"), "time": Config.get_config("mute", "MUTE_DEFAULT_TIME"), "duration": Config.get_config("mute", "MUTE_DEFAULT_DURATION")}
|
||||
msg = arg.extract_plain_text().strip()
|
||||
if cmd[0] == "刷屏检测设置":
|
||||
await mute_setting.finish(
|
||||
f'最大次数:{mute_data[group_id]["count"]} 次\n'
|
||||
f'规定时间:{mute_data[group_id]["time"]} 秒\n'
|
||||
f'禁言时长:{mute_data[group_id]["duration"] / 60} 分钟\n'
|
||||
f'禁言时长:{mute_data[group_id]["duration"]:.2f} 分钟\n'
|
||||
f"【在规定时间内发送相同消息超过最大次数则禁言\n当禁言时长为0时关闭此功能】"
|
||||
)
|
||||
if not is_number(msg):
|
||||
await mute.finish("设置的参数必须是数字啊!", at_sender=True)
|
||||
if cmd[0] == "设置检测时间":
|
||||
if cmd[0] == "设置刷屏检测时间":
|
||||
mute_data[group_id]["time"] = int(msg)
|
||||
msg += "秒"
|
||||
if cmd[0] == "设置检测次数":
|
||||
if cmd[0] == "设置刷屏检测次数":
|
||||
mute_data[group_id]["count"] = int(msg)
|
||||
msg += " 次"
|
||||
if cmd[0] == "设置禁言时长":
|
||||
mute_data[group_id]["duration"] = int(msg) * 60
|
||||
if cmd[0] == "设置刷屏禁言时长":
|
||||
mute_data[group_id]["duration"] = int(msg)
|
||||
msg += " 分钟"
|
||||
await mute_setting.send(f'刷屏检测:{cmd[0]}为 {msg}')
|
||||
logger.info(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Type
|
||||
from typing import Type, Tuple, Any
|
||||
from nonebot import on_command
|
||||
from nonebot.matcher import Matcher
|
||||
from utils.utils import scheduler, is_number
|
||||
@@ -9,7 +9,7 @@ from nonebot.permission import SUPERUSER
|
||||
import random
|
||||
from nonebot.plugin import MatcherGroup
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from nonebot.params import CommandArg
|
||||
from nonebot.params import CommandArg, RegexGroup
|
||||
from .open_cases_c import (
|
||||
open_case,
|
||||
total_open_statistics,
|
||||
@@ -145,12 +145,12 @@ async def _(event: GroupMessageEvent):
|
||||
)
|
||||
|
||||
|
||||
open_shilian: Type[Matcher] = cases_matcher_group.on_regex("(.*)连开箱(.*?)")
|
||||
open_shilian: Type[Matcher] = cases_matcher_group.on_regex("(.*)连开箱(.*)?")
|
||||
|
||||
|
||||
@open_shilian.handle()
|
||||
async def _(event: GroupMessageEvent, state: T_State):
|
||||
num = state["_matched_groups"][0].strip()
|
||||
async def _(event: GroupMessageEvent, state: T_State, reg_group: Tuple[Any, ...] = RegexGroup()):
|
||||
num, case_name = reg_group
|
||||
if is_number(num) or num_dict.get(num):
|
||||
try:
|
||||
num = num_dict[num]
|
||||
@@ -162,7 +162,6 @@ async def _(event: GroupMessageEvent, state: T_State):
|
||||
await open_shilian.finish("再负开箱就扣你明天开箱数了!", at_sender=True)
|
||||
else:
|
||||
await open_shilian.finish("必须要是数字切不要超过30啊笨蛋!中文也可!", at_sender=True)
|
||||
case_name = state["_matched_groups"][1].strip()
|
||||
case_name = case_name.replace("武器箱", "").strip()
|
||||
if not case_name:
|
||||
case_name = random.choice(cases_name)
|
||||
@@ -212,16 +211,16 @@ update_price = on_command("更新开箱价格", priority=1, permission=SUPERUSER
|
||||
|
||||
|
||||
@update_price.handle()
|
||||
async def _( event: MessageEvent):
|
||||
await update_price.send(await util_get_buff_price(str(event.get_message())))
|
||||
async def _(event: MessageEvent, arg: Message = CommandArg()):
|
||||
await update_price.send(await util_get_buff_price(arg.extract_plain_text().strip() or "狂牙大行动"))
|
||||
|
||||
|
||||
update_img = on_command("更新开箱图片", priority=1, permission=SUPERUSER, block=True)
|
||||
|
||||
|
||||
@update_img.handle()
|
||||
async def _(event: MessageEvent):
|
||||
await update_img.send(await util_get_buff_img(str(event.get_message())))
|
||||
async def _(event: MessageEvent, arg: Message = CommandArg()):
|
||||
await update_img.send(await util_get_buff_img(arg.extract_plain_text().strip() or "狂牙大行动"))
|
||||
|
||||
|
||||
# 重置开箱
|
||||
|
||||
+166
-171
@@ -1,171 +1,166 @@
|
||||
from nonebot import on_message
|
||||
from services.log import logger
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||
from utils.utils import get_message_json, get_local_proxy, is_number, get_message_text
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from bilibili_api import video
|
||||
from utils.message_builder import image
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from utils.image_utils import BuildImage
|
||||
from utils.browser import get_browser
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from configs.config import Config
|
||||
from utils.user_agent import get_user_agent
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import time
|
||||
from bilibili_api import settings
|
||||
import ujson as json
|
||||
|
||||
|
||||
__zx_plugin_name__ = "B站转发解析"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
B站转发解析,解析b站分享信息,支持bv,bilibili链接,b站手机端转发卡片,cv,b23.tv,且5分钟内不解析相同url
|
||||
""".strip()
|
||||
__plugin_des__ = "B站转发解析"
|
||||
__plugin_type__ = ("其他",)
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_task__ = {"bilibili_parse": "b站转发解析"}
|
||||
Config.add_plugin_config(
|
||||
"_task",
|
||||
"DEFAULT_BILIBILI_PARSE",
|
||||
True,
|
||||
help_="被动 B站转发解析 进群默认开关状态",
|
||||
default_value=True,
|
||||
)
|
||||
|
||||
|
||||
if get_local_proxy():
|
||||
settings.proxy = get_local_proxy()
|
||||
|
||||
parse_bilibili_json = on_message(priority=1, permission=GROUP, block=False)
|
||||
|
||||
_tmp = {}
|
||||
|
||||
|
||||
@parse_bilibili_json.handle()
|
||||
async def _(event: GroupMessageEvent):
|
||||
vd_info = None
|
||||
url = None
|
||||
if get_message_json(event.json()):
|
||||
try:
|
||||
data = json.loads(get_message_json(event.json())[0]["data"])
|
||||
except (IndexError, KeyError):
|
||||
data = None
|
||||
if data:
|
||||
# 转发视频
|
||||
if data.get("desc") == "哔哩哔哩":
|
||||
async with aiohttp.ClientSession(
|
||||
headers=get_user_agent()
|
||||
) as session:
|
||||
async with session.get(
|
||||
data["meta"]["detail_1"]["qqdocurl"],
|
||||
proxy=get_local_proxy(),
|
||||
timeout=7,
|
||||
) as response:
|
||||
url = str(response.url).split("?")[0]
|
||||
bvid = url.split("/")[-1]
|
||||
vd_info = await video.Video(bvid=bvid).get_info()
|
||||
# response = await AsyncHttpx.get(
|
||||
# data["meta"]["detail_1"]["qqdocurl"], timeout=7
|
||||
# )
|
||||
# url = str(response.url).split("?")[0]
|
||||
# bvid = url.split("/")[-1]
|
||||
# vd_info = await video.Video(bvid=bvid).get_info()
|
||||
# 转发专栏
|
||||
if (
|
||||
data.get("meta")
|
||||
and data["meta"].get("news")
|
||||
and data["meta"]["news"].get("desc") == "哔哩哔哩专栏"
|
||||
):
|
||||
url = data["meta"]["news"]["jumpUrl"]
|
||||
page = None
|
||||
try:
|
||||
browser = await get_browser()
|
||||
if not browser:
|
||||
return
|
||||
page = await browser.new_page(
|
||||
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
||||
" (KHTML, like Gecko) Chrome/93.0.4530.0 Safari/537.36"
|
||||
)
|
||||
await page.goto(url, wait_until="networkidle", timeout=10000)
|
||||
await page.set_viewport_size({"width": 2560, "height": 1080})
|
||||
await page.click("#app > div")
|
||||
div = await page.query_selector("#app > div")
|
||||
await div.screenshot(
|
||||
path=f"{IMAGE_PATH}/temp/cv_{event.user_id}.png",
|
||||
timeout=100000,
|
||||
)
|
||||
await asyncio.get_event_loop().run_in_executor(
|
||||
None, resize, f"{IMAGE_PATH}/temp/cv_{event.user_id}.png"
|
||||
)
|
||||
await parse_bilibili_json.send(
|
||||
"[[_task|bilibili_parse]]" + image(f"cv_{event.user_id}.png", "temp")
|
||||
)
|
||||
await page.close()
|
||||
logger.info(
|
||||
f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"尝试解析bilibili专栏 {url} 失败 {type(e)}:{e}")
|
||||
if page:
|
||||
await page.close()
|
||||
return
|
||||
# BV
|
||||
if msg := get_message_text(event.json()):
|
||||
if "BV" in msg:
|
||||
index = msg.find("BV")
|
||||
if len(msg[index + 2 :]) >= 10:
|
||||
msg = msg[index : index + 12]
|
||||
url = f"https://www.bilibili.com/video/{msg}"
|
||||
vd_info = await video.Video(bvid=msg).get_info()
|
||||
elif "av" in msg:
|
||||
index = msg.find("av")
|
||||
if len(msg[index + 2 :]) >= 9:
|
||||
msg = msg[index + 2 : index + 11]
|
||||
if is_number(msg):
|
||||
url = f"https://www.bilibili.com/video/{msg}"
|
||||
vd_info = await video.Video(aid=int(msg)).get_info()
|
||||
elif "https://b23.tv" in msg:
|
||||
url = "https://" + msg[msg.find("b23.tv") : msg.find("b23.tv") + 13]
|
||||
res = await AsyncHttpx.get(url, timeout=7)
|
||||
url = str(res.url).split("?")[0]
|
||||
bvid = url.split("/")[-1]
|
||||
vd_info = await video.Video(bvid=bvid).get_info()
|
||||
if vd_info:
|
||||
if (
|
||||
url in _tmp.keys() and time.time() - _tmp[url] > 30
|
||||
) or url not in _tmp.keys():
|
||||
_tmp[url] = time.time()
|
||||
aid = vd_info["aid"]
|
||||
title = vd_info["title"]
|
||||
author = vd_info["owner"]["name"]
|
||||
reply = vd_info["stat"]["reply"] # 回复
|
||||
favorite = vd_info["stat"]["favorite"] # 收藏
|
||||
coin = vd_info["stat"]["coin"] # 投币
|
||||
# like = vd_info['stat']['like'] # 点赞
|
||||
# danmu = vd_info['stat']['danmaku'] # 弹幕
|
||||
date = time.strftime("%Y-%m-%d", time.localtime(vd_info["ctime"]))
|
||||
try:
|
||||
await parse_bilibili_json.send(
|
||||
"[[_task|bilibili_parse]]" +
|
||||
image(vd_info["pic"]) + f"\nav{aid}\n标题:{title}\n"
|
||||
f"UP:{author}\n"
|
||||
f"上传日期:{date}\n"
|
||||
f"回复:{reply},收藏:{favorite},投币:{coin}\n"
|
||||
f"{url}"
|
||||
)
|
||||
except ActionFailed:
|
||||
logger.warning(f"{event.group_id} 发送bilibili解析失败")
|
||||
else:
|
||||
logger.info(
|
||||
f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}"
|
||||
)
|
||||
|
||||
|
||||
def resize(path: str):
|
||||
A = BuildImage(0, 0, background=path, ratio=0.5)
|
||||
A.save(path)
|
||||
# from nonebot import on_message
|
||||
# from services.log import logger
|
||||
# from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||
# from utils.utils import get_message_json, get_local_proxy, is_number, get_message_text
|
||||
# from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
# from utils.message_builder import image
|
||||
# from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
# from utils.image_utils import BuildImage
|
||||
# from utils.browser import get_browser
|
||||
# from configs.path_config import IMAGE_PATH
|
||||
# from utils.http_utils import AsyncHttpx
|
||||
# from configs.config import Config
|
||||
# from utils.user_agent import get_user_agent
|
||||
# import aiohttp
|
||||
# import asyncio
|
||||
# import time
|
||||
# import ujson as json
|
||||
#
|
||||
#
|
||||
# __zx_plugin_name__ = "B站转发解析"
|
||||
# __plugin_usage__ = """
|
||||
# usage:
|
||||
# B站转发解析,解析b站分享信息,支持bv,bilibili链接,b站手机端转发卡片,cv,b23.tv,且5分钟内不解析相同url
|
||||
# """.strip()
|
||||
# __plugin_des__ = "B站转发解析"
|
||||
# __plugin_type__ = ("其他",)
|
||||
# __plugin_version__ = 0.1
|
||||
# __plugin_author__ = "HibiKier"
|
||||
# __plugin_task__ = {"bilibili_parse": "b站转发解析"}
|
||||
# Config.add_plugin_config(
|
||||
# "_task",
|
||||
# "DEFAULT_BILIBILI_PARSE",
|
||||
# True,
|
||||
# help_="被动 B站转发解析 进群默认开关状态",
|
||||
# default_value=True,
|
||||
# )
|
||||
#
|
||||
#
|
||||
# parse_bilibili_json = on_message(priority=1, permission=GROUP, block=False)
|
||||
#
|
||||
# _tmp = {}
|
||||
#
|
||||
#
|
||||
# @parse_bilibili_json.handle()
|
||||
# async def _(event: GroupMessageEvent):
|
||||
# vd_info = None
|
||||
# url = None
|
||||
# if get_message_json(event.json()):
|
||||
# try:
|
||||
# data = json.loads(get_message_json(event.json())[0]["data"])
|
||||
# except (IndexError, KeyError):
|
||||
# data = None
|
||||
# if data:
|
||||
# # 转发视频
|
||||
# if data.get("desc") == "哔哩哔哩":
|
||||
# async with aiohttp.ClientSession(
|
||||
# headers=get_user_agent()
|
||||
# ) as session:
|
||||
# async with session.get(
|
||||
# data["meta"]["detail_1"]["qqdocurl"],
|
||||
# proxy=get_local_proxy(),
|
||||
# timeout=7,
|
||||
# ) as response:
|
||||
# url = str(response.url).split("?")[0]
|
||||
# bvid = url.split("/")[-1]
|
||||
# vd_info = await video.Video(bvid=bvid).get_info()
|
||||
# # response = await AsyncHttpx.get(
|
||||
# # data["meta"]["detail_1"]["qqdocurl"], timeout=7
|
||||
# # )
|
||||
# # url = str(response.url).split("?")[0]
|
||||
# # bvid = url.split("/")[-1]
|
||||
# # vd_info = await video.Video(bvid=bvid).get_info()
|
||||
# # 转发专栏
|
||||
# if (
|
||||
# data.get("meta")
|
||||
# and data["meta"].get("news")
|
||||
# and data["meta"]["news"].get("desc") == "哔哩哔哩专栏"
|
||||
# ):
|
||||
# url = data["meta"]["news"]["jumpUrl"]
|
||||
# page = None
|
||||
# try:
|
||||
# browser = await get_browser()
|
||||
# if not browser:
|
||||
# return
|
||||
# page = await browser.new_page(
|
||||
# user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
||||
# " (KHTML, like Gecko) Chrome/93.0.4530.0 Safari/537.36"
|
||||
# )
|
||||
# await page.goto(url, wait_until="networkidle", timeout=10000)
|
||||
# await page.set_viewport_size({"width": 2560, "height": 1080})
|
||||
# await page.click("#app > div")
|
||||
# div = await page.query_selector("#app > div")
|
||||
# await div.screenshot(
|
||||
# path=f"{IMAGE_PATH}/temp/cv_{event.user_id}.png",
|
||||
# timeout=100000,
|
||||
# )
|
||||
# await asyncio.get_event_loop().run_in_executor(
|
||||
# None, resize, f"{IMAGE_PATH}/temp/cv_{event.user_id}.png"
|
||||
# )
|
||||
# await parse_bilibili_json.send(
|
||||
# "[[_task|bilibili_parse]]" + image(f"cv_{event.user_id}.png", "temp")
|
||||
# )
|
||||
# await page.close()
|
||||
# logger.info(
|
||||
# f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}"
|
||||
# )
|
||||
# except Exception as e:
|
||||
# logger.error(f"尝试解析bilibili专栏 {url} 失败 {type(e)}:{e}")
|
||||
# if page:
|
||||
# await page.close()
|
||||
# return
|
||||
# # BV
|
||||
# if msg := get_message_text(event.json()):
|
||||
# if "BV" in msg:
|
||||
# index = msg.find("BV")
|
||||
# if len(msg[index + 2 :]) >= 10:
|
||||
# msg = msg[index : index + 12]
|
||||
# url = f"https://www.bilibili.com/video/{msg}"
|
||||
# vd_info = await video.Video(bvid=msg).get_info()
|
||||
# elif "av" in msg:
|
||||
# index = msg.find("av")
|
||||
# if len(msg[index + 2 :]) >= 9:
|
||||
# msg = msg[index + 2 : index + 11]
|
||||
# if is_number(msg):
|
||||
# url = f"https://www.bilibili.com/video/{msg}"
|
||||
# vd_info = await video.Video(aid=int(msg)).get_info()
|
||||
# elif "https://b23.tv" in msg:
|
||||
# url = "https://" + msg[msg.find("b23.tv") : msg.find("b23.tv") + 13]
|
||||
# res = await AsyncHttpx.get(url, timeout=7)
|
||||
# url = str(res.url).split("?")[0]
|
||||
# bvid = url.split("/")[-1]
|
||||
# vd_info = await video.Video(bvid=bvid).get_info()
|
||||
# if vd_info:
|
||||
# if (
|
||||
# url in _tmp.keys() and time.time() - _tmp[url] > 30
|
||||
# ) or url not in _tmp.keys():
|
||||
# _tmp[url] = time.time()
|
||||
# aid = vd_info["aid"]
|
||||
# title = vd_info["title"]
|
||||
# author = vd_info["owner"]["name"]
|
||||
# reply = vd_info["stat"]["reply"] # 回复
|
||||
# favorite = vd_info["stat"]["favorite"] # 收藏
|
||||
# coin = vd_info["stat"]["coin"] # 投币
|
||||
# # like = vd_info['stat']['like'] # 点赞
|
||||
# # danmu = vd_info['stat']['danmaku'] # 弹幕
|
||||
# date = time.strftime("%Y-%m-%d", time.localtime(vd_info["ctime"]))
|
||||
# try:
|
||||
# await parse_bilibili_json.send(
|
||||
# "[[_task|bilibili_parse]]" +
|
||||
# image(vd_info["pic"]) + f"\nav{aid}\n标题:{title}\n"
|
||||
# f"UP:{author}\n"
|
||||
# f"上传日期:{date}\n"
|
||||
# f"回复:{reply},收藏:{favorite},投币:{coin}\n"
|
||||
# f"{url}"
|
||||
# )
|
||||
# except ActionFailed:
|
||||
# logger.warning(f"{event.group_id} 发送bilibili解析失败")
|
||||
# else:
|
||||
# logger.info(
|
||||
# f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}"
|
||||
# )
|
||||
#
|
||||
#
|
||||
# def resize(path: str):
|
||||
# A = BuildImage(0, 0, background=path, ratio=0.5)
|
||||
# A.save(path)
|
||||
|
||||
+117
-100
@@ -1,100 +1,117 @@
|
||||
from asyncio.exceptions import TimeoutError
|
||||
from nonebot import on_command
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent, Message, GroupMessageEvent
|
||||
from nonebot.typing import T_State
|
||||
from configs.path_config import TEMP_PATH
|
||||
from services.log import logger
|
||||
from utils.message_builder import image
|
||||
from utils.utils import is_number
|
||||
from utils.manager import withdraw_message_manager
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from nonebot.params import CommandArg, Arg
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
except ModuleNotFoundError:
|
||||
import json
|
||||
|
||||
|
||||
__zx_plugin_name__ = "pid搜索"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
通过 pid 搜索图片
|
||||
指令:
|
||||
p搜 [pid]
|
||||
""".strip()
|
||||
__plugin_des__ = "通过 pid 搜索图片"
|
||||
__plugin_cmd__ = ["p搜 [pid]"]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_settings__ = {
|
||||
"level": 5,
|
||||
"default_status": True,
|
||||
"limit_superuser": False,
|
||||
"cmd": ["p搜"],
|
||||
}
|
||||
|
||||
pid_search = on_command("p搜", aliases={"pixiv搜", "P搜"}, priority=5, block=True)
|
||||
|
||||
url = "https://api.fantasyzone.cc/tu/search.php"
|
||||
|
||||
|
||||
@pid_search.handle()
|
||||
async def _(state: T_State, arg: Message = CommandArg()):
|
||||
pid = arg.extract_plain_text().strip()
|
||||
if pid:
|
||||
state["pid"] = pid
|
||||
|
||||
|
||||
@pid_search.got("pid", prompt="需要查询的图片PID是?")
|
||||
async def _(event: MessageEvent, state: T_State, pid: str = Arg("pid")):
|
||||
if pid in ["取消", "算了"]:
|
||||
await pid_search.finish("已取消操作...")
|
||||
if is_number(pid):
|
||||
await pid_search.reject_arg("pid", "笨蛋,重新输入数!字!")
|
||||
params = {
|
||||
"id": pid,
|
||||
"p": 1,
|
||||
}
|
||||
for _ in range(3):
|
||||
try:
|
||||
data = (await AsyncHttpx.get(url, params=params, timeout=5)).json()
|
||||
except TimeoutError:
|
||||
pass
|
||||
except Exception as e:
|
||||
await pid_search.finish(f"发生了一些错误..{type(e)}:{e}")
|
||||
else:
|
||||
if not data["width"] and not data["height"]:
|
||||
await pid_search.finish(f"没有搜索到 PID:{pid} 的图片", at_sender=True)
|
||||
pid = data["id"]
|
||||
title = data["title"]
|
||||
author = data["userName"]
|
||||
author_id = data["userId"]
|
||||
img_url = data["url"]
|
||||
if not await AsyncHttpx.download_file(
|
||||
img_url, TEMP_PATH / f"pid_search_{event.user_id}.png"
|
||||
):
|
||||
await pid_search.finish("图片下载失败了....", at_sender=True)
|
||||
tmp = ""
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
tmp = "\n【注】将在30后撤回......"
|
||||
msg_id = await pid_search.send(
|
||||
Message(
|
||||
f"title:{title}\n"
|
||||
f"pid:{pid}\n"
|
||||
f"author:{author}\n"
|
||||
f"author_id:{author_id}\n"
|
||||
f'{image(TEMP_PATH / f"pid_search_{event.user_id}.png")}'
|
||||
f"{tmp}"
|
||||
)
|
||||
)
|
||||
logger.info(
|
||||
f"(USER {event.user_id}, "
|
||||
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
|
||||
f" 查询图片 PID:{pid}"
|
||||
)
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
withdraw_message_manager.append((msg_id, 30))
|
||||
break
|
||||
else:
|
||||
await pid_search.finish("图片下载失败了....", at_sender=True)
|
||||
from nonebot import on_command
|
||||
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, Message, GroupMessageEvent
|
||||
from nonebot.typing import T_State
|
||||
|
||||
from configs.config import Config
|
||||
from utils.utils import is_number, change_pixiv_image_links
|
||||
from utils.message_builder import image
|
||||
from services.log import logger
|
||||
from asyncio.exceptions import TimeoutError
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from utils.manager import withdraw_message_manager
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from nonebot.params import CommandArg, Arg
|
||||
|
||||
|
||||
__zx_plugin_name__ = "pid搜索"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
通过 pid 搜索图片
|
||||
指令:
|
||||
p搜 [pid]
|
||||
""".strip()
|
||||
__plugin_des__ = "通过 pid 搜索图片"
|
||||
__plugin_cmd__ = ["p搜 [pid]"]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_settings__ = {
|
||||
"level": 5,
|
||||
"default_status": True,
|
||||
"limit_superuser": False,
|
||||
"cmd": ["p搜"],
|
||||
}
|
||||
|
||||
pid_search = on_command("p搜", aliases={"pixiv搜", "P搜"}, priority=5, block=True)
|
||||
|
||||
|
||||
@pid_search.handle()
|
||||
async def _h(event: MessageEvent, state: T_State, arg: Message = CommandArg()):
|
||||
pid = arg.extract_plain_text().strip()
|
||||
if pid:
|
||||
state["pid"] = pid
|
||||
|
||||
|
||||
headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;"
|
||||
" rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
|
||||
"Referer": "https://www.pixiv.net",
|
||||
}
|
||||
|
||||
|
||||
@pid_search.got("pid", prompt="需要查询的图片PID是?")
|
||||
async def _g(event: MessageEvent, state: T_State, pid: str = Arg("pid")):
|
||||
url = Config.get_config("hibiapi", "HIBIAPI") + "/api/pixiv/"
|
||||
if pid in ["取消", "算了"]:
|
||||
await pid_search.finish("已取消操作...")
|
||||
if not is_number(pid):
|
||||
await pid_search.reject_arg("pid", "笨蛋,重新输入数!字!")
|
||||
for _ in range(3):
|
||||
try:
|
||||
data = (
|
||||
await AsyncHttpx.get(
|
||||
url,
|
||||
params={"id": pid},
|
||||
timeout=5,
|
||||
)
|
||||
).json()
|
||||
except TimeoutError:
|
||||
pass
|
||||
except Exception as e:
|
||||
await pid_search.finish(f"发生了一些错误..{type(e)}:{e}")
|
||||
else:
|
||||
if data.get("error"):
|
||||
await pid_search.finish(data["error"]["user_message"], at_sender=True)
|
||||
data = data["illust"]
|
||||
if not data["width"] and not data["height"]:
|
||||
await pid_search.finish(f"没有搜索到 PID:{pid} 的图片", at_sender=True)
|
||||
pid = data["id"]
|
||||
title = data["title"]
|
||||
author = data["user"]["name"]
|
||||
author_id = data["user"]["id"]
|
||||
image_list = []
|
||||
try:
|
||||
image_list.append(data["meta_single_page"]["original_image_url"])
|
||||
except KeyError:
|
||||
for image_url in data["meta_pages"]:
|
||||
image_list.append(image_url["image_urls"]["original"])
|
||||
for i, img_url in enumerate(image_list):
|
||||
img_url = change_pixiv_image_links(img_url)
|
||||
if not await AsyncHttpx.download_file(
|
||||
img_url,
|
||||
IMAGE_PATH / "temp" / f"pid_search_{event.user_id}_{i}.png",
|
||||
headers=headers,
|
||||
):
|
||||
await pid_search.send("图片下载失败了....", at_sender=True)
|
||||
tmp = ""
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
tmp = "\n【注】将在30后撤回......"
|
||||
msg_id = await pid_search.send(
|
||||
Message(
|
||||
f"title:{title}\n"
|
||||
f"pid:{pid}\n"
|
||||
f"author:{author}\n"
|
||||
f"author_id:{author_id}\n"
|
||||
f'{image(f"pid_search_{event.user_id}_{i}.png", "temp")}'
|
||||
f"{tmp}"
|
||||
)
|
||||
)
|
||||
logger.info(
|
||||
f"(USER {event.user_id}, "
|
||||
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
|
||||
f" 查询图片 PID:{pid}"
|
||||
)
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
withdraw_message_manager.append((msg_id, 30))
|
||||
break
|
||||
else:
|
||||
await pid_search.finish("图片下载失败了....", at_sender=True)
|
||||
|
||||
@@ -20,9 +20,9 @@ try:
|
||||
except ModuleNotFoundError:
|
||||
import json
|
||||
|
||||
if str(platform.system()).lower() == "windows":
|
||||
policy = asyncio.WindowsSelectorEventLoopPolicy()
|
||||
asyncio.set_event_loop_policy(policy)
|
||||
# if str(platform.system()).lower() == "windows":
|
||||
# policy = asyncio.WindowsSelectorEventLoopPolicy()
|
||||
# asyncio.set_event_loop_policy(policy)
|
||||
|
||||
headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;"
|
||||
|
||||
@@ -139,15 +139,14 @@ class OmegaPixivIllusts(db.Model):
|
||||
data = await cls.select('pid').gino.all()
|
||||
return [x[0] for x in data]
|
||||
|
||||
@classmethod
|
||||
async def test(cls, nsfw_tag: int = 1):
|
||||
if nsfw_tag is not None:
|
||||
query = cls.query.where(cls.nsfw_tag == nsfw_tag)
|
||||
else:
|
||||
query = cls.query
|
||||
query = query.where((cls.width - cls.height) < 50)
|
||||
for x in await query.gino.all():
|
||||
print(x.pid)
|
||||
# async def test(cls, nsfw_tag: int = 1):
|
||||
# if nsfw_tag is not None:
|
||||
# query = cls.query.where(cls.nsfw_tag == nsfw_tag)
|
||||
# else:
|
||||
# query = cls.query
|
||||
# query = query.where((cls.width - cls.height) < 50)
|
||||
# for x in await query.gino.all():
|
||||
# print(x.pid)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ usage:
|
||||
查看 pix 好康图库
|
||||
指令:
|
||||
pix ?*[tags]: 通过 tag 获取相似图片,不含tag时随机抽取
|
||||
pid [uid]: 通过uid获取图片
|
||||
pix pid[pid]: 查看图库中指定pid图片
|
||||
""".strip()
|
||||
__plugin_superuser_usage__ = """
|
||||
@@ -49,7 +50,9 @@ __plugin_configs__ = {
|
||||
"value": None,
|
||||
"help": "单次发送的图片数量达到指定值时转发为合并消息",
|
||||
"default_value": None,
|
||||
}
|
||||
},
|
||||
"ALLOW_GROUP_SETU": {"value": False, "help": "允许非超级用户使用-s参数", "default_value": False},
|
||||
"ALLOW_GROUP_R18": {"value": False, "help": "允许非超级用户使用-r参数", "default_value": False},
|
||||
}
|
||||
|
||||
|
||||
@@ -80,10 +83,13 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
nsfw_tag = 2
|
||||
else:
|
||||
nsfw_tag = 0
|
||||
if nsfw_tag != 0 and str(event.user_id) not in bot.config.superusers:
|
||||
await pix.finish("你不能看这些噢,这些都是是留给管理员看的...")
|
||||
if n := len(x) == 1 and is_number(x[0]):
|
||||
num = int(x[-1])
|
||||
if str(event.user_id) not in bot.config.superusers:
|
||||
if (nsfw_tag == 1 and not Config.get_config("pix", "ALLOW_GROUP_SETU")) or (
|
||||
nsfw_tag == 2 and not Config.get_config("pix", "ALLOW_GROUP_R18")
|
||||
):
|
||||
await pix.finish("你不能看这些噢,这些都是是留给管理员看的...")
|
||||
if n := len(x) == 1 and is_number(x[0]) and int(x[0]) < 100:
|
||||
num = int(x[0])
|
||||
keyword = ""
|
||||
elif n > 1:
|
||||
if is_number(x[-1]):
|
||||
|
||||
@@ -44,6 +44,8 @@ usage:
|
||||
搜图 樱岛麻衣
|
||||
搜图 樱岛麻衣 5
|
||||
搜图 樱岛麻衣 5 r18
|
||||
搜图 樱岛麻衣#1000users 5
|
||||
【多个关键词用#分割】
|
||||
【默认为 热度排序】
|
||||
【注意空格!!】【在线搜索会较慢】【数量可能不符?可能该页数量不够,也可能被R-18屏蔽】
|
||||
""".strip()
|
||||
@@ -69,6 +71,11 @@ __plugin_configs__ = {
|
||||
"value": 20,
|
||||
"help": "作品最大页数限制,超过的作品会被略过",
|
||||
"default_value": 20
|
||||
},
|
||||
"ALLOW_GROUP_R18": {
|
||||
"value": False,
|
||||
"help": "允许群聊中使用 r18 参数",
|
||||
"default_value": False
|
||||
}
|
||||
}
|
||||
Config.add_plugin_config(
|
||||
@@ -152,7 +159,7 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
msg = arg.extract_plain_text().strip()
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
if "r18" in msg.lower():
|
||||
if "r18" in msg.lower() and not Config.get_config("pixiv_rank_search", "ALLOW_GROUP_R18"):
|
||||
await pixiv_keyword.finish("(脸红#) 你不会害羞的 八嘎!", at_sender=True)
|
||||
r18 = 0 if "r18" in msg else 1
|
||||
msg = msg.replace("r18", "").strip().split()
|
||||
@@ -161,8 +168,8 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
info_list = None
|
||||
num = 10
|
||||
page = 1
|
||||
if (n := len(msg)) == 1:
|
||||
keyword = msg[0]
|
||||
if (n := len(msg)) > 0:
|
||||
keyword = msg[0].replace("#"," ")
|
||||
if n > 1:
|
||||
if not is_number(msg[1]):
|
||||
await pixiv_keyword.finish("图片数量必须是数字!", at_sender=True)
|
||||
|
||||
@@ -8,10 +8,10 @@ from services.log import logger
|
||||
from pathlib import Path
|
||||
import platform
|
||||
|
||||
if platform.system() == "Windows":
|
||||
import asyncio
|
||||
|
||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
# if platform.system() == "Windows":
|
||||
# import asyncio
|
||||
#
|
||||
# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
|
||||
|
||||
headers = {
|
||||
|
||||
@@ -62,12 +62,12 @@ async def _poke_event(event: PokeNotifyEvent):
|
||||
rst = "气死我了!"
|
||||
await poke_.finish(rst + random.choice(poke__reply), at_sender=True)
|
||||
rand = random.random()
|
||||
if rand <= 0.3:
|
||||
path = random.choice(["luoli", "meitu"])
|
||||
path = random.choice(["luoli", "meitu"])
|
||||
if rand <= 0.3 and len(os.listdir(IMAGE_PATH / "image_management" / path)) > 0:
|
||||
index = random.randint(0, len(os.listdir(IMAGE_PATH / "image_management" / path)))
|
||||
result = f"id:{index}" + image(f"{index}.jpg", "image_management/" + path)
|
||||
await poke_.send(result)
|
||||
logger.info(f"USER {event.user_id} 戳了戳我 回复: {result} \n {result}")
|
||||
logger.info(f"USER {event.user_id} 戳了戳我 回复: {result} {result}")
|
||||
elif 0.3 < rand < 0.6:
|
||||
voice = random.choice(os.listdir(RECORD_PATH / "dinggong"))
|
||||
result = record(voice, "dinggong")
|
||||
|
||||
@@ -49,6 +49,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State, key_word: str = ArgSt
|
||||
Config.get_config("search_anime", "SEARCH_ANIME_MAX_INFO"),
|
||||
)
|
||||
if anime_report:
|
||||
if isinstance(anime_report, str):
|
||||
await search_anime.finish(anime_report)
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
mes_list = custom_forward_msg(anime_report, bot.self_id)
|
||||
await bot.send_group_forward_msg(group_id=event.group_id, messages=mes_list)
|
||||
|
||||
@@ -3,18 +3,18 @@ import feedparser
|
||||
from urllib import parse
|
||||
from services.log import logger
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from typing import List
|
||||
from typing import List, Union
|
||||
import time
|
||||
|
||||
|
||||
async def from_anime_get_info(key_word: str, max_: int) -> List[str]:
|
||||
async def from_anime_get_info(key_word: str, max_: int) -> Union[str, List[str]]:
|
||||
s_time = time.time()
|
||||
repass = ""
|
||||
url = "https://share.dmhy.org/topics/rss/rss.xml?keyword=" + parse.quote(key_word)
|
||||
try:
|
||||
repass = await get_repass(url, max_)
|
||||
except Exception as e:
|
||||
logger.error("Timeout! {}".format(e))
|
||||
logger.error(f"发生了一些错误 {type(e)}:{e}")
|
||||
return "发生了一些错误!"
|
||||
repass.insert(0, f"搜索 {key_word} 结果(耗时 {int(time.time() - s_time)} 秒):\n")
|
||||
return repass
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ async def get_saucenao_image(url: str) -> Union[str, List[str]]:
|
||||
}
|
||||
data = (await AsyncHttpx.post(API_URL_SAUCENAO, params=params)).json()
|
||||
if data["header"]["status"] != 0:
|
||||
return "Saucenao识图失败.."
|
||||
return f"Saucenao识图失败..status:{data['header']['status']}"
|
||||
data = data["results"]
|
||||
data = (
|
||||
data
|
||||
|
||||
@@ -4,7 +4,7 @@ from services.log import logger
|
||||
from models.sign_group_user import SignGroupUser
|
||||
from nonebot.message import run_postprocessor
|
||||
from nonebot.matcher import Matcher
|
||||
from typing import Optional, Type
|
||||
from typing import Optional, Type, Any
|
||||
from gino.exceptions import UninitializedError
|
||||
from utils.utils import (
|
||||
is_number,
|
||||
@@ -33,7 +33,7 @@ from .data_source import (
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from configs.config import Config, NICKNAME
|
||||
from utils.manager import withdraw_message_manager
|
||||
from nonebot.params import CommandArg, Command
|
||||
from nonebot.params import CommandArg, Command, RegexGroup
|
||||
from typing import Tuple
|
||||
import re
|
||||
|
||||
@@ -97,6 +97,7 @@ __plugin_configs__ = {
|
||||
},
|
||||
"TIMEOUT": {"value": 10, "help": "色图下载超时限制(秒)", "default_value": 10},
|
||||
"SHOW_INFO": {"value": True, "help": "是否显示色图的基本信息,如PID等", "default_value": True},
|
||||
"ALLOW_GROUP_R18": {"value": False, "help": "在群聊中启用R18权限", "default_value": False},
|
||||
}
|
||||
Config.add_plugin_config("pixiv", "PIXIV_NGINX_URL", "i.pixiv.re", help_="Pixiv反向代理")
|
||||
|
||||
@@ -148,10 +149,16 @@ async def _(
|
||||
if cmd[0] == "色图r" and isinstance(event, PrivateMessageEvent):
|
||||
r18 = 1
|
||||
num = 10
|
||||
elif cmd[0] == "色图r" and isinstance(event, GroupMessageEvent):
|
||||
await setu.finish(
|
||||
random.choice(["这种不好意思的东西怎么可能给这么多人看啦", "羞羞脸!给我滚出克私聊!", "变态变态变态变态大变态!"])
|
||||
)
|
||||
elif (
|
||||
cmd[0] == "色图r"
|
||||
and isinstance(event, GroupMessageEvent)
|
||||
):
|
||||
if not Config.get_config("send_setu", "ALLOW_GROUP_R18"):
|
||||
await setu.finish(
|
||||
random.choice(["这种不好意思的东西怎么可能给这么多人看啦", "羞羞脸!给我滚出克私聊!", "变态变态变态变态大变态!"])
|
||||
)
|
||||
else:
|
||||
r18 = 1
|
||||
# 有 数字 的话先尝试本地色图id
|
||||
if msg and is_number(msg):
|
||||
setu_list, code = await get_setu_list(int(msg), r18=r18)
|
||||
@@ -190,7 +197,7 @@ num_key = {
|
||||
|
||||
|
||||
@setu_reg.handle()
|
||||
async def _(event: MessageEvent, arg: Message = CommandArg()):
|
||||
async def _(event: MessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
impression = (
|
||||
await SignGroupUser.ensure(event.user_id, event.group_id)
|
||||
@@ -198,28 +205,15 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
|
||||
luox = get_luoxiang(impression)
|
||||
if luox:
|
||||
await setu.finish(luox, at_sender=True)
|
||||
msg = arg.extract_plain_text().strip()
|
||||
num = 1
|
||||
msg = re.search(r"(.*)[份发张个次点](.*)[瑟涩色]图", msg)
|
||||
# 解析 tags 以及 num
|
||||
if msg:
|
||||
num = msg.group(1)
|
||||
tags = msg.group(2)
|
||||
if tags:
|
||||
tags = tags[:-1] if tags[-1] == "的" else tags
|
||||
if num:
|
||||
num = num[-1]
|
||||
if num_key.get(num):
|
||||
num = num_key[num]
|
||||
elif is_number(num):
|
||||
try:
|
||||
num = int(num)
|
||||
except ValueError:
|
||||
num = 1
|
||||
else:
|
||||
num = 1
|
||||
else:
|
||||
return
|
||||
num, tags = reg_group
|
||||
num = num or 1
|
||||
tags = tags[:-1] if tags and tags[-1] == "的" else tags
|
||||
if num_key.get(num):
|
||||
num = num_key[num]
|
||||
try:
|
||||
num = int(num)
|
||||
except ValueError:
|
||||
num = 1
|
||||
await send_setu_handle(setu_reg, event, "色图", tags, num, 0)
|
||||
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ async def update_setu_img(flag: bool = False):
|
||||
f"--> /{path}/{image.local_id}.jpg"
|
||||
)
|
||||
os.rename(
|
||||
TEMP_PATH / f"/{image.local_id}.jpg",
|
||||
TEMP_PATH / f"{image.local_id}.jpg",
|
||||
path / f"{image.local_id}.jpg",
|
||||
)
|
||||
except FileNotFoundError:
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from typing import Tuple, Any
|
||||
|
||||
from .group_user_checkin import (
|
||||
group_user_check_in,
|
||||
group_user_check,
|
||||
@@ -8,9 +10,9 @@ from .group_user_checkin import (
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from utils.message_builder import image
|
||||
from nonebot import on_command
|
||||
from nonebot import on_command, on_regex
|
||||
from utils.utils import scheduler
|
||||
from nonebot.params import CommandArg
|
||||
from nonebot.params import CommandArg, RegexGroup
|
||||
from pathlib import Path
|
||||
from configs.path_config import DATA_PATH
|
||||
from services.log import logger
|
||||
@@ -68,7 +70,7 @@ except (FileNotFoundError, ValueError, TypeError):
|
||||
data = {"0": []}
|
||||
|
||||
|
||||
sign = on_command("签到", priority=5, permission=GROUP, block=True)
|
||||
sign = on_regex("^签到(all)?$", priority=5, permission=GROUP, block=True)
|
||||
my_sign = on_command(
|
||||
cmd="我的签到", aliases={"好感度"}, priority=5, permission=GROUP, block=True
|
||||
)
|
||||
@@ -85,13 +87,13 @@ total_sign_rank = on_command(
|
||||
|
||||
|
||||
@sign.handle()
|
||||
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
async def _(event: GroupMessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
|
||||
nickname = event.sender.card or event.sender.nickname
|
||||
await sign.send(
|
||||
await group_user_check_in(nickname, event.user_id, event.group_id),
|
||||
at_sender=True,
|
||||
)
|
||||
if arg.extract_plain_text().strip() == "all":
|
||||
if reg_group[0]:
|
||||
await check_in_all(nickname, event.user_id)
|
||||
|
||||
|
||||
|
||||
+17
-21
@@ -24,6 +24,8 @@ from io import BytesIO
|
||||
import asyncio
|
||||
import random
|
||||
import nonebot
|
||||
import time
|
||||
import locale
|
||||
import os
|
||||
|
||||
|
||||
@@ -55,13 +57,10 @@ async def get_card(
|
||||
date = datetime.now().date()
|
||||
_type = "view" if is_card_view else "sign"
|
||||
card_file = (
|
||||
Path(SIGN_TODAY_CARD_PATH)
|
||||
/ f"{user_id}_{user.group_id}_{_type}_{date}.png"
|
||||
Path(SIGN_TODAY_CARD_PATH) / f"{user_id}_{user.group_id}_{_type}_{date}.png"
|
||||
)
|
||||
if card_file.exists():
|
||||
return image(
|
||||
f"{user_id}_{user.group_id}_{_type}_{date}.png", "sign/today_card"
|
||||
)
|
||||
return image(f"{user_id}_{user.group_id}_{_type}_{date}.png", "sign/today_card")
|
||||
else:
|
||||
if add_impression == -1:
|
||||
card_file = (
|
||||
@@ -75,9 +74,7 @@ async def get_card(
|
||||
)
|
||||
is_card_view = True
|
||||
ava = BytesIO(await get_user_avatar(user_id))
|
||||
uid = await GroupInfoUser.get_group_member_uid(
|
||||
user.user_qq, user.group_id
|
||||
)
|
||||
uid = await GroupInfoUser.get_group_member_uid(user.user_qq, user.group_id)
|
||||
impression_list = None
|
||||
if is_card_view:
|
||||
_, impression_list, _ = await SignGroupUser.get_all_impression(
|
||||
@@ -128,9 +125,13 @@ def _generate_card(
|
||||
level, next_impression, previous_impression = get_level_and_next_impression(
|
||||
user.impression
|
||||
)
|
||||
interpolation = next_impression - user.impression
|
||||
if level == "9":
|
||||
level = "8"
|
||||
interpolation = 0
|
||||
info_img.text((0, 0), f"· 好感度等级:{level} [{lik2relation[level]}]")
|
||||
info_img.text((0, 20), f"· {NICKNAME}对你的态度:{level2attitude[level]}")
|
||||
info_img.text((0, 40), f"· 距离升级还差 {next_impression - user.impression:.2f} 好感度")
|
||||
info_img.text((0, 40), f"· 距离升级还差 {interpolation:.2f} 好感度")
|
||||
|
||||
bar_bk = BuildImage(220, 20, background=SIGN_RESOURCE_PATH / "bar_white.png")
|
||||
bar = BuildImage(220, 20, background=SIGN_RESOURCE_PATH / "bar.png")
|
||||
@@ -240,7 +241,9 @@ def _generate_card(
|
||||
f"上次签到日期:{'从未' if user.checkin_time_last == datetime.min else user.checkin_time_last.date()}",
|
||||
)
|
||||
today_data.text((0, 25), f"总金币:{gold}")
|
||||
default_setu_prob = Config.get_config("send_setu", "INITIAL_SETU_PROBABILITY") * 100
|
||||
default_setu_prob = (
|
||||
Config.get_config("send_setu", "INITIAL_SETU_PROBABILITY") * 100
|
||||
)
|
||||
today_data.text(
|
||||
(0, 50),
|
||||
f"色图概率:{(default_setu_prob + user.impression if user.impression < 100 else 100):.2f}%",
|
||||
@@ -259,15 +262,12 @@ def _generate_card(
|
||||
today_data.text((0, 25), f"金币 + {gold}")
|
||||
_type = "sign"
|
||||
current_date = datetime.now()
|
||||
week = current_date.isoweekday()
|
||||
current_datetime_str = current_date.strftime("%Y-%m-%d %a %H:%M:%S")
|
||||
data = current_date.date()
|
||||
hour = current_date.hour
|
||||
minute = current_date.minute
|
||||
second = current_date.second
|
||||
data_img = BuildImage(
|
||||
0,
|
||||
0,
|
||||
plain_text=f"时间:{data} {weekdays[week]} {hour}:{minute}:{second}",
|
||||
plain_text=f"时间:{current_datetime_str}",
|
||||
color=(255, 255, 255, 0),
|
||||
font_size=20,
|
||||
)
|
||||
@@ -286,12 +286,8 @@ def _generate_card(
|
||||
bk.paste(today_sign_text_img, (550, 180), True)
|
||||
bk.paste(today_data, (580, 220), True)
|
||||
bk.paste(watermark, (15, 400), True)
|
||||
bk.save(
|
||||
SIGN_TODAY_CARD_PATH / f"{user_id}_{user.group_id}_{_type}_{data}.png"
|
||||
)
|
||||
return image(
|
||||
f"{user_id}_{user.group_id}_{_type}_{data}.png", "sign/today_card"
|
||||
)
|
||||
bk.save(SIGN_TODAY_CARD_PATH / f"{user_id}_{user.group_id}_{_type}_{data}.png")
|
||||
return image(f"{user_id}_{user.group_id}_{_type}_{data}.png", "sign/today_card")
|
||||
|
||||
|
||||
def generate_progress_bar_pic():
|
||||
|
||||
@@ -96,7 +96,7 @@ async def _(
|
||||
global _prefix_count_dict
|
||||
if (
|
||||
matcher.type == "message"
|
||||
and matcher.priority not in [1, 9]
|
||||
and matcher.priority not in [1, 999]
|
||||
and matcher.plugin_name not in ["update_info", "statistics_handle"]
|
||||
):
|
||||
module = matcher.plugin_name
|
||||
|
||||
@@ -16,7 +16,7 @@ async def translate_msg(language_type, msg):
|
||||
}
|
||||
data = (await AsyncHttpx.post(url, data=data)).json()
|
||||
if data["errorCode"] == 0:
|
||||
return f'原文:{msg}\n翻译{data["translateResult"][0][0]["tgt"]}'
|
||||
return f'原文:{msg}\n翻译:{data["translateResult"][0][0]["tgt"]}'
|
||||
return "翻译惜败.."
|
||||
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ import asyncio
|
||||
import platform
|
||||
import os
|
||||
|
||||
if platform.system() == "Windows":
|
||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
# if platform.system() == "Windows":
|
||||
# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
|
||||
|
||||
url = "https://github.com/Mrs4s/go-cqhttp/releases"
|
||||
|
||||
Executable → Regular
+78
-70
@@ -1,70 +1,78 @@
|
||||
from nonebot import on_command
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent, Message
|
||||
from nonebot.params import CommandArg
|
||||
from services.log import logger
|
||||
from ._data_source import get_data, gen_wbtop_pic
|
||||
from utils.utils import is_number
|
||||
from configs.path_config import IMAGE_PATH
|
||||
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 _(event: MessageEvent, arg: Message = CommandArg()):
|
||||
global wbtop_data
|
||||
msg = arg.extract_plain_text().strip()
|
||||
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"]
|
||||
await wbtop.send("开始截取数据...")
|
||||
img = await AsyncPlaywright.screenshot(
|
||||
url,
|
||||
f"{IMAGE_PATH}/temp/wbtop_{event.user_id}.png",
|
||||
"#pl_feedlist_index",
|
||||
wait_time=5
|
||||
)
|
||||
if img:
|
||||
await wbtop.send(img)
|
||||
else:
|
||||
await wbtop.send("发生了一些错误.....")
|
||||
|
||||
from nonebot import on_command
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent, Message
|
||||
from nonebot.params import CommandArg
|
||||
from services.log import logger
|
||||
from .data_source import gen_wbtop_pic,get_wbtop
|
||||
from utils.utils import is_number
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from utils.http_utils import AsyncPlaywright
|
||||
import asyncio
|
||||
import datetime
|
||||
__zx_plugin_name__ = "微博热搜"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
在QQ上吃个瓜
|
||||
指令:
|
||||
微博热搜:发送实时热搜
|
||||
微博热搜 [id]:截图该热搜页面
|
||||
示例:微博热搜 5
|
||||
""".strip()
|
||||
__plugin_des__ = "刚买完瓜,在吃瓜现场"
|
||||
__plugin_cmd__ = ["微博热搜", "微博热搜 [id]"]
|
||||
__plugin_version__ = 0.2
|
||||
__plugin_author__ = "HibiKier & yajiwa"
|
||||
__plugin_settings__ = {
|
||||
"level": 5,
|
||||
"default_status": True,
|
||||
"limit_superuser": False,
|
||||
"cmd": ["微博热搜"],
|
||||
}
|
||||
|
||||
wbtop = on_command("wbtop", aliases={"微博热搜"}, priority=5, block=True)
|
||||
|
||||
|
||||
wbtop_url = "https://weibo.com/ajax/side/hotSearch"
|
||||
|
||||
wbtop_data = []
|
||||
|
||||
|
||||
@wbtop.handle()
|
||||
async def _(event: MessageEvent, arg: Message = CommandArg()):
|
||||
global wbtop_data
|
||||
msg = arg.extract_plain_text().strip()
|
||||
if not wbtop_data or not msg:
|
||||
if wbtop_data:
|
||||
now_time = datetime.datetime.now()
|
||||
if now_time > wbtop_data["time"] + datetime.timedelta(minutes=5):
|
||||
data, code = await get_wbtop(wbtop_url)
|
||||
if code != 200:
|
||||
await wbtop.finish(data, at_sender=True)
|
||||
wbtop_data = data
|
||||
else:
|
||||
data, code = await get_wbtop(wbtop_url)
|
||||
if code != 200:
|
||||
await wbtop.finish(data, at_sender=True)
|
||||
wbtop_data = data
|
||||
if not msg:
|
||||
img = await asyncio.get_event_loop().run_in_executor(
|
||||
None, gen_wbtop_pic, wbtop_data["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["data"][int(msg) - 1]["url"]
|
||||
await wbtop.send("开始截取数据...")
|
||||
img = await AsyncPlaywright.screenshot(
|
||||
url,
|
||||
f"{IMAGE_PATH}/temp/wbtop_{event.user_id}.png",
|
||||
"#pl_feed_main",
|
||||
wait_time=12
|
||||
)
|
||||
if img:
|
||||
await wbtop.send(img)
|
||||
else:
|
||||
await wbtop.send("发生了一些错误.....")
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
from nonebot.adapters.onebot.v11 import MessageSegment
|
||||
from utils.image_utils import BuildImage
|
||||
from utils.message_builder import image
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from typing import Tuple, Union
|
||||
from utils.http_utils import AsyncHttpx
|
||||
import datetime
|
||||
|
||||
|
||||
async def get_wbtop(url: str) -> Tuple[Union[dict, str], int]:
|
||||
"""
|
||||
:param url: 请求链接
|
||||
"""
|
||||
n = 0
|
||||
while True:
|
||||
try:
|
||||
data = []
|
||||
get_response = (await AsyncHttpx.get(url, timeout=20))
|
||||
if get_response.status_code == 200:
|
||||
data_json = get_response.json()['data']['realtime']
|
||||
for data_item in data_json:
|
||||
# 如果是广告,则不添加
|
||||
if 'is_ad' in data_item:
|
||||
continue
|
||||
dic = {
|
||||
'hot_word': data_item['note'],
|
||||
'hot_word_num': str(data_item['num']),
|
||||
'url': 'https://s.weibo.com/weibo?q=%23' + data_item['word'] + '%23',
|
||||
}
|
||||
data.append(dic)
|
||||
if not data:
|
||||
return "没有搜索到...", 997
|
||||
return {'data': data, 'time': datetime.datetime.now()}, 200
|
||||
else:
|
||||
if n > 2:
|
||||
return f'获取失败,请十分钟后再试', 999
|
||||
else:
|
||||
n += 1
|
||||
continue
|
||||
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 = str(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())
|
||||
@@ -12,7 +12,7 @@ __plugin_usage__ = """
|
||||
usage:
|
||||
普普通通的查天气吧
|
||||
指令:
|
||||
[城市]天气/天气[城市]
|
||||
[城市]天气
|
||||
""".strip()
|
||||
__plugin_des__ = "出门要看看天气,不要忘了带伞"
|
||||
__plugin_cmd__ = ["[城市]天气/天气[城市]"]
|
||||
@@ -27,13 +27,13 @@ __plugin_settings__ = {
|
||||
}
|
||||
|
||||
|
||||
weather = on_regex(r".{0,10}?(.*)的?天气.*?.{0,10}", priority=5, block=True)
|
||||
weather = on_regex(r".{0,10}?(.*)的?天气.{0,10}", priority=5, block=True)
|
||||
|
||||
|
||||
@weather.handle()
|
||||
async def _(event: MessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
|
||||
msg = reg_group[0]
|
||||
if msg[-1] != "市":
|
||||
if msg and msg[-1] != "市":
|
||||
msg += "市"
|
||||
city = ""
|
||||
if msg:
|
||||
|
||||
@@ -127,6 +127,8 @@ def _(plugin: Plugin, user: User = Depends(token_to_user)) -> Result:
|
||||
else:
|
||||
if plugin.plugin_settings:
|
||||
for key, value in plugin.plugin_settings:
|
||||
if plugin.plugin_settings.cmd:
|
||||
plugin.plugin_settings.cmd = plugin.plugin_settings.cmd.split(',')
|
||||
plugins2settings_manager.set_module_data(plugin.model, key, value)
|
||||
if plugin.plugin_manager:
|
||||
for key, value in plugin.plugin_manager:
|
||||
|
||||
@@ -45,7 +45,7 @@ class CountLimit(BaseModel):
|
||||
"""
|
||||
max_count: int
|
||||
status: bool
|
||||
limit_type: bool
|
||||
limit_type: str
|
||||
rst: Optional[str]
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent, Message, Bot
|
||||
from nonebot.internal.params import ArgStr, Arg
|
||||
from nonebot.params import CommandArg
|
||||
|
||||
from .data_source import get_anime
|
||||
from nonebot import on_command
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.params import Arg
|
||||
from utils.utils import get_message_img
|
||||
from services.log import logger
|
||||
|
||||
@@ -31,10 +33,10 @@ what_anime = on_command("识番", priority=5, block=True)
|
||||
|
||||
|
||||
@what_anime.handle()
|
||||
async def _(bot: Bot, event: MessageEvent, state: T_State):
|
||||
async def _(bot: Bot, event: MessageEvent, state: T_State, args: Message = CommandArg()):
|
||||
img_url = get_message_img(event.json())
|
||||
if img_url:
|
||||
state["img_url"] = img_url[0]
|
||||
state["img_url"] = args
|
||||
|
||||
|
||||
@what_anime.got("img_url", prompt="虚空识番?来图来图GKD")
|
||||
|
||||
@@ -7,9 +7,8 @@ class WordBankBuilder:
|
||||
def __init__(self, user_id: int, group_id: int, problem: str):
|
||||
self._data = {
|
||||
"user_id": user_id,
|
||||
"group_id": group_id,
|
||||
"problem": problem
|
||||
}
|
||||
"group_id": group_id}
|
||||
self.problem = problem
|
||||
|
||||
def set_placeholder(self, id_: int, placeholder: Union[str, int]):
|
||||
"""
|
||||
@@ -28,13 +27,20 @@ class WordBankBuilder:
|
||||
"""
|
||||
self._data["answer"] = answer
|
||||
|
||||
async def save(self,search_type):
|
||||
def set_problem(self, problem: str):
|
||||
"""
|
||||
设置问题
|
||||
:param problem: 问题
|
||||
"""
|
||||
self._data["problem"] = problem
|
||||
|
||||
async def save(self, search_type):
|
||||
user_id = self._data["user_id"]
|
||||
group_id = self._data["group_id"]
|
||||
problem = self._data["problem"]
|
||||
answer = self._data["answer"]
|
||||
placeholder = self._data.get("placeholder")
|
||||
return await WordBank.add_problem_answer(user_id, group_id, search_type,problem, answer, placeholder)
|
||||
return await WordBank.add_problem_answer(user_id, group_id, search_type, problem, answer, placeholder)
|
||||
|
||||
async def update(self, index):
|
||||
user_id = self._data["user_id"]
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import re
|
||||
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Event
|
||||
from utils.utils import get_message_text
|
||||
from utils.utils import get_message_img_file
|
||||
from nonebot.typing import T_State
|
||||
from .model import WordBank
|
||||
|
||||
|
||||
async def check(bot: Bot, event: Event, state: T_State) -> bool:
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
msg = event.raw_message
|
||||
list_img = get_message_img_file(event.json())
|
||||
if list_img:
|
||||
for img_file in list_img:
|
||||
strinfo = re.compile(f"{img_file},subType=\d*]")
|
||||
msg = strinfo.sub(f'{img_file}]', msg)
|
||||
return bool(
|
||||
await WordBank.check(event.group_id, get_message_text(event.json()), event.is_tome())
|
||||
await WordBank.check(event.group_id, msg, event.is_tome())
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -3,9 +3,11 @@ from ._rule import check
|
||||
from .model import WordBank
|
||||
from configs.path_config import DATA_PATH
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||
from utils.utils import get_message_text
|
||||
from utils.utils import get_message_at, get_message_img
|
||||
from nonebot import on_message
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from utils.utils import get_message_img_file, is_number
|
||||
import re
|
||||
|
||||
__zx_plugin_name__ = "词库问答回复操作 [Hidden]"
|
||||
|
||||
@@ -17,8 +19,14 @@ message_handle = on_message(priority=6, block=True, rule=check)
|
||||
|
||||
@message_handle.handle()
|
||||
async def _(event: GroupMessageEvent):
|
||||
msg = event.raw_message
|
||||
list_img = get_message_img_file(event.json())
|
||||
if list_img:
|
||||
for img_file in list_img:
|
||||
strinfo = re.compile(f"{img_file},subType=\d*]")
|
||||
msg = strinfo.sub(f'{img_file}]', msg)
|
||||
q = await WordBank.check(
|
||||
event.group_id, get_message_text(event.json()), event.is_tome()
|
||||
event.group_id, msg, event.is_tome()
|
||||
)
|
||||
await message_handle.send(await get_one_answer(event, q.format, q.answer))
|
||||
|
||||
@@ -54,3 +62,68 @@ async def get_one_answer(event, format, _answer, all=1):
|
||||
answer += _a[: _a.find(f"[__placeholder_{idx}]")] + "@" + q.user_name
|
||||
_a = _a[_a.find(f"[__placeholder_{idx}]") + len(f"[__placeholder_{idx}]"):]
|
||||
return answer + _a
|
||||
|
||||
|
||||
# 处理单条问题
|
||||
async def get_one_problem(event, problem):
|
||||
strinfo = re.compile(f",subType=\d")
|
||||
problem = strinfo.sub('', problem)
|
||||
_problem = problem
|
||||
_p = problem
|
||||
problem = ''
|
||||
for img in get_message_img(event.json()):
|
||||
_x = img.split("?")[0]
|
||||
r = re.search(rf"\[CQ:image,file=(.*),url={_x}.*?]", _p)
|
||||
if r:
|
||||
_problem = _problem.replace(
|
||||
rf",url={img}",
|
||||
f"",
|
||||
)
|
||||
problem += _p[: _p.find(f"[CQ:image,file={r.group(1)},url={img}]")] + image(img)
|
||||
_p = _p[
|
||||
_p.find(f"[CQ:image,file={r.group(1)},url={img}]") + len(f"[CQ:image,file={r.group(1)},url={img}]"):]
|
||||
for at_ in get_message_at(event.json()):
|
||||
r = re.search(rf"\[CQ:at,qq={at_}]", problem)
|
||||
if r:
|
||||
q = await GroupInfoUser.get_member_info(
|
||||
int(at_), event.group_id)
|
||||
problem += _p[: _p.find(f"[CQ:at,qq={at_}]")] + "@" + q.user_name
|
||||
_p = _p[_p.find(f"[CQ:at,qq={at_}]") + len(f"[CQ:at,qq={at_}]"):]
|
||||
return _problem, problem + _p
|
||||
|
||||
|
||||
# 显示单条数据库问题
|
||||
async def get_one_image_problem(event, problem):
|
||||
path = data_dir / f"{event.group_id}" / "problem"
|
||||
placeholder_list = []
|
||||
idx = 0
|
||||
img_list = re.findall(rf"\[CQ:image,file=(.*?)]", problem)
|
||||
at_list = re.findall(rf"\[CQ:at,qq=(.*?)]", problem)
|
||||
if img_list:
|
||||
for img in img_list:
|
||||
problem = problem.replace(f'[CQ:image,file={img}]', f'[__placeholder_{idx}]', 1)
|
||||
placeholder_list.append([idx, img])
|
||||
idx += 1
|
||||
if at_list:
|
||||
for ats in at_list:
|
||||
problem = problem.replace(f'[CQ:at,qq={ats}]', f'[__placeholder_{idx}]', 1)
|
||||
placeholder_list.append([idx, ats])
|
||||
idx += 1
|
||||
_p = problem
|
||||
problem = ''
|
||||
if not placeholder_list:
|
||||
problem = _p
|
||||
return problem
|
||||
else:
|
||||
for idx, placeholder in placeholder_list:
|
||||
if is_number(placeholder):
|
||||
q = await GroupInfoUser.get_member_info(
|
||||
int(placeholder), event.group_id)
|
||||
problem += _p[: _p.find(f"[__placeholder_{idx}]")] + "@" + q.user_name
|
||||
else:
|
||||
problem += _p[: _p.find(f"[__placeholder_{idx}]")] + image(
|
||||
path / f"{placeholder}.jpg"
|
||||
)
|
||||
_p = _p[_p.find(f"[__placeholder_{idx}]") + len(f"[__placeholder_{idx}]"):]
|
||||
|
||||
return problem + _p
|
||||
|
||||
@@ -108,11 +108,6 @@ class WordBank(db.Model):
|
||||
:param group_id: 群号
|
||||
:param problem: 问题
|
||||
"""
|
||||
if problem.startswith("id:"):
|
||||
problem_index = int(problem.split(":")[-1])
|
||||
q = await cls.get_group_all_problem(group_id)
|
||||
if len(q) > problem_index:
|
||||
problem = q[problem_index]
|
||||
q = await cls.query.where(
|
||||
(cls.group_id == group_id) & (cls.problem == problem)
|
||||
).gino.all()
|
||||
|
||||
@@ -6,7 +6,7 @@ from utils.http_utils import AsyncHttpx
|
||||
from ._data_source import WordBankBuilder
|
||||
from utils.message_builder import image
|
||||
from utils.image_utils import text2image
|
||||
from .message_handle import get_one_answer
|
||||
from .message_handle import get_one_answer, get_one_problem, get_one_image_problem
|
||||
from .model import WordBank
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
Bot,
|
||||
@@ -19,6 +19,7 @@ import random
|
||||
import os
|
||||
import re
|
||||
from configs.config import NICKNAME, Config
|
||||
from models.group_member_info import GroupInfoUser
|
||||
|
||||
__zx_plugin_name__ = "词库问答 [Admin]"
|
||||
__plugin_usage__ = """
|
||||
@@ -26,7 +27,7 @@ usage:
|
||||
对指定问题的随机回答,对相同问题可以设置多个不同回答
|
||||
删除词条后每个词条的id可能会变化,请查看后再删除
|
||||
指令:
|
||||
添加词条 ?[模糊/关键字]...答...:添加问答词条,可重复添加相同问题的不同回答
|
||||
添加词条 ?[模糊/关键字|词]...答...:添加问答词条,可重复添加相同问题的不同回答
|
||||
删除词条 [问题/下标] ?[下标]:删除指定词条指定或全部回答
|
||||
修改词条 [问题/下标] ?[下标/新回答] [新回答]:修改指定词条指定回答默认修改为第一条
|
||||
查看词条 ?[问题/下标]:查看全部词条或对应词条回答
|
||||
@@ -48,8 +49,8 @@ __plugin_cmd__ = [
|
||||
"修改词条 [问题/下标] ?[下标/新回答] [新回答]",
|
||||
"查看词条 ?[问题/下标]",
|
||||
]
|
||||
__plugin_version__ = 0.2
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_version__ = 0.3
|
||||
__plugin_author__ = "HibiKier & yajiwa"
|
||||
__plugin_settings__ = {
|
||||
"admin_level": Config.get_config("word_bank", "WORD_BANK_LEVEL [LEVEL]"),
|
||||
"cmd": ["词库问答", "添加词条", "删除词条", "修改词条", "查看词条"],
|
||||
@@ -87,21 +88,21 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State, arg: Message = C
|
||||
else:
|
||||
_problem = problem
|
||||
search_type = 0
|
||||
if re.search("^关键字(.*)", msg):
|
||||
if re.search("^关键字|词(.*)", msg):
|
||||
search_type = 1
|
||||
elif re.search("^模糊(.*)", msg):
|
||||
search_type = 2
|
||||
_builder = await get__builder(event, _problem, answer, idx)
|
||||
if await _builder.save(search_type):
|
||||
logger.info(f"已保存词条 问:{problem} 答:{msg}")
|
||||
await add_word.send(f"已保存词条:{problem}")
|
||||
logger.info(f"已保存词条 问:{_builder.problem} 答:{msg}")
|
||||
await add_word.send("已保存词条:" + _builder.problem)
|
||||
else:
|
||||
await delete_word.send("保存失败,可能是回答重复")
|
||||
|
||||
|
||||
@delete_word.handle()
|
||||
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
msg = arg.extract_plain_text().strip()
|
||||
msg = str(arg).strip()
|
||||
if not msg:
|
||||
await delete_word.finish("此命令之后需要跟随指定词条,通过“显示词条“查看")
|
||||
index = None
|
||||
@@ -119,17 +120,18 @@ async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
if p:
|
||||
problem = p[int(x)]
|
||||
try:
|
||||
_problem, problem = await get_one_problem(event, problem)
|
||||
if answer := await WordBank.delete_problem_answer(
|
||||
event.user_id, event.group_id, problem, index
|
||||
event.user_id, event.group_id, _problem, index
|
||||
):
|
||||
await delete_word.send(f"删除词条成功:{problem}\n回答:\n{answer}")
|
||||
await delete_word.send("删除词条成功:" + problem + f"\n回答:\n{answer}")
|
||||
logger.info(
|
||||
f"(USER {event.user_id}, GROUP "
|
||||
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
|
||||
f" 删除词条: {problem}"
|
||||
)
|
||||
else:
|
||||
await delete_word.send(f"删除词条:{problem} 失败,可能该词条不存在")
|
||||
await delete_word.send("删除词条:" + problem + "失败,可能该词条不存在")
|
||||
except IndexError:
|
||||
await delete_word.send("指定下标错误...请通过查看词条来确定..")
|
||||
|
||||
@@ -165,21 +167,21 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
|
||||
try:
|
||||
if await _builder.update(index):
|
||||
await update_word.send(f"修改词条成功:{problem}")
|
||||
await update_word.send(f"修改词条成功:" + _builder.problem)
|
||||
logger.info(
|
||||
f"(USER {event.user_id}, GROUP "
|
||||
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
|
||||
f" 修改词条: {problem}"
|
||||
)
|
||||
else:
|
||||
await update_word.send(f"修改词条:{problem} 失败,可能该词条不存在")
|
||||
await update_word.send(f"修改词条:" + _builder.problem + f"失败,可能该词条不存在")
|
||||
except IndexError:
|
||||
await update_word.send("指定下标错误...请通过查看词条来确定..")
|
||||
|
||||
|
||||
@show_word.handle()
|
||||
async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
msg = arg.extract_plain_text().strip()
|
||||
msg = str(arg).strip()
|
||||
if not msg:
|
||||
_problem_list = await WordBank.get_group_all_problem(event.group_id)
|
||||
if not _problem_list:
|
||||
@@ -225,17 +227,28 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
)
|
||||
)
|
||||
else:
|
||||
_answer_list = await WordBank.get_group_all_answer(event.group_id, msg)
|
||||
_answer_list = []
|
||||
if msg.startswith("id:"):
|
||||
x = msg.split(":")[-1]
|
||||
if not is_number(x) or int(x) < 0:
|
||||
return await delete_word.finish("id必须为数字且符合规范!")
|
||||
p = await WordBank.get_group_all_problem(event.group_id)
|
||||
if p:
|
||||
_problem = p[int(x)]
|
||||
_answer_list = await WordBank.get_group_all_answer(event.group_id, _problem)
|
||||
msg += '问' + await get_one_image_problem(event, _problem)
|
||||
else:
|
||||
_problem, msg = await get_one_problem(event, msg)
|
||||
_answer_list = await WordBank.get_group_all_answer(event.group_id, _problem)
|
||||
if not _answer_list:
|
||||
await show_word.send("未收录该词条...")
|
||||
|
||||
else:
|
||||
|
||||
# 解析图片和@
|
||||
_answer_img_nu_list = [await get_one_answer(event, format, answer, 0) for answer, format in _answer_list]
|
||||
word_nu = len(_answer_img_nu_list)
|
||||
img_nu = 0
|
||||
answer = f"词条 {msg} 回答:"
|
||||
answer = "词条" + msg + "回答:"
|
||||
for i, x, in enumerate(_answer_img_nu_list):
|
||||
r = re.findall(rf"\[CQ:image,file=", str(x))
|
||||
if r:
|
||||
@@ -254,15 +267,25 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
|
||||
async def get__builder(event, _problem, answer, idx):
|
||||
(data_dir / f"{event.group_id}").mkdir(exist_ok=True, parents=True)
|
||||
(data_dir / f"{event.group_id}" / "problem").mkdir(exist_ok=True, parents=True)
|
||||
_builder = WordBankBuilder(event.user_id, event.group_id, _problem)
|
||||
problem = ''
|
||||
_p = _problem
|
||||
for at_ in get_message_at(event.json()):
|
||||
r = re.search(rf"\[CQ:at,qq={at_}]", answer)
|
||||
if r:
|
||||
answer = answer.replace(f"[CQ:at,qq={at_}]", f"[__placeholder_{idx}]", 1)
|
||||
_builder.set_placeholder(idx, at_)
|
||||
idx += 1
|
||||
r_problem = re.search(rf"\[CQ:at,qq={at_}]", _problem)
|
||||
if r_problem:
|
||||
q = await GroupInfoUser.get_member_info(
|
||||
int(at_), event.group_id)
|
||||
problem += _p[: _p.find(f"[CQ:at,qq={at_}]")] + "@" + q.user_name
|
||||
_p = _p[_p.find(f"[CQ:at,qq={at_}]") + len(f"[CQ:at,qq={at_}]"):]
|
||||
for img in get_message_img(event.json()):
|
||||
_x = img.split("?")[0]
|
||||
_x_list = img.split("?")
|
||||
r = re.search(rf"\[CQ:image,file=(.*),url={_x}.*?]", answer)
|
||||
if r:
|
||||
rand = random.randint(1, 10000) + random.randint(1, 114514)
|
||||
@@ -270,16 +293,26 @@ async def get__builder(event, _problem, answer, idx):
|
||||
if f"__placeholder_{rand}_{idx}.jpg" not in os.listdir(data_dir / f"{event.group_id}"):
|
||||
break
|
||||
rand = random.randint(1, 10000) + random.randint(1, 114514)
|
||||
for i in range(3):
|
||||
answer = answer.replace(f",subType={i}", "")
|
||||
answer = answer.replace(
|
||||
rf"[CQ:image,file={r.group(1)},url={img}]",
|
||||
f"[__placeholder_{idx}]",
|
||||
)
|
||||
strinfo = re.compile(f"\[CQ:image,file={r.group(1)},url={_x_list[0]}\?{_x_list[1]},subType=\d*?]")
|
||||
answer = strinfo.sub(f"[__placeholder_{idx}]", answer)
|
||||
await AsyncHttpx.download_file(
|
||||
img, data_dir / f"{event.group_id}" / f"__placeholder_{rand}_{idx}.jpg"
|
||||
)
|
||||
_builder.set_placeholder(idx, f"__placeholder_{rand}_{idx}.jpg")
|
||||
idx += 1
|
||||
r_problem = re.search(rf"\[CQ:image,file=(.*?),url={_x}.*?]", _p)
|
||||
if r_problem:
|
||||
strinfo = re.compile(f",url={_x_list[0]}\?{_x_list[1]},subType=\d*?]")
|
||||
_problem = strinfo.sub(f"]", _problem)
|
||||
_p = strinfo.sub(f"]", _p)
|
||||
problem += _p[: _p.find(f"[CQ:image,file={r_problem.group(1)}]")] + image(img)
|
||||
_p = _p[_p.find(f"[CQ:image,file={r_problem.group(1)}]") + len(f"[CQ:image,file={r_problem.group(1)}]"):]
|
||||
problem_img = r_problem.group(1)
|
||||
if f"{problem_img}.jpg" not in os.listdir(data_dir / f"{event.group_id}" / f"problem"):
|
||||
await AsyncHttpx.download_file(
|
||||
img, data_dir / f"{event.group_id}" / f"problem" / f"{problem_img}.jpg"
|
||||
)
|
||||
_builder.set_answer(answer)
|
||||
_builder.set_problem(_problem)
|
||||
_builder.problem = problem + _p
|
||||
return _builder
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
import re
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Tuple, Union
|
||||
import pytz
|
||||
from nonebot import on_command, get_driver
|
||||
from nonebot.adapters.onebot.v11 import Message, MessageSegment
|
||||
from nonebot.adapters.onebot.v11.event import GroupMessageEvent
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.params import Arg, Command, CommandArg, Depends
|
||||
from nonebot.typing import T_State
|
||||
from .data_source import draw_word_cloud, get_list_msg
|
||||
from configs.config import Config
|
||||
|
||||
__zx_plugin_name__ = "词云"
|
||||
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
词云
|
||||
指令:
|
||||
今日词云:获取今天的词云
|
||||
昨日词云:获取昨天的词云
|
||||
本周词云:获取本周词云
|
||||
本月词云:获取本月词云
|
||||
年度词云:获取年度词云
|
||||
|
||||
历史词云(支持 ISO8601 格式的日期与时间,如 2022-02-22T22:22:22)
|
||||
获取某日的词云
|
||||
历史词云 2022-01-01
|
||||
获取指定时间段的词云
|
||||
历史词云
|
||||
示例:历史词云 2022-01-01~2022-02-22
|
||||
示例:历史词云 2022-02-22T11:11:11~2022-02-22T22:22:22
|
||||
|
||||
如果想要获取自己的发言,可在命令前添加 我的
|
||||
示例:我的今日词云
|
||||
""".strip()
|
||||
__plugin_des__ = "词云"
|
||||
__plugin_cmd__ = ["今日词云", "昨日词云", "本周词云"]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "yajiwa"
|
||||
__plugin_settings__ = {
|
||||
"level": 5,
|
||||
"default_status": True,
|
||||
"limit_superuser": False,
|
||||
"cmd": __plugin_cmd__,
|
||||
}
|
||||
wordcloud_cmd = on_command(
|
||||
"wordcloud",
|
||||
aliases={
|
||||
"词云",
|
||||
"今日词云",
|
||||
"昨日词云",
|
||||
"本周词云",
|
||||
"本月词云",
|
||||
"年度词云",
|
||||
"历史词云",
|
||||
"我的今日词云",
|
||||
"我的昨日词云",
|
||||
"我的本周词云",
|
||||
"我的本月词云",
|
||||
"我的年度词云",
|
||||
"我的历史词云",
|
||||
}, block=True, priority=5
|
||||
)
|
||||
Config.add_plugin_config(
|
||||
"word_clouds",
|
||||
"WORD_CLOUDS_TEMPLATE",
|
||||
1,
|
||||
help_="词云模板 参1:图片生成,默认使用真寻图片,可在项目路径resources/image/wordcloud下配置图片,多张则随机 | 参2/其他:黑底图片"
|
||||
)
|
||||
|
||||
|
||||
def parse_datetime(key: str):
|
||||
"""解析数字,并将结果存入 state 中"""
|
||||
|
||||
async def _key_parser(
|
||||
matcher: Matcher,
|
||||
state: T_State,
|
||||
input_: Union[datetime, Message] = Arg(key),
|
||||
):
|
||||
if isinstance(input_, datetime):
|
||||
return
|
||||
|
||||
plaintext = input_.extract_plain_text()
|
||||
try:
|
||||
state[key] = get_datetime_fromisoformat_with_timezone(plaintext)
|
||||
except ValueError:
|
||||
await matcher.reject_arg(key, "请输入正确的日期,不然我没法理解呢!")
|
||||
|
||||
return _key_parser
|
||||
|
||||
|
||||
def get_datetime_now_with_timezone() -> datetime:
|
||||
"""获取当前时间,并包含时区信息"""
|
||||
return datetime.now().astimezone()
|
||||
|
||||
|
||||
def get_datetime_fromisoformat_with_timezone(date_string: str) -> datetime:
|
||||
"""从 iso8601 格式字符串中获取时间,并包含时区信息"""
|
||||
return datetime.fromisoformat(date_string).astimezone()
|
||||
|
||||
|
||||
@wordcloud_cmd.handle()
|
||||
async def handle_first_receive(
|
||||
event: GroupMessageEvent,
|
||||
state: T_State,
|
||||
commands: Tuple[str, ...] = Command(),
|
||||
args: Message = CommandArg(),
|
||||
):
|
||||
command = commands[0]
|
||||
|
||||
if command.startswith("我的"):
|
||||
state["my"] = True
|
||||
command = command[2:]
|
||||
else:
|
||||
state["my"] = False
|
||||
|
||||
if command == "今日词云":
|
||||
dt = get_datetime_now_with_timezone()
|
||||
state["start"] = dt.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
state["stop"] = dt
|
||||
elif command == "昨日词云":
|
||||
dt = get_datetime_now_with_timezone()
|
||||
state["stop"] = dt.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
state["start"] = state["stop"] - timedelta(days=1)
|
||||
elif command == "本周词云":
|
||||
dt = get_datetime_now_with_timezone()
|
||||
state["start"] = dt.replace(
|
||||
hour=0, minute=0, second=0, microsecond=0
|
||||
) - timedelta(days=dt.weekday())
|
||||
state["stop"] = dt
|
||||
elif command == "本月词云":
|
||||
dt = get_datetime_now_with_timezone()
|
||||
state["start"] = dt.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
|
||||
state["stop"] = dt
|
||||
elif command == "年度词云":
|
||||
dt = get_datetime_now_with_timezone()
|
||||
state["start"] = dt.replace(
|
||||
month=1, day=1, hour=0, minute=0, second=0, microsecond=0
|
||||
)
|
||||
state["stop"] = dt
|
||||
elif command == "历史词云":
|
||||
plaintext = args.extract_plain_text().strip()
|
||||
match = re.match(r"^(.+?)(?:~(.+))?$", plaintext)
|
||||
if match:
|
||||
start = match.group(1)
|
||||
stop = match.group(2)
|
||||
try:
|
||||
state["start"] = get_datetime_fromisoformat_with_timezone(start)
|
||||
if stop:
|
||||
state["stop"] = get_datetime_fromisoformat_with_timezone(stop)
|
||||
else:
|
||||
# 如果没有指定结束日期,则认为是指查询这一天的词云
|
||||
state["start"] = state["start"].replace(
|
||||
hour=0, minute=0, second=0, microsecond=0
|
||||
)
|
||||
state["stop"] = state["start"] + timedelta(days=1)
|
||||
except ValueError:
|
||||
await wordcloud_cmd.finish("请输入正确的日期,不然我没法理解呢!")
|
||||
else:
|
||||
await wordcloud_cmd.finish()
|
||||
|
||||
|
||||
@wordcloud_cmd.got(
|
||||
"start",
|
||||
prompt="请输入你要查询的起始日期(如 2022-01-01)",
|
||||
parameterless=[Depends(parse_datetime("start"))],
|
||||
)
|
||||
@wordcloud_cmd.got(
|
||||
"stop",
|
||||
prompt="请输入你要查询的结束日期(如 2022-02-22)",
|
||||
parameterless=[Depends(parse_datetime("stop"))],
|
||||
)
|
||||
async def handle_message(
|
||||
event: GroupMessageEvent,
|
||||
start: datetime = Arg(),
|
||||
stop: datetime = Arg(),
|
||||
my: bool = Arg(),
|
||||
):
|
||||
# 是否只查询自己的记录
|
||||
if my:
|
||||
user_id = int(event.user_id)
|
||||
else:
|
||||
user_id = None
|
||||
# 将时间转换到 东八 时区
|
||||
messages = await get_list_msg(user_id, int(event.group_id),
|
||||
days=(start.astimezone(pytz.timezone("Asia/Shanghai")),
|
||||
stop.astimezone(pytz.timezone("Asia/Shanghai"))))
|
||||
if messages:
|
||||
image_bytes = await draw_word_cloud(messages, get_driver().config)
|
||||
if image_bytes:
|
||||
await wordcloud_cmd.finish(MessageSegment.image(image_bytes), at_sender=my)
|
||||
else:
|
||||
await wordcloud_cmd.finish("生成词云失败", at_sender=my)
|
||||
else:
|
||||
await wordcloud_cmd.finish("没有获取到词云数据", at_sender=my)
|
||||
@@ -0,0 +1,129 @@
|
||||
import asyncio
|
||||
import os
|
||||
import random
|
||||
import jieba.analyse
|
||||
import re
|
||||
from typing import List
|
||||
from PIL import Image as IMG
|
||||
import jieba
|
||||
from emoji import replace_emoji # type: ignore
|
||||
from wordcloud import WordCloud, ImageColorGenerator
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from io import BytesIO
|
||||
from configs.path_config import IMAGE_PATH, FONT_PATH
|
||||
from services import logger
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from models.chat_history import ChatHistory
|
||||
from configs.config import Config
|
||||
|
||||
|
||||
async def pre_precess(msg: List[str], config) -> str:
|
||||
return await asyncio.get_event_loop().run_in_executor(
|
||||
None, _pre_precess, msg, config
|
||||
)
|
||||
|
||||
|
||||
def _pre_precess(msg: List[str], config) -> str:
|
||||
"""对消息进行预处理"""
|
||||
# 过滤掉命令
|
||||
command_start = tuple([i for i in config.command_start if i])
|
||||
msg = " ".join([m for m in msg if not m.startswith(command_start)])
|
||||
|
||||
# 去除网址
|
||||
msg = re.sub(r"https?://[\w/:%#\$&\?\(\)~\.=\+\-]+", "", msg)
|
||||
|
||||
# 去除 \u200b
|
||||
msg = re.sub(r"[\u200b]", "", msg)
|
||||
|
||||
# 去除cq码
|
||||
msg = re.sub(r"\[CQ:.*?]", "", msg)
|
||||
|
||||
# 去除[]
|
||||
msg = re.sub("[	(1|3);]", "", msg)
|
||||
|
||||
# 去除 emoji
|
||||
# https://github.com/carpedm20/emoji
|
||||
msg = replace_emoji(msg)
|
||||
return msg
|
||||
|
||||
|
||||
async def draw_word_cloud(messages, config):
|
||||
wordcloud_dir = IMAGE_PATH / "wordcloud"
|
||||
wordcloud_dir.mkdir(exist_ok=True, parents=True)
|
||||
# 默认用真寻图片
|
||||
zx_logo_path = wordcloud_dir / "default.png"
|
||||
wordcloud_ttf = FONT_PATH / "STKAITI.TTF"
|
||||
if not os.listdir(wordcloud_dir):
|
||||
url = "https://ghproxy.com/https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/resources/image/wordcloud/default.png"
|
||||
try:
|
||||
await AsyncHttpx.download_file(url, zx_logo_path)
|
||||
except Exception as e:
|
||||
logger.error(f"词云图片资源下载发生错误 {type(e)}:{e}")
|
||||
return False
|
||||
if not wordcloud_ttf.exists():
|
||||
ttf_url = "https://ghproxy.com/https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/resources/font/STKAITI.TTF"
|
||||
try:
|
||||
await AsyncHttpx.download_file(ttf_url, wordcloud_ttf)
|
||||
except Exception as e:
|
||||
logger.error(f"词云字体资源下载发生错误 {type(e)}:{e}")
|
||||
return False
|
||||
|
||||
topK = min(int(len(messages)), 100000)
|
||||
read_name = jieba.analyse.extract_tags(
|
||||
await pre_precess(messages, config), topK=topK, withWeight=True, allowPOS=()
|
||||
)
|
||||
name = []
|
||||
value = []
|
||||
for t in read_name:
|
||||
name.append(t[0])
|
||||
value.append(t[1])
|
||||
for i in range(len(name)):
|
||||
name[i] = str(name[i])
|
||||
dic = dict(zip(name, value))
|
||||
if Config.get_config("word_clouds", "WORD_CLOUDS_TEMPLATE") == 1:
|
||||
|
||||
def random_pic(base_path: str) -> str:
|
||||
path_dir = os.listdir(base_path)
|
||||
path = random.sample(path_dir, 1)[0]
|
||||
return str(base_path) + "/" + str(path)
|
||||
|
||||
mask = np.array(IMG.open(random_pic(wordcloud_dir)))
|
||||
wc = WordCloud(
|
||||
font_path=f"{wordcloud_ttf}",
|
||||
background_color="white",
|
||||
max_font_size=100,
|
||||
width=1920,
|
||||
height=1080,
|
||||
mask=mask,
|
||||
)
|
||||
wc.generate_from_frequencies(dic)
|
||||
image_colors = ImageColorGenerator(mask, default_color=(255, 255, 255))
|
||||
wc.recolor(color_func=image_colors)
|
||||
plt.imshow(wc.recolor(color_func=image_colors), interpolation="bilinear")
|
||||
plt.axis("off")
|
||||
else:
|
||||
wc = WordCloud(
|
||||
font_path=str(wordcloud_ttf),
|
||||
width=1920,
|
||||
height=1200,
|
||||
background_color="black",
|
||||
)
|
||||
wc.generate_from_frequencies(dic)
|
||||
bytes_io = BytesIO()
|
||||
img = wc.to_image()
|
||||
img.save(bytes_io, format="PNG")
|
||||
return bytes_io.getvalue()
|
||||
|
||||
|
||||
async def get_list_msg(user_id, group_id, days):
|
||||
messages_list = (
|
||||
await ChatHistory()
|
||||
._get_msg(uid=user_id, gid=group_id, type_="group", days=days)
|
||||
.gino.all()
|
||||
)
|
||||
if messages_list:
|
||||
messages = [i.text for i in messages_list]
|
||||
return messages
|
||||
else:
|
||||
return False
|
||||
@@ -11,7 +11,7 @@ china_city = TEXT_PATH / "china_city.json"
|
||||
data = {}
|
||||
|
||||
|
||||
url = "https://view.inews.qq.com/g2/getOnsInfo?name=disease_h5"
|
||||
url = "https://api.inews.qq.com/newsqa/v1/query/inner/publish/modules/list?modules=diseaseh5Shelf"
|
||||
|
||||
|
||||
async def get_yiqing_data(area: str) -> Union[str, MessageSegment]:
|
||||
@@ -37,7 +37,7 @@ async def get_yiqing_data(area: str) -> Union[str, MessageSegment]:
|
||||
if area in data[p]:
|
||||
province = p
|
||||
city = area
|
||||
epidemic_data = json.loads((await AsyncHttpx.get(url)).json()["data"])
|
||||
epidemic_data = (await AsyncHttpx.get(url)).json()["data"]["diseaseh5Shelf"]
|
||||
last_update_time = epidemic_data["lastUpdateTime"]
|
||||
if area == "中国":
|
||||
data_ = epidemic_data["areaTree"][0]
|
||||
@@ -59,12 +59,12 @@ async def get_yiqing_data(area: str) -> Union[str, MessageSegment]:
|
||||
add_confirm = data_["today"]["confirm"] # 新增确诊
|
||||
grade = ""
|
||||
_grade_color = ""
|
||||
if data_["total"].get("grade"):
|
||||
grade = data_["total"]["grade"]
|
||||
if "中风险" in grade:
|
||||
_grade_color = "#fa9424"
|
||||
else:
|
||||
_grade_color = "red"
|
||||
# if data_["total"].get("grade"):
|
||||
# grade = data_["total"]["grade"]
|
||||
# if "中风险" in grade:
|
||||
# _grade_color = "#fa9424"
|
||||
# else:
|
||||
# _grade_color = "red"
|
||||
|
||||
dead_rate = f"{dead / confirm * 100:.2f}" # 死亡率
|
||||
heal_rate = f"{heal / confirm * 100:.2f}" # 治愈率
|
||||
|
||||
Generated
+630
-1082
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user