Compare commits

...
86 Commits
Author SHA1 Message Date
HibiKier c1862e9c68 fix bt 2022-05-29 19:15:29 +08:00
HibiKier 731bde8e28 update v0.1.5.7 2022-05-29 19:14:36 +08:00
HibiKier 47c87f8694 update db_context 2022-05-28 22:50:23 +08:00
HibiKier 5558cdacc3 Update README.md 2022-05-28 21:09:58 +08:00
HibiKier 4650d244a5 Merge pull request #688 from McElaga/main
修复微博热搜报错,发红包小bug
2022-05-28 21:08:17 +08:00
HibiKier c65b8aae07 update webui 2022-05-28 20:57:08 +08:00
HibiKier 7ba2fb9584 update black_word 2022-05-28 20:55:14 +08:00
HibiKier 9a2cfd0c9b zhenxun_update add config 2022-05-28 18:28:38 +08:00
HibiKier a7d61b1f2b add config 2022-05-28 18:12:08 +08:00
HibiKier 7ab185f7d7 fix poke 2022-05-28 17:48:28 +08:00
HibiKier 724541d691 fix bug 2022-05-28 17:22:51 +08:00
McElaga baa6cda9c2 commit on main 2022-05-28 10:31:27 +08:00
HibiKier 9c987e9aad log2cn 2022-05-26 22:49:48 +08:00
HibiKier 134974ea9c Merge pull request #667 from NumberSir/main
更新抽卡插件
2022-05-26 22:43:11 +08:00
HibiKier 2d946958e3 Merge pull request #668 from HanakoLuna/patch-1
更新BT搜索源地址
2022-05-26 22:42:37 +08:00
Lya_Yami ffe010e9ce 更新BT搜索源地址 2022-05-26 20:47:12 +08:00
HibiKier 1c67252b0c Update README.md 2022-05-26 20:26:14 +08:00
HibiKier 60c1be25d4 Merge pull request #643 from LambdaYH/patch-1
修复[滴滴滴]会被转义成[滴滴滴]导致无法触发的问题
2022-05-26 20:25:45 +08:00
Number_Sir 18323a0916 更新抽卡插件
修复了方舟抽卡图文不符的问题
修复了方舟许多三星抽不出来的问题
修复了方舟重载卡池失败的问题
2022-05-26 20:07:09 +08:00
HibiKier 9d98abded9 Merge pull request #660 from Sakuracio/main
Replace URL
2022-05-26 19:49:23 +08:00
MangataAkihi b06a742b8c Update README.md 2022-05-26 17:20:37 +08:00
github-actions[bot] 87fe44f188 Auto generate poetry.lock 2022-05-26 09:18:16 +00:00
Cinte 96f6ef1693 修复[滴滴滴]会被转义成[滴滴滴]导致无法触发的问题 2022-05-26 09:59:45 +08:00
HibiKier 23c4141e55 Update README.md 2022-05-25 20:53:09 +08:00
HibiKier c21200c2a9 Merge pull request #610 from yajiwa/main
修复碧蓝航线/明日方舟up池解析出错的问题
2022-05-25 20:52:58 +08:00
HibiKier 4a8b94259d Merge pull request #602 from HiDolen/main-patch
修改翻译插件,添加缺失的冒号
2022-05-25 20:49:51 +08:00
HibiKier 28a58df27b Merge pull request #607 from MingxuanGame/main
feat: stream downloading and progress
2022-05-25 20:49:20 +08:00
HibiKier caaf486bbe Merge branch 'main' into main 2022-05-25 20:49:07 +08:00
HibiKier 0a972a399e Merge pull request #617 from mouhensama/main
把gamedraw的ba卡池搬运了过来并且进行了真寻的适配。
2022-05-25 20:44:07 +08:00
HibiKier 245dad1239 Merge pull request #624 from HiDolen/main-patch2
修改nickname插件:一处文案错误,添加敏感词
2022-05-25 20:42:51 +08:00
Dolen a6b6a2fbff 修改nickname插件:一处文案错误 2022-05-25 17:21:00 +08:00
Dolen 53000a949b 修改nickname插件:添加一个敏感词 2022-05-25 17:03:44 +08:00
Dolen eae1dd59a7 修改nickname插件:一处文案错误 2022-05-25 17:02:09 +08:00
HibiKier e6347a76a8 Update issue-template.md 2022-05-25 14:44:28 +08:00
HibiKier 13e0fb3df7 Delete bug-issue-template.md 2022-05-25 13:42:22 +08:00
HibiKier 3f1b23ef8a Update issue templates 2022-05-25 13:41:35 +08:00
HibiKier acae7f0658 Create ISSUE_TEMPLATE.md 2022-05-25 13:37:29 +08:00
HibiKier a9456b1bd6 Merge pull request #608 from AkashiCoin/main
Use Action to automatically generate poetry.lock
2022-05-25 13:35:39 +08:00
mouhensama e4d5a230d2 把nonebot_plugin_gamedraw的ba卡池搬运了过来并且进行了真寻的适配。 2022-05-25 11:47:08 +08:00
HibiKier fedf44b9fa Update README.md 2022-05-25 10:54:52 +08:00
HibiKier 451db104e8 Update config.py 2022-05-25 08:52:22 +08:00
HibiKier 9ac4c7f4e3 Update README.md 2022-05-25 08:52:15 +08:00
HibiKier 7cf1c6f016 Update README.md 2022-05-25 08:41:59 +08:00
yajiwa 6dd87ee301 fix draw_card 2022-05-25 07:02:42 +08:00
MingxuanGame 96837fa8db fix: rich from dev dependent to main dependent 2022-05-25 01:19:12 +08:00
MingxuanGame d816e76962 fix: rich from dev dependent to main dependent 2022-05-25 01:13:27 +08:00
AkashiCoin 310945d9cd Merge branch 'HibiKier:main' into main 2022-05-25 01:01:11 +08:00
MingxuanGame 1aba480495 feat: stream downloading and progress 2022-05-25 00:56:41 +08:00
HibiKier 07407749fe Update README.md 2022-05-24 22:05:38 +08:00
HibiKier dfab70dd90 Merge pull request #581 from HiDolen/main
修复抽卡插件:方舟抽卡的抽取和显示问题
2022-05-24 22:05:13 +08:00
Dolen 3b15469c98 修改翻译插件,发送消息添加冒号 2022-05-24 08:59:14 -05:00
HibiKier e4590800c2 Update README.md 2022-05-24 21:20:59 +08:00
HibiKier c63d4b8ad0 Merge pull request #573 from jk6589jk/patch-2
修复复读不能复读图片的问题
2022-05-24 21:19:41 +08:00
HibiKier 1fb913e2ed Merge pull request #576 from MingxuanGame/main
refactor&fix(manager): modify argument
2022-05-24 21:18:22 +08:00
HibiKier 72ace2dab7 Merge pull request #579 from yajiwa/main
更新"微博热搜"接口
2022-05-24 21:14:51 +08:00
HibiKier 3bcef18629 Merge pull request #600 from sdy623/main
bugfix: 修正了签到时日期时间的显示不补齐零的bug (符合日期时间表示法 ISO 8601)
2022-05-24 21:13:33 +08:00
sdy623 c91758519c bugfix: 修正了签到时日期时间的显示不补齐零的bug (符合日期时间表示法 ISO 8601) 2022-05-24 20:19:32 +08:00
jk6589jk 9c8f2e7aa0 Update fudu.py 2022-05-24 14:01:08 +08:00
Dolen 5f377af6d5 修复抽卡插件:方舟抽卡的抽取和显示问题 2022-05-24 09:13:10 +08:00
yajiwa 570b5b1766 update wbtop 2022-05-24 06:01:23 +08:00
MingxuanGame c4d53d58eb refactor&fix(manager): modify argument 2022-05-24 01:27:55 +08:00
jk6589jk ed65033cf1 修复复读不能复读图片的问题
IMAGE_PATH = Path() / "resources" / "image"
TEMP_PATH = Path() / "resources" / "temp"
image()默认读取在 resource/img 目录下,读写路径不同,导致找不到图片

打断打断施法!,rst.endswith("打断施法!")在复读内容含有图片时会报错
2022-05-23 22:06:53 +08:00
HibiKier 41836bde26 Update README.md 2022-05-23 22:05:19 +08:00
HibiKier 8381858742 Update README.md 2022-05-23 22:02:49 +08:00
HibiKier 1040bd0d58 update v0.1.5.6 2022-05-23 22:01:56 +08:00
HibiKier 404e47332e Merge pull request #441 from with9/main
添加pixiv搜图多关键词支持;修复p站搜图数量参数问题
2022-05-23 21:52:33 +08:00
HibiKier 3db128785e Update __init__.py 2022-05-23 21:52:08 +08:00
HibiKier 2189ea11df Update README.md 2022-05-23 19:46:30 +08:00
HibiKier 49b9bd65b0 Merge pull request #552 from jk6589jk/patch-1
修复有置顶的up主B站动态获取失败
2022-05-23 19:45:59 +08:00
jk6589jk b3fdb1ff58 修复B站动态获取失败
bili-dyn-item__tag的父项的父项才是完整的一条置顶动态
2022-05-23 12:59:53 +08:00
HibiKier c79e3fdcf0 Update README.md 2022-05-23 07:52:27 +08:00
HibiKier d7509f7030 Merge pull request #545 from yajiwa/main
修复"清除已删除插件数据"
2022-05-23 07:51:54 +08:00
yajiwa 5c99b55502 fix none_plugin_count 2022-05-23 02:35:57 +08:00
yajiwa fd255f022b Merge branch 'HibiKier:main' into main 2022-05-23 02:27:00 +08:00
github-actions[bot] 8d3f101a68 Auto generate poetry.lock 2022-05-21 18:38:05 +00:00
AkashiCoin c8ac2b6281 Update Generate.yml 2022-05-22 02:35:20 +08:00
AkashiCoin d7d8efd305 Merge branch 'HibiKier:main' into main 2022-05-22 02:04:34 +08:00
HibiKier a4bc04c958 Update pid_search.py 2022-05-22 01:46:52 +08:00
AkashiCoin 5fc208c95a Create Generate.yml 2022-05-22 00:29:54 +08:00
HibiKier 02743d5955 Merge pull request #384 from AkashiCoin/main
Update anime.json of Kyomotoi/AnimeThesaurus
2022-05-21 23:40:13 +08:00
HibiKier a7ffebea35 Merge pull request #503 from yajiwa/main
修复命令不唯一的问题
2022-05-21 23:39:39 +08:00
AkashiCoin 1de5ae09bc Merge branch 'HibiKier:main' into main 2022-05-21 20:46:04 +08:00
yajiwa 8b735f1cb5 fix word_clouds 2022-05-21 20:08:53 +08:00
with9 9cdd2326c8 添加pixiv搜图多关键词支持;修复p站搜图数量参数问题 2022-05-20 19:35:18 +08:00
AkashiCoin d75653631d Merge branch 'HibiKier:main' into main 2022-05-19 23:21:02 +08:00
AkashiCoin e234c8906e Update anime.json 2022-05-19 23:20:00 +08:00
60 changed files with 1155 additions and 842 deletions
+23
View File
@@ -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错误...
+38
View File
@@ -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 }}
+9
View File
@@ -0,0 +1,9 @@
### 系统版本:Ubuntu 20.04
### 真寻版本:0.1.5.3
### 错误截图
[img]
### 错误说明
发生了xx错误...
+61 -2
View File
@@ -18,6 +18,11 @@
## 声明 ## 声明
此项目仅用于学习交流,请勿用于非法用途 此项目仅用于学习交流,请勿用于非法用途
# Nonebot2
<img style="height: 200px;width: 200px;" src="https://camo.githubusercontent.com/0ef71e86056da694c540790aa4a4e314396884d6c4fdb95362a7538b27a1b034/68747470733a2f2f76322e6e6f6e65626f742e6465762f6c6f676f2e706e67">
非常 [ **[NICE](https://github.com/nonebot/nonebot2)** ] 的OneBot框架
## 未完成的文档 ## 未完成的文档
# [传送门](https://hibikier.github.io/zhenxun_bot/) # [传送门](https://hibikier.github.io/zhenxun_bot/)
@@ -109,7 +114,7 @@
- [x] 获取b站视频封面 - [x] 获取b站视频封面
- [x] 通过PID获取图片 - [x] 通过PID获取图片
- [x] 功能统计可视化 - [x] 功能统计可视化
- [x] 词云
- [x] 关于 - [x] 关于
### 已实现的管理员功能 ### 已实现的管理员功能
@@ -231,13 +236,67 @@ python bot.py
## 使用Docker ## 使用Docker
__Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__ __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) #### DockerHub:[hibikier/zhenxun_bot](https://hub.docker.com/r/hibikier/zhenxun_bot)
## 更新 ## 更新
### 2022/5/29 \[v0.1.5.7]
* 修复错误的resource路径会删除IMAGE_PATH等重要文件夹
* 提供了真寻适配仓库的插件 安装/卸载 操作
### 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
* 修复\[滴滴滴]会被转义成&#91;滴滴滴&#93;导致无法触发的问题
* 将一些错误以中文提示输出
* 更新BT搜索源地址 [@pull/668](https://github.com/HibiKier/zhenxun_bot/pull/668)
* 更新抽卡插件 [@pull/667](https://github.com/HibiKier/zhenxun_bot/pull/667)
### 2022/5/25
* 修复webui中CountLimit字段limit_type类型错误
* 修改nickname插件:一处文案错误,添加敏感词 [@pull/624](https://github.com/HibiKier/zhenxun_bot/pull/624)
* gamedraw的ba卡池搬运了过来并且进行了真寻的适配 [@pull/617](https://github.com/HibiKier/zhenxun_bot/pull/617)
* feat: stream downloading and progress [@pull/607](https://github.com/HibiKier/zhenxun_bot/pull/607)
* 修改翻译插件,添加缺失的冒号 [@pull/602](https://github.com/HibiKier/zhenxun_bot/pull/602)
* 修复碧蓝航线/明日方舟up池解析出错的问题 [@pull/610](https://github.com/HibiKier/zhenxun_bot/pull/602)
### 2022/5/24
* fix: 修正了签到时日期时间的显示不补齐零的bug (符合日期时间表示法 ISO 8601)[@pull/600](https://github.com/HibiKier/zhenxun_bot/pull/600)
* 更新"微博热搜"接口 [@pull/579](https://github.com/HibiKier/zhenxun_bot/pull/579)
* refactor&fix(manager): modify argument [@pull/576](https://github.com/HibiKier/zhenxun_bot/pull/576)
* 修复复读不能复读图片的问题 [@pull/573](https://github.com/HibiKier/zhenxun_bot/pull/573)
* 修复抽卡插件:方舟抽卡的抽取和显示问题 [@pull/581](https://github.com/HibiKier/zhenxun_bot/pull/581)
### 2022/5/23 \[v0.1.5.6]
* 修复"清除已删除插件数据" [@pull/545](https://github.com/HibiKier/zhenxun_bot/pull/545)
* 修复有置顶的up主B站动态获取失败 [@pull/552](https://github.com/HibiKier/zhenxun_bot/pull/552)
* 添加pixiv搜图多关键词支持;修复p站搜图数量参数问题 [@pull/441](https://github.com/HibiKier/zhenxun_bot/pull/441)
* 修复开箱更新价格错误传参
* 修复pix无法正确查询uid
* 新增色图插件添加配置项```ALLOW_GROUP_R18```:允许群聊中使用色图r
* 新增PIX插件添加配置项```ALLOW_GROUP_SETU```:允许非超级用户使用-s参数
* 新增PIX插件添加配置项```ALLOW_GROUP_R18```:允许非超级用户使用-r参数
### 2022/5/22 \[v0.1.5.4] ### 2022/5/22 \[v0.1.5.4]
* 使用action自动更新poetry.lock [@pull/515](https://github.com/HibiKier/zhenxun_bot/pull/515) * 使用action自动更新poetry.lock [@pull/515](https://github.com/HibiKier/zhenxun_bot/pull/515)
+1 -1
View File
@@ -1 +1 @@
__version__: v0.1.5.4 __version__: v0.1.5.6
+3 -2
View File
@@ -2,6 +2,7 @@ from configs.config import Config
from models.chat_history import ChatHistory from models.chat_history import ChatHistory
from nonebot import on_message from nonebot import on_message
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
from utils.utils import get_message_text
from ._rule import rule from ._rule import rule
@@ -22,10 +23,10 @@ chat_history = on_message(rule=rule, priority=1, block=False)
async def _(event: MessageEvent): async def _(event: MessageEvent):
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
await ChatHistory.add_chat_msg( 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: 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() # @test.handle()
+9 -2
View File
@@ -91,7 +91,10 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
except AttributeError: except AttributeError:
pass 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: try:
if ( if (
group_manager.get_group_level(event.group_id) < 0 group_manager.get_group_level(event.group_id) < 0
@@ -148,7 +151,11 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
raise IgnoredException("权限不足") raise IgnoredException("权限不足")
if module in plugins2info_dict.keys() and matcher.priority not in [1, 999]: if module in plugins2info_dict.keys() and matcher.priority not in [1, 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: if status_message_manager.get(event.group_id) is None:
status_message_manager.delete(event.group_id) status_message_manager.delete(event.group_id)
if plugins2info_dict[module]["level"] > group_manager.get_group_level( if plugins2info_dict[module]["level"] > group_manager.get_group_level(
@@ -20,26 +20,36 @@ def init_none_plugin_count_manager():
""" """
清除已删除插件数据 清除已删除插件数据
""" """
modules = [x.module for x in get_matchers()] modules = [x.plugin_name for x in get_matchers()]
for module in none_plugin_count_manager.keys(): plugins_manager_list = list(plugins_manager.keys())
if module not in modules: for module in plugins_manager_list:
none_plugin_count_manager.add_count(module) if module not in modules or none_plugin_count_manager.check(module):
else:
none_plugin_count_manager.reset(module)
if none_plugin_count_manager.check(module):
try: try:
plugin_name = plugins_manager.get(module)["plugin_name"] plugin_name = plugins_manager.get(module)["plugin_name"]
except (AttributeError, KeyError): except (AttributeError, KeyError):
plugin_name = "" plugin_name = ""
try: if none_plugin_count_manager.check(module):
plugins2settings_manager.delete(module) try:
plugins2count_manager.delete(module) plugins2settings_manager.delete(module)
plugins2cd_manager.delete(module) plugins2settings_manager.save()
plugins2block_manager.delete(module) plugins2count_manager.delete(module)
plugins_manager.delete(module) plugins2count_manager.save()
resources_manager.remove_resource(module) plugins2cd_manager.delete(module)
logger.info(f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据...") plugins2cd_manager.save()
except Exception as e: plugins2block_manager.delete(module)
logger.error( plugins2block_manager.save()
f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据失败...{type(e)}:{e}" 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 utils.manager import admin_manager, plugins_manager
from configs.config import Config from configs.config import Config
from services.log import logger from services.log import logger
from utils.text_utils import prompt2cn
from utils.utils import get_matchers from utils.utils import get_matchers
from ruamel import yaml from ruamel import yaml
import nonebot import nonebot
@@ -56,7 +57,9 @@ def init_plugins_config(data_path):
_override=True, _override=True,
) )
else: else:
Config.add_plugin_config(matcher.plugin_name, key, plugin_configs[key]) Config.add_plugin_config(
matcher.plugin_name, key, plugin_configs[key]
)
else: else:
plugin_configs = _data[matcher.plugin_name] plugin_configs = _data[matcher.plugin_name]
for key in plugin_configs: 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) _data[plugin].yaml_set_start_comment(plugin_name, indent=2)
# 初始化未设置的管理员权限等级 # 初始化未设置的管理员权限等级
for k, v in Config.get_admin_level_data(): for k, v in Config.get_admin_level_data():
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: with open(plugins2config_file, "w", encoding="utf8") as wf:
round_trip_dump( round_trip_dump(
@@ -96,14 +102,23 @@ def init_plugins_config(data_path):
for plugin in Config.keys(): for plugin in Config.keys():
_tmp_data[plugin] = {} _tmp_data[plugin] = {}
for k in Config[plugin].keys(): for k in Config[plugin].keys():
if _data.get(plugin) and k in _data[plugin].keys(): try:
Config.set_config(plugin, k, _data[plugin][k]) if _data.get(plugin) and k in _data[plugin].keys():
if level2module := Config.get_level2module(plugin, k): Config.set_config(plugin, k, _data[plugin][k])
try: if level2module := Config.get_level2module(plugin, k):
admin_manager.set_admin_level(level2module, _data[plugin][k]) try:
except KeyError: admin_manager.set_admin_level(
logger.warning(f"{level2module} 设置权限等级失败:{_data[plugin][k]}") level2module, _data[plugin][k]
_tmp_data[plugin][k] = Config.get_config(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() Config.save()
temp_file = Path() / "configs" / "temp_config.yaml" temp_file = Path() / "configs" / "temp_config.yaml"
try: try:
@@ -28,7 +28,7 @@ def init_plugins_cd_limit(data_path):
_module = _plugin.module _module = _plugin.module
plugin_cd_limit = _module.__getattribute__("__plugin_cd_limit__") plugin_cd_limit = _module.__getattribute__("__plugin_cd_limit__")
plugins2cd_manager.add_cd_limit( plugins2cd_manager.add_cd_limit(
matcher.plugin_name, data_dict=plugin_cd_limit matcher.plugin_name, **plugin_cd_limit
) )
except AttributeError: except AttributeError:
pass pass
@@ -76,7 +76,7 @@ def init_plugins_block_limit(data_path):
_module = _plugin.module _module = _plugin.module
plugin_block_limit = _module.__getattribute__("__plugin_block_limit__") plugin_block_limit = _module.__getattribute__("__plugin_block_limit__")
plugins2block_manager.add_block_limit( plugins2block_manager.add_block_limit(
matcher.plugin_name, data_dict=plugin_block_limit matcher.plugin_name, **plugin_block_limit
) )
except AttributeError: except AttributeError:
pass pass
@@ -124,7 +124,7 @@ def init_plugins_count_limit(data_path):
_module = _plugin.module _module = _plugin.module
plugin_count_limit = _module.__getattribute__("__plugin_count_limit__") plugin_count_limit = _module.__getattribute__("__plugin_count_limit__")
plugins2count_manager.add_count_limit( plugins2count_manager.add_count_limit(
matcher.plugin_name, data_dict=plugin_count_limit matcher.plugin_name, **plugin_count_limit
) )
except AttributeError: except AttributeError:
pass pass
+5 -5
View File
@@ -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 import on_command
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.rule import to_me from nonebot.rule import to_me
@@ -24,7 +24,7 @@ __plugin_version__ = 0.1
__plugin_author__ = "HibiKier" __plugin_author__ = "HibiKier"
__plugin_configs__ = { __plugin_configs__ = {
"BLACK_WORD": { "BLACK_WORD": {
"value": ["爸", "爹", "爷"], "value": ["爸", "爹", "爷", "父亲"],
"help": "昵称所屏蔽的关键词,会被替换为 *", "help": "昵称所屏蔽的关键词,会被替换为 *",
"default_value": None "default_value": None
} }
@@ -47,7 +47,7 @@ cancel_nickname = on_command("取消昵称", rule=to_me(), priority=5, block=Tru
@nickname.handle() @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() msg = arg.extract_plain_text().strip()
if not msg: if not msg:
await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True) await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True)
@@ -73,7 +73,7 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧", f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}", f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
f"好突然,突然要叫你昵称什么的...{msg}..", f"好突然,突然要叫你昵称什么的...{msg}..",
f"{NICKNAME}会好好记住的{msg}的,放心吧", f"{NICKNAME}会好好记住{msg}的,放心吧",
f"好..好.,那窝以后就叫你{msg}了.", f"好..好.,那窝以后就叫你{msg}了.",
] ]
) )
@@ -90,7 +90,7 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧", f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}", f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
f"好突然,突然要叫你昵称什么的...{msg}..", f"好突然,突然要叫你昵称什么的...{msg}..",
f"{NICKNAME}会好好记住的{msg}的,放心吧", f"{NICKNAME}会好好记住{msg}的,放心吧",
f"好..好.,那窝以后就叫你{msg}了.", f"好..好.,那窝以后就叫你{msg}了.",
] ]
) )
+57
View File
@@ -0,0 +1,57 @@
from nonebot import on_command, on_regex
from .data_source import show_plugin_repo, install_plugin, uninstall_plugin
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)
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)
@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" 查看插件仓库"
)
+183
View File
@@ -0,0 +1,183 @@
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()}"
)
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 + 100, h + 100, color="#f9f6f2")
cur_h = 50
for img in image_list:
await A.apaste(img, (50, 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
+4
View File
@@ -108,6 +108,10 @@ async def _():
"ALTER TABLE genshin ADD bind_group Integer;", "ALTER TABLE genshin ADD bind_group Integer;",
"genshin" "genshin"
), # 新增原神群号绑定字段 ), # 新增原神群号绑定字段
(
"ALTER TABLE chat_history ADD plain_text Text;",
"chat_history"
), # 新增纯文本
] ]
for sql in sql_str: for sql in sql_str:
try: try:
+1
View File
@@ -12,6 +12,7 @@ driver.on_shutdown(disconnect)
nonebot.load_plugin("nonebot_plugin_apscheduler") nonebot.load_plugin("nonebot_plugin_apscheduler")
nonebot.load_plugins("basic_plugins") nonebot.load_plugins("basic_plugins")
nonebot.load_plugins("plugins") nonebot.load_plugins("plugins")
nonebot.load_plugins("extensive_plugin")
# 最后加载权限控制 # 最后加载权限控制
nonebot.load_plugins("basic_plugins/hooks") nonebot.load_plugins("basic_plugins/hooks")
+30 -5
View File
@@ -2,6 +2,7 @@ from typing import Optional, Any, Union
from pathlib import Path from pathlib import Path
from ruamel.yaml import YAML from ruamel.yaml import YAML
from ruamel import yaml from ruamel import yaml
from ruamel.yaml.scanner import ScannerError
class ConfigsManager: class ConfigsManager:
@@ -21,9 +22,24 @@ class ConfigsManager:
if file.exists(): if file.exists():
with open(file, "r", encoding="utf8") as f: with open(file, "r", encoding="utf8") as f:
self._data = _yaml.load(f) self._data = _yaml.load(f)
if not self._data:
self.file.unlink()
raise ValueError(
"配置文件为空!\n"
"***********************************************************\n"
"****** 配置文件 plugins2config.yaml 为空,已删除,请重启 ******\n"
"***********************************************************"
)
if self._simple_file.exists(): if self._simple_file.exists():
with open(self._simple_file, "r", encoding="utf8") as f: try:
self._simple_data = _yaml.load(f) with open(self._simple_file, "r", encoding="utf8") as f:
self._simple_data = _yaml.load(f)
except ScannerError as e:
raise ScannerError(
f"{e}\n**********************************************\n"
f"****** 可能为config.yaml配置文件填写不规范 ******\n"
f"**********************************************"
)
def add_plugin_config( def add_plugin_config(
self, self,
@@ -86,7 +102,10 @@ class ConfigsManager:
:param value: 值 :param value: 值
""" """
if module in self._data.keys(): 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._data[module][key]["value"] = value
self._simple_data[module][key] = value self._simple_data[module][key] = value
self.save() self.save()
@@ -115,7 +134,9 @@ class ConfigsManager:
self._data[module][key]["default_value"] = value self._data[module][key]["default_value"] = value
self.save() 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: 模块名 :param module: 模块名
@@ -161,7 +182,11 @@ class ConfigsManager:
if save_simple_data: if save_simple_data:
with open(self._simple_file, "w", encoding="utf8") as f: with open(self._simple_file, "w", encoding="utf8") as f:
yaml.dump( 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 path = path if path else self.file
with open(path, "w", encoding="utf8") as f: with open(path, "w", encoding="utf8") as f:
+3 -3
View File
@@ -11,12 +11,13 @@ class ChatHistory(db.Model):
user_qq = db.Column(db.BigInteger(), nullable=False) user_qq = db.Column(db.BigInteger(), nullable=False)
group_id = db.Column(db.BigInteger()) group_id = db.Column(db.BigInteger())
text = db.Column(db.Text()) text = db.Column(db.Text())
plain_text = db.Column(db.Text())
create_time = db.Column(db.DateTime(timezone=True), nullable=False) create_time = db.Column(db.DateTime(timezone=True), nullable=False)
@classmethod @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( 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 @classmethod
@@ -97,7 +98,6 @@ class ChatHistory(db.Model):
if date_scope: if date_scope:
sql += f"AND create_time BETWEEN '{date_scope[0]}' AND '{date_scope[1]}' " 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}" 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)) return await db.all(db.text(sql))
@classmethod @classmethod
-25
View File
@@ -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 typing import Optional, Tuple, Union
from configs.config import Config from configs.config import Config
from utils.http_utils import AsyncHttpx from utils.http_utils import AsyncHttpx
@@ -28,24 +24,3 @@ async def get_data(url: str, params: Optional[dict] = None) -> Tuple[Union[dict,
return f'发生了错误...code:{data["code"]}', 999 return f'发生了错误...code:{data["code"]}', 999
except TimeoutError: except TimeoutError:
return "超时了....", 998 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())
+1 -1
View File
@@ -330,7 +330,7 @@ async def get_user_dynamic(
""" """
xs = document.getElementsByClassName('bili-dyn-item__tag'); xs = document.getElementsByClassName('bili-dyn-item__tag');
for (x of xs) { for (x of xs) {
x.parentNode.remove(); x.parentNode.parentNode.remove();
} }
""" """
) )
-1
View File
@@ -38,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 = BuildImage(100, 100, background=BytesIO(await get_pic(face)))
ava.circle() ava.circle()
cover = BuildImage(470, 265, background=BytesIO(await get_pic(cover))) cover = BuildImage(470, 265, background=BytesIO(await get_pic(cover)))
print()
def _create_live_des_image( def _create_live_des_image(
+5 -1
View File
@@ -8,6 +8,7 @@ from nonebot.adapters.onebot.v11 import (
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor from nonebot.message import run_preprocessor
from utils.image_utils import BuildImage from utils.image_utils import BuildImage
from utils.manager import group_manager
from utils.utils import get_message_text, is_number from utils.utils import get_message_text, is_number
from nonebot.params import RegexGroup, CommandArg from nonebot.params import RegexGroup, CommandArg
from .utils import black_word_manager from .utils import black_word_manager
@@ -155,7 +156,11 @@ async def _(
and matcher.plugin_name == "black_word" and matcher.plugin_name == "black_word"
and not await BanUser.is_ban(event.user_id) and not await BanUser.is_ban(event.user_id)
and str(event.user_id) not in bot.config.superusers and str(event.user_id) not in bot.config.superusers
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 user_id = event.user_id
group_id = event.group_id if isinstance(event, GroupMessageEvent) else None group_id = event.group_id if isinstance(event, GroupMessageEvent) else None
msg = get_message_text(event.json()) msg = get_message_text(event.json())
@@ -241,7 +246,6 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
uid = int(msg[0]) uid = int(msg[0])
id_ = int(msg[1]) id_ = int(msg[1])
punish_level = int(msg[2]) punish_level = int(msg[2])
print(uid, id_, punish_level)
rst = await set_user_punish(uid, id_, punish_level) rst = await set_user_punish(uid, id_, punish_level)
await set_punish.send(rst) await set_punish.send(rst)
logger.info( logger.info(
-1
View File
@@ -68,7 +68,6 @@ class BlackWord(db.Model):
user = await query.where(cls.black_word == black_word).order_by(cls.id.desc()).gino.first() user = await query.where(cls.black_word == black_word).order_by(cls.id.desc()).gino.first()
elif id_: elif id_:
user_list = await query.gino.all() user_list = await query.gino.all()
print(len(user_list))
if len(user_list) == 0 or (id_ < 0 or id_ > len(user_list)): if len(user_list) == 0 or (id_ < 0 or id_ > len(user_list)):
return False return False
user = user_list[id_] user = user_list[id_]
-2
View File
@@ -88,9 +88,7 @@ class BlackWordManager:
:param group_id: 群号 :param group_id: 群号
:param message: 消息 :param message: 消息
""" """
print(user_id, group_id, message)
if data := self._check(message): if data := self._check(message):
print(data)
if data[0]: if data[0]:
await _add_user_black_word( await _add_user_black_word(
user_id, group_id, data[0], message, int(data[1]) user_id, group_id, data[0], message, int(data[1])
+2 -2
View File
@@ -9,7 +9,7 @@ import platform
# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # 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): async def get_bt_info(keyword: str, page: int):
@@ -19,7 +19,7 @@ async def get_bt_info(keyword: str, page: int):
:param page: 页数 :param page: 页数
""" """
text = (await AsyncHttpx.get(f"{url}/s/{keyword}_rel_{page}.html", timeout=5)).text text = (await AsyncHttpx.get(f"{url}/s/{keyword}_rel_{page}.html", timeout=5)).text
if text.find("大约0条结果") != -1: if "大约0条结果" in text:
return return
soup = BeautifulSoup(text, "lxml") soup = BeautifulSoup(text, "lxml")
item_lst = soup.find_all("div", {"class": "search-item"}) item_lst = soup.find_all("div", {"class": "search-item"})
+22 -16
View File
@@ -25,6 +25,11 @@ __plugin_cmd__ = ["检查更新真寻", "重启"]
__plugin_version__ = 0.1 __plugin_version__ = 0.1
__plugin_author__ = "HibiKier" __plugin_author__ = "HibiKier"
__plugin_configs__ = { __plugin_configs__ = {
"UPDATE_REMIND": {
"value": True,
"help": "真寻是否自动检查更新",
"default": True,
},
"AUTO_UPDATE_ZHENXUN": { "AUTO_UPDATE_ZHENXUN": {
"value": False, "value": False,
"help": "真寻是否自动检查更新", "help": "真寻是否自动检查更新",
@@ -72,7 +77,7 @@ async def _():
await restart.finish("暂无windows重启脚本...") await restart.finish("暂无windows重启脚本...")
@restart.got("flag", prompt="确定是否重启真寻?(重启失败咱们将失去联系,请谨慎!)") @restart.got("flag", prompt="确定是否重启真寻?确定请回复[是|好|确定](重启失败咱们将失去联系,请谨慎!)")
async def _(flag: str = ArgStr("flag")): async def _(flag: str = ArgStr("flag")):
if flag.lower() in ["true", "是", "好", "确定", "确定是"]: if flag.lower() in ["true", "是", "好", "确定", "确定是"]:
await restart.send("开始重启真寻..请稍等...") await restart.send("开始重启真寻..请稍等...")
@@ -88,7 +93,7 @@ async def _(flag: str = ArgStr("flag")):
minute=0, minute=0,
) )
async def _(): 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 = "v0.0.0"
_version_file = Path() / "__version__" _version_file = Path() / "__version__"
if _version_file.exists(): if _version_file.exists():
@@ -108,17 +113,18 @@ async def _():
message=f"检测到真寻版本更新\n" message=f"检测到真寻版本更新\n"
f"当前版本:{_version},最新版本:{latest_version}", f"当前版本:{_version},最新版本:{latest_version}",
) )
# try: if Config.get_config("check_zhenxun_update", "AUTO_UPDATE_ZHENXUN"):
# code = await check_update(bot) try:
# except Exception as e: code = await check_update(bot)
# logger.error(f"更新真寻未知错误 {type(e)}:{e}") except Exception as e:
# await bot.send_private_msg( logger.error(f"更新真寻未知错误 {type(e)}:{e}")
# user_id=int(list(bot.config.superusers)[0]), await bot.send_private_msg(
# message=f"更新真寻未知错误 {type(e)}:{e}\n", user_id=int(list(bot.config.superusers)[0]),
# ) message=f"更新真寻未知错误 {type(e)}:{e}\n",
# else: )
# if code == 200: else:
# await bot.send_private_msg( if code == 200:
# user_id=int(list(bot.config.superusers)[0]), await bot.send_private_msg(
# message=f"更新完毕,请重启真寻....", user_id=int(list(bot.config.superusers)[0]),
# ) message=f"更新完毕,请重启真寻....",
)
+1 -1
View File
@@ -53,7 +53,7 @@ __plugin_settings__ = {
dialogue_data = {} dialogue_data = {}
dialogue = on_command("[滴滴滴]", aliases={"滴滴滴-"}, priority=5, block=True) dialogue = on_command("&#91;滴滴滴&#93;", aliases={"滴滴滴-"}, priority=5, block=True)
reply = on_command("/t", priority=1, permission=SUPERUSER, block=True) reply = on_command("/t", priority=1, permission=SUPERUSER, block=True)
+11 -2
View File
@@ -23,6 +23,7 @@ from .handles.onmyoji_handle import OnmyojiHandle
from .handles.pcr_handle import PcrHandle from .handles.pcr_handle import PcrHandle
from .handles.pretty_handle import PrettyHandle from .handles.pretty_handle import PrettyHandle
from .handles.prts_handle import PrtsHandle from .handles.prts_handle import PrtsHandle
from .handles.ba_handle import BaHandle
from .config import draw_config from .config import draw_config
@@ -30,7 +31,7 @@ from .config import draw_config
__zx_plugin_name__ = "游戏抽卡" __zx_plugin_name__ = "游戏抽卡"
__plugin_usage__ = """ __plugin_usage__ = """
usage: usage:
模拟赛马娘,原神,明日方舟,坎公骑冠剑,公主连结(国/台),碧蓝航线,FGO,阴阳师进行抽卡 模拟赛马娘,原神,明日方舟,坎公骑冠剑,公主连结(国/台),碧蓝航线,FGO,阴阳师,碧蓝档案进行抽卡
指令: 指令:
原神[1-180]抽: 原神常驻池 原神[1-180]抽: 原神常驻池
原神角色[1-180]抽: 原神角色UP池子 原神角色[1-180]抽: 原神角色UP池子
@@ -43,6 +44,7 @@ usage:
碧蓝航线/碧蓝[重型/轻型/特型/活动][1-300]抽: 碧蓝航线重型/轻型/特型/活动卡池 碧蓝航线/碧蓝[重型/轻型/特型/活动][1-300]抽: 碧蓝航线重型/轻型/特型/活动卡池
fgo[1-300]抽: fgo卡池 fgo[1-300]抽: fgo卡池
阴阳师[1-300]抽: 阴阳师卡池 阴阳师[1-300]抽: 阴阳师卡池
ba/碧蓝档案[1-200]抽:碧蓝档案卡池
* 以上指令可以通过 XX一井 来指定最大抽取数量 * * 以上指令可以通过 XX一井 来指定最大抽取数量 *
* 示例:原神一井 * * 示例:原神一井 *
""".strip() """.strip()
@@ -73,6 +75,7 @@ __plugin_cmd__ = [
"pcr/公主连接[1-300]抽", "pcr/公主连接[1-300]抽",
"fgo[1-300]抽", "fgo[1-300]抽",
"阴阳师[1-300]抽", "阴阳师[1-300]抽",
"碧蓝档案[1-200]抽",
"更新方舟信息 [_superuser]", "更新方舟信息 [_superuser]",
"重载方舟卡池 [_superuser]", "重载方舟卡池 [_superuser]",
"更新原神信息 [_superuser]", "更新原神信息 [_superuser]",
@@ -83,6 +86,7 @@ __plugin_cmd__ = [
"更新碧蓝航线信息 [_superuser]", "更新碧蓝航线信息 [_superuser]",
"更新fgo信息 [_superuser]", "更新fgo信息 [_superuser]",
"更新阴阳师信息 [_superuser]", "更新阴阳师信息 [_superuser]",
"更新碧蓝档案信息 [_superuser]",
] ]
__plugin_type__ = ("抽卡相关", 1) __plugin_type__ = ("抽卡相关", 1)
__plugin_version__ = 0.1 __plugin_version__ = 0.1
@@ -105,7 +109,7 @@ class Game:
games = ( games = (
Game({"azur", "碧蓝", "碧蓝航线"}, AzurHandle(), draw_config.AZUR_FLAG), Game({"azur", "碧蓝航线"}, AzurHandle(), draw_config.AZUR_FLAG),
Game({"fgo", "命运冠位指定"}, FgoHandle(), draw_config.FGO_FLAG), Game({"fgo", "命运冠位指定"}, FgoHandle(), draw_config.FGO_FLAG),
Game( Game(
{"genshin", "原神"}, {"genshin", "原神"},
@@ -130,6 +134,11 @@ games = (
reload_time=4, reload_time=4,
), ),
Game({"prts", "方舟", "明日方舟"}, PrtsHandle(), draw_config.PRTS_FLAG, reload_time=4), Game({"prts", "方舟", "明日方舟"}, PrtsHandle(), draw_config.PRTS_FLAG, reload_time=4),
Game(
{"ba","碧蓝档案"},BaHandle(),
draw_config.BA_FLAG,
max_count=200,
),
) )
+10 -1
View File
@@ -87,6 +87,12 @@ class OnmyojiConfig(BaseModel, extra=Extra.ignore):
ONMYOJI_SR: float = 0.2 ONMYOJI_SR: float = 0.2
ONMYOJI_R: float = 0.7875 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): 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") AZUR_FLAG: bool = AConfig.get_config("draw_card", "AZUR_FLAG")
FGO_FLAG: bool = AConfig.get_config("draw_card", "FGO_FLAG") FGO_FLAG: bool = AConfig.get_config("draw_card", "FGO_FLAG")
ONMYOJI_FLAG: bool = AConfig.get_config("draw_card", "ONMYOJI_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") PCR_TAI: bool = AConfig.get_config("draw_card", "PCR_TAI")
@@ -112,6 +119,7 @@ class Config(BaseModel, extra=Extra.ignore):
azur: AzurConfig = AzurConfig() azur: AzurConfig = AzurConfig()
fgo: FgoConfig = FgoConfig() fgo: FgoConfig = FgoConfig()
onmyoji: OnmyojiConfig = OnmyojiConfig() onmyoji: OnmyojiConfig = OnmyojiConfig()
ba: BaConfig = BaConfig()
driver = nonebot.get_driver() driver = nonebot.get_driver()
@@ -133,8 +141,9 @@ for game_flag, game_name in zip(
"FGO_FLAG", "FGO_FLAG",
"ONMYOJI_FLAG", "ONMYOJI_FLAG",
"PCR_TAI", "PCR_TAI",
"BA_FLAG"
], ],
["明日方舟", "原神", "赛马娘", "坎公骑冠剑", "公主连结", "碧蓝航线", "命运-冠位指定(FGO)", "阴阳师", "pcr台服卡池"], ["明日方舟", "原神", "赛马娘", "坎公骑冠剑", "公主连结", "碧蓝航线", "命运-冠位指定(FGO)", "阴阳师", "pcr台服卡池","碧蓝档案"],
): ):
AConfig.add_plugin_config( AConfig.add_plugin_config(
"draw_card", "draw_card",
+1 -1
View File
@@ -293,7 +293,7 @@ class AzurHandle(BaseHandle[AzurChar]):
type_ = ship.xpath("./tbody/tr/td[2]/p/small/text()")[0] # 舰船类型 type_ = ship.xpath("./tbody/tr/td[2]/p/small/text()")[0] # 舰船类型
try: try:
p = float(str(ship.xpath(".//sup/text()")[0]).strip("%")) p = float(str(ship.xpath(".//sup/text()")[0]).strip("%"))
except IndexError: except (IndexError, ValueError):
p = 0 p = 0
star = self.parse_star( star = self.parse_star(
ship.xpath("./tbody/tr/td[1]/div/div/div/a/img/@alt")[0] ship.xpath("./tbody/tr/td[1]/div/div/div/a/img/@alt")[0]
+150
View File
@@ -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"
)
+48 -29
View File
@@ -3,7 +3,6 @@ import random
import dateparser import dateparser
from lxml import etree from lxml import etree
from PIL import ImageDraw from PIL import ImageDraw
from bs4 import BeautifulSoup
from datetime import datetime from datetime import datetime
from urllib.parse import unquote from urllib.parse import unquote
from typing import List, Optional, Tuple from typing import List, Optional, Tuple
@@ -32,8 +31,9 @@ class Operator(BaseData):
class PrtsHandle(BaseHandle[Operator]): class PrtsHandle(BaseHandle[Operator]):
def __init__(self): def __init__(self):
super().__init__("prts", "明日方舟", "#eff2f5") super().__init__(game_name="prts", game_name_cn="明日方舟")
self.max_star = 6 self.max_star = 6
self.game_card_color = "#eff2f5"
self.config = draw_config.prts self.config = draw_config.prts
self.ALL_OPERATOR: List[Operator] = [] self.ALL_OPERATOR: List[Operator] = []
@@ -41,8 +41,8 @@ class PrtsHandle(BaseHandle[Operator]):
def get_card(self, add: float) -> Operator: def get_card(self, add: float) -> Operator:
star = self.get_star( 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_SIX_P + add,
self.config.PRTS_FIVE_P, self.config.PRTS_FIVE_P,
self.config.PRTS_FOUR_P, self.config.PRTS_FOUR_P,
@@ -53,7 +53,7 @@ class PrtsHandle(BaseHandle[Operator]):
all_operators = [ all_operators = [
x x
for x in self.ALL_OPERATOR 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 acquire_operator = None
@@ -74,7 +74,7 @@ class PrtsHandle(BaseHandle[Operator]):
acquire_operator = random.choice(all_operators) acquire_operator = random.choice(all_operators)
return acquire_operator 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 = [] # 获取所有角色 card_list = [] # 获取所有角色
add = 0.0 add = 0.0
count_idx = 0 count_idx = 0
@@ -109,7 +109,8 @@ class PrtsHandle(BaseHandle[Operator]):
def _draw(self, count: int, **kwargs) -> Message: def _draw(self, count: int, **kwargs) -> Message:
index2card = self.get_cards(count) 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 [] 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) result = self.format_result(index2card, up_list=up_list)
pool_info = self.format_pool_info() pool_info = self.format_pool_info()
@@ -162,7 +163,11 @@ class PrtsHandle(BaseHandle[Operator]):
def load_up_char(self): def load_up_char(self):
try: try:
data = self.load_data(f"draw_card_up/{self.game_name}_up_char.json") 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: except ValidationError:
logger.warning(f"{self.game_name}_up_char 解析出错") 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] avatar = char.xpath("./td[1]/div/div/div/a/img/@srcset")[0]
name = char.xpath("./td[2]/a/text()")[0] name = char.xpath("./td[2]/a/text()")[0]
star = char.xpath("./td[5]/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: except IndexError:
continue continue
member_dict = { member_dict = {
"头像": unquote(str(avatar).split(" ")[-2]), "头像": unquote(str(avatar).split(" ")[-2]),
"名称": remove_prohibited_str(str(name).strip()), "名称": remove_prohibited_str(str(name).strip()),
"星级": int(str(star).strip()), "星级": int(str(star).strip()),
"获取途径": [s for s in sources if s], "获取途径": sources,
} }
info[member_dict["名称"]] = member_dict info[member_dict["名称"]] = member_dict
self.dump_data(info) self.dump_data(info)
@@ -208,6 +214,7 @@ class PrtsHandle(BaseHandle[Operator]):
await self.update_up_char() await self.update_up_char()
async def update_up_char(self): async def update_up_char(self):
"""重载卡池"""
announcement_url = "https://ak.hypergryph.com/news.html" announcement_url = "https://ak.hypergryph.com/news.html"
result = await self.get_url(announcement_url) result = await self.get_url(announcement_url)
if not result: if not result:
@@ -221,41 +228,53 @@ class PrtsHandle(BaseHandle[Operator]):
end_time = None end_time = None
up_chars = [] up_chars = []
pool_img = "" pool_img = ""
title = "" for activity_url in activity_urls[:10]: # 减少响应时间, 10个就够了
for activity_url in activity_urls:
activity_url = f"https://ak.hypergryph.com{activity_url}" activity_url = f"https://ak.hypergryph.com{activity_url}"
result = await self.get_url(activity_url) result = await self.get_url(activity_url)
if not result: if not result:
logger.warning(f"{self.game_name_cn}获取公告 {activity_url} 出错") logger.warning(f"{self.game_name_cn}获取公告 {activity_url} 出错")
continue 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): for index, content in enumerate(contents):
if re.search("(.*)(寻访|复刻).*?开启", content.text): if re.search("(.*)(寻访|复刻).*?开启", content):
title = content.text title = re.split(r"[【】]", content)
if "【" in title and "】" in title: title = "".join(title[1:-1]) if "-" in title else title[1]
title = re.split(r"[【】]", title)[1] lines = [contents[index-2+_] for _ in range(8)] # 从 -2 开始是因为xpath获取的时间有的会在寻访开启这一句之前
lines = [str(contents[index + i + 1].text) for i in range(5)] lines.append("") # 防止IndexError,加个空字符串
time = "" for idx, line in enumerate(lines):
chars: List[str] = []
for line in lines:
match = re.search( match = re.search(
r"(\d{1,2}月\d{1,2}日.*?-.*?\d{1,2}月\d{1,2}日.*?$)", line r"(\d{1,2}月\d{1,2}日.*?-.*?\d{1,2}月\d{1,2}日.*?$)", line
) )
if match: if match:
time = match.group(1) time = match.group(1)
if "★" in line: """因为 <p> 的诡异排版,所以有了下面的一段"""
chars.append(line) 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: if not time:
continue continue
start, end = time.replace("月", "/").replace("日", "").split("-")[:2] start, end = time.replace("月", "/").replace("日", " ").split("-")[:2] # 日替换为空格是因为有日后面不接空格的情况,导致 split 出问题
start_time = dateparser.parse(start) start_time = dateparser.parse(start)
end_time = dateparser.parse(end) end_time = dateparser.parse(end)
pool_img = content.find_previous("img")["src"] pool_img = contents[index-2]
r"""两类格式:用/分割,用\分割;★+(概率)+名字,★+名字+(概率)"""
for char in chars: for char in chars:
star = char.split("(")[0].count("★") star = char.split("(")[0].count("★")
name = re.split(r"[:(]", char)[1] name = re.split(r"[:(]", char)[1] if "★(" not in char else re.split("):", char)[1] # 有的括号在前面有的在后面
names = name.split("/") if "/" in name else [name] if "\\" in name:
names = name.split("\\")
elif "/" in name:
names = name.split("/")
else:
names = [name] # 既有用/分割的,又有用\分割的
names = [name.replace("[限定]", "").strip() for name in names] names = [name.replace("[限定]", "").strip() for name in names]
if "权值" in char: if "权值" in char:
match = re.search(r"(在.*?以.*?(\d+).*?倍权值.*?)", char) match = re.search(r"(在.*?以.*?(\d+).*?倍权值.*?)", char)
@@ -269,7 +288,7 @@ class PrtsHandle(BaseHandle[Operator]):
up_chars.append( up_chars.append(
UpChar(name=name, star=star, limited=False, zoom=zoom) UpChar(name=name, star=star, limited=False, zoom=zoom)
) )
break break # 这里break会导致个问题:如果一个公告里有两个池子,会漏掉下面的池子,比如 5.19 的定向寻访。但目前我也没啥好想法解决
if title and start_time and end_time: if title and start_time and end_time:
if start_time <= datetime.now() <= end_time: if start_time <= datetime.now() <= end_time:
self.UP_EVENT = UpEvent( self.UP_EVENT = UpEvent(
+7 -6
View File
@@ -106,20 +106,21 @@ async def _(event: GroupMessageEvent):
"fudu", "FUDU_PROBABILITY" "fudu", "FUDU_PROBABILITY"
) and not _fudu_list.is_repeater(event.group_id): ) and not _fudu_list.is_repeater(event.group_id):
if random.random() < 0.2: 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) _fudu_list.set_repeater(event.group_id)
if img and msg: 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: 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: elif msg:
rst = msg rst = msg
else: else:
rst = "" rst = ""
if rst: if rst:
if rst.endswith("打断施法!"): await fudu.finish("[[_task|fudu]]" + rst)
rst = "打断" + rst
await fudu.send("[[_task|fudu]]" + rst)
async def get_fudu_img_hash(url, group_id): async def get_fudu_img_hash(url, group_id):
@@ -330,18 +330,19 @@ def get_country_data_image(world_data_dict: Dict) -> BuildImage:
f"{world_data_dict['层岩巨渊']['exploration_percentage'] / 10}%", f"{world_data_dict['层岩巨渊']['exploration_percentage'] / 10}%",
fill=(255, 255, 255), fill=(255, 255, 255),
) )
content_bk.text((300, 85), "地下矿区探索", fill=(239, 211, 114)) if world_data_dict.get('层岩巨渊·地下矿区'):
content_bk.text( content_bk.text((300, 85), "地下矿区探索", fill=(239, 211, 114))
(570, 85), content_bk.text(
f"{world_data_dict['层岩巨渊·地下矿区']['exploration_percentage'] / 10}%", (570, 85),
fill=(255, 255, 255), f"{world_data_dict['层岩巨渊·地下矿区']['exploration_percentage'] / 10}%",
) fill=(255, 255, 255),
content_bk.text((300, 150), "流明石触媒", fill=(239, 211, 114)) )
content_bk.text( content_bk.text((300, 150), "流明石触媒", fill=(239, 211, 114))
(570, 150), content_bk.text(
f"LV.{world_data_dict['层岩巨渊·地下矿区']['offerings'][0]['level']}", (570, 150),
fill=(255, 255, 255), f"LV.{world_data_dict['层岩巨渊·地下矿区']['offerings'][0]['level']}",
) fill=(255, 255, 255),
)
elif country in ["龙脊雪山"]: elif country in ["龙脊雪山"]:
content_bk.text((300, 40), "探索", fill=(239, 211, 114)) content_bk.text((300, 40), "探索", fill=(239, 211, 114))
content_bk.text( content_bk.text(
+3 -3
View File
@@ -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') 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) size = BuildImage(0, 0, font_size=50).getsize(send_user_nickname)
# QQ头像 # QQ头像
ava_bk = BuildImage(100 + size[0], 66, color='white', font_size=50) ava_bk = BuildImage(100 + size[0], 66, is_alpha=True, font_size=50)
ava = BuildImage(66, 66, background=BytesIO(await get_user_avatar(user_id))) ava = BuildImage(66, 66, is_alpha=True, background=BytesIO(await get_user_avatar(user_id)))
ava_bk.paste(ava) ava_bk.paste(ava)
ava_bk.text((100, 7), send_user_nickname) ava_bk.text((100, 7), send_user_nickname)
# ava_bk.show() # 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)) head.paste(ava_bk, (int((1000 - ava_bk_w) / 2), 300))
# 金额 # 金额
size = BuildImage(0, 0, font_size=150).getsize(amount) 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)) price.text((0, 0), amount, fill=(209, 171, 108))
# 金币中文 # 金币中文
head.paste(price, (int((1000 - size[0]) / 2) - 50, 460)) head.paste(price, (int((1000 - size[0]) / 2) - 50, 460))
+4 -4
View File
@@ -211,16 +211,16 @@ update_price = on_command("更新开箱价格", priority=1, permission=SUPERUSER
@update_price.handle() @update_price.handle()
async def _( event: MessageEvent): async def _(event: MessageEvent, arg: Message = CommandArg()):
await update_price.send(await util_get_buff_price(str(event.get_message()))) 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 = on_command("更新开箱图片", priority=1, permission=SUPERUSER, block=True)
@update_img.handle() @update_img.handle()
async def _(event: MessageEvent): async def _(event: MessageEvent, arg: Message = CommandArg()):
await update_img.send(await util_get_buff_img(str(event.get_message()))) await update_img.send(await util_get_buff_img(arg.extract_plain_text().strip() or "狂牙大行动"))
# 重置开箱 # 重置开箱
@@ -139,15 +139,14 @@ class OmegaPixivIllusts(db.Model):
data = await cls.select('pid').gino.all() data = await cls.select('pid').gino.all()
return [x[0] for x in data] return [x[0] for x in data]
@classmethod # async def test(cls, nsfw_tag: int = 1):
async def test(cls, nsfw_tag: int = 1): # if nsfw_tag is not None:
if nsfw_tag is not None: # query = cls.query.where(cls.nsfw_tag == nsfw_tag)
query = cls.query.where(cls.nsfw_tag == nsfw_tag) # else:
else: # query = cls.query
query = cls.query # query = query.where((cls.width - cls.height) < 50)
query = query.where((cls.width - cls.height) < 50) # for x in await query.gino.all():
for x in await query.gino.all(): # print(x.pid)
print(x.pid)
+11 -5
View File
@@ -18,6 +18,7 @@ usage:
查看 pix 好康图库 查看 pix 好康图库
指令: 指令:
pix ?*[tags]: 通过 tag 获取相似图片,不含tag时随机抽取 pix ?*[tags]: 通过 tag 获取相似图片,不含tag时随机抽取
pid [uid]: 通过uid获取图片
pix pid[pid]: 查看图库中指定pid图片 pix pid[pid]: 查看图库中指定pid图片
""".strip() """.strip()
__plugin_superuser_usage__ = """ __plugin_superuser_usage__ = """
@@ -49,7 +50,9 @@ __plugin_configs__ = {
"value": None, "value": None,
"help": "单次发送的图片数量达到指定值时转发为合并消息", "help": "单次发送的图片数量达到指定值时转发为合并消息",
"default_value": None, "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 nsfw_tag = 2
else: else:
nsfw_tag = 0 nsfw_tag = 0
if nsfw_tag != 0 and str(event.user_id) not in bot.config.superusers: if str(event.user_id) not in bot.config.superusers:
await pix.finish("你不能看这些噢,这些都是是留给管理员看的...") if (nsfw_tag == 1 and not Config.get_config("pix", "ALLOW_GROUP_SETU")) or (
if n := len(x) == 1 and is_number(x[0]): nsfw_tag == 2 and not Config.get_config("pix", "ALLOW_GROUP_R18")
num = int(x[-1]) ):
await pix.finish("你不能看这些噢,这些都是是留给管理员看的...")
if n := len(x) == 1 and is_number(x[0]) and int(x[0]) < 100:
num = int(x[0])
keyword = "" keyword = ""
elif n > 1: elif n > 1:
if is_number(x[-1]): if is_number(x[-1]):
+10 -3
View File
@@ -44,6 +44,8 @@ usage:
搜图 樱岛麻衣 搜图 樱岛麻衣
搜图 樱岛麻衣 5 搜图 樱岛麻衣 5
搜图 樱岛麻衣 5 r18 搜图 樱岛麻衣 5 r18
搜图 樱岛麻衣#1000users 5
【多个关键词用#分割】
【默认为 热度排序】 【默认为 热度排序】
【注意空格!!】【在线搜索会较慢】【数量可能不符?可能该页数量不够,也可能被R-18屏蔽】 【注意空格!!】【在线搜索会较慢】【数量可能不符?可能该页数量不够,也可能被R-18屏蔽】
""".strip() """.strip()
@@ -69,6 +71,11 @@ __plugin_configs__ = {
"value": 20, "value": 20,
"help": "作品最大页数限制,超过的作品会被略过", "help": "作品最大页数限制,超过的作品会被略过",
"default_value": 20 "default_value": 20
},
"ALLOW_GROUP_R18": {
"value": False,
"help": "允许群聊中使用 r18 参数",
"default_value": False
} }
} }
Config.add_plugin_config( 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()): async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip() msg = arg.extract_plain_text().strip()
if isinstance(event, GroupMessageEvent): 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) await pixiv_keyword.finish("(脸红#) 你不会害羞的 八嘎!", at_sender=True)
r18 = 0 if "r18" in msg else 1 r18 = 0 if "r18" in msg else 1
msg = msg.replace("r18", "").strip().split() msg = msg.replace("r18", "").strip().split()
@@ -161,8 +168,8 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
info_list = None info_list = None
num = 10 num = 10
page = 1 page = 1
if (n := len(msg)) == 1: if (n := len(msg)) > 0:
keyword = msg[0] keyword = msg[0].replace("#"," ")
if n > 1: if n > 1:
if not is_number(msg[1]): if not is_number(msg[1]):
await pixiv_keyword.finish("图片数量必须是数字!", at_sender=True) await pixiv_keyword.finish("图片数量必须是数字!", at_sender=True)
+3 -3
View File
@@ -62,12 +62,12 @@ async def _poke_event(event: PokeNotifyEvent):
rst = "气死我了!" rst = "气死我了!"
await poke_.finish(rst + random.choice(poke__reply), at_sender=True) await poke_.finish(rst + random.choice(poke__reply), at_sender=True)
rand = random.random() 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))) index = random.randint(0, len(os.listdir(IMAGE_PATH / "image_management" / path)))
result = f"id:{index}" + image(f"{index}.jpg", "image_management/" + path) result = f"id:{index}" + image(f"{index}.jpg", "image_management/" + path)
await poke_.send(result) 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: elif 0.3 < rand < 0.6:
voice = random.choice(os.listdir(RECORD_PATH / "dinggong")) voice = random.choice(os.listdir(RECORD_PATH / "dinggong"))
result = record(voice, "dinggong") result = record(voice, "dinggong")
+11 -4
View File
@@ -97,6 +97,7 @@ __plugin_configs__ = {
}, },
"TIMEOUT": {"value": 10, "help": "色图下载超时限制(秒)", "default_value": 10}, "TIMEOUT": {"value": 10, "help": "色图下载超时限制(秒)", "default_value": 10},
"SHOW_INFO": {"value": True, "help": "是否显示色图的基本信息,如PID等", "default_value": True}, "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反向代理") 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): if cmd[0] == "色图r" and isinstance(event, PrivateMessageEvent):
r18 = 1 r18 = 1
num = 10 num = 10
elif cmd[0] == "色图r" and isinstance(event, GroupMessageEvent): elif (
await setu.finish( cmd[0] == "色图r"
random.choice(["这种不好意思的东西怎么可能给这么多人看啦", "羞羞脸!给我滚出克私聊!", "变态变态变态变态大变态!"]) and isinstance(event, GroupMessageEvent)
) ):
if not Config.get_config("send_setu", "ALLOW_GROUP_R18"):
await setu.finish(
random.choice(["这种不好意思的东西怎么可能给这么多人看啦", "羞羞脸!给我滚出克私聊!", "变态变态变态变态大变态!"])
)
else:
r18 = 1
# 有 数字 的话先尝试本地色图id # 有 数字 的话先尝试本地色图id
if msg and is_number(msg): if msg and is_number(msg):
setu_list, code = await get_setu_list(int(msg), r18=r18) setu_list, code = await get_setu_list(int(msg), r18=r18)
+4 -5
View File
@@ -24,6 +24,8 @@ from io import BytesIO
import asyncio import asyncio
import random import random
import nonebot import nonebot
import time
import locale
import os import os
@@ -260,15 +262,12 @@ def _generate_card(
today_data.text((0, 25), f"金币 + {gold}") today_data.text((0, 25), f"金币 + {gold}")
_type = "sign" _type = "sign"
current_date = datetime.now() 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() data = current_date.date()
hour = current_date.hour
minute = current_date.minute
second = current_date.second
data_img = BuildImage( data_img = BuildImage(
0, 0,
0, 0,
plain_text=f"时间:{data} {weekdays[week]} {hour}:{minute}:{second}", plain_text=f"时间:{current_datetime_str}",
color=(255, 255, 255, 0), color=(255, 255, 255, 0),
font_size=20, font_size=20,
) )
+1 -1
View File
@@ -16,7 +16,7 @@ async def translate_msg(language_type, msg):
} }
data = (await AsyncHttpx.post(url, data=data)).json() data = (await AsyncHttpx.post(url, data=data)).json()
if data["errorCode"] == 0: if data["errorCode"] == 0:
return f'原文:{msg}\n翻译{data["translateResult"][0][0]["tgt"]}' return f'原文:{msg}\n翻译:{data["translateResult"][0][0]["tgt"]}'
return "翻译惜败.." return "翻译惜败.."
+78 -70
View File
@@ -1,70 +1,78 @@
from nonebot import on_command from nonebot import on_command
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent, Message from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent, Message
from nonebot.params import CommandArg from nonebot.params import CommandArg
from services.log import logger from services.log import logger
from ._data_source import get_data, gen_wbtop_pic from .data_source import gen_wbtop_pic,get_wbtop
from utils.utils import is_number from utils.utils import is_number
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from utils.http_utils import AsyncPlaywright from utils.http_utils import AsyncPlaywright
import asyncio import asyncio
import datetime
__zx_plugin_name__ = "微博热搜" __zx_plugin_name__ = "微博热搜"
__plugin_usage__ = """ __plugin_usage__ = """
usage: usage:
在QQ上吃个瓜 在QQ上吃个瓜
指令: 指令:
微博热搜:发送实时热搜 微博热搜:发送实时热搜
微博热搜 [id]:截图该热搜页面 微博热搜 [id]:截图该热搜页面
示例:微博热搜 5 示例:微博热搜 5
""".strip() """.strip()
__plugin_des__ = "刚买完瓜,在吃瓜现场" __plugin_des__ = "刚买完瓜,在吃瓜现场"
__plugin_cmd__ = ["微博热搜", "微博热搜 [id]"] __plugin_cmd__ = ["微博热搜", "微博热搜 [id]"]
__plugin_version__ = 0.1 __plugin_version__ = 0.2
__plugin_author__ = "HibiKier" __plugin_author__ = "HibiKier & yajiwa"
__plugin_settings__ = { __plugin_settings__ = {
"level": 5, "level": 5,
"default_status": True, "default_status": True,
"limit_superuser": False, "limit_superuser": False,
"cmd": ["微博热搜"], "cmd": ["微博热搜"],
} }
wbtop = on_command("wbtop", aliases={"微博热搜"}, priority=5, block=True) wbtop = on_command("wbtop", aliases={"微博热搜"}, priority=5, block=True)
wbtop_url = "https://v2.alapi.cn/api/new/wbtop" wbtop_url = "https://weibo.com/ajax/side/hotSearch"
wbtop_data = [] wbtop_data = []
@wbtop.handle() @wbtop.handle()
async def _(event: MessageEvent, arg: Message = CommandArg()): async def _(event: MessageEvent, arg: Message = CommandArg()):
global wbtop_data global wbtop_data
msg = arg.extract_plain_text().strip() msg = arg.extract_plain_text().strip()
if not wbtop_data or not msg: if not wbtop_data or not msg:
data, code = await get_data(wbtop_url) if wbtop_data:
if code != 200: now_time = datetime.datetime.now()
await wbtop.finish(data, at_sender=True) if now_time > wbtop_data["time"] + datetime.timedelta(minutes=5):
wbtop_data = data["data"] data, code = await get_wbtop(wbtop_url)
if not msg: if code != 200:
img = await asyncio.get_event_loop().run_in_executor( await wbtop.finish(data, at_sender=True)
None, gen_wbtop_pic, wbtop_data wbtop_data = data
) else:
await wbtop.send(img) data, code = await get_wbtop(wbtop_url)
logger.info( if code != 200:
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" await wbtop.finish(data, at_sender=True)
f" 查询微博热搜" wbtop_data = data
) if not msg:
if is_number(msg) and 0 < int(msg) <= 50: img = await asyncio.get_event_loop().run_in_executor(
url = wbtop_data[int(msg) - 1]["url"] None, gen_wbtop_pic, wbtop_data["data"]
await wbtop.send("开始截取数据...") )
img = await AsyncPlaywright.screenshot( await wbtop.send(img)
url, logger.info(
f"{IMAGE_PATH}/temp/wbtop_{event.user_id}.png", f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
"#pl_feed_main", f" 查询微博热搜"
wait_time=12 )
) if is_number(msg) and 0 < int(msg) <= 50:
if img: url = wbtop_data["data"][int(msg) - 1]["url"]
await wbtop.send(img) await wbtop.send("开始截取数据...")
else: img = await AsyncPlaywright.screenshot(
await wbtop.send("发生了一些错误.....") 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("发生了一些错误.....")
+62
View File
@@ -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())
+2
View File
@@ -127,6 +127,8 @@ def _(plugin: Plugin, user: User = Depends(token_to_user)) -> Result:
else: else:
if plugin.plugin_settings: if plugin.plugin_settings:
for key, value in 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) plugins2settings_manager.set_module_data(plugin.model, key, value)
if plugin.plugin_manager: if plugin.plugin_manager:
for key, value in plugin.plugin_manager: for key, value in plugin.plugin_manager:
+1 -1
View File
@@ -45,7 +45,7 @@ class CountLimit(BaseModel):
""" """
max_count: int max_count: int
status: bool status: bool
limit_type: bool limit_type: str
rst: Optional[str] rst: Optional[str]
+19 -11
View File
@@ -12,6 +12,7 @@ import numpy as np
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from io import BytesIO from io import BytesIO
from configs.path_config import IMAGE_PATH, FONT_PATH from configs.path_config import IMAGE_PATH, FONT_PATH
from services import logger
from utils.http_utils import AsyncHttpx from utils.http_utils import AsyncHttpx
from models.chat_history import ChatHistory from models.chat_history import ChatHistory
from configs.config import Config from configs.config import Config
@@ -19,10 +20,11 @@ from configs.config import Config
async def pre_precess(msg: List[str], config) -> str: async def pre_precess(msg: List[str], config) -> str:
return await asyncio.get_event_loop().run_in_executor( return await asyncio.get_event_loop().run_in_executor(
None, _pre_precess, msg,config) None, _pre_precess, msg, config
)
def _pre_precess(msg: List[str],config) -> str: def _pre_precess(msg: List[str], config) -> str:
"""对消息进行预处理""" """对消息进行预处理"""
# 过滤掉命令 # 过滤掉命令
command_start = tuple([i for i in config.command_start if i]) command_start = tuple([i for i in config.command_start if i])
@@ -46,7 +48,6 @@ def _pre_precess(msg: List[str],config) -> str:
return msg return msg
async def draw_word_cloud(messages, config): async def draw_word_cloud(messages, config):
wordcloud_dir = IMAGE_PATH / "wordcloud" wordcloud_dir = IMAGE_PATH / "wordcloud"
wordcloud_dir.mkdir(exist_ok=True, parents=True) wordcloud_dir.mkdir(exist_ok=True, parents=True)
@@ -57,19 +58,21 @@ async def draw_word_cloud(messages, config):
url = "https://ghproxy.com/https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/resources/image/wordcloud/default.png" url = "https://ghproxy.com/https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/resources/image/wordcloud/default.png"
try: try:
await AsyncHttpx.download_file(url, zx_logo_path) await AsyncHttpx.download_file(url, zx_logo_path)
except: except Exception as e:
logger.error(f"词云图片资源下载发生错误 {type(e)}:{e}")
return False return False
if not wordcloud_ttf.exists(): if not wordcloud_ttf.exists():
ttf_url = 'https://ghproxy.com/https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/resources/font/STKAITI.TTF' ttf_url = "https://ghproxy.com/https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/resources/font/STKAITI.TTF"
try: try:
await AsyncHttpx.download_file(ttf_url, wordcloud_ttf) await AsyncHttpx.download_file(ttf_url, wordcloud_ttf)
except: except Exception as e:
logger.error(f"词云字体资源下载发生错误 {type(e)}:{e}")
return False return False
topK = min(int(len(messages)), 100000) topK = min(int(len(messages)), 100000)
read_name = jieba.analyse.extract_tags(await pre_precess(messages, config), topK=topK, read_name = jieba.analyse.extract_tags(
withWeight=True, await pre_precess(messages, config), topK=topK, withWeight=True, allowPOS=()
allowPOS=()) )
name = [] name = []
value = [] value = []
for t in read_name: for t in read_name:
@@ -79,10 +82,11 @@ async def draw_word_cloud(messages, config):
name[i] = str(name[i]) name[i] = str(name[i])
dic = dict(zip(name, value)) dic = dict(zip(name, value))
if Config.get_config("word_clouds", "WORD_CLOUDS_TEMPLATE") == 1: if Config.get_config("word_clouds", "WORD_CLOUDS_TEMPLATE") == 1:
def random_pic(base_path: str) -> str: def random_pic(base_path: str) -> str:
path_dir = os.listdir(base_path) path_dir = os.listdir(base_path)
path = random.sample(path_dir, 1)[0] path = random.sample(path_dir, 1)[0]
return (str(base_path) + "/" + str(path)) return str(base_path) + "/" + str(path)
mask = np.array(IMG.open(random_pic(wordcloud_dir))) mask = np.array(IMG.open(random_pic(wordcloud_dir)))
wc = WordCloud( wc = WordCloud(
@@ -113,7 +117,11 @@ async def draw_word_cloud(messages, config):
async def get_list_msg(user_id, group_id, days): 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() messages_list = (
await ChatHistory()
._get_msg(uid=user_id, gid=group_id, type_="group", days=days)
.gino.all()
)
if messages_list: if messages_list:
messages = [i.text for i in messages_list] messages = [i.text for i in messages_list]
return messages return messages
Generated
+70 -482
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -42,6 +42,7 @@ python-multipart = "^0.0.5"
bilireq = "^0.1.2" bilireq = "^0.1.2"
emoji = "^1.7.0" emoji = "^1.7.0"
wordcloud = "^1.8.1" wordcloud = "^1.8.1"
rich = "^12.4.3"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
+3
View File
@@ -1,6 +1,7 @@
from gino import Gino from gino import Gino
from .log import logger from .log import logger
from utils.text_utils import prompt2cn
from configs.config import bind, sql_name, user, password, address, port, database from configs.config import bind, sql_name, user, password, address, port, database
@@ -9,6 +10,8 @@ db = Gino()
async def init(): async def init():
if not bind and (not user and not password and not address and not port and not database):
raise ValueError("\n" + prompt2cn("数据库配置未填写", 28))
i_bind = bind i_bind = bind
if not i_bind: if not i_bind:
i_bind = f"{sql_name}://{user}:{password}@{address}:{port}/{database}" i_bind = f"{sql_name}://{user}:{password}@{address}:{port}/{database}"
+2 -1
View File
@@ -9,7 +9,8 @@
"configs/utils", "configs/utils",
"poetry.lock", "poetry.lock",
"pyproject.toml", "pyproject.toml",
"resources/font" "resources/font",
"bot.py"
], ],
"add_file": [], "add_file": [],
"delete_file": [] "delete_file": []
+57 -19
View File
@@ -14,6 +14,7 @@ from retrying import retry
import asyncio import asyncio
import aiofiles import aiofiles
import httpx import httpx
import rich
class AsyncHttpx: class AsyncHttpx:
@@ -121,6 +122,7 @@ class AsyncHttpx:
headers: Optional[Dict[str, str]] = None, headers: Optional[Dict[str, str]] = None,
cookies: Optional[Dict[str, str]] = None, cookies: Optional[Dict[str, str]] = None,
timeout: Optional[int] = 30, timeout: Optional[int] = 30,
stream: bool = False,
**kwargs, **kwargs,
) -> bool: ) -> bool:
""" """
@@ -135,31 +137,67 @@ class AsyncHttpx:
:param headers: 请求头 :param headers: 请求头
:param cookies: cookies :param cookies: cookies
:param timeout: 超时时间 :param timeout: 超时时间
:param stream: 是否使用流式下载(流式写入+进度条,适用于下载大文件)
""" """
if isinstance(path, str): if isinstance(path, str):
path = Path(path) path = Path(path)
path.parent.mkdir(parents=True, exist_ok=True) path.parent.mkdir(parents=True, exist_ok=True)
try: try:
for _ in range(3): for _ in range(3):
try: if not stream:
content = ( try:
await cls.get( content = (
url, await cls.get(
params=params, url,
headers=headers, params=params,
cookies=cookies, headers=headers,
use_proxy=use_proxy, cookies=cookies,
proxy=proxy, use_proxy=use_proxy,
timeout=timeout, proxy=proxy,
**kwargs, timeout=timeout,
) **kwargs,
).content )
async with aiofiles.open(path, "wb") as wf: ).content
await wf.write(content) async with aiofiles.open(path, "wb") as wf:
logger.info(f"下载 {url} 成功.. Path:{path.absolute()}") await wf.write(content)
return True logger.info(f"下载 {url} 成功.. Path:{path.absolute()}")
except (TimeoutError, ConnectTimeout): return True
pass except (TimeoutError, ConnectTimeout):
pass
else:
if not headers:
headers = get_user_agent()
proxy = proxy if proxy else cls.proxy if use_proxy else None
try:
async with httpx.AsyncClient(proxies=proxy) as client:
async with client.stream(
"GET",
url,
params=params,
headers=headers,
cookies=cookies,
timeout=timeout,
**kwargs
) as response:
logger.info(f"开始下载 {path.name}.. Path: {path.absolute()}")
async with aiofiles.open(path, "wb") as wf:
total = int(response.headers["Content-Length"])
with rich.progress.Progress(
rich.progress.TextColumn(path.name),
"[progress.percentage]{task.percentage:>3.0f}%",
rich.progress.BarColumn(bar_width=None),
rich.progress.DownloadColumn(),
rich.progress.TransferSpeedColumn()
) as progress:
download_task = progress.add_task("Download", total=total)
async for chunk in response.aiter_bytes():
await wf.write(chunk)
await wf.flush()
progress.update(download_task, completed=response.num_bytes_downloaded)
logger.info(f"下载 {url} 成功.. Path:{path.absolute()}")
return True
except (TimeoutError, ConnectTimeout):
pass
else: else:
logger.error(f"下载 {url} 下载超时.. Path:{path.absolute()}") logger.error(f"下载 {url} 下载超时.. Path:{path.absolute()}")
except Exception as e: except Exception as e:
+1 -1
View File
@@ -1508,7 +1508,7 @@ async def text2image(
font=font, font=font,
) )
await A.atext((left_padding, top_padding), text, font_color) await A.atext((left_padding, top_padding), text, font_color)
# A.show() # A.show()
return A return A
+3 -7
View File
@@ -4,7 +4,6 @@ from pathlib import Path
class NonePluginCountManager(StaticData): class NonePluginCountManager(StaticData):
""" """
插件加载容忍管理器,当连续 max_count 次插件加载,视为删除插件,清楚数据 插件加载容忍管理器,当连续 max_count 次插件加载,视为删除插件,清楚数据
""" """
@@ -15,6 +14,8 @@ class NonePluginCountManager(StaticData):
:param max_count: 容忍最大次数 :param max_count: 容忍最大次数
""" """
super().__init__(file) super().__init__(file)
if not self._data:
self._data = {}
self._max_count = max_count self._max_count = max_count
def add_count(self, module: str, count: int = 1): def add_count(self, module: str, count: int = 1):
@@ -42,10 +43,5 @@ class NonePluginCountManager(StaticData):
:param module: 模块 :param module: 模块
""" """
if module in self._data.keys(): if module in self._data.keys():
return self._data.keys() > self._max_count return self._data[module] >= self._max_count
return False return False
+6 -13
View File
@@ -21,18 +21,17 @@ class Plugins2blockManager(StaticData):
with open(file, "r", encoding="utf8") as f: with open(file, "r", encoding="utf8") as f:
self._data = yaml.load(f) self._data = yaml.load(f)
if "PluginBlockLimit" in self._data.keys(): if "PluginBlockLimit" in self._data.keys():
self._data = ( self._data = self._data["PluginBlockLimit"] or {}
self._data["PluginBlockLimit"] if self._data["PluginBlockLimit"] else {}
)
def add_block_limit( def add_block_limit(
self, self,
plugin: str, plugin: str,
*,
status: Optional[bool] = True, status: Optional[bool] = True,
check_type: Optional[str] = "all", check_type: Optional[str] = "all",
limit_type: Optional[str] = "user", limit_type: Optional[str] = "user",
rst: Optional[str] = None, rst: Optional[str] = None,
data_dict: Optional[dict] = None, **kwargs # 用于接收额外实参
): ):
""" """
添加插件调用 block 限制 添加插件调用 block 限制
@@ -41,16 +40,10 @@ class Plugins2blockManager(StaticData):
:param check_type: 检查类型 'private'/'group'/'all',限制私聊/群聊/全部 :param check_type: 检查类型 'private'/'group'/'all',限制私聊/群聊/全部
:param limit_type: 限制类型 监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id :param limit_type: 限制类型 监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id
:param rst: 回复的话,为空则不回复 :param rst: 回复的话,为空则不回复
:param data_dict: 封装好的字典数据
""" """
if data_dict: status = status or True
status = data_dict.get("status") check_type = check_type or "all"
check_type = data_dict.get("check_type") limit_type = limit_type or "user"
limit_type = data_dict.get("limit_type")
rst = data_dict.get("rst")
status = status if status is not None else True
check_type = check_type if check_type else "all"
limit_type = limit_type if limit_type else "user"
if check_type not in ["all", "group", "private"]: if check_type not in ["all", "group", "private"]:
raise ValueError( raise ValueError(
f"{plugin} 添加block限制错误,‘check_type‘ 必须为 'private'/'group'/'all'" f"{plugin} 添加block限制错误,‘check_type‘ 必须为 'private'/'group'/'all'"
+5 -12
View File
@@ -34,7 +34,7 @@ class Plugins2cdManager(StaticData):
check_type: Optional[str] = "all", check_type: Optional[str] = "all",
limit_type: Optional[str] = "user", limit_type: Optional[str] = "user",
rst: Optional[str] = None, rst: Optional[str] = None,
data_dict: Optional[dict] = None, **kwargs # 用于接收额外实参
): ):
""" """
添加插件调用 cd 限制 添加插件调用 cd 限制
@@ -44,18 +44,11 @@ class Plugins2cdManager(StaticData):
:param check_type: 检查类型 'private'/'group'/'all',限制私聊/群聊/全部 :param check_type: 检查类型 'private'/'group'/'all',限制私聊/群聊/全部
:param limit_type: 限制类型 监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id :param limit_type: 限制类型 监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id
:param rst: 回复的话,为空则不回复 :param rst: 回复的话,为空则不回复
:param data_dict: 封装好的字典数据
""" """
if data_dict: cd = cd or 5
cd = data_dict.get("cd") status = status or True
status = data_dict.get("status") check_type = check_type or "all"
check_type = data_dict.get("check_type") limit_type = limit_type or "user"
limit_type = data_dict.get("limit_type")
rst = data_dict.get("rst")
cd = cd if cd is not None else 5
status = status if status is not None else True
check_type = check_type if check_type else "all"
limit_type = limit_type if limit_type else "user"
if check_type not in ["all", "group", "private"]: if check_type not in ["all", "group", "private"]:
raise ValueError( raise ValueError(
f"{plugin} 添加cd限制错误,‘check_type‘ 必须为 'private'/'group'/'all'" f"{plugin} 添加cd限制错误,‘check_type‘ 必须为 'private'/'group'/'all'"
+4 -10
View File
@@ -33,7 +33,7 @@ class Plugins2countManager(StaticData):
status: Optional[bool] = True, status: Optional[bool] = True,
limit_type: Optional[str] = "user", limit_type: Optional[str] = "user",
rst: Optional[str] = None, rst: Optional[str] = None,
data_dict: Optional[dict] = None, **kwargs # 用于接收额外实参
): ):
""" """
添加插件调用 次数 限制 添加插件调用 次数 限制
@@ -42,16 +42,10 @@ class Plugins2countManager(StaticData):
:param status: 默认开关状态 :param status: 默认开关状态
:param limit_type: 限制类型 监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id :param limit_type: 限制类型 监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id
:param rst: 回复的话,为空则不回复 :param rst: 回复的话,为空则不回复
:param data_dict: 封装好的字典数据
""" """
if data_dict: max_count = max_count or 5
max_count = data_dict.get("max_count") status = status or True
status = data_dict.get("status") limit_type = limit_type or "user"
limit_type = data_dict.get("limit_type")
rst = data_dict.get("rst")
status = status if status is not None else True
limit_type = limit_type if limit_type else "user"
max_count = max_count if max_count is not None else 5
if limit_type not in ["user", "group"]: if limit_type not in ["user", "group"]:
raise ValueError(f"{plugin} 添加count限制错误,‘limit_type‘ 必须为 'user'/'group'") raise ValueError(f"{plugin} 添加count限制错误,‘limit_type‘ 必须为 'user'/'group'")
self._data[plugin] = { self._data[plugin] = {
+8 -17
View File
@@ -47,26 +47,17 @@ class Plugins2settingsManager(StaticData):
:param plugin_type: 插件类型 :param plugin_type: 插件类型
:param cost_gold: 需要消费的金币 :param cost_gold: 需要消费的金币
""" """
if kwargs: level = level or 5
level = kwargs.get("level") if kwargs.get("level") is not None else 5 cmd = cmd or []
default_status = ( cost_gold = cost_gold or 0
kwargs.get("default_status")
if kwargs.get("default_status") is not None
else True
)
limit_superuser = (
kwargs.get("limit_superuser")
if kwargs.get("limit_superuser") is not None
else False
)
cmd = kwargs.get("cmd") if kwargs.get("cmd") is not None else []
cost_gold = cost_gold if kwargs.get("cost_gold") else 0
self._data[plugin] = { self._data[plugin] = {
"level": level if level is not None else 5, "level": level if level is not None else 5,
"default_status": default_status if default_status is not None else True, "default_status": default_status if default_status is not None else True,
"limit_superuser": limit_superuser "limit_superuser": (
if limit_superuser is not None limit_superuser
else False, if limit_superuser is not None
else False
),
"cmd": cmd, "cmd": cmd,
"plugin_type": list( "plugin_type": list(
plugin_type if plugin_type is not None else ("normal",) plugin_type if plugin_type is not None else ("normal",)
+11 -8
View File
@@ -1,4 +1,6 @@
from typing import Union, List, Optional from typing import Union, List, Optional
from configs.path_config import IMAGE_PATH, DATA_PATH, RECORD_PATH, TEXT_PATH, FONT_PATH, LOG_PATH
from .data_class import StaticData from .data_class import StaticData
from pathlib import Path from pathlib import Path
from ruamel.yaml import YAML from ruamel.yaml import YAML
@@ -47,19 +49,21 @@ class ResourcesManager(StaticData):
if module in self._data.keys(): if module in self._data.keys():
for x in self._data[module].keys(): for x in self._data[module].keys():
move_file = Path(self._data[module][x]) move_file = Path(self._data[module][x])
if move_file.exists(): if move_file not in [IMAGE_PATH, DATA_PATH, RECORD_PATH, TEXT_PATH, FONT_PATH, LOG_PATH]:
shutil.rmtree(move_file.absolute(), ignore_errors=True) if move_file.exists():
logger.info(f"已清除插件 {module} 资源路径:{self._data[module][x]}") shutil.rmtree(move_file.absolute(), ignore_errors=True)
del self._data[module][x] logger.info(f"已清除插件 {module} 资源路径:{self._data[module][x]}")
del self._data[module][x]
else: else:
if isinstance(source_file, Path): if isinstance(source_file, Path):
source_file = str(source_file.absolute()) source_file = str(source_file.absolute())
if source_file: if source_file:
if module in self._data.keys() and source_file in self._data[module].keys(): if module in self._data.keys() and source_file in self._data[module].keys():
move_file = Path(self._data[module][source_file]) move_file = Path(self._data[module][source_file])
if move_file.exists(): if move_file not in [IMAGE_PATH, DATA_PATH, RECORD_PATH, TEXT_PATH, FONT_PATH, LOG_PATH]:
shutil.rmtree(move_file.absolute(), ignore_errors=True) if move_file.exists():
del self._data[module][source_file] shutil.rmtree(move_file.absolute(), ignore_errors=True)
del self._data[module][source_file]
self.save() self.save()
def start_move(self): def start_move(self):
@@ -97,7 +101,6 @@ class ResourcesManager(StaticData):
""" """
添加临时清理文件夹 添加临时清理文件夹
:param path: 路径 :param path: 路径
:param recursive: 是否将该目录下的所有目录也添加为临时文件夹
""" """
if isinstance(path, str): if isinstance(path, str):
path = Path(path) path = Path(path)
+12
View File
@@ -0,0 +1,12 @@
def prompt2cn(text: str, count: int, s: str = "#") -> str:
"""
格式化中文提示
:param text: 文本
:param count: 个数
:param s: #
"""
return s * count + "\n" + s * 6 + " " + text + " " + s * 6 + "\n" + s * count