Compare commits

...
73 Commits
Author SHA1 Message Date
HibiKier 0f59895c3b update v0.1.4.5 2022-04-04 20:33:37 +08:00
HibiKier b5ce4fab31 Merge pull request #135 from Sakuracio/main
fix GB18030 to UTF-8.
2022-03-28 00:04:32 +08:00
MangataAkihi 062e4c2062 fix GB18030 to UTF-8. 2022-03-26 23:43:21 +08:00
MangataAkihi 0adb66a8bf fix GB18030 to UTF-8. 2022-03-26 23:43:06 +08:00
HibiKier 2aad517112 fix statistics_handle 2022-03-21 18:43:06 +08:00
HibiKier 14d1512b1e update v0.1.4.4 2022-03-18 20:55:05 +08:00
HibiKier 3f533516af Update README.md 2022-03-07 20:46:48 +08:00
HibiKier f1354308fd Update set_admin_permissions.py 2022-03-07 20:46:13 +08:00
HibiKier c06248e46b Update README.md 2022-03-06 18:00:58 +08:00
HibiKier 9c8eaed938 Update pid_search.py 2022-03-06 18:00:35 +08:00
HibiKier 2081302e32 Update README.md 2022-03-06 17:04:34 +08:00
HibiKier 678fc19f2d Update __init__.py 2022-03-06 17:02:59 +08:00
HibiKier adc2757e8e Merge pull request #128 from Sakuracio/main
去除背景图上的(白点?
2022-03-03 21:18:43 +08:00
Sakuracio 82a1261371 去除背景图上的(白点? 2022-03-03 00:38:54 +08:00
HibiKier 067da9dc57 update 0.1.4.3 2022-03-03 00:18:59 +08:00
HibiKier 0eb9f9fc6f Update README.md 2022-03-01 21:02:36 +08:00
HibiKier aee1340714 Update README.md 2022-03-01 21:02:18 +08:00
HibiKier a72a9bd8cb Update __version__ 2022-03-01 21:01:52 +08:00
HibiKier 7250410653 Update __version__ 2022-03-01 20:59:52 +08:00
HibiKier 12f55800b0 x 2022-03-01 20:26:11 +08:00
HibiKier 53b375b4b2 Add files via upload 2022-02-27 18:26:39 +08:00
HibiKier fba27abe29 Update pyproject.toml 2022-02-27 17:56:56 +08:00
HibiKier 4caa3dec04 Update poetry.lock 2022-02-27 17:56:26 +08:00
HibiKier 516b0f5ccb Add files via upload 2022-02-27 17:34:19 +08:00
HibiKier b5098be08e Update __version__ 2022-02-27 17:32:40 +08:00
HibiKier 5b08a4650c Update README.md 2022-02-27 17:31:51 +08:00
HibiKier eb02157b94 Add files via upload 2022-02-27 17:30:26 +08:00
HibiKier 222e1719a4 Delete plugins/draw_card directory 2022-02-27 17:29:57 +08:00
HibiKier 3c8983e753 Update task_hook.py 2022-02-26 00:11:26 +08:00
HibiKier 1fe41dd823 Update __version__ 2022-02-25 23:21:03 +08:00
HibiKier e8df59203c Update README.md 2022-02-25 23:17:42 +08:00
HibiKier 0f6690606b Update README.md 2022-02-25 22:50:24 +08:00
HibiKier a0115309dd Merge pull request #124 from Nova-Noir/main
Fixed error in custom_welcome_msg and message_builder
2022-02-24 16:12:39 +08:00
HibiKier ca18d45786 Update message_builder.py 2022-02-24 16:12:25 +08:00
NovaNo1r 76df21036a Fixed error in custom_welcome_msg and message_builder 2022-02-23 21:45:53 -08:00
HibiKier c674d581b7 Update README.md 2022-02-23 13:52:10 +08:00
HibiKier fc53c3c487 Update _data_source.py 2022-02-23 13:51:19 +08:00
HibiKier 7a4e31df80 Update __init__.py 2022-02-23 13:49:57 +08:00
HibiKier f1d308d559 Update __init__.py 2022-02-23 13:49:21 +08:00
HibiKier b7914bac05 Update README.md 2022-02-21 19:05:42 +08:00
HibiKier 8c40d88cc4 Update .gitignore 2022-02-21 18:20:43 +08:00
HibiKier b4ed6dcb5d Delete server_ip.py 2022-02-21 18:18:30 +08:00
HibiKier 0d3bd8a89c fix bug 2022-02-21 15:59:17 +08:00
HibiKier 87f0189abd Update README.md 2022-02-20 11:39:00 +08:00
HibiKier 1c17ac1f9d Update __init__.py 2022-02-20 11:06:32 +08:00
HibiKier f038763041 fix bug 2022-02-20 11:06:04 +08:00
HibiKier eaf6d54757 Update README.md 2022-02-19 23:24:21 +08:00
HibiKier d7368c83bc Update query_resource.py 2022-02-19 20:12:51 +08:00
HibiKier 4011620da1 Update README.md 2022-02-19 20:06:53 +08:00
HibiKier 6e0b91531e Update __version__ 2022-02-19 20:05:36 +08:00
HibiKier b8ca1d68aa Update update_info.json 2022-02-19 20:04:19 +08:00
HibiKier 2b9cda40a0 Update query_resource.py 2022-02-19 20:03:40 +08:00
HibiKier d8983bdcbf Update README.md 2022-02-19 18:32:09 +08:00
HibiKier 14bce52f2f Update README.md 2022-02-19 18:31:41 +08:00
HibiKier a7b0f7da68 Update README.md 2022-02-19 18:29:40 +08:00
HibiKier 7dd68cf1ef fit nonebot.beta2 2022-02-19 18:20:19 +08:00
HibiKier ab443a21ec Merge pull request #122 from AkashiCoin/main
fix: 修复远古时期残留的epic推送问题
2022-02-19 15:54:59 +08:00
HibiKier ed7e8a8a4c Merge pull request #109 from SAGIRI-kawaii/main
优化BuildImage.circle()锯齿问题
2022-02-19 15:54:14 +08:00
HibiKier a1435e2fc4 Merge branch 'main' into main 2022-02-19 15:53:47 +08:00
AkashiCoin 44a80f89da fix: 修复远古时期残留的epic推送问题 2022-02-19 14:57:23 +08:00
HibiKier 164f6af30c Merge pull request #119 from evlic/fixEPIC
epic restful 替换
2022-02-18 16:22:16 +08:00
evlic 9093a79101 epic restful 替换 2022-02-16 13:31:24 +08:00
HibiKier 14cce89a18 Update README.md 2022-02-11 13:16:48 +08:00
HibiKier 4bce20a0a9 Update data_source.py 2022-02-11 13:16:10 +08:00
HibiKier f32d8b4eff Update __version__ 2022-02-10 11:58:22 +08:00
HibiKier 685f2b8c53 Update README.md 2022-02-10 11:57:41 +08:00
HibiKier 1e63ca2549 Update buy.py 2022-02-10 11:55:36 +08:00
HibiKier c1bc4eff2b Update README.md 2022-02-09 23:02:14 +08:00
HibiKier bc903d3813 Update __init__.py 2022-02-09 22:28:49 +08:00
HibiKier 80fc17b610 Update __version__ 2022-02-09 21:44:09 +08:00
HibiKier 0efb2bbc60 update v0.1 2022-02-09 20:05:49 +08:00
SAGIRI-kawaii 95ad533b87 优化BuildImage.circle()锯齿问题 2022-01-24 23:52:57 +08:00
HibiKier b6e3240df9 Update README.md 2022-01-16 19:22:03 +08:00
763 changed files with 12086 additions and 8684 deletions
+1 -2
View File
@@ -139,10 +139,9 @@ dmypy.json
cython_debug/
test.py
server.py
server_ip.py
member_activity_handle.py
Yu-Gi-Oh/
search_image/
black_word/
csgo/
fantasy_card/
+129 -5
View File
@@ -18,6 +18,10 @@
## 声明
此项目仅用于学习交流,请勿用于非法用途
## Gitee
# [Gitee](https://gitee.com/two_Dimension/zhenxun_bot)
## 未完成的文档
# [传送门](https://hibikier.github.io/zhenxun_bot/)
@@ -33,7 +37,7 @@
[AkashiCoin/nonebot_plugins_zhenxun_bot](https://github.com/AkashiCoin/nonebot_plugins_zhenxun_bot)
## 来点优点?
一.作为bot:
* 实现了许多功能,且提供了大量功能管理命令
* 通过Config配置项将所有插件配置统计保存至config.yaml,利于统一用户修改
* 方便增删插件,原生nonebot2 matcher,不需要额外修改,仅仅通过简单的配置属性就可以生成`帮助图片`和`帮助信息`
@@ -67,6 +71,8 @@
- [x] 原神资源查询 (借鉴[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)插件)
- [x] 原神便笺查询
- [x] 原神玩家查询
- [x] 原神树脂提醒
- [x] 原神签到/自动签到
- [x] 金币红包
- [x] 微博热搜
- [x] B站主播/UP/番剧订阅
@@ -147,6 +153,7 @@
- [x] 清理临时数据
- [x] 增删群认证
- [x] 同意/拒绝好友/群聊请求
- [x] 配置重载
#### 超级用户的被动技能
- [x] 邀请入群提醒(别人邀请真寻入群)
@@ -171,6 +178,7 @@
- [x] 群管理员监控,自动为新晋管理员增加权限,为失去群管理员的用户删除权限
- [x] 群权限系统
- [x] 定时更新权限
- [x] 自动配置重载
</details>
## 详细配置请前往文档,以下为最简部署和配置,如果你有基础并学习过nonebot2的话
@@ -183,7 +191,7 @@
# 配置gocq
在 https://github.com/Mrs4s/go-cqhttp 下载Releases最新版本,运行后选择反向代理,
后将gocq的配置文件config.yml中的universal改为universal: ws://127.0.0.1:8080/cqhttp/ws
后将gocq的配置文件config.yml中的universal改为universal: ws://127.0.0.1:8080/onebot/v11/ws
# 获取代码
git clone https://github.com/HibiKier/zhenxun_bot.git
@@ -192,12 +200,14 @@ git clone https://github.com/HibiKier/zhenxun_bot.git
cd zhenxun_bot
# 安装依赖
pip install -r requirements.txt
pip install poetry # 安装 poetry
poetry install # 安装依赖
# 进行基础配置
####请查看 配置 部分####
# 开始运行
poetry shell # 进入虚拟环境
python bot.py
```
@@ -217,10 +227,125 @@ python bot.py
```
## 使用Docker
__Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
#### GitHub:[Sakuracio/zxenv](https://github.com/Sakuracio/zxenv)
#### DockerHub:[hibikier/zhenxun_bot](https://hub.docker.com/r/hibikier/zhenxun_bot)
## 更新
### 2022/4/4 \[v0.1.4.5]
* 替换了bt搜索URL
* 优化使用playwright的相关代码
* 原神玩家查询新增层岩巨渊探索
* 修复原神便笺角色头像黑框
* 修复同意群聊请求错误
* 提供webui方面的api
* 新增web-ui(前端简易管理页面插件)插件
### 2022/3/21
* 修复statistics_handle.py乱码
### 2022/3/18 \[v0.1.4.4]
* 修复戳一戳无法功能关闭与ban禁用
* 新增图片搜索 search_image
### 2022/3/7
* 优化增删权限插件
### 2022/3/6
* 修复树脂提醒无法开启
* 修复p搜图片路径错误
### 2022/3/3 \[v0.1.4.3]
* 修复手动同意群聊请求依旧退出
### 2022/3/1 \[v0.1.4.2]
* 0.1.4内容
### 2022/2/27 \[v0.1.4.1]
* 优化抽卡
### 2022/2/25 \[v0.1.4]
* PIX提供配置MAX_ONCE_NUM2FORWARD:当单次发送图片超过指定张数且在群聊时,将转为合并消息
* 修复点歌无法正确发送
* 修复我有一个朋友有时文本会包含CQ码
* 修复群欢消息被动控制文本未删除 [@pull/124](https://github.com/HibiKier/zhenxun_bot/pull/124)
* message_builder.image不再提供参数:abspath
### 2022/2/23
* 插件状态将从已加载插件模块中读取
* 修复b站订阅插件订阅失败问题
* 修复重启命令无法使用
### 2022/2/21 \[v0.1.3.2]
* 群权限为-1时,超级用户发送的命令依旧生效
* 当群权限为-1时,被动技能也将不会发送
* 修复功能开关,b站转发解析,复读 ignore无法使用
* 修复色图下载文件名与路径错误
* 修复被动技能提醒有时无法删除控制文本
### 2022/2/20 \[v0.1.3.1]
* 修复pix下载临时文件目录错误
* 修复AI,天气,发送图片ignore导致无法使用
* 修复纯文本被动技能提醒有时无法删除控制文本
### 2022/2/19 \[v0.1.3] (nonebot beta2!)
* 由于nonebot升级版本,提供更新建议(__该次升级将会导致nonebot.beta1以下的插件无法使用__
* 保证services,utils,configs,plugins,basic_plugins,文件夹均为最新
* 根目录有pyproject.toml与poetry.lock
* 执行命令:
* pip3 install poetry
* poetry install
* poetry shell
* playwright install chromium
* python3 bot.py
* 适配nonebot.beta2
* 删除图片搜索 nonebot_plugin_picsearcher
* 替换cos api
* 原神签到树脂提醒新增绑定群里,在某群绑定uid就会在某群发送提醒信息(有好友则私聊,需要重新绑定uid
* 修改update_info.json
* 修复原神资源查询下载数据失败时导致报错
* 优化BuildImage.circle()锯齿问题 [@pull/109](https://github.com/HibiKier/zhenxun_bot/pull/109)
* epic restful 替换 [@pull/119](https://github.com/HibiKier/zhenxun_bot/pull/119)
* fix: 修复远古时期残留的epic推送问题 [@pull/122](https://github.com/HibiKier/zhenxun_bot/pull/122)
### 2021/2/11
* 修复pix不使用反代无法下载图片
### 2021/2/10 \[v0.1.1]
* 修复购买道具出错
### 2021/2/9 \[v0.1]
* 新增原神自动签到和手动签到
* 新增原神树脂提醒
* 新增手动重载Config.yaml命令以及重载配置定时任务(极少部分帮助或配置可能需要重启
* 修改了发送本地图库的matcher,改为on_message
* register_use可以通过返回值发送消息
* 修复修改商品时限制时间出错
* 修复超时商品依旧可以被购买
### 2021/1/16 \[v0.0.9.0]
* Ai提供文本敏感词过滤器
@@ -296,7 +421,6 @@ __..... 更多更新信息请查看文档__
## Todo
- [ ] docker容器
- [ ] web管理
## 感谢
@@ -311,5 +435,5 @@ __..... 更多更新信息请查看文档__
[H-K-Y / Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot) :原神bot,这是一个基于nonebot和HoshinoBot的原神娱乐及信息查询插件
[NothAmor / nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says) :基于nonebot2机器人框架的鲁迅说插件
[Kyomotoi / AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus) :一个~~特二刺螈~~(文爱)的适用于任何bot的词库
[Ailitonia / omega-miya](https://github.com/Ailitonia/omega-miya) :基于nonebot2的qq机器人
[Ailitonia / omega-miya](https://github.com/Ailitonia/omega-miya) :基于nonebot2的qq机器人
[KimigaiiWuyi / GenshinUID]("https://github.com/KimigaiiWuyi/GenshinUID") :一个基于HoshinoBot/NoneBot2的原神UID查询插件
+1 -1
View File
@@ -1 +1 @@
__version__: v0.0.9.0
__version__: v0.1.4.5
@@ -1,319 +1,323 @@
from typing import List
from nonebot.adapters.cqhttp.message import MessageSegment
from services.log import logger
from configs.path_config import DATA_PATH
from utils.message_builder import image
from utils.utils import get_local_proxy, get_bot
from pathlib import Path
from models.group_member_info import GroupInfoUser
from datetime import datetime
from services.db_context import db
from models.level_user import LevelUser
from configs.config import Config
from utils.manager import group_manager, plugins2settings_manager, plugins_manager
from utils.image_utils import BuildImage
from utils.http_utils import AsyncHttpx
import asyncio
import time
import os
try:
import ujson as json
except ModuleNotFoundError:
import json
async def group_current_status(group_id: int) -> str:
"""
获取当前所有通知的开关
:param group_id: 群号
"""
rst = "[被动技能 状态]\n"
_data = group_manager.get_task_data()
for task in _data.keys():
rst += f'{_data[task]}: {"√" if await group_manager.check_group_task_status(group_id, task) else "×"}\n'
return rst.strip()
custom_welcome_msg_json = (
Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json"
)
async def custom_group_welcome(
msg: str, imgs: List[str], user_id: int, group_id: int
) -> str:
"""
替换群欢迎消息
:param msg: 欢迎消息文本
:param imgs: 欢迎消息图片,只取第一张
:param user_id: 用户id,用于log记录
:param group_id: 群号
"""
img_result = ""
img = imgs[0] if imgs else ""
result = ""
if os.path.exists(DATA_PATH + f"custom_welcome_msg/{group_id}.jpg"):
os.remove(DATA_PATH + f"custom_welcome_msg/{group_id}.jpg")
if not custom_welcome_msg_json.exists():
custom_welcome_msg_json.parent.mkdir(parents=True, exist_ok=True)
data = {}
else:
try:
data = json.load(open(custom_welcome_msg_json, "r"))
except FileNotFoundError:
data = {}
try:
if msg:
data[str(group_id)] = str(msg)
json.dump(
data, open(custom_welcome_msg_json, "w"), indent=4, ensure_ascii=False
)
logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息 {msg}")
result += msg
if img:
await AsyncHttpx.download_file(
img, DATA_PATH + f"custom_welcome_msg/{group_id}.jpg"
)
img_result = image(abspath=DATA_PATH + f"custom_welcome_msg/{group_id}.jpg")
logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息图片")
except Exception as e:
logger.error(f"GROUP {group_id} 替换群消息失败 e:{e}")
return "替换群消息失败.."
return f"替换群欢迎消息成功:\n{result}" + img_result
task_data = None
async def change_group_switch(cmd: str, group_id: int, is_super: bool = False):
global task_data
"""
修改群功能状态
:param cmd: 功能名称
:param group_id: 群号
:param is_super: 是否位超级用户,超级用户用于私聊开关功能状态
"""
if not task_data:
task_data = group_manager.get_task_data()
group_help_file = Path(DATA_PATH) / "group_help" / f"{group_id}.png"
status = cmd[:2]
cmd = cmd[2:]
type_ = "plugin"
modules = plugins2settings_manager.get_plugin_module(cmd, True)
if cmd == "全部被动":
for task in task_data:
if status == "开启":
if not await group_manager.check_group_task_status(group_id, task):
await group_manager.open_group_task(group_id, task)
else:
if await group_manager.check_group_task_status(group_id, task):
await group_manager.close_group_task(group_id, task)
if group_help_file.exists():
group_help_file.unlink()
return f"已 {status} 全部被动技能!"
if cmd == "全部功能":
for f in plugins2settings_manager.get_data():
if status == "开启":
group_manager.unblock_plugin(f, group_id)
else:
group_manager.block_plugin(f, group_id)
return f"已 {status} 全部功能!"
if cmd in [task_data[x] for x in task_data.keys()]:
type_ = "task"
modules = [x for x in task_data.keys() if task_data[x] == cmd]
for module in modules:
if is_super:
module = f"{module}:super"
if status == "开启":
if type_ == "task":
if await group_manager.check_group_task_status(group_id, module):
return f"被动 {task_data[module]} 正处于开启状态!不要重复开启."
await group_manager.open_group_task(group_id, module)
else:
if group_manager.get_plugin_status(module, group_id):
return f"功能 {cmd} 正处于开启状态!不要重复开启."
group_manager.unblock_plugin(module, group_id)
else:
if type_ == "task":
if not await group_manager.check_group_task_status(group_id, module):
return f"被动 {task_data[module]} 正处于关闭状态!不要重复关闭."
await group_manager.close_group_task(group_id, module)
else:
if not group_manager.get_plugin_status(module, group_id):
return f"功能 {cmd} 正处于关闭状态!不要重复关闭."
group_manager.block_plugin(module, group_id)
if group_help_file.exists():
group_help_file.unlink()
if is_super:
for file in os.listdir(Path(DATA_PATH) / "group_help"):
file = Path(DATA_PATH) / "group_help" / file
file.unlink()
else:
_help_image = Path(DATA_PATH) / "group_help" / f"{group_id}.png"
if _help_image.exists():
_help_image.unlink()
return f"{status} {cmd} 功能!"
def set_plugin_status(cmd: str, block_type: str = "all"):
"""
设置插件功能状态(超级用户使用)
:param cmd: 功能名称
:param block_type: 限制类型, 'all': 私聊+群里, 'private': 私聊, 'group': 群聊
"""
status = cmd[:2]
cmd = cmd[2:]
module = plugins2settings_manager.get_plugin_module(cmd)
if status == "开启":
plugins_manager.unblock_plugin(module)
else:
plugins_manager.block_plugin(module, block_type=block_type)
for file in os.listdir(Path(DATA_PATH) / "group_help"):
file = Path(DATA_PATH) / "group_help" / file
file.unlink()
async def get_plugin_status():
"""
获取功能状态
"""
return await asyncio.get_event_loop().run_in_executor(None, _get_plugin_status)
def _get_plugin_status() -> MessageSegment:
"""
合成功能状态图片
"""
rst = "\t功能\n"
flag_str = "状态".rjust(4) + "\n"
for module in plugins_manager.get_data():
flag = plugins_manager.get_plugin_block_type(module)
flag = flag.upper() + " CLOSE" if flag else "OPEN"
try:
plugin_name = plugins_manager.get(module)["plugin_name"]
if (
"[Hidden]" in plugin_name
or "[Admin]" in plugin_name
or "[Superuser]" in plugin_name
):
continue
rst += f"{plugin_name}"
except KeyError:
rst += f"{module}"
if plugins_manager.get(module)["error"]:
rst += "[ERROR]"
rst += "\n"
flag_str += f"{flag}\n"
height = len(rst.split("\n")) * 24
a = BuildImage(250, height, font_size=20)
a.text((10, 10), rst)
b = BuildImage(200, height, font_size=20)
b.text((10, 10), flag_str)
A = BuildImage(500, height)
A.paste(a)
A.paste(b, (270, 0))
return image(b64=A.pic2bs4())
async def update_member_info(group_id: int, remind_superuser: bool = False) -> bool:
"""
更新群成员信息
:param group_id: 群号
:param remind_superuser: 失败信息提醒超级用户
"""
bot = get_bot()
_group_user_list = await bot.get_group_member_list(group_id=group_id)
_error_member_list = []
_exist_member_list = []
# try:
for user_info in _group_user_list:
if user_info["card"] == "":
nickname = user_info["nickname"]
else:
nickname = user_info["card"]
async with db.transaction():
# 更新权限
if (
user_info["role"]
in [
"owner",
"admin",
]
and not await LevelUser.is_group_flag(user_info["user_id"], group_id)
):
await LevelUser.set_level(
user_info["user_id"],
user_info["group_id"],
Config.get_config("admin_bot_manage", "ADMIN_DEFAULT_AUTH"),
)
if str(user_info["user_id"]) in bot.config.superusers:
await LevelUser.set_level(
user_info["user_id"], user_info["group_id"], 9
)
user = await GroupInfoUser.get_member_info(
user_info["user_id"], user_info["group_id"]
)
if user:
if user.user_name != nickname:
await user.update(user_name=nickname).apply()
logger.info(
f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新群昵称成功"
)
_exist_member_list.append(int(user_info["user_id"]))
continue
join_time = datetime.strptime(
time.strftime(
"%Y-%m-%d %H:%M:%S", time.localtime(user_info["join_time"])
),
"%Y-%m-%d %H:%M:%S",
)
if await GroupInfoUser.add_member_info(
user_info["user_id"],
user_info["group_id"],
nickname,
join_time,
):
_exist_member_list.append(int(user_info["user_id"]))
logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功")
else:
_error_member_list.append(
f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败\n"
)
_del_member_list = list(
set(_exist_member_list).difference(
set(await GroupInfoUser.get_group_member_id_list(group_id))
)
)
if _del_member_list:
for del_user in _del_member_list:
if await GroupInfoUser.delete_member_info(del_user, group_id):
logger.info(f"退群用户{del_user} 所属{group_id} 已删除")
else:
logger.info(f"退群用户{del_user} 所属{group_id} 删除失败")
if _error_member_list and remind_superuser:
result = ""
for error_user in _error_member_list:
result += error_user
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), message=result[:-1]
)
return True
def set_group_bot_status(group_id: int, status: bool) -> str:
"""
设置群聊bot开关状态
:param group_id: 群号
:param status: 状态
"""
if status:
if group_manager.check_group_bot_status(group_id):
return "我还醒着呢!"
group_manager.turn_on_group_bot_status(group_id)
return "呜..醒来了..."
else:
group_manager.shutdown_group_bot_status(group_id)
# for x in group_manager.get_task_data():
# group_manager.close_group_task(group_id, x)
return "那我先睡觉了..."
from typing import List
from nonebot.adapters.onebot.v11.message import MessageSegment
from services.log import logger
from configs.path_config import DATA_PATH
from utils.message_builder import image
from utils.utils import get_bot, get_matchers
from pathlib import Path
from models.group_member_info import GroupInfoUser
from datetime import datetime
from services.db_context import db
from models.level_user import LevelUser
from configs.config import Config
from utils.manager import group_manager, plugins2settings_manager, plugins_manager
from utils.image_utils import BuildImage
from utils.http_utils import AsyncHttpx
import asyncio
import time
import os
try:
import ujson as json
except ModuleNotFoundError:
import json
async def group_current_status(group_id: int) -> str:
"""
获取当前所有通知的开关
:param group_id: 群号
"""
rst = "[被动技能 状态]\n"
_data = group_manager.get_task_data()
for task in _data.keys():
rst += f'{_data[task]}: {"√" if await group_manager.check_group_task_status(group_id, task) else "×"}\n'
return rst.strip()
custom_welcome_msg_json = (
Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json"
)
async def custom_group_welcome(
msg: str, imgs: List[str], user_id: int, group_id: int
) -> str:
"""
替换群欢迎消息
:param msg: 欢迎消息文本
:param imgs: 欢迎消息图片,只取第一张
:param user_id: 用户id,用于log记录
:param group_id: 群号
"""
img_result = ""
img = imgs[0] if imgs else ""
result = ""
if (DATA_PATH / f"custom_welcome_msg/{group_id}.jpg").exists():
(DATA_PATH / f"custom_welcome_msg/{group_id}.jpg").unlink()
if not custom_welcome_msg_json.exists():
custom_welcome_msg_json.parent.mkdir(parents=True, exist_ok=True)
data = {}
else:
try:
data = json.load(open(custom_welcome_msg_json, "r"))
except FileNotFoundError:
data = {}
try:
if msg:
data[str(group_id)] = str(msg)
json.dump(
data, open(custom_welcome_msg_json, "w"), indent=4, ensure_ascii=False
)
logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息 {msg}")
result += msg
if img:
await AsyncHttpx.download_file(
img, DATA_PATH / "custom_welcome_msg" / f"{group_id}.jpg"
)
img_result = image(DATA_PATH / "custom_welcome_msg" / f"{group_id}.jpg")
logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息图片")
except Exception as e:
logger.error(f"GROUP {group_id} 替换群消息失败 e:{e}")
return "替换群消息失败.."
return f"替换群欢迎消息成功:\n{result}" + img_result
task_data = None
async def change_group_switch(cmd: str, group_id: int, is_super: bool = False):
global task_data
"""
修改群功能状态
:param cmd: 功能名称
:param group_id: 群号
:param is_super: 是否位超级用户,超级用户用于私聊开关功能状态
"""
if not task_data:
task_data = group_manager.get_task_data()
group_help_file = DATA_PATH / "group_help" / f"{group_id}.png"
status = cmd[:2]
cmd = cmd[2:]
type_ = "plugin"
modules = plugins2settings_manager.get_plugin_module(cmd, True)
if cmd == "全部被动":
for task in task_data:
if status == "开启":
if not await group_manager.check_group_task_status(group_id, task):
await group_manager.open_group_task(group_id, task)
else:
if await group_manager.check_group_task_status(group_id, task):
await group_manager.close_group_task(group_id, task)
if group_help_file.exists():
group_help_file.unlink()
return f"已 {status} 全部被动技能!"
if cmd == "全部功能":
for f in plugins2settings_manager.get_data():
if status == "开启":
group_manager.unblock_plugin(f, group_id)
else:
group_manager.block_plugin(f, group_id)
return f"已 {status} 全部功能!"
if cmd in [task_data[x] for x in task_data.keys()]:
type_ = "task"
modules = [x for x in task_data.keys() if task_data[x] == cmd]
for module in modules:
if is_super:
module = f"{module}:super"
if status == "开启":
if type_ == "task":
if await group_manager.check_group_task_status(group_id, module):
return f"被动 {task_data[module]} 正处于开启状态!不要重复开启."
await group_manager.open_group_task(group_id, module)
else:
if group_manager.get_plugin_status(module, group_id):
return f"功能 {cmd} 正处于开启状态!不要重复开启."
group_manager.unblock_plugin(module, group_id)
else:
if type_ == "task":
if not await group_manager.check_group_task_status(group_id, module):
return f"被动 {task_data[module]} 正处于关闭状态!不要重复关闭."
await group_manager.close_group_task(group_id, module)
else:
if not group_manager.get_plugin_status(module, group_id):
return f"功能 {cmd} 正处于关闭状态!不要重复关闭."
group_manager.block_plugin(module, group_id)
if group_help_file.exists():
group_help_file.unlink()
if is_super:
for file in os.listdir(DATA_PATH / "group_help"):
file = DATA_PATH / "group_help" / file
file.unlink()
else:
_help_image = DATA_PATH / "group_help" / f"{group_id}.png"
if _help_image.exists():
_help_image.unlink()
return f"{status} {cmd} 功能!"
def set_plugin_status(cmd: str, block_type: str = "all"):
"""
设置插件功能状态(超级用户使用)
:param cmd: 功能名称
:param block_type: 限制类型, 'all': 私聊+群里, 'private': 私聊, 'group': 群聊
"""
status = cmd[:2]
cmd = cmd[2:]
module = plugins2settings_manager.get_plugin_module(cmd)
if status == "开启":
plugins_manager.unblock_plugin(module)
else:
plugins_manager.block_plugin(module, block_type=block_type)
for file in os.listdir(DATA_PATH / "group_help"):
file = DATA_PATH / "group_help" / file
file.unlink()
async def get_plugin_status():
"""
获取功能状态
"""
return await asyncio.get_event_loop().run_in_executor(None, _get_plugin_status)
def _get_plugin_status() -> MessageSegment:
"""
合成功能状态图片
"""
rst = "\t功能\n"
flag_str = "状态".rjust(4) + "\n"
tmp_name = []
for matcher in get_matchers():
if matcher.plugin_name not in tmp_name:
tmp_name.append(matcher.plugin_name)
module = matcher.plugin_name
flag = plugins_manager.get_plugin_block_type(module)
flag = flag.upper() + " CLOSE" if flag else "OPEN"
try:
plugin_name = plugins_manager.get(module)["plugin_name"]
if (
"[Hidden]" in plugin_name
or "[Admin]" in plugin_name
or "[Superuser]" in plugin_name
):
continue
rst += f"{plugin_name}"
except KeyError:
rst += f"{module}"
if plugins_manager.get(module)["error"]:
rst += "[ERROR]"
rst += "\n"
flag_str += f"{flag}\n"
height = len(rst.split("\n")) * 24
a = BuildImage(250, height, font_size=20)
a.text((10, 10), rst)
b = BuildImage(200, height, font_size=20)
b.text((10, 10), flag_str)
A = BuildImage(500, height)
A.paste(a)
A.paste(b, (270, 0))
return image(b64=A.pic2bs4())
async def update_member_info(group_id: int, remind_superuser: bool = False) -> bool:
"""
更新群成员信息
:param group_id: 群号
:param remind_superuser: 失败信息提醒超级用户
"""
bot = get_bot()
_group_user_list = await bot.get_group_member_list(group_id=group_id)
_error_member_list = []
_exist_member_list = []
# try:
for user_info in _group_user_list:
if user_info["card"] == "":
nickname = user_info["nickname"]
else:
nickname = user_info["card"]
async with db.transaction():
# 更新权限
if (
user_info["role"]
in [
"owner",
"admin",
]
and not await LevelUser.is_group_flag(user_info["user_id"], group_id)
):
await LevelUser.set_level(
user_info["user_id"],
user_info["group_id"],
Config.get_config("admin_bot_manage", "ADMIN_DEFAULT_AUTH"),
)
if str(user_info["user_id"]) in bot.config.superusers:
await LevelUser.set_level(
user_info["user_id"], user_info["group_id"], 9
)
user = await GroupInfoUser.get_member_info(
user_info["user_id"], user_info["group_id"]
)
if user:
if user.user_name != nickname:
await user.update(user_name=nickname).apply()
logger.info(
f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新群昵称成功"
)
_exist_member_list.append(int(user_info["user_id"]))
continue
join_time = datetime.strptime(
time.strftime(
"%Y-%m-%d %H:%M:%S", time.localtime(user_info["join_time"])
),
"%Y-%m-%d %H:%M:%S",
)
if await GroupInfoUser.add_member_info(
user_info["user_id"],
user_info["group_id"],
nickname,
join_time,
):
_exist_member_list.append(int(user_info["user_id"]))
logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功")
else:
_error_member_list.append(
f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败\n"
)
_del_member_list = list(
set(_exist_member_list).difference(
set(await GroupInfoUser.get_group_member_id_list(group_id))
)
)
if _del_member_list:
for del_user in _del_member_list:
if await GroupInfoUser.delete_member_info(del_user, group_id):
logger.info(f"退群用户{del_user} 所属{group_id} 已删除")
else:
logger.info(f"退群用户{del_user} 所属{group_id} 删除失败")
if _error_member_list and remind_superuser:
result = ""
for error_user in _error_member_list:
result += error_user
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), message=result[:-1]
)
return True
def set_group_bot_status(group_id: int, status: bool) -> str:
"""
设置群聊bot开关状态
:param group_id: 群号
:param status: 状态
"""
if status:
if group_manager.check_group_bot_status(group_id):
return "我还醒着呢!"
group_manager.turn_on_group_bot_status(group_id)
return "呜..醒来了..."
else:
group_manager.shutdown_group_bot_status(group_id)
# for x in group_manager.get_task_data():
# group_manager.close_group_task(group_id, x)
return "那我先睡觉了..."
@@ -1,7 +1,6 @@
from nonebot import on_notice
from services.log import logger
from nonebot.adapters.cqhttp import Bot, GroupAdminNoticeEvent
from nonebot.typing import T_State
from nonebot.adapters.onebot.v11 import GroupAdminNoticeEvent
from models.level_user import LevelUser
from models.group_member_info import GroupInfoUser
from configs.config import Config
@@ -16,7 +15,7 @@ admin_notice = on_notice(priority=5)
@admin_notice.handle()
async def _(bot: Bot, event: GroupAdminNoticeEvent, state: T_State):
async def _(event: GroupAdminNoticeEvent):
try:
nickname = (
await GroupInfoUser.get_member_info(event.user_id, event.group_id)
@@ -1,9 +1,9 @@
from nonebot import on_command
from utils.utils import get_message_text, get_message_img
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from .data_source import custom_group_welcome
from nonebot.adapters.cqhttp.permission import GROUP
from utils.utils import get_message_img
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message
from nonebot.params import CommandArg
from ._data_source import custom_group_welcome
from nonebot.adapters.onebot.v11.permission import GROUP
from configs.config import Config
from services.log import logger
@@ -35,14 +35,14 @@ custom_welcome = on_command(
@custom_welcome.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
try:
msg = get_message_text(event.json())
imgs = get_message_img(event.json())
if not msg and not imgs:
msg = arg.extract_plain_text().strip()
img = get_message_img(event.json())
if not msg and not img:
await custom_welcome.finish(__plugin_usage__)
await custom_welcome.send(
await custom_group_welcome(msg, imgs, event.user_id, event.group_id),
await custom_group_welcome(msg, img, event.user_id, event.group_id),
at_sender=True,
)
logger.info(f"USER {event.user_id} GROUP {event.group_id} 自定义群欢迎消息:{msg}")
+13 -10
View File
@@ -1,5 +1,4 @@
from nonebot.adapters.cqhttp import Bot, Event
from nonebot.typing import T_State
from nonebot.adapters.onebot.v11 import Event
from utils.manager import group_manager, plugins2settings_manager
from utils.utils import get_message_text
from services.log import logger
@@ -7,12 +6,10 @@ from services.log import logger
cmd = []
def switch_rule(bot: Bot, event: Event, state: T_State) -> bool:
def switch_rule(event: Event) -> bool:
"""
检测文本是否是关闭功能命令
:param bot: pass
:param event: pass
:param state: pass
"""
global cmd
try:
@@ -27,11 +24,17 @@ def switch_rule(bot: Bot, event: Event, state: T_State) -> bool:
_data = plugins2settings_manager.get_data()
for key in _data:
try:
for x in _data[key]["cmd"]:
cmd.append(f"开启{x}")
cmd.append(f"关闭{x}")
cmd.append(f"开启 {x}")
cmd.append(f"关闭 {x}")
if isinstance(_data[key]["cmd"], list):
for x in _data[key]["cmd"]:
cmd.append(f"开启{x}")
cmd.append(f"关闭{x}")
cmd.append(f"开启 {x}")
cmd.append(f"关闭 {x}")
else:
cmd.append(f"开启{key}")
cmd.append(f"关闭{key}")
cmd.append(f"开启 {key}")
cmd.append(f"关闭 {key}")
except KeyError:
pass
msg = get_message_text(event.json()).split()
+16 -17
View File
@@ -1,7 +1,7 @@
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, MessageEvent, GROUP
from nonebot import on_command, on_message, on_regex
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent, GROUP
from .data_source import (
from nonebot.params import RegexGroup
from ._data_source import (
change_group_switch,
set_plugin_status,
get_plugin_status,
@@ -12,8 +12,8 @@ from services.log import logger
from configs.config import NICKNAME, Config
from utils.utils import get_message_text, is_number
from nonebot.permission import SUPERUSER
from typing import Tuple, Any
from .rule import switch_rule
import re
__zx_plugin_name__ = "群功能开关 [Admin]"
@@ -65,7 +65,7 @@ group_status = on_regex("^(休息吧|醒来)$", permission=GROUP, priority=5, bl
@switch_rule_matcher.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _(bot: Bot, event: MessageEvent):
_cmd = get_message_text(event.json()).split()[0]
if isinstance(event, GroupMessageEvent):
await switch_rule_matcher.send(await change_group_switch(_cmd, event.group_id))
@@ -77,7 +77,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
if is_number(block_type):
if not int(block_type) in [
g["group_id"]
for g in await bot.get_group_list(self_id=int(bot.self_id))
for g in await bot.get_group_list()
]:
await switch_rule_matcher.finish(f"{NICKNAME}未加入群聊:{block_type}")
await change_group_switch(_cmd, int(block_type), True)
@@ -104,22 +104,21 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@plugins_status.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _():
await plugins_status.send(await get_plugin_status())
@group_task_status.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
async def _(event: GroupMessageEvent):
await group_task_status.send(await group_current_status(event.group_id))
@group_status.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
r = re.search("^(休息吧|醒来)$", get_message_text(event.json()))
if r:
if r.group(1) == "休息吧":
msg = set_group_bot_status(event.group_id, False)
else:
msg = set_group_bot_status(event.group_id, True)
await group_status.send(msg)
logger.info(f"USER {event.user_id} GROUP {event.group_id} 使用总开关命令:{r.group(1)}")
async def _(event: GroupMessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
cmd = reg_group[0]
if cmd == "休息吧":
msg = set_group_bot_status(event.group_id, False)
else:
msg = set_group_bot_status(event.group_id, True)
await group_status.send(msg)
logger.info(f"USER {event.user_id} GROUP {event.group_id} 使用总开关命令:{cmd}")
@@ -1,5 +1,5 @@
from utils.utils import scheduler, get_bot
from .data_source import update_member_info
from ._data_source import update_member_info
from services.log import logger
from models.group_info import GroupInfo
from asyncpg.exceptions import ConnectionDoesNotExistError, UndefinedColumnError
@@ -1,7 +1,6 @@
from nonebot import on_command, on_notice
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, GROUP, GroupIncreaseNoticeEvent
from .data_source import update_member_info
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, GROUP, GroupIncreaseNoticeEvent
from ._data_source import update_member_info
__zx_plugin_name__ = "更新群组成员列表 [Admin]"
__plugin_usage__ = """
@@ -25,7 +24,7 @@ refresh_member_group = on_command(
@refresh_member_group.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
async def _(event: GroupMessageEvent):
if await update_member_info(event.group_id):
await refresh_member_group.finish("更新群员信息成功!", at_sender=True)
else:
@@ -36,6 +35,6 @@ group_increase_handle = on_notice(priority=1, block=False)
@group_increase_handle.handle()
async def _(bot: Bot, event: GroupIncreaseNoticeEvent, state: dict):
async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
if event.user_id == int(bot.self_id):
await update_member_info(event.group_id)
+1 -1
View File
@@ -1,7 +1,7 @@
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters import Bot
from nonebot.adapters.cqhttp import GroupMessageEvent
from nonebot.adapters.onebot.v11 import GroupMessageEvent
from utils.message_builder import image
from .data_source import create_help_image, admin_help_image
+5 -6
View File
@@ -3,8 +3,7 @@ from configs.path_config import IMAGE_PATH
from services.log import logger
from utils.utils import get_matchers
from utils.manager import group_manager
from nonebot.adapters.cqhttp import Bot
from pathlib import Path
from nonebot.adapters.onebot.v11 import Bot
from nonebot import Driver
import asyncio
import nonebot
@@ -12,9 +11,9 @@ import nonebot
driver: Driver = nonebot.get_driver()
background = Path(IMAGE_PATH) / "background" / "0.png"
background = IMAGE_PATH / "background" / "0.png"
admin_help_image = Path(IMAGE_PATH) / 'admin_help_img.png'
admin_help_image = IMAGE_PATH / 'admin_help_img.png'
@driver.on_bot_connect
@@ -42,7 +41,7 @@ def _create_help_image():
width = 0
_plugin_level = {}
for matcher in _matchers:
_plugin = nonebot.plugin.get_plugin(matcher.module)
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
_module = _plugin.module
try:
plugin_name = _module.__getattribute__("__zx_plugin_name__")
@@ -71,7 +70,7 @@ def _create_help_image():
x = len(f"[{admin_level}] {plugin_des} -> " + " / ".join(plugin_cmd)) * 23
width = width if width > x else x
except AttributeError:
logger.warning(f"获取管理插件 {matcher.module}: {plugin_name} 设置失败...")
logger.warning(f"获取管理插件 {matcher.plugin_name}: {plugin_name} 设置失败...")
help_str = "* 注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数 *\n\n" \
"[权限等级] 管理员帮助:\n\n"
x = list(_plugin_level.keys())
+13 -15
View File
@@ -4,7 +4,7 @@ from nonebot import on_message
from services.log import logger
from models.group_info import GroupInfo
from models.friend_user import FriendUser
from nonebot.adapters.cqhttp.exception import ActionFailed
from nonebot.adapters.onebot.v11.exception import ActionFailed
from configs.config import NICKNAME, Config
from utils.manager import group_manager
from pathlib import Path
@@ -57,12 +57,11 @@ async def _():
gl = await bot.get_group_list()
gl = [g["group_id"] for g in gl]
for g in gl:
if await group_manager.check_group_task_status(g, 'zwa'):
result = image("zao.jpg", "zhenxun")
try:
await bot.send_group_msg(group_id=g, message="早上好" + result)
except ActionFailed:
logger.warning(f"{g} 群被禁言中,无法发送早安")
result = image("zao.jpg", "zhenxun")
try:
await bot.send_group_msg(group_id=g, message="[[_task|zwa]]早上好" + result)
except ActionFailed:
logger.warning(f"{g} 群被禁言中,无法发送早安")
except Exception as e:
logger.error(f"早晚安错误 e:{e}")
@@ -79,14 +78,13 @@ async def _():
gl = await bot.get_group_list()
gl = [g["group_id"] for g in gl]
for g in gl:
if await group_manager.check_group_task_status(g, 'zwa'):
result = image("sleep.jpg", "zhenxun")
try:
await bot.send_group_msg(
group_id=g, message=f"{NICKNAME}要睡觉了,你们也要早点睡呀" + result
)
except ActionFailed:
logger.warning(f"{g} 群被禁言中,无法发送晚安")
result = image("sleep.jpg", "zhenxun")
try:
await bot.send_group_msg(
group_id=g, message=f"[[_task|zwa]]{NICKNAME}要睡觉了,你们也要早点睡呀" + result
)
except ActionFailed:
logger.warning(f"{g} 群被禁言中,无法发送晚安")
except Exception as e:
logger.error(f"早晚安错误 e:{e}")
+47 -98
View File
@@ -1,12 +1,13 @@
from nonebot.adapters.onebot.v11 import GroupMessageEvent, PrivateMessageEvent, MessageEvent, Message, Bot
from nonebot.params import CommandArg, Command
from nonebot import on_command
from models.ban_user import BanUser
from models.level_user import LevelUser
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot
from nonebot.adapters.cqhttp import GroupMessageEvent, PrivateMessageEvent, MessageEvent
from utils.utils import get_message_at, get_message_text, is_number
from typing import Tuple
from utils.utils import get_message_at, is_number
from configs.config import NICKNAME, Config
from nonebot.permission import SUPERUSER
from .data_source import parse_ban_time, a_ban
from services.log import logger
@@ -61,62 +62,35 @@ super_ban = on_command('b了', permission=SUPERUSER, priority=5, block=True)
@ban.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
async def _(bot: Bot, event: GroupMessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
cmd = cmd[0]
result = ""
qq = get_message_at(event.json())
if qq:
qq = qq[0]
user_name = await bot.get_group_member_info(group_id=event.group_id, user_id=qq)
user_name = user_name['card'] if user_name['card'] else user_name['nickname']
msg = get_message_text(event.json())
if msg:
msg = msg.split()
if len(msg) == 2:
if not is_number(msg[0].strip()) or not is_number(msg[1].strip()):
await ban.finish("参数必须是数字!", at_sender=True)
time = int(msg[0]) * 60 * 60 + int(msg[1]) * 60
else:
if not is_number(msg[0].strip()):
await ban.finish("参数必须是数字!", at_sender=True)
time = int(msg[0]) * 60 * 60
else:
time = -1
if state["_prefix"]["raw_command"] in [".ban", "/ban"]:
user_name = user_name['card'] or user_name['nickname']
msg = arg.extract_plain_text().strip()
time = parse_ban_time(msg)
if isinstance(time, str):
await ban.finish(time, at_sender=True)
if cmd in [".ban", "/ban"]:
if (
await LevelUser.get_user_level(event.user_id, event.group_id)
<= await LevelUser.get_user_level(qq, event.group_id)
and str(event.user_id) not in bot.config.superusers
await LevelUser.get_user_level(event.user_id, event.group_id)
<= await LevelUser.get_user_level(qq, event.group_id)
and str(event.user_id) not in bot.config.superusers
):
await ban.finish(
f"您的权限等级比对方低或相等, {NICKNAME}不能为您使用此功能!",
at_sender=True,
)
if await BanUser.ban(
qq, await LevelUser.get_user_level(event.user_id, event.group_id), time
):
logger.info(
f"USER {event.user_id} GROUP {event.group_id} 将 USER {qq} 封禁 时长 {time/60} 分钟"
)
result = f"已经将 {user_name} 加入{NICKNAME}的黑名单了!"
if time != -1:
result += f"将在 {time/60} 分钟后解封"
else:
time = await BanUser.check_ban_time(qq)
if is_number(time):
time = abs(int(time))
if time < 60:
time = str(time) + " 秒"
else:
time = str(int(time / 60)) + " 分钟"
else:
time += " 分钟"
result = f"{user_name} 已在黑名单!预计 {time}后解封"
result = await a_ban(qq, time, user_name, event)
else:
if (
await BanUser.check_ban_level(
qq, await LevelUser.get_user_level(event.user_id, event.group_id)
)
and str(event.user_id) not in bot.config.superusers
await BanUser.check_ban_level(
qq, await LevelUser.get_user_level(event.user_id, event.group_id)
)
and str(event.user_id) not in bot.config.superusers
):
await ban.finish(
f"ban掉 {user_name} 的管理员权限比您高,无法进行unban", at_sender=True
@@ -130,78 +104,53 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
result = f"{user_name} 不在黑名单!"
else:
await ban.finish("艾特人了吗??", at_sender=True)
await ban.finish(result, at_sender=True)
await ban.send(result, at_sender=True)
@ban.handle()
async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
if str(event.user_id) in bot.config.superusers:
msg = get_message_text(event.json())
msg = msg.split()
if is_number(msg[0]):
qq = int(msg[0])
if state["_prefix"]["raw_command"] in [".ban", "/ban"]:
hour = 0
minute = 0
if len(msg) > 1 and is_number(msg[1]):
hour = int(msg[1])
if len(msg) > 2 and is_number(msg[2]):
minute = int(msg[2])
time = hour * 60 * 60 + minute * 60
time = time if time else -1
if await BanUser.ban(
qq, 9, time
):
logger.info(
f"USER {event.user_id} 将 USER {qq} 封禁 时长 {time/60} 分钟"
)
result = f"已经将 {qq} 加入{NICKNAME}的黑名单了!"
if time != -1:
result += f"将在 {time/60} 分钟后解封"
else:
result += f"将在 ∞ 分钟后解封"
await ban.send(result)
async def _(bot: Bot, event: PrivateMessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
cmd = cmd[0]
msg = arg.extract_plain_text().strip()
if msg:
if str(event.user_id) in bot.config.superusers:
if is_number(arg.extract_plain_text().strip().split()[0]):
qq = int(msg[0])
msg = msg[1:]
if cmd in [".ban", "/ban"]:
time = parse_ban_time(msg)
if isinstance(time, str):
await ban.finish(time)
result = await a_ban(qq, time, str(qq), event, 9)
else:
time = await BanUser.check_ban_time(qq)
if is_number(time):
time = abs(int(time))
if time < 60:
time = str(time) + " 秒"
else:
time = str(int(time / 60)) + " 分钟"
if await BanUser.unban(qq):
logger.info(
f"USER {event.user_id} 将 USER {qq} 解禁"
)
result = f"已经把 {qq} 从黑名单中删除了!"
else:
time += " 分钟"
await ban.send(f"{qq} 已在黑名单!预计 {time}后解封")
else:
if await BanUser.unban(qq):
logger.info(
f"USER {event.user_id} 将 USER {qq} 解禁"
)
result = f"已经把 {qq} 从黑名单中删除了!"
else:
result = f"{qq} 不在黑名单!"
result = f"{qq} 不在黑名单!"
await ban.send(result)
else:
await ban.finish('qq号必须是数字!\n格式:.ban [qq] [hour]? [minute]?', at_sender=True)
else:
await ban.finish('qq号必须是数字!\n格式:.ban [qq] [hour]? [minute]?', at_sender=True)
@super_ban.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
if isinstance(event, GroupMessageEvent):
qq = get_message_at(event.json())
else:
qq = get_message_text(event.json())
qq = arg.extract_plain_text().strip()
if not is_number(qq):
await super_ban.finish("对象qq必须为纯数字...")
qq = [qq]
if qq:
qq = qq[0]
user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq)
user_name = user['card'] if user['card'] else user['nickname']
user_name = user['card'] or user['nickname']
if not await BanUser.ban(qq, 10, 99999999):
await BanUser.unban(qq)
await BanUser.ban(qq, 10, 99999999)
await ban.send(f"已将 {user_name} 拉入黑名单!")
else:
await super_ban.send('需要艾特被super ban的对象..')
await super_ban.send('需要添加被super ban的对象,可以使用at或者指定qq..')
+69
View File
@@ -0,0 +1,69 @@
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent
from configs.config import NICKNAME
from models.level_user import LevelUser
from utils.utils import is_number
from models.ban_user import BanUser
from services.log import logger
from typing import Union
def parse_ban_time(msg: str) -> Union[int, str]:
"""
解析ban时长
:param msg: 文本消息
"""
if not msg:
return -1
msg = msg.split()
if len(msg) == 1:
if not is_number(msg[0].strip()):
return "参数必须是数字!"
return int(msg[0]) * 60 * 60
else:
if not is_number(msg[0].strip()) or not is_number(msg[1].strip()):
return "参数必须是数字!"
return int(msg[0]) * 60 * 60 + int(msg[1]) * 60
async def a_ban(qq: int, time: int, user_name: str, event: MessageEvent, ban_level: int = None) -> str:
"""
ban
:param qq: qq
:param time: ban时长
:param user_name: ban用户昵称
:param event: event
:param ban_level: ban级别
"""
if isinstance(event, GroupMessageEvent):
ban_level = await LevelUser.get_user_level(event.user_id, event.group_id)
if await BanUser.ban(qq, ban_level, time):
logger.info(
f"USER {event.user_id} GROUP {event.group_id} 将 USER {qq} 封禁 时长 {time / 60} 分钟"
)
result = f"已经将 {user_name} 加入{NICKNAME}的黑名单了!"
if time != -1:
result += f"将在 {time / 60} 分钟后解封"
else:
result += f"将在 ∞ 分钟后解封"
else:
time = await BanUser.check_ban_time(qq)
if is_number(time):
time = abs(int(time))
if time < 60:
time = str(time) + " 秒"
else:
time = str(int(time / 60)) + " 分钟"
else:
time += " 分钟"
result = f"{user_name} 已在黑名单!预计 {time}后解封"
return result
+11 -11
View File
@@ -1,13 +1,14 @@
from nonebot.adapters.onebot.v11 import Bot, Message, MessageEvent
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters import Bot, Event
from nonebot.permission import SUPERUSER
import asyncio
from utils.utils import get_message_text, get_message_img
from nonebot.params import CommandArg
from utils.utils import get_message_img
from services.log import logger
from utils.message_builder import image
from utils.manager import group_manager
from configs.config import Config
import asyncio
__zx_plugin_name__ = "广播 [Superuser]"
__plugin_usage__ = """
@@ -33,14 +34,13 @@ broadcast = on_command("广播-", priority=1, permission=SUPERUSER, block=True)
@broadcast.handle()
async def _(bot: Bot, event: Event, state: T_State):
msg = get_message_text(event.json())
imgs = get_message_img(event.json())
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
img_list = get_message_img(event.json())
rst = ""
for img in imgs:
for img in img_list:
rst += image(img)
sid = bot.self_id
gl = await bot.get_group_list(self_id=sid)
gl = await bot.get_group_list()
gl = [
g["group_id"]
for g in gl
@@ -56,7 +56,7 @@ async def _(bot: Bot, event: Event, state: T_State):
await broadcast.send(f"已播报至 {int(cnt / g_cnt * 100)}% 的群聊")
x += 0.25
try:
await bot.send_group_msg(self_id=sid, group_id=g, message=msg + rst)
await bot.send_group_msg(group_id=g, message=msg + rst)
logger.info(f"GROUP {g} 投递广播成功")
except Exception as e:
logger.error(f"GROUP {g} 投递广播失败:{type(e)}")
+25 -30
View File
@@ -4,12 +4,12 @@ from utils.message_builder import image
from models.group_member_info import GroupInfoUser
from datetime import datetime
from services.log import logger
from nonebot.adapters.cqhttp import (
from nonebot.adapters.onebot.v11 import (
Bot,
GroupIncreaseNoticeEvent,
GroupDecreaseNoticeEvent,
)
from nonebot.adapters.cqhttp.exception import ActionFailed
from nonebot.adapters.onebot.v11.exception import ActionFailed
from utils.manager import group_manager, plugins2settings_manager, requests_manager
from configs.config import NICKNAME
from models.group_info import GroupInfo
@@ -66,7 +66,7 @@ add_group = on_request(priority=1, block=False)
@group_increase_handle.handle()
async def _(bot: Bot, event: GroupIncreaseNoticeEvent, state: dict):
async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
if event.user_id == int(bot.self_id):
group = await GroupInfo.get_group_info(event.group_id)
# 群聊不存在或被强制拉群,退出该群
@@ -112,9 +112,7 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent, state: dict):
logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败")
# 群欢迎消息
if await group_manager.check_group_task_status(
event.group_id, "group_welcome"
) and _flmt.check(event.group_id):
if _flmt.check(event.group_id):
_flmt.start_cd(event.group_id)
msg = ""
img = ""
@@ -129,23 +127,25 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent, state: dict):
if msg.find("[at]") != -1:
msg = msg.replace("[at]", "")
at_flag = True
if os.path.exists(DATA_PATH + f"custom_welcome_msg/{event.group_id}.jpg"):
if (DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg").exists():
img = image(
abspath=DATA_PATH + f"custom_welcome_msg/{event.group_id}.jpg"
DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg"
)
if msg or img:
msg = msg.strip() + img
msg = "\n" + msg if at_flag else msg
await group_increase_handle.send(
"\n" + msg.strip() + img, at_sender=at_flag
"[[_task|group_welcome]]" + msg, at_sender=at_flag
)
else:
await group_increase_handle.send(
"新人快跑啊!!本群现状↓(快使用自定义!)"
+ image(random.choice(os.listdir(IMAGE_PATH + "qxz/")), "qxz")
"[[_task|group_welcome]]新人快跑啊!!本群现状↓(快使用自定义!)"
+ image(random.choice(os.listdir(IMAGE_PATH / "qxz")), "qxz")
)
@group_decrease_handle.handle()
async def _(bot: Bot, event: GroupDecreaseNoticeEvent, state: dict):
async def _(bot: Bot, event: GroupDecreaseNoticeEvent):
# 被踢出群
if event.sub_type == "kick_me":
group_id = event.group_id
@@ -180,21 +180,16 @@ async def _(bot: Bot, event: GroupDecreaseNoticeEvent, state: dict):
logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除成功")
else:
logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除失败")
if await group_manager.check_group_task_status(
event.group_id, "refund_group_remind"
):
rst = ""
if event.sub_type == "leave":
rst = f"{user_name}离开了我们..."
if event.sub_type == "kick":
operator = await bot.get_group_member_info(
user_id=event.operator_id, group_id=event.group_id
)
operator_name = (
operator["card"] if operator["card"] else operator["nickname"]
)
rst = f"{user_name} 被 {operator_name} 送走了."
try:
await group_decrease_handle.send(f"{rst}")
except ActionFailed:
return
rst = ""
if event.sub_type == "leave":
rst = f"{user_name}离开了我们..."
if event.sub_type == "kick":
operator = await bot.get_group_member_info(
user_id=event.operator_id, group_id=event.group_id
)
operator_name = operator["card"] if operator["card"] else operator["nickname"]
rst = f"{user_name} 被 {operator_name} 送走了."
try:
await group_decrease_handle.send(f"[[_task|refund_group_remind]]{rst}")
except ActionFailed:
return
+9 -9
View File
@@ -1,24 +1,24 @@
from nonebot import on_command
from nonebot.adapters.cqhttp import (
from nonebot.adapters.onebot.v11 import (
Bot,
MessageEvent,
GroupMessageEvent
GroupMessageEvent,
Message
)
from nonebot.params import CommandArg
from nonebot.typing import T_State
from nonebot.rule import to_me
from configs.path_config import IMAGE_PATH, DATA_PATH
from utils.message_builder import image
from .data_source import create_help_img, get_plugin_help
from utils.utils import get_message_text
from pathlib import Path
import os
__zx_plugin_name__ = "帮助"
group_help_path = Path(DATA_PATH) / "group_help"
help_image = Path(IMAGE_PATH) / "help.png"
simple_help_image = Path(IMAGE_PATH) / "simple_help.png"
group_help_path = DATA_PATH / "group_help"
help_image = IMAGE_PATH / "help.png"
simple_help_image = IMAGE_PATH / "simple_help.png"
if help_image.exists():
help_image.unlink()
if simple_help_image.exists():
@@ -44,8 +44,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@simple_help.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
async def _(bot: Bot, event: MessageEvent, state: T_State, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
is_super = False
if msg:
if '-super' in msg:
+9 -9
View File
@@ -16,9 +16,9 @@ import nonebot
import os
random_bk_path = Path(IMAGE_PATH) / "background" / "help" / "simple_help"
random_bk_path = IMAGE_PATH / "background" / "help" / "simple_help"
background = Path(IMAGE_PATH) / "background" / "0.png"
background = IMAGE_PATH / "background" / "0.png"
async def create_help_img(
@@ -55,7 +55,7 @@ def _create_help_img(
# 插件分类
for matcher in _matchers:
plugin_name = None
_plugin = nonebot.plugin.get_plugin(matcher.module)
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
_module = _plugin.module
try:
plugin_name = _module.__getattribute__("__zx_plugin_name__")
@@ -74,10 +74,10 @@ def _create_help_img(
plugin_type = ("normal",)
text_type = 0
if plugins2settings_manager.get(
matcher.module
) and plugins2settings_manager[matcher.module].get("plugin_type"):
matcher.plugin_name
) and plugins2settings_manager[matcher.plugin_name].get("plugin_type"):
plugin_type = tuple(
plugins2settings_manager.get_plugin_data(matcher.module)[
plugins2settings_manager.get_plugin_data(matcher.plugin_name)[
"plugin_type"
]
)
@@ -111,7 +111,7 @@ def _create_help_img(
logger.warning(f"{type(e)}: {e}")
else:
matchers_data[plugin_type][plugin_name] = {
"modules": matcher.module,
"modules": matcher.plugin_name,
"des": plugin_des,
"cmd": plugin_cmd,
"text_type": text_type,
@@ -129,7 +129,7 @@ def _create_help_img(
_des_tmp[plugin_des] = plugin_name
except AttributeError as e:
if plugin_name not in _plugin_name_tmp:
logger.warning(f"获取功能 {matcher.module}: {plugin_name} 设置失败...e:{e}")
logger.warning(f"获取功能 {matcher.plugin_name}: {plugin_name} 设置失败...e:{e}")
if plugin_name not in _plugin_name_tmp:
_plugin_name_tmp.append(plugin_name)
help_img_list = []
@@ -357,7 +357,7 @@ def get_plugin_help(msg: str, is_super: bool = False) -> Optional[str]:
bk = BuildImage(
width,
height,
background=Path(IMAGE_PATH) / "background" / "1.png",
background=IMAGE_PATH / "background" / "1.png",
)
A.paste(bk, alpha=True)
A.text((int(width * 0.048), int(height * 0.21)), result)
+52 -48
View File
@@ -1,48 +1,52 @@
from nonebot.adapters.cqhttp import GroupMessageEvent, PrivateMessageEvent
from utils.manager import plugins2block_manager, StaticData
import time
class StatusMessageManager(StaticData):
def __init__(self):
super().__init__(None)
def add(self, id_: int):
self._data[id_] = time.time()
def delete(self, id_: int):
if self._data.get(id_):
del self._data[id_]
def check(self, id_: int, t: int = 30) -> bool:
if self._data.get(id_):
if time.time() - self._data[id_] > t:
del self._data[id_]
return True
return False
return True
status_message_manager = StatusMessageManager()
def set_block_limit_false(event, module):
"""
设置用户block为false
:param event: event
:param module: 插件模块
"""
if plugins2block_manager.check_plugin_block_status(module):
plugin_block_data = plugins2block_manager.get_plugin_block_data(module)
check_type = plugin_block_data["check_type"]
limit_type = plugin_block_data["limit_type"]
if not (
(isinstance(event, GroupMessageEvent) and check_type == "private")
or (isinstance(event, PrivateMessageEvent) and check_type == "group")
):
block_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
block_type_ = event.group_id
plugins2block_manager.set_false(block_type_, module)
from nonebot.adapters.onebot.v11 import GroupMessageEvent, PrivateMessageEvent
from utils.manager import plugins2block_manager, StaticData
import time
ignore_rst_module = ["ai", "poke", "dialogue"]
other_limit_plugins = ["poke"]
class StatusMessageManager(StaticData):
def __init__(self):
super().__init__(None)
def add(self, id_: int):
self._data[id_] = time.time()
def delete(self, id_: int):
if self._data.get(id_):
del self._data[id_]
def check(self, id_: int, t: int = 30) -> bool:
if self._data.get(id_):
if time.time() - self._data[id_] > t:
del self._data[id_]
return True
return False
return True
status_message_manager = StatusMessageManager()
def set_block_limit_false(event, module):
"""
设置用户block为false
:param event: event
:param module: 插件模块
"""
if plugins2block_manager.check_plugin_block_status(module):
plugin_block_data = plugins2block_manager.get_plugin_block_data(module)
check_type = plugin_block_data["check_type"]
limit_type = plugin_block_data["limit_type"]
if not (
(isinstance(event, GroupMessageEvent) and check_type == "private")
or (isinstance(event, PrivateMessageEvent) and check_type == "group")
):
block_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
block_type_ = event.group_id
plugins2block_manager.set_false(block_type_, module)
+62 -54
View File
@@ -1,6 +1,6 @@
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, run_postprocessor, IgnoredException
from nonebot.adapters.cqhttp.exception import ActionFailed
from nonebot.adapters.onebot.v11.exception import ActionFailed
from models.friend_user import FriendUser
from models.group_member_info import GroupInfoUser
from models.bag_user import BagUser
@@ -11,19 +11,24 @@ from utils.manager import (
plugins_manager,
plugins2cd_manager,
plugins2block_manager,
plugins2count_manager
plugins2count_manager,
)
from ._utils import (
set_block_limit_false,
status_message_manager,
ignore_rst_module,
other_limit_plugins,
)
from .utils import set_block_limit_false, status_message_manager
from nonebot.typing import T_State
from typing import Optional
from nonebot.adapters.cqhttp import (
from nonebot.adapters.onebot.v11 import (
Bot,
MessageEvent,
GroupMessageEvent,
PokeNotifyEvent,
PrivateMessageEvent,
Message,
Event
Event,
)
from configs.config import Config
from models.ban_user import BanUser
@@ -37,35 +42,38 @@ _flmt_g = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
_flmt_s = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
_flmt_c = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
ignore_rst_module = ["ai", "poke", "dialogue"]
# 权限检测
@run_preprocessor
async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
module = matcher.module
async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
module = matcher.plugin_name
plugins2info_dict = plugins2settings_manager.get_data()
# 功能的金币检测 #######################################
# 功能的金币检测 #######################################
# 功能的金币检测 #######################################
cost_gold = 0
if isinstance(event, GroupMessageEvent) and plugins2settings_manager.get_plugin_data(module).get('cost_gold'):
cost_gold = plugins2settings_manager.get_plugin_data(module).get('cost_gold')
if isinstance(
event, GroupMessageEvent
) and plugins2settings_manager.get_plugin_data(module).get("cost_gold"):
cost_gold = plugins2settings_manager.get_plugin_data(module).get("cost_gold")
if await BagUser.get_gold(event.user_id, event.group_id) < cost_gold:
await send_msg(f"金币不足..该功能需要{cost_gold}金币..", bot, event)
raise IgnoredException(f"{module} 金币限制...")
# 当插件不阻塞超级用户时,超级用户提前扣除金币
if str(event.user_id) in bot.config.superusers and not plugins2info_dict[module]["limit_superuser"]:
if (
str(event.user_id) in bot.config.superusers
and not plugins2info_dict[module]["limit_superuser"]
):
await BagUser.spend_gold(event.user_id, event.group_id, cost_gold)
try:
if (
(not isinstance(event, MessageEvent) and module != "poke")
or await BanUser.is_ban(event.user_id)
and str(event.user_id) not in bot.config.superusers
(not isinstance(event, MessageEvent) and module not in other_limit_plugins)
or await BanUser.is_ban(event.user_id)
and str(event.user_id) not in bot.config.superusers
) or (
str(event.user_id) in bot.config.superusers
and plugins2info_dict.get(module)
and not plugins2info_dict[module]["limit_superuser"]
str(event.user_id) in bot.config.superusers
and plugins2info_dict.get(module)
and not plugins2info_dict[module]["limit_superuser"]
):
return
except AttributeError:
@@ -76,16 +84,19 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
_module = _plugin.module
plugin_name = _module.__getattribute__("__zx_plugin_name__")
if (
"[superuser]" in plugin_name.lower()
and str(event.user_id) in bot.config.superusers
"[superuser]" in plugin_name.lower()
and str(event.user_id) in bot.config.superusers
):
return
except AttributeError:
pass
# 群黑名单检测 群总开关检测
if isinstance(event, GroupMessageEvent) or matcher.module == "poke":
if isinstance(event, GroupMessageEvent) or matcher.plugin_name == other_limit_plugins:
try:
if group_manager.get_group_level(event.group_id) < 0:
if (
group_manager.get_group_level(event.group_id) < 0
and str(event.user_id) not in bot.config.superusers
):
raise IgnoredException("群黑名单")
if not group_manager.check_group_bot_status(event.group_id):
try:
@@ -99,12 +110,12 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if isinstance(event, GroupMessageEvent):
# 个人权限
if (
not await LevelUser.check_level(
event.user_id,
event.group_id,
admin_manager.get_plugin_level(module),
)
and admin_manager.get_plugin_level(module) > 0
not await LevelUser.check_level(
event.user_id,
event.group_id,
admin_manager.get_plugin_level(module),
)
and admin_manager.get_plugin_level(module) > 0
):
try:
if _flmt.check(event.user_id):
@@ -112,7 +123,7 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
await bot.send_group_msg(
group_id=event.group_id,
message=f"{at(event.user_id)}你的权限不足喔,该功能需要的权限等级:"
f"{admin_manager.get_plugin_level(module)}",
f"{admin_manager.get_plugin_level(module)}",
)
except ActionFailed:
pass
@@ -122,7 +133,7 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
raise IgnoredException("权限不足")
else:
if not await LevelUser.check_level(
event.user_id, 0, admin_manager.get_plugin_level(module)
event.user_id, 0, admin_manager.get_plugin_level(module)
):
try:
await bot.send_private_msg(
@@ -137,13 +148,11 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
raise IgnoredException("权限不足")
if module in plugins2info_dict.keys() and matcher.priority not in [1, 9]:
# 戳一戳单独判断
if isinstance(event, GroupMessageEvent) or (
isinstance(event, PokeNotifyEvent) and event.group_id
):
if isinstance(event, GroupMessageEvent) or isinstance(event, PokeNotifyEvent) or matcher.plugin_name in other_limit_plugins:
if status_message_manager.get(event.group_id) is None:
status_message_manager.delete(event.group_id)
if plugins2info_dict[module]["level"] > group_manager.get_group_level(
event.group_id
event.group_id
):
try:
if _flmt_g.check(event.user_id) and module not in ignore_rst_module:
@@ -161,7 +170,7 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if not group_manager.get_plugin_status(module, event.group_id):
try:
if module not in ignore_rst_module and _flmt_s.check(
event.group_id
event.group_id
):
_flmt_s.start_cd(event.group_id)
await bot.send_group_msg(
@@ -177,8 +186,8 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if not group_manager.get_plugin_status(f"{module}:super", event.group_id):
try:
if (
_flmt_s.check(event.group_id)
and module not in ignore_rst_module
_flmt_s.check(event.group_id)
and module not in ignore_rst_module
):
_flmt_s.start_cd(event.group_id)
await bot.send_group_msg(
@@ -194,8 +203,8 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if not plugins_manager.get_plugin_status(module, block_type="group"):
try:
if (
_flmt_c.check(event.group_id)
and module not in ignore_rst_module
_flmt_c.check(event.group_id)
and module not in ignore_rst_module
):
_flmt_c.start_cd(event.group_id)
await bot.send_group_msg(
@@ -225,14 +234,14 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
# 维护
if not plugins_manager.get_plugin_status(module, block_type="all"):
if isinstance(
event, GroupMessageEvent
event, GroupMessageEvent
) and group_manager.check_group_is_white(event.group_id):
return
try:
if isinstance(event, GroupMessageEvent):
if (
_flmt_c.check(event.group_id)
and module not in ignore_rst_module
_flmt_c.check(event.group_id)
and module not in ignore_rst_module
):
_flmt_c.start_cd(event.group_id)
await bot.send_group_msg(
@@ -267,9 +276,9 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
limit_type = plugin_cd_data["limit_type"]
rst = plugin_cd_data["rst"]
if (
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or plugins2cd_manager.get_plugin_data(module).get("check_type") == "all"
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or plugins2cd_manager.get_plugin_data(module).get("check_type") == "all"
):
cd_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
@@ -288,9 +297,9 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
limit_type = plugin_block_data["limit_type"]
rst = plugin_block_data["rst"]
if (
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or check_type == "all"
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or check_type == "all"
):
block_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
@@ -304,8 +313,8 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
plugins2block_manager.set_true(block_type_, module)
# Count
if (
plugins2count_manager.check_plugin_count_status(module)
and event.user_id not in bot.config.superusers
plugins2count_manager.check_plugin_count_status(module)
and event.user_id not in bot.config.superusers
):
plugin_count_data = plugins2count_manager.get_plugin_count_data(module)
limit_type = plugin_count_data["limit_type"]
@@ -354,15 +363,15 @@ async def _(
event: Event,
state: T_State,
):
if not isinstance(event, MessageEvent) and matcher.module != "poke":
if not isinstance(event, MessageEvent) and matcher.plugin_name != "poke":
return
module = matcher.module
module = matcher.plugin_name
set_block_limit_false(event, module)
async def init_rst(rst: str, event: MessageEvent):
if "[uname]" in rst:
uname = event.sender.card if event.sender.card else event.sender.nickname
uname = event.sender.card or event.sender.nickname
rst = rst.replace("[uname]", uname)
if "[nickname]" in rst:
if isinstance(event, GroupMessageEvent):
@@ -375,4 +384,3 @@ async def init_rst(rst: str, event: MessageEvent):
if "[at]" in rst and isinstance(event, GroupMessageEvent):
rst = rst.replace("[at]", str(at(event.user_id)))
return rst
+25 -17
View File
@@ -1,16 +1,19 @@
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, IgnoredException
from nonebot.adapters.cqhttp.exception import ActionFailed
from nonebot.adapters.onebot.v11.exception import ActionFailed
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import (
from nonebot.adapters.onebot.v11 import (
Bot,
Event,
MessageEvent,
PokeNotifyEvent,
GroupMessageEvent,
)
from configs.config import Config
from models.ban_user import BanUser
from utils.utils import is_number, static_flmt, FreqLimiter
from utils.message_builder import at
from ._utils import ignore_rst_module, other_limit_plugins
Config.add_plugin_config(
@@ -25,18 +28,19 @@ _flmt = FreqLimiter(300)
# 检查是否被ban
@run_preprocessor
async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
try:
if (
await BanUser.is_super_ban(event.user_id)
and str(event.user_id) not in bot.config.superusers
):
raise IgnoredException("用户处于超级黑名单中")
except AttributeError:
pass
if not isinstance(event, MessageEvent):
return
if matcher.type == "message" and matcher.priority not in [1, 9]:
async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
if (
(isinstance(event, MessageEvent) or isinstance(event, PokeNotifyEvent))
and matcher.priority not in [1, 9]
) or matcher.plugin_name in other_limit_plugins:
try:
if (
await BanUser.is_super_ban(event.user_id)
and str(event.user_id) not in bot.config.superusers
):
raise IgnoredException("用户处于超级黑名单中")
except AttributeError:
pass
if (
await BanUser.is_ban(event.user_id)
and str(event.user_id) not in bot.config.superusers
@@ -57,7 +61,11 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if matcher.priority != 9:
try:
ban_result = Config.get_config("hook", "BAN_RESULT")
if ban_result and _flmt.check(event.user_id):
if (
ban_result
and _flmt.check(event.user_id)
and matcher.plugin_name not in ignore_rst_module
):
_flmt.start_cd(event.user_id)
await bot.send_group_msg(
group_id=event.group_id,
@@ -74,7 +82,7 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if matcher.priority != 9:
try:
ban_result = Config.get_config("hook", "BAN_RESULT")
if ban_result:
if ban_result and matcher.plugin_name not in ignore_rst_module:
await bot.send_private_msg(
user_id=event.user_id,
message=at(event.user_id)
@@ -83,4 +91,4 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
)
except ActionFailed:
pass
raise IgnoredException("用户处于黑名单中")
raise IgnoredException("用户处于黑名单中")
+2 -2
View File
@@ -1,8 +1,8 @@
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, IgnoredException
from nonebot.adapters.cqhttp.exception import ActionFailed
from nonebot.adapters.onebot.v11.exception import ActionFailed
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import (
from nonebot.adapters.onebot.v11 import (
Bot,
MessageEvent,
GroupMessageEvent,
+14 -4
View File
@@ -1,13 +1,16 @@
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, IgnoredException
from nonebot.typing import T_State
from .utils import status_message_manager
from nonebot.adapters.cqhttp import (
from ._utils import status_message_manager
from utils.image_utils import text2image
from typing import Dict, Any
from nonebot.adapters.onebot.v11 import (
Bot,
MessageEvent,
PrivateMessageEvent,
GroupMessageEvent,
)
import re
# 为什么AI会自己和自己聊天
@@ -24,7 +27,7 @@ async def _(matcher: Matcher, bot: Bot, event: PrivateMessageEvent, state: T_Sta
async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if not isinstance(event, MessageEvent):
return
if matcher.type == "message" and matcher.module == "ai":
if matcher.type == "message" and matcher.plugin_name == "ai":
if (
isinstance(event, GroupMessageEvent)
and not status_message_manager.check(event.group_id)
@@ -35,7 +38,14 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
isinstance(event, PrivateMessageEvent)
and not status_message_manager.check(event.user_id)
):
print(status_message_manager)
status_message_manager.delete(event.user_id)
raise IgnoredException("有命令就别说话了")
# @Bot.on_calling_api
# async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
# if api in ["send_msg", "send_group_msg", "send_private_msg"]:
# msg = str(data["message"])
# if (r := re.search("\[\[To_Img\|?(.*?)]]", msg)) or (r := re.search("&#91;&#91;To_Img\|?(.*?)&#91;&#91;")):
+47
View File
@@ -0,0 +1,47 @@
from nonebot.exception import MockApiException
from nonebot.adapters.onebot.v11 import Bot, Message
from utils.manager import group_manager
from typing import Dict, Any
import re
@Bot.on_calling_api
async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
r = None
if (
(
(api == "send_msg" and data["message_type"] == "group")
or api == "send_group_msg"
)
and (
(
r := re.search(
"^\[\[_task\|(.*)]]",
data["message"].strip()
if isinstance(data["message"], str)
else str(data["message"]["text"]).strip(),
)
)
or (
r := re.search(
"^&#91;&#91;_task\|(.*)&#93;&#93;",
data["message"].strip()
if isinstance(data["message"], str)
else str(data["message"]["text"]).strip(),
)
)
)
and r.group(1) in group_manager.get_task_data().keys()
):
task = r.group(1)
group_id = data["group_id"]
if group_manager.get_group_level(
group_id
) < 0 or not await group_manager.check_group_task_status(group_id, task):
raise MockApiException(f"被动技能 {task} 处于关闭状态...")
else:
msg = str(data["message"]).strip()
msg = msg.replace(f"&#91;&#91;_task|{task}&#93;&#93;", "").replace(
f"[[_task|{task}]]", ""
)
data["message"] = Message(msg)
+2 -2
View File
@@ -2,7 +2,7 @@ from nonebot.matcher import Matcher
from nonebot.message import run_postprocessor
from typing import Optional
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, Event
from nonebot.adapters.onebot.v11 import Bot, Event
from utils.manager import withdraw_message_manager
import asyncio
@@ -25,4 +25,4 @@ async def _(
async def _withdraw_message(bot: Bot, id_: int, time: int):
await asyncio.sleep(time)
await bot.delete_msg(message_id=id_, self_id=int(bot.self_id))
await bot.delete_msg(message_id=id_)
+2 -3
View File
@@ -11,8 +11,7 @@ from .init_plugins_limit import (
)
from .init import init
from .check_plugin_status import check_plugin_status
from utils.manager import admin_manager
from nonebot.adapters.cqhttp import Bot
from nonebot.adapters.onebot.v11 import Bot
from configs.path_config import DATA_PATH
from services.log import logger
from pathlib import Path
@@ -34,7 +33,7 @@ def _():
初始化数据
"""
_flag = False
config_file = Path(DATA_PATH) / "configs" / "plugins2config.yaml"
config_file = DATA_PATH / "configs" / "plugins2config.yaml"
if not config_file.exists():
_flag = True
init()
@@ -1,5 +1,5 @@
from utils.manager import plugins_manager
from nonebot.adapters.cqhttp import Bot
from nonebot.adapters.onebot.v11 import Bot
async def check_plugin_status(bot: Bot):
@@ -15,14 +15,14 @@ def init_plugins_config(data_path):
"""
初始化插件数据配置
"""
plugins2config_file = Path(data_path) / "configs" / "plugins2config.yaml"
plugins2config_file = data_path / "configs" / "plugins2config.yaml"
plugins2config_file.parent.mkdir(parents=True, exist_ok=True)
_data = {}
if plugins2config_file.exists():
_data = _yaml.load(open(plugins2config_file, "r", encoding="utf8"))
_matchers = get_matchers()
for matcher in _matchers:
_plugin = nonebot.plugin.get_plugin(matcher.module)
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
try:
_module = _plugin.module
except AttributeError:
@@ -39,16 +39,16 @@ def init_plugins_config(data_path):
if (
plugin_version is None
or (
_data.get(matcher.module)
and _data[matcher.module].keys() != plugin_configs.keys()
_data.get(matcher.plugin_name)
and _data[matcher.plugin_name].keys() != plugin_configs.keys()
)
or plugin_version > plugins_manager.get(matcher.module)["version"]
or matcher.module not in _data.keys()
or plugin_version > plugins_manager.get(matcher.plugin_name)["version"]
or matcher.plugin_name not in _data.keys()
):
for key in plugin_configs:
if isinstance(plugin_configs[key], dict):
Config.add_plugin_config(
matcher.module,
matcher.plugin_name,
key,
plugin_configs[key].get("value"),
help_=plugin_configs[key].get("help"),
@@ -56,12 +56,12 @@ def init_plugins_config(data_path):
_override=True,
)
else:
Config.add_plugin_config(matcher.module, key, plugin_configs[key])
Config.add_plugin_config(matcher.plugin_name, key, plugin_configs[key])
else:
plugin_configs = _data[matcher.module]
plugin_configs = _data[matcher.plugin_name]
for key in plugin_configs:
Config.add_plugin_config(
matcher.module,
matcher.plugin_name,
key,
plugin_configs[key]["value"],
help_=plugin_configs[key]["help"],
@@ -99,7 +99,10 @@ def init_plugins_config(data_path):
if _data.get(plugin) and k in _data[plugin].keys():
Config.set_config(plugin, k, _data[plugin][k])
if level2module := Config.get_level2module(plugin, k):
admin_manager.set_admin_level(level2module, _data[plugin][k])
try:
admin_manager.set_admin_level(level2module, _data[plugin][k])
except KeyError:
logger.warning(f"{level2module} 设置权限等级失败:{_data[plugin][k]}")
_tmp_data[plugin][k] = Config.get_config(plugin, k)
Config.save()
temp_file = Path() / "configs" / "temp_config.yaml"
@@ -17,27 +17,27 @@ def init_plugins_data(data_path):
"""
初始化插件数据信息
"""
plugin2data_file = Path(data_path) / "manager" / "plugin_manager.json"
plugin2data_file = data_path / "manager" / "plugin_manager.json"
plugin2data_file.parent.mkdir(parents=True, exist_ok=True)
_data = {}
if plugin2data_file.exists():
_data = json.load(open(plugin2data_file, "r", encoding="utf8"))
_matchers = get_matchers()
for matcher in _matchers:
_plugin = nonebot.plugin.get_plugin(matcher.module)
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
try:
_module = _plugin.module
except AttributeError:
if matcher.module not in _data.keys():
if matcher.plugin_name not in _data.keys():
plugins_manager.add_plugin_data(
matcher.module, matcher.module, error=True
matcher.plugin_name, matcher.plugin_name, error=True
)
else:
plugins_manager.set_module_data(matcher.module, "error", True)
plugin_data = plugins_manager.get(matcher.module)
plugins_manager.set_module_data(matcher.plugin_name, "error", True)
plugin_data = plugins_manager.get(matcher.plugin_name)
if plugin_data:
plugins_manager.set_module_data(
matcher.module, "version", plugin_data.get("version")
matcher.plugin_name, "version", plugin_data.get("version")
)
else:
try:
@@ -47,36 +47,36 @@ def init_plugins_data(data_path):
try:
plugin_name = _module.__getattribute__("__zx_plugin_name__")
except AttributeError:
plugin_name = matcher.module
plugin_name = matcher.plugin_name
try:
plugin_author = _module.__getattribute__("__plugin_author__")
except AttributeError:
plugin_author = None
if matcher.module in plugins_manager.keys():
plugins_manager.set_module_data(matcher.module, "error", False)
if matcher.module not in plugins_manager.keys():
if matcher.plugin_name in plugins_manager.keys():
plugins_manager.set_module_data(matcher.plugin_name, "error", False)
if matcher.plugin_name not in plugins_manager.keys():
plugins_manager.add_plugin_data(
matcher.module,
matcher.plugin_name,
plugin_name=plugin_name,
author=plugin_author,
version=plugin_version,
)
elif plugins_manager[matcher.module]["version"] is None or (
elif plugins_manager[matcher.plugin_name]["version"] is None or (
plugin_version is not None
and plugin_version > plugins_manager[matcher.module]["version"]
and plugin_version > plugins_manager[matcher.plugin_name]["version"]
):
plugins_manager.set_module_data(
matcher.module, "plugin_name", plugin_name
matcher.plugin_name, "plugin_name", plugin_name
)
plugins_manager.set_module_data(matcher.module, "author", plugin_author)
plugins_manager.set_module_data(matcher.plugin_name, "author", plugin_author)
plugins_manager.set_module_data(
matcher.module, "version", plugin_version
matcher.plugin_name, "version", plugin_version
)
if matcher.module in _data.keys():
if matcher.plugin_name in _data.keys():
plugins_manager.set_module_data(
matcher.module, "error", _data[matcher.module]["error"]
matcher.plugin_name, "error", _data[matcher.plugin_name]["error"]
)
plugins_manager.set_module_data(
matcher.module, "plugin_name", _data[matcher.module]["plugin_name"]
matcher.plugin_name, "plugin_name", _data[matcher.plugin_name]["plugin_name"]
)
plugins_manager.save()
@@ -17,18 +17,18 @@ def init_plugins_cd_limit(data_path):
"""
加载 cd 限制
"""
plugins2cd_file = Path(data_path) / "configs" / "plugins2cd.yaml"
plugins2cd_file = data_path / "configs" / "plugins2cd.yaml"
plugins2cd_file.parent.mkdir(exist_ok=True, parents=True)
_data = {}
_matchers = get_matchers()
for matcher in _matchers:
if not plugins2cd_manager.get_plugin_cd_data(matcher.module):
_plugin = nonebot.plugin.get_plugin(matcher.module)
if not plugins2cd_manager.get_plugin_cd_data(matcher.plugin_name):
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
try:
_module = _plugin.module
plugin_cd_limit = _module.__getattribute__("__plugin_cd_limit__")
plugins2cd_manager.add_cd_limit(
matcher.module, data_dict=plugin_cd_limit
matcher.plugin_name, data_dict=plugin_cd_limit
)
except AttributeError:
pass
@@ -65,18 +65,18 @@ def init_plugins_block_limit(data_path):
"""
加载阻塞限制
"""
plugins2block_file = Path(data_path) / "configs" / "plugins2block.yaml"
plugins2block_file = data_path / "configs" / "plugins2block.yaml"
plugins2block_file.parent.mkdir(exist_ok=True, parents=True)
_data = {}
_matchers = get_matchers()
for matcher in _matchers:
if not plugins2block_manager.get_plugin_block_data(matcher.module):
_plugin = nonebot.plugin.get_plugin(matcher.module)
if not plugins2block_manager.get_plugin_block_data(matcher.plugin_name):
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
try:
_module = _plugin.module
plugin_block_limit = _module.__getattribute__("__plugin_block_limit__")
plugins2block_manager.add_block_limit(
matcher.module, data_dict=plugin_block_limit
matcher.plugin_name, data_dict=plugin_block_limit
)
except AttributeError:
pass
@@ -113,18 +113,18 @@ def init_plugins_count_limit(data_path):
"""
加载次数限制
"""
plugins2count_file = Path(data_path) / "configs" / "plugins2count.yaml"
plugins2count_file = data_path / "configs" / "plugins2count.yaml"
plugins2count_file.parent.mkdir(exist_ok=True, parents=True)
_data = {}
_matchers = get_matchers()
for matcher in _matchers:
if not plugins2count_manager.get_plugin_count_data(matcher.module):
_plugin = nonebot.plugin.get_plugin(matcher.module)
if not plugins2count_manager.get_plugin_count_data(matcher.plugin_name):
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
try:
_module = _plugin.module
plugin_count_limit = _module.__getattribute__("__plugin_count_limit__")
plugins2count_manager.add_count_limit(
matcher.module, data_dict=plugin_count_limit
matcher.plugin_name, data_dict=plugin_count_limit
)
except AttributeError:
pass
@@ -11,13 +11,13 @@ def init_plugins_resources():
"""
_tmp = []
for matcher in get_matchers():
if matcher.module not in _tmp:
_tmp.append(matcher.module)
_plugin = nonebot.plugin.get_plugin(matcher.module)
if matcher.plugin_name not in _tmp:
_tmp.append(matcher.plugin_name)
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
try:
_module = _plugin.module
except AttributeError:
logger.warning(f"插件 {matcher.module} 加载失败...,资源控制未加载...")
logger.warning(f"插件 {matcher.plugin_name} 加载失败...,资源控制未加载...")
else:
try:
resources = _module.__getattribute__("__plugin_resources__")
@@ -26,7 +26,7 @@ def init_plugins_resources():
else:
path = Path(_module.__getattribute__("__file__")).parent
for resource in resources.keys():
resources_manager.add_resource(matcher.module, (path / resource).absolute(), resources[resource])
resources_manager.add_resource(matcher.plugin_name, path / resource, resources[resource])
resources_manager.save()
resources_manager.start_move()
@@ -14,7 +14,7 @@ def init_plugins_settings(data_path: str):
"""
初始化插件设置,从插件中获取 __zx_plugin_name__,__plugin_cmd__,__plugin_settings__
"""
plugins2settings_file = Path(data_path) / "configs" / "plugins2settings.yaml"
plugins2settings_file = data_path / "configs" / "plugins2settings.yaml"
plugins2settings_file.parent.mkdir(exist_ok=True, parents=True)
_matchers = get_matchers()
_tmp_module = {}
@@ -29,12 +29,12 @@ def init_plugins_settings(data_path: str):
logger.warning(f"配置文件 模块:{x} 获取 plugin_name 失败...{e}")
_tmp_module[x] = ""
for matcher in _matchers:
if matcher.module not in plugins2settings_manager.keys():
_plugin = nonebot.plugin.get_plugin(matcher.module)
if matcher.plugin_name not in plugins2settings_manager.keys():
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
try:
_module = _plugin.module
except AttributeError:
logger.warning(f"插件 {matcher.module} 加载失败...,插件控制未加载.")
logger.warning(f"插件 {matcher.plugin_name} 加载失败...,插件控制未加载.")
else:
try:
plugin_name = _module.__getattribute__("__zx_plugin_name__")
@@ -51,41 +51,41 @@ def init_plugins_settings(data_path: str):
if level is None:
level = 5
admin_manager.add_admin_plugin_settings(
matcher.module, cmd, level
matcher.plugin_name, cmd, level
)
if (
"[hidden]" in plugin_name.lower()
or "[admin]" in plugin_name.lower()
or "[superuser]" in plugin_name.lower()
or matcher.module in plugins2settings_manager.keys()
or matcher.plugin_name in plugins2settings_manager.keys()
):
continue
except AttributeError:
if matcher.module not in _tmp:
if matcher.plugin_name not in _tmp:
logger.warning(
f"获取插件 {matcher.module} __zx_plugin_name__ 失败...,插件控制未加载."
f"获取插件 {matcher.plugin_name} __zx_plugin_name__ 失败...,插件控制未加载."
)
else:
try:
_tmp_module[matcher.module] = plugin_name
_tmp_module[matcher.plugin_name] = plugin_name
plugin_settings = _module.__getattribute__(
"__plugin_settings__"
)
if plugin_settings.get('cost_gold') is None:
plugin_settings['cost_gold'] = 0
if (
plugin_settings["cmd"] is not None
plugin_settings.get("cmd") is not None
and plugin_name not in plugin_settings["cmd"]
):
plugin_settings["cmd"].append(plugin_name)
if plugins2settings_manager.get(
matcher.module
) and plugins2settings_manager[matcher.module].get(
matcher.plugin_name
) and plugins2settings_manager[matcher.plugin_name].get(
"plugin_type"
):
plugin_type = tuple(
plugins2settings_manager.get_plugin_data(
matcher.module
matcher.plugin_name
)["plugin_type"]
)
else:
@@ -95,15 +95,15 @@ def init_plugins_settings(data_path: str):
)
except AttributeError:
plugin_type = ("normal",)
if plugin_settings and matcher.module:
if plugin_settings and matcher.plugin_name:
plugins2settings_manager.add_plugin_settings(
matcher.module,
matcher.plugin_name,
plugin_type=plugin_type,
**plugin_settings,
)
except AttributeError:
pass
_tmp.append(matcher.module)
_tmp.append(matcher.plugin_name)
_tmp_data = {"PluginSettings": plugins2settings_manager.get_data()}
with open(plugins2settings_file, "w", encoding="utf8") as wf:
yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
+5 -5
View File
@@ -1,5 +1,5 @@
from nonebot import on_request, on_message
from nonebot.adapters.cqhttp import (
from nonebot.adapters.onebot.v11 import (
Bot,
FriendRequestEvent,
GroupRequestEvent,
@@ -8,7 +8,7 @@ from nonebot.adapters.cqhttp import (
from models.friend_user import FriendUser
from datetime import datetime
from configs.config import NICKNAME, Config
from nonebot.adapters.cqhttp.exception import ActionFailed
from nonebot.adapters.onebot.v11.exception import ActionFailed
from utils.manager import requests_manager
from models.group_info import GroupInfo
from utils.utils import scheduler
@@ -31,7 +31,7 @@ exists_data = {"private": {}, "group": {}}
@friend_req.handle()
async def _(bot: Bot, event: FriendRequestEvent, state: dict):
async def _(bot: Bot, event: FriendRequestEvent):
global exists_data
if exists_data["private"].get(event.user_id):
if time.time() - exists_data["private"][event.user_id] < 60 * 5:
@@ -66,7 +66,7 @@ async def _(bot: Bot, event: FriendRequestEvent, state: dict):
@group_req.handle()
async def _(bot: Bot, event: GroupRequestEvent, state: dict):
async def _(bot: Bot, event: GroupRequestEvent):
global exists_data
if event.sub_type == "invite":
if str(event.user_id) in bot.config.superusers:
@@ -125,7 +125,7 @@ async def _(bot: Bot, event: GroupRequestEvent, state: dict):
@x.handle()
async def _(bot: Bot, event: MessageEvent, state: dict):
async def _(event: MessageEvent):
await asyncio.sleep(0.1)
r = re.search(r'groupcode="(.*?)"', str(event.get_message()))
if r:
+9 -10
View File
@@ -1,15 +1,14 @@
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, PrivateMessageEvent, Message
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, PrivateMessageEvent
from nonebot.rule import to_me
from utils.utils import get_message_text
from models.group_member_info import GroupInfoUser
from models.friend_user import FriendUser
import random
from models.ban_user import BanUser
from services.log import logger
from configs.config import NICKNAME, Config
from nonebot.params import CommandArg
import random
__zx_plugin_name__ = "昵称系统"
__plugin_usage__ = f"""
@@ -48,8 +47,8 @@ cancel_nickname = on_command("取消昵称", rule=to_me(), priority=5, block=Tru
@nickname.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
msg = get_message_text(event.json())
async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if not msg:
await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True)
if len(msg) > 10:
@@ -103,7 +102,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
@my_nickname.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
async def _(event: GroupMessageEvent):
try:
nickname_ = await GroupInfoUser.get_group_member_nickname(
event.user_id, event.group_id
@@ -124,7 +123,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
)
)
else:
nickname_ = event.sender.card if event.sender.card else event.sender.nickname
nickname_ = event.sender.card or event.sender.nickname
await my_nickname.send(
random.choice(
["没..没有昵称嘛,{}", "啊,你是{}啊,我想叫你的昵称!", "是{}啊,有什么事吗?", "你是{}?"]
@@ -158,7 +157,7 @@ async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
@cancel_nickname.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
async def _(event: GroupMessageEvent):
nickname_ = await GroupInfoUser.get_group_member_nickname(
event.user_id, event.group_id
)
@@ -181,7 +180,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
@cancel_nickname.handle()
async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
async def _(event: PrivateMessageEvent):
nickname_ = await FriendUser.get_friend_nickname(event.user_id)
if nickname_:
await cancel_nickname.send(
+18 -3
View File
@@ -7,14 +7,13 @@ from nonebot import Driver
from services.db_context import db
from models.group_info import GroupInfo
from models.bag_user import BagUser
from nonebot.adapters.cqhttp import Bot
from nonebot.adapters.onebot.v11 import Bot
from services.log import logger
from configs.path_config import TEXT_PATH
from asyncio.exceptions import TimeoutError
from typing import List
from utils.http_utils import AsyncHttpx
from utils.utils import scheduler
from pathlib import Path
import nonebot
try:
@@ -32,7 +31,7 @@ async def update_city():
部分插件需要中国省份城市
这里直接更新,避免插件内代码重复
"""
china_city = Path(TEXT_PATH) / "china_city.json"
china_city = TEXT_PATH / "china_city.json"
data = {}
if not china_city.exists():
try:
@@ -93,6 +92,22 @@ async def _():
"ALTER TABLE bag_users ADD property json NOT NULL DEFAULT '{}';",
"bag_users",
), # bag_users 新增字段 property 替代 props
(
"ALTER TABLE genshin ADD auto_sign_time timestamp with time zone;",
"genshin"
), # 新增原神自动签到字段
(
"ALTER TABLE genshin ADD resin_remind boolean DEFAULT False;",
"genshin"
), # 新增原神自动签到字段
(
"ALTER TABLE genshin ADD resin_recovery_time timestamp with time zone;",
"genshin"
), # 新增原神自动签到字段
(
"ALTER TABLE genshin ADD bind_group Integer;",
"genshin"
), # 新增原神群号绑定字段
]
for sql in sql_str:
try:
+25 -20
View File
@@ -1,12 +1,13 @@
from nonebot import on_command
from services.log import logger
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from nonebot.typing import T_State
from utils.utils import get_message_text, is_number
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message
from nonebot.params import CommandArg
from utils.utils import is_number
from models.bag_user import BagUser
from services.db_context import db
from nonebot.adapters.cqhttp.permission import GROUP
from nonebot.adapters.onebot.v11.permission import GROUP
from models.goods_info import GoodsInfo
import time
__zx_plugin_name__ = "商店 - 购买道具"
@@ -20,7 +21,7 @@ usage:
""".strip()
__plugin_des__ = "商店 - 购买道具"
__plugin_cmd__ = ["购买 [序号或名称] ?[数量=1]"]
__plugin_type__ = ('商店',)
__plugin_type__ = ("商店",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
@@ -29,39 +30,43 @@ __plugin_settings__ = {
"limit_superuser": False,
"cmd": ["商店", "购买道具"],
}
__plugin_cd_limit__ = {
"cd": 3
}
__plugin_cd_limit__ = {"cd": 3}
buy = on_command("购买", aliases={"购买道具"}, priority=5, block=True, permission=GROUP)
@buy.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
goods = None
if get_message_text(event.json()) in ["神秘药水"]:
if arg.extract_plain_text().strip() in ["神秘药水"]:
await buy.finish("你们看看就好啦,这是不可能卖给你们的~", at_sender=True)
goods_lst = await GoodsInfo.get_all_goods()
goods_name_lst = [x.goods_name for x in goods_lst]
msg = get_message_text(event.json()).split()
goods_list = [
x
for x in await GoodsInfo.get_all_goods()
if x.goods_limit_time > time.time() or x.goods_limit_time == 0
]
goods_name_list = [
x.goods_name
for x in goods_list
]
msg = arg.extract_plain_text().strip().split()
num = 1
if len(msg) > 1:
if is_number(msg[1]) and int(msg[1]) > 0:
num = int(msg[1])
else:
await buy.finish("购买的数量要是数字且大于0!", at_sender=True)
# print(msg, num)
if is_number(msg[0]):
msg = int(msg[0])
if msg > len(goods_lst) or msg < 1:
if msg > len(goods_name_list) or msg < 1:
await buy.finish("请输入正确的商品id!", at_sender=True)
goods = goods_lst[msg - 1]
goods = goods_list[msg - 1]
else:
if msg[0] in goods_name_lst:
for i in range(len(goods_name_lst)):
if msg[0] == goods_name_lst[i]:
goods = goods_lst[i]
if msg[0] in goods_name_list:
for i in range(len(goods_name_list)):
if msg[0] == goods_name_list[i]:
goods = goods_list[i]
break
else:
await buy.finish("请输入正确的商品名称!")
+7 -7
View File
@@ -1,11 +1,11 @@
from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from nonebot.typing import T_State
from nonebot.adapters.cqhttp.permission import GROUP
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message
from nonebot.params import CommandArg
from nonebot.adapters.onebot.v11.permission import GROUP
from utils.data_utils import init_rank
from models.bag_user import BagUser
from utils.message_builder import image
from utils.utils import get_message_text, is_number
from utils.utils import is_number
__zx_plugin_name__ = "商店 - 我的金币"
__plugin_usage__ = """
@@ -33,13 +33,13 @@ gold_rank = on_command("金币排行", priority=5, block=True, permission=GROUP)
@my_gold.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
async def _(event: GroupMessageEvent):
await my_gold.finish(await BagUser.get_user_total_gold(event.user_id, event.group_id))
@gold_rank.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
num = get_message_text(event.json())
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
num = arg.extract_plain_text().strip()
if is_number(num) and 51 > int(num) > 10:
num = int(num)
else:
+3 -4
View File
@@ -1,9 +1,8 @@
from nonebot import on_command
from services.log import logger
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from nonebot.typing import T_State
from nonebot.adapters.onebot.v11 import GroupMessageEvent
from models.bag_user import BagUser
from nonebot.adapters.cqhttp.permission import GROUP
from nonebot.adapters.onebot.v11.permission import GROUP
__zx_plugin_name__ = "商店 - 我的道具"
@@ -30,7 +29,7 @@ my_props = on_command("我的道具", priority=5, block=True, permission=GROUP)
@my_props.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
async def _(event: GroupMessageEvent):
props = await BagUser.get_property(event.user_id, event.group_id)
if props:
rst = ""
+11 -11
View File
@@ -1,11 +1,11 @@
from .data_source import create_shop_help, delete_goods, update_goods, register_goods, parse_goods_info
from nonebot.adapters.onebot.v11 import MessageEvent, Message
from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent
from nonebot.typing import T_State
from configs.path_config import IMAGE_PATH
from utils.message_builder import image
from .data_source import create_shop_help, delete_goods, update_goods, register_goods, parse_goods_info
from nonebot.permission import SUPERUSER
from utils.utils import get_message_text, is_number
from utils.utils import is_number
from nonebot.params import CommandArg
from nonebot.plugin import export
from services.log import logger
import os
@@ -67,13 +67,13 @@ shop_update_goods = on_command("修改商品", priority=5, permission=SUPERUSER,
@shop_help.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
async def _():
await shop_help.send(image(b64=await create_shop_help()))
@shop_add_goods.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
async def _(event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if msg:
data = parse_goods_info(msg)
if isinstance(data, str):
@@ -94,8 +94,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@shop_del_goods.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
async def _(event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if msg:
name = ""
id_ = 0
@@ -115,8 +115,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@shop_update_goods.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
async def _(event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if msg:
data = parse_goods_info(msg)
if isinstance(data, str):
@@ -217,11 +217,12 @@ async def update_goods(**kwargs) -> "str, str, int":
tmp += f'折扣:{discount} --> {kwargs["discount"]}\n'
discount = kwargs["discount"]
if kwargs.get("limit_time"):
kwargs["limit_time"] = float(kwargs["limit_time"])
new_time = time.strftime(
"%Y-%m-%d %H:%M:%S",
time.localtime(time.time() + int(kwargs["limit_time"] * 60 * 60)),
time.localtime(time.time() + kwargs["limit_time"] * 60 * 60),
)
tmp += f"折扣至: {new_time}\n"
tmp += f"限时至: {new_time}\n"
limit_time = kwargs["limit_time"]
return (
await GoodsInfo.update_goods(
@@ -249,7 +250,6 @@ def parse_goods_info(msg: str) -> Union[dict, str]:
sp = x.split(":", maxsplit=1)
if str(sp[1]).strip():
sp[1] = sp[1].strip()
print(sp)
if sp[0] == "name":
data["name"] = sp[1]
elif sp[0] == "price":
+9 -9
View File
@@ -1,10 +1,10 @@
from nonebot import on_command
from services.log import logger
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from nonebot.typing import T_State
from utils.utils import is_number, get_message_text
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message
from nonebot.params import CommandArg
from utils.utils import is_number
from models.bag_user import BagUser
from nonebot.adapters.cqhttp.permission import GROUP
from nonebot.adapters.onebot.v11.permission import GROUP
from services.db_context import db
from nonebot.plugin import export
from .data_source import effect, register_use, func_manager
@@ -38,10 +38,8 @@ use_props = on_command("使用道具", priority=5, block=True, permission=GROUP)
@use_props.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
msg = get_message_text(event.json())
if msg in ["", "帮助"]:
await use_props.finish(__plugin_usage__)
async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
num = 1
msg_sp = msg.split()
if len(msg_sp) > 1 and is_number(msg_sp[-1]) and int(msg_sp[-1]) > 0:
@@ -66,9 +64,11 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
await use_props.finish(f"该道具单次只能使用 {n} 个!")
if await BagUser.delete_property(
event.user_id, event.group_id, name, num
) and await effect(bot, event, name, num):
):
if func_manager.check_send_success_message(name):
await use_props.send(f"使用道具 {name} {num} 次成功!", at_sender=True)
if msg := await effect(bot, event, name, num):
await use_props.send(msg, at_sender=True)
logger.info(
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} {num} 次成功"
)
+11 -9
View File
@@ -1,6 +1,7 @@
from nonebot.adapters.cqhttp import GroupMessageEvent
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageSegment
from services.log import logger
from nonebot.adapters.cqhttp import Bot
from nonebot.adapters.onebot.v11 import Bot
from typing import Optional, Union
import asyncio
@@ -32,7 +33,7 @@ class GoodsUseFuncManager:
return self._data[goods_name]["kwargs"]["_max_num_limit"]
return 1
async def use(self, **kwargs):
async def use(self, **kwargs) -> Optional[Union[str, MessageSegment]]:
"""
使用道具
:param kwargs: kwargs
@@ -40,11 +41,11 @@ class GoodsUseFuncManager:
goods_name = kwargs.get("goods_name")
if self.exists(goods_name):
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
await self._data[goods_name]["func"](
return await self._data[goods_name]["func"](
**kwargs,
)
else:
self._data[goods_name]["func"](
return self._data[goods_name]["func"](
**kwargs,
)
@@ -70,7 +71,9 @@ class GoodsUseFuncManager:
func_manager = GoodsUseFuncManager()
async def effect(bot: Bot, event: GroupMessageEvent, goods_name: str, num: int) -> bool:
async def effect(
bot: Bot, event: GroupMessageEvent, goods_name: str, num: int
) -> Optional[Union[str, MessageSegment]]:
"""
商品生效
:param bot: Bot
@@ -83,7 +86,7 @@ async def effect(bot: Bot, event: GroupMessageEvent, goods_name: str, num: int)
try:
if func_manager.exists(goods_name):
_kwargs = func_manager.get_kwargs(goods_name)
await func_manager.use(
return await func_manager.use(
**{
**_kwargs,
"_bot": bot,
@@ -94,10 +97,9 @@ async def effect(bot: Bot, event: GroupMessageEvent, goods_name: str, num: int)
"goods_name": goods_name,
}
)
return True
except Exception as e:
logger.error(f"use 商品生效函数effect 发生错误 {type(e)}:{e}")
return False
return None
def register_use(goods_name: str, func, **kwargs):
+22 -26
View File
@@ -1,9 +1,10 @@
from nonebot import on_command
from nonebot.permission import SUPERUSER
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, Message
from nonebot.adapters.onebot.v11 import Bot, Message
from nonebot.params import Command, CommandArg
from typing import Tuple
from nonebot.rule import to_me
from utils.utils import get_message_text, is_number
from utils.utils import is_number
from utils.manager import requests_manager
from utils.message_builder import image
from models.group_info import GroupInfo
@@ -57,37 +58,30 @@ cls_request = on_command("查看所有请求", permission=SUPERUSER, priority=1,
@cls_group.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
gl = await bot.get_group_list(self_id=int(bot.self_id))
async def _(bot: Bot):
gl = await bot.get_group_list()
msg = ["{group_id} {group_name}".format_map(g) for g in gl]
msg = "\n".join(msg)
msg = f"bot:{bot.self_id}\n| 群号 | 群名 | 共{len(gl)}个群\n" + msg
await bot.send_private_msg(
self_id=int(bot.self_id),
user_id=int(list(bot.config.superusers)[0]),
message=msg,
)
await cls_group.send(msg)
@cls_friend.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
gl = await bot.get_friend_list(self_id=int(bot.self_id))
async def _(bot: Bot):
gl = await bot.get_friend_list()
msg = ["{user_id} {nickname}".format_map(g) for g in gl]
msg = "\n".join(msg)
msg = f"| QQ号 | 昵称 | 共{len(gl)}个好友\n" + msg
await bot.send_private_msg(
self_id=int(bot.self_id),
user_id=int(list(bot.config.superusers)[0]),
message=msg,
)
await cls_friend.send(msg)
@friend_handle.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
id_ = get_message_text(event.json())
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
cmd = cmd[0]
id_ = arg.extract_plain_text().strip()
if is_number(id_):
id_ = int(id_)
if state["_prefix"]["raw_command"][:2] == "同意":
if cmd[:2] == "同意":
if await requests_manager.approve(bot, id_, "private"):
await friend_handle.send("同意好友请求成功..")
else:
@@ -102,12 +96,13 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@group_handle.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
id_ = get_message_text(event.json())
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
cmd = cmd[0]
id_ = arg.extract_plain_text().strip()
if is_number(id_):
id_ = int(id_)
if state["_prefix"]["raw_command"][:2] == "同意":
rid = await requests_manager.approve(bot, id_, "group")
if cmd[:2] == "同意":
rid = requests_manager.get_group_id(id_)
if rid:
await friend_handle.send("同意群聊请求成功..")
if await GroupInfo.get_group_info(rid):
@@ -121,6 +116,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
group_info["member_count"],
1
)
await requests_manager.approve(bot, id_, "group")
else:
await friend_handle.send("同意群聊请求失败,可能是未找到此id的请求..")
else:
@@ -133,7 +129,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@cls_request.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _():
_str = ""
for type_ in ["private", "group"]:
msg = await requests_manager.show(type_)
@@ -147,6 +143,6 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@clear_request.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _():
requests_manager.clear()
await cls_request.send("已清空所有好友/群聊请求..")
+9 -10
View File
@@ -1,7 +1,5 @@
from nonebot import on_command
from nonebot.permission import SUPERUSER
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent
from configs.path_config import TEMP_PATH
from nonebot.rule import to_me
from utils.utils import scheduler
@@ -35,7 +33,7 @@ resources_manager.add_temp_dir(TEMP_PATH)
@clear_data.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _():
await clear_data.send("开始清理临时数据....")
size = await asyncio.get_event_loop().run_in_executor(None, _clear_data)
await clear_data.send("共清理了 {:.2f}MB 的数据...".format(size / 1024 / 1024))
@@ -47,13 +45,14 @@ def _clear_data() -> float:
if dir_.exists():
for file in os.listdir(dir_):
file = dir_ / file
try:
if time.time() - os.path.getatime(file) > 300:
file_size = os.path.getsize(file)
file.unlink()
size += file_size
except Exception as e:
logger.error(f"清理临时数据错误...{type(e)}:{e}")
if file.is_file():
try:
if time.time() - os.path.getatime(file) > 300:
file_size = os.path.getsize(file)
file.unlink()
size += file_size
except Exception as e:
logger.error(f"清理临时数据错误...{type(e)}:{e}")
return float(size)
-74
View File
@@ -1,74 +0,0 @@
# async def open_remind(group: int, name: str) -> str:
# _name = ""
# if name == "zwa":
# _name = "早晚安"
# if name == "dz":
# _name = "地震播报"
# if name == "hy":
# _name = "群欢迎"
# if name == "kxcz":
# _name = "开箱重置提醒"
# if name == "gb":
# _name = "广播"
# if await GroupRemind.get_status(group, name):
# return f"该群已经开启过 {_name} 通知,请勿重复开启!"
# if await GroupRemind.set_status(group, name, True):
# return f"成功开启 {_name} 通知!0v0"
# else:
# return f"开启 {_name} 通知失败了..."
#
#
# async def close_remind(group: int, name: str) -> str:
# _name = ""
# if name == "zwa":
# _name = "早晚安"
# if name == "dz":
# _name = "地震播报"
# if name == "hy":
# _name = "群欢迎"
# if name == "kxcz":
# _name = "开箱重置提醒"
# if name == "gb":
# _name = "广播"
# if not await GroupRemind.get_status(group, name):
# return f"该群已经取消过 {_name} 通知,请勿重复取消!"
# if await GroupRemind.set_status(group, name, False):
# return f"成功关闭 {_name} 通知!0v0"
# else:
# return f"关闭 {_name} 通知失败了..."
# cmd_list = ['总开关', '签到', '发送图片', '色图', '黑白草图', 'coser', '鸡汤/语录', '骂我', '开箱', '鲁迅说', '假消息', '商店系统',
# '操作图片', '查询皮肤', '天气', '疫情', '识番', '搜番', '点歌', 'pixiv', 'rss', '方舟一井', '查干员', '骰子娘', '原神一井']
#
#
# def check_group_switch_json(group_id):
# if not os.path.exists(DATA_PATH + f'rule/group_switch/'):
# os.mkdir(DATA_PATH + f'rule/group_switch/')
# if not os.path.exists(DATA_PATH + f'rule/group_switch/{group_id}.json'):
# with open(DATA_PATH + f'rule/group_switch/{group_id}.json', 'w', encoding='utf8') as f:
# data = {}
# for cmd in cmd_list:
# data[cmd] = True
# f.write(json.dumps(data, ensure_ascii=False))
# else:
# with open(DATA_PATH + f'rule/group_switch/{group_id}.json', 'r', encoding='utf8') as f:
# try:
# data = json.load(f)
# except ValueError:
# data = {}
# if len(data.keys()) - 1 != len(cmd_list):
# for cmd in cmd_list:
# if cmd not in data.keys():
# data[cmd] = True
# with open(DATA_PATH + f'rule/group_switch/{group_id}.json', 'w', encoding='utf8') as wf:
# wf.write(json.dumps(data, ensure_ascii=False))
# reload(data)
# for file in os.listdir(DATA_PATH + 'group_help'):
# os.remove(DATA_PATH + f'group_help/{file}')
def reload(data):
static_group_dict = data
+4 -5
View File
@@ -1,10 +1,9 @@
from nonebot.adapters.onebot.v11 import Message
from nonebot import on_command
from nonebot.permission import SUPERUSER
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent
from nonebot.rule import to_me
from services.db_context import db
from utils.utils import get_message_text
from nonebot.params import CommandArg
from services.log import logger
__zx_plugin_name__ = "执行sql [Superuser]"
@@ -26,8 +25,8 @@ exec_ = on_command("exec", rule=to_me(), permission=SUPERUSER, priority=1, block
@exec_.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
sql = get_message_text(event.json())
async def _(arg: Message = CommandArg()):
sql = arg.extract_plain_text().strip()
async with db.transaction():
try:
query = db.text(sql)
+23 -19
View File
@@ -1,14 +1,16 @@
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GROUP, GroupMessageEvent, Message
from nonebot import on_command, on_regex
from nonebot.permission import SUPERUSER
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GROUP, GroupMessageEvent
from nonebot.rule import to_me
from utils.utils import get_message_text, is_number
from utils.utils import is_number
from utils.manager import group_manager, plugins2settings_manager
from models.group_info import GroupInfo
from services.log import logger
from configs.config import NICKNAME
from nonebot.adapters.cqhttp.exception import ActionFailed
from nonebot.adapters.onebot.v11.exception import ActionFailed
from nonebot.params import Command, CommandArg
from typing import Tuple
__zx_plugin_name__ = "管理群操作 [Superuser]"
@@ -65,8 +67,8 @@ group_auth = on_command(
@del_group.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
group_id = get_message_text(event.json())
async def _(bot: Bot, arg: Message = CommandArg()):
group_id = arg.extract_plain_text().strip()
if group_id:
if is_number(group_id):
try:
@@ -84,8 +86,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@add_group_level.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
group_id = 0
level = 0
if not msg:
@@ -109,7 +111,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@my_group_level.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
async def _(event: GroupMessageEvent):
level = group_manager.get_group_level(event.group_id)
tmp = ""
data = plugins2settings_manager.get_data()
@@ -125,17 +127,18 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
@what_up_group_level.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
async def _():
await what_up_group_level.finish(
f"[此功能用于防止内鬼,如果引起不便那真是抱歉了]\n" f"目前提高群权限的方法:\n" f"\t1.管理员修改权限"
)
@manager_group_whitelist.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json()).split()
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
cmd = cmd[0]
msg = arg.extract_plain_text().strip()
all_group = [
g["group_id"] for g in await bot.get_group_list(self_id=int(bot.self_id))
g["group_id"] for g in await bot.get_group_list()
]
group_list = []
for group in msg:
@@ -143,20 +146,20 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
group_list.append(int(group))
if group_list:
for group in group_list:
if state["_prefix"]["raw_command"] in ["添加群白名单"]:
if cmd in ["添加群白名单"]:
group_manager.add_group_white_list(group)
else:
group_manager.delete_group_white_list(group)
group_list = [str(x) for x in group_list]
await manager_group_whitelist.send(
"已成功将 " + "\n".join(group_list) + " " + state["_prefix"]["raw_command"]
"已成功将 " + "\n".join(group_list) + " " + cmd
)
else:
await manager_group_whitelist.send(f"添加失败,请检查{NICKNAME}是否已加入这些群聊或重复添加/删除群白单名")
@show_group_whitelist.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _():
x = group_manager.get_group_white_list()
x = [str(g) for g in x]
if x:
@@ -166,13 +169,14 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@group_auth.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json()).split()
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
cmd = cmd[0]
msg = arg.extract_plain_text().strip().split()
for group_id in msg:
if not is_number(group_id):
await group_auth.send(f"{group_id}非纯数字,已跳过该项..")
group_id = int(group_id)
if state["_prefix"]["raw_command"][:2] == "添加":
if cmd[:2] == "添加":
if await GroupInfo.get_group_info(group_id):
await GroupInfo.set_group_flag(group_id, 1)
else:
@@ -196,5 +200,5 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
if await GroupInfo.get_group_info(group_id):
await GroupInfo.set_group_flag(group_id, 0)
await group_auth.send(
f'已为 {group_id} {state["_prefix"]["raw_command"][:2]}群认证..'
f'已为 {group_id} {cmd[:2]}群认证..'
)
+31 -3
View File
@@ -7,8 +7,9 @@ from utils.manager import (
plugins2block_manager,
group_manager,
)
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent
from configs.config import Config
from services.log import logger
from utils.utils import scheduler
__zx_plugin_name__ = "重载插件配置 [Superuser]"
@@ -28,6 +29,18 @@ __plugin_cmd__ = [
]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_configs__ = {
"AUTO_RELOAD": {
"value": False,
"help": "自动重载配置文件",
"default_value": False
},
"AUTO_RELOAD_TIME": {
"value": 180,
"help": "控制自动重载配置文件时长",
"default_value": 180
}
}
reload_plugins_manager = on_command(
@@ -36,9 +49,24 @@ reload_plugins_manager = on_command(
@reload_plugins_manager.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _():
plugins2settings_manager.reload()
plugins2cd_manager.reload()
plugins2block_manager.reload()
group_manager.reload()
Config.reload()
await reload_plugins_manager.send("重载完成...")
@scheduler.scheduled_job(
'interval',
seconds=Config.get_config("reload_setting", "AUTO_RELOAD_TIME", 180),
)
async def _():
if Config.get_config("reload_setting", "AUTO_RELOAD"):
plugins2settings_manager.reload()
plugins2cd_manager.reload()
plugins2block_manager.reload()
group_manager.reload()
Config.reload()
logger.debug("已自动重载所有配置文件...")
@@ -1,11 +1,12 @@
from nonebot import on_command
from nonebot.permission import SUPERUSER
from models.level_user import LevelUser
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, Message
from utils.utils import get_message_at, get_message_text, is_number
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, Message, GroupMessageEvent
from utils.utils import get_message_at, is_number
from services.log import logger
from utils.message_builder import at
from nonebot.params import Command, CommandArg
from typing import Tuple
__zx_plugin_name__ = "用户权限管理 [Superuser]"
__plugin_usage__ = """
@@ -36,40 +37,49 @@ super_cmd = on_command(
@super_cmd.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
group_id = -1
level = 0
async def _(
bot: Bot,
event: MessageEvent,
cmd: Tuple[str, ...] = Command(),
arg: Message = CommandArg(),
):
group_id = event.group_id if isinstance(event, GroupMessageEvent) else -1
level = None
args = arg.extract_plain_text().strip().split()
qq = get_message_at(event.json())
flag = 2
try:
args = get_message_text(event.json()).split()
qq = get_message_at(event.json())
flag = -1
if not qq:
if len(args) > 2:
if is_number(args[0]) and is_number(args[1]) and is_number(args[2]):
if qq:
qq = qq[0]
if cmd[0][:2] == "添加" and args and is_number(args[0]):
level = int(args[0])
else:
if cmd[0][:2] == "添加":
if (
len(args) > 2
and is_number(args[0])
and is_number(args[1])
and is_number(args[2])
):
qq = int(args[0])
group_id = int(args[1])
level = int(args[2])
flag = 1
else:
await super_cmd.finish("所有参数必须是数字!", at_sender=True)
else:
await super_cmd.finish(
"权限参数不完全\n\t格式:添加/删除权限 [at] [level]"
"\n\t格式:添加/删除权限 [qq] [group_id] [level]",
at_sender=True,
)
else:
if not is_number(args[0]):
await super_cmd.finish("所有参数必须是数字!", at_sender=True)
level = int(args[0])
qq = qq[0]
group_id = event.group_id
flag = 2
if state["_prefix"]["raw_command"][:2] == "添加":
if len(args) > 1 and is_number(args[0]) and is_number(args[1]):
qq = int(args[0])
group_id = int(args[1])
flag = 1
level = -1 if cmd[0][:2] == "删除" else level
if group_id == -1 or not level or not qq:
raise IndexError()
except IndexError:
await super_cmd.finish(__plugin_usage__)
try:
if cmd[0][:2] == "添加":
if await LevelUser.set_level(qq, group_id, level, 1):
result = "添加管理成功, 权限: " + str(level)
result = f"添加管理成功, 权限: {level}"
else:
result = "管理已存在, 更新权限: " + str(level)
result = f"管理已存在, 更新权限: {level}"
else:
if await LevelUser.delete_level(qq, event.group_id):
result = "删除管理成功!"
@@ -80,7 +90,10 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
elif flag == 1:
await bot.send_group_msg(
group_id=group_id,
message=Message(f"{at(qq)}管理员修改了你的权限" f"\n--------\n你当前的权限等级:{level}"),
message=Message(
f"{at(qq)}管理员修改了你的权限"
f"\n--------\n你当前的权限等级:{level if level != -1 else 0}"
),
)
await super_cmd.send("修改成功")
except Exception as e:
+8 -6
View File
@@ -1,11 +1,12 @@
from nonebot import on_command
from nonebot.permission import SUPERUSER
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, Message
from nonebot.rule import to_me
from utils.utils import get_message_text, is_number
from utils.utils import is_number
from services.log import logger
from utils.manager import group_manager
from nonebot.params import Command, CommandArg
from typing import Tuple
__zx_plugin_name__ = "超级用户被动开关 [Superuser]"
@@ -34,8 +35,9 @@ oc_gb = on_command(
@oc_gb.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
group = get_message_text(event.json())
async def _(bot: Bot, event: MessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
cmd = cmd[0]
group = arg.extract_plain_text().strip()
if group:
if is_number(group):
group = int(group)
@@ -44,7 +46,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
break
else:
await oc_gb.finish("没有加入这个群...", at_sender=True)
if state["_prefix"]["raw_command"] == "开启广播通知":
if cmd == "开启广播通知":
logger.info(f"USER {event.user_id} 开启了 GROUP {group} 的广播")
await oc_gb.finish(await group_manager.open_group_task(group, "broadcast",), at_sender=True)
else:
@@ -1,7 +1,5 @@
from nonebot import on_command
from nonebot.permission import SUPERUSER
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent
from nonebot.rule import to_me
from utils.utils import get_bot
from services.log import logger
@@ -34,7 +32,7 @@ update_friend_info = on_command(
@update_group_info.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _():
bot = get_bot()
gl = await bot.get_group_list()
gl = [g["group_id"] for g in gl]
@@ -57,7 +55,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@update_friend_info.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _():
num = 0
rst = ""
fl = await get_bot().get_friend_list()
+2 -5
View File
@@ -1,18 +1,15 @@
from nonebot import on_command
from nonebot.permission import SUPERUSER
from nonebot.typing import T_State
from nonebot.adapters import Bot, Event
from nonebot.rule import to_me
from configs.path_config import IMAGE_PATH
from utils.message_builder import image
from .data_source import create_help_image
from pathlib import Path
__zx_plugin_name__ = '超级用户帮助 [Superuser]'
superuser_help_image = Path(IMAGE_PATH) / 'superuser_help.png'
superuser_help_image = IMAGE_PATH / 'superuser_help.png'
if superuser_help_image.exists():
superuser_help_image.unlink()
@@ -23,7 +20,7 @@ super_help = on_command(
@super_help.handle()
async def _(bot: Bot, event: Event, state: T_State):
async def _():
if not superuser_help_image.exists():
await create_help_image()
x = image(superuser_help_image)
+5 -6
View File
@@ -2,8 +2,7 @@ from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH
from services.log import logger
from utils.utils import get_matchers
from nonebot.adapters.cqhttp import Bot
from pathlib import Path
from nonebot.adapters.onebot.v11 import Bot
from nonebot import Driver
import asyncio
import nonebot
@@ -11,9 +10,9 @@ import nonebot
driver: Driver = nonebot.get_driver()
background = Path(IMAGE_PATH) / "background" / "0.png"
background = IMAGE_PATH / "background" / "0.png"
superuser_help_image = Path(IMAGE_PATH) / "superuser_help.png"
superuser_help_image = IMAGE_PATH / "superuser_help.png"
@driver.on_bot_connect
@@ -36,7 +35,7 @@ def _create_help_image():
for matcher in _matchers:
plugin_name = ""
try:
_plugin = nonebot.plugin.get_plugin(matcher.module)
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
_module = _plugin.module
try:
plugin_name = _module.__getattribute__("__zx_plugin_name__")
@@ -68,7 +67,7 @@ def _create_help_image():
width = width if width > x else x
except Exception as e:
logger.warning(
f"获取超级用户插件 {matcher.module}: {plugin_name} 设置失败... {type(e)}:{e}"
f"获取超级用户插件 {matcher.plugin_name}: {plugin_name} 设置失败... {type(e)}:{e}"
)
height = len(help_str.split("\n")) * 33
width += 500
+1 -3
View File
@@ -1,6 +1,4 @@
from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, Event
from nonebot.typing import T_State
from utils.message_builder import image
@@ -27,7 +25,7 @@ update_info = on_command("更新信息", aliases={"更新日志"}, priority=5, b
@update_info.handle()
async def _(bot: Bot, event: Event, state: T_State):
async def _():
img = image("update_info.png")
if img:
await update_info.finish(image("update_info.png"))
+2 -3
View File
@@ -1,11 +1,10 @@
import nonebot
from nonebot.adapters.cqhttp import Bot as CQHTTPBot
from nonebot.adapters.onebot.v11 import Adapter
from services.db_context import init, disconnect
nonebot.init()
driver = nonebot.get_driver()
driver.register_adapter("cqhttp", CQHTTPBot)
driver.register_adapter(Adapter)
config = driver.config
driver.on_startup(init)
driver.on_shutdown(disconnect)
+25 -21
View File
@@ -1,41 +1,45 @@
from pathlib import Path
import os
# 图片路径
IMAGE_PATH = Path("resources/img/")
# 音频路径
VOICE_PATH = Path("resources/voice/")
IMAGE_PATH = Path() / "resources" / "image"
# 语音路径
RECORD_PATH = Path() / "resources" / "record"
# 文本路径
TEXT_PATH = Path("resources/txt/")
TEXT_PATH = Path() / "resources" / "text"
# 日志路径
LOG_PATH = Path("log/")
LOG_PATH = Path() / "log"
# 字体路径
FONT_PATH = Path("resources/ttf/")
FONT_PATH = Path() / "resources" / "font"
# 数据路径
DATA_PATH = Path("data/")
# 临时图片路径
TEMP_PATH = Path("resources/img/temp/")
DATA_PATH = Path() / "data"
# 临时数据路径
TEMP_PATH = Path() / "resources" / "temp"
def init_path():
global IMAGE_PATH, VOICE_PATH, TEXT_PATH, LOG_PATH, FONT_PATH, DATA_PATH, TEMP_PATH
def load_path():
old_img_dir = Path() / "resources" / "img"
if not IMAGE_PATH.exists() and old_img_dir.exists():
os.rename(old_img_dir, IMAGE_PATH)
old_voice_dir = Path() / "resources" / "voice"
if not RECORD_PATH.exists() and old_voice_dir.exists():
os.rename(old_voice_dir, RECORD_PATH)
old_ttf_dir = Path() / "resources" / "ttf"
if not FONT_PATH.exists() and old_ttf_dir.exists():
os.rename(old_ttf_dir, FONT_PATH)
old_txt_dir = Path() / "resources" / "txt"
if not TEXT_PATH.exists() and old_txt_dir.exists():
os.rename(old_txt_dir, TEXT_PATH)
IMAGE_PATH.mkdir(parents=True, exist_ok=True)
VOICE_PATH.mkdir(parents=True, exist_ok=True)
RECORD_PATH.mkdir(parents=True, exist_ok=True)
TEXT_PATH.mkdir(parents=True, exist_ok=True)
LOG_PATH.mkdir(parents=True, exist_ok=True)
FONT_PATH.mkdir(parents=True, exist_ok=True)
DATA_PATH.mkdir(parents=True, exist_ok=True)
TEMP_PATH.mkdir(parents=True, exist_ok=True)
IMAGE_PATH = str(IMAGE_PATH.absolute()) + '/'
VOICE_PATH = str(VOICE_PATH.absolute()) + '/'
TEXT_PATH = str(TEXT_PATH.absolute()) + '/'
LOG_PATH = str(LOG_PATH.absolute()) + '/'
FONT_PATH = str(FONT_PATH.absolute()) + '/'
DATA_PATH = str(DATA_PATH.absolute()) + '/'
TEMP_PATH = str(TEMP_PATH.absolute()) + '/'
init_path()
load_path()
+14
View File
@@ -153,6 +153,20 @@ class ConfigsManager:
self._data, f, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True
)
def reload(self):
"""
重新加载配置文件
"""
_yaml = YAML()
temp_file = Path() / "configs" / "config.yaml"
if temp_file.exists():
with open(temp_file, "r", encoding="utf8") as f:
temp = _yaml.load(f)
for key in temp.keys():
for k in temp[key].keys():
self._data[key][k]["value"] = temp[key][k]
self.save()
def get_admin_level_data(self):
"""
获取管理插件等级
+4 -2
View File
@@ -131,8 +131,10 @@ class GroupInfoUser(db.Model):
@classmethod
async def get_user_all_group(cls, user_qq: int) -> List[int]:
"""
获取该用户所在的所有群聊
:param user_qq: 用户qq
说明:
获取该用户所在的所有群聊
参数:
:param user_qq: 用户qq
"""
query = await cls.query.where(cls.user_qq == user_qq).gino.all()
if query:
+1 -3
View File
@@ -1,6 +1,4 @@
from nonebot import on_regex
from nonebot.adapters.cqhttp import Bot, MessageEvent
from nonebot.typing import T_State
from nonebot.rule import to_me
from pathlib import Path
@@ -29,7 +27,7 @@ about = on_regex("^关于$", priority=5, block=True, rule=to_me())
@about.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _():
ver_file = Path() / '__version__'
version = None
if ver_file.exists():
+9 -10
View File
@@ -1,15 +1,14 @@
import random
from nonebot import on_keyword
import os
from utils.message_builder import image
from configs.path_config import IMAGE_PATH
from nonebot import on_command
from nonebot.rule import to_me
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent
from nonebot.adapters.cqhttp.permission import GROUP
from nonebot.adapters.onebot.v11 import GroupMessageEvent
from nonebot.adapters.onebot.v11.permission import GROUP
from utils.utils import FreqLimiter
from configs.config import NICKNAME
import random
from nonebot import on_keyword
import os
__zx_plugin_name__ = "基本设置 [Hidden]"
@@ -25,12 +24,12 @@ config_play_game = on_keyword({"打游戏"}, permission=GROUP, priority=1, block
@config_play_game.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
async def _(event: GroupMessageEvent):
if not _flmt.check(event.group_id):
return
_flmt.start_cd(event.group_id)
await config_play_game.finish(
image(random.choice(os.listdir(IMAGE_PATH + "dayouxi/")), "dayouxi")
image(random.choice(os.listdir(IMAGE_PATH / "dayouxi")), "dayouxi")
)
@@ -40,7 +39,7 @@ self_introduction = on_command(
@self_introduction.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _():
if NICKNAME.find('真寻') != -1:
result = (
"我叫绪山真寻\n"
@@ -57,6 +56,6 @@ my_wife = on_keyword({"老婆"}, rule=to_me(), priority=5, block=True)
@my_wife.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _():
await my_wife.finish(image("laopo.jpg", "other"))
+5 -7
View File
@@ -1,17 +1,15 @@
from nonebot import on_message
from nonebot.adapters.cqhttp import (
from nonebot.adapters.onebot.v11 import (
Bot,
GroupMessageEvent,
Message,
MessageEvent,
)
from nonebot.rule import to_me
from nonebot.typing import T_State
from models.friend_user import FriendUser
from models.group_member_info import GroupInfoUser
from services.log import logger
from utils.utils import get_message_text, get_message_img
from utils.utils import get_message_img, get_message_text
from .data_source import get_chat_result, hello, no_result
from configs.config import NICKNAME, Config
@@ -36,14 +34,14 @@ __plugin_configs__ = {
},
}
Config.add_plugin_config(
"alapi", "ALAPI_TOKEN", None, help_="在https://admin.alapi.cn/user/login登录后获取token"
"alapi", "ALAPI_TOKEN", None, help_="在 https://admin.alapi.cn/user/login 登录后获取token"
)
ai = on_message(rule=to_me(), priority=8)
@ai.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _(bot: Bot, event: MessageEvent):
msg = get_message_text(event.json())
img = get_message_img(event.json())
if "CQ:xml" in str(event.get_message()):
@@ -69,7 +67,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
nickname = await FriendUser.get_friend_nickname(event.user_id)
if not nickname:
if isinstance(event, GroupMessageEvent):
nickname = event.sender.card if event.sender.card else event.sender.nickname
nickname = event.sender.card or event.sender.nickname
else:
nickname = event.sender.nickname
result = await get_chat_result(msg, img, event.user_id, nickname)
+5 -5
View File
@@ -20,7 +20,7 @@ check_url = "https://v2.alapi.cn/api/censor/text"
index = 0
anime_data = json.load(open(DATA_PATH + "anime.json", "r", encoding="utf8"))
anime_data = json.load(open(DATA_PATH / "anime.json", "r", encoding="utf8"))
async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str) -> str:
@@ -57,7 +57,7 @@ async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str)
if random.random() < 0.2:
if nickname.find("大人") == -1:
nickname += "大~人~"
rst = rst.replace("小主人", nickname).replace("小朋友", nickname)
rst = str(rst).replace("小主人", nickname).replace("小朋友", nickname)
ai_message_manager.add_result(user_id, rst)
return rst
@@ -139,7 +139,7 @@ async def xie_ai(text: str) -> str:
content = content.replace("{br}", "\n")
if "提示" in content:
content = content[: content.find("提示")]
if "淘宝" in content:
if "淘宝" in content or "taobao.com" in content:
return ""
while True:
r = re.search("{face:(.*)}", content)
@@ -170,7 +170,7 @@ def hello() -> str:
"呼呼,叫俺干嘛",
)
)
img = random.choice(os.listdir(IMAGE_PATH + "zai/"))
img = random.choice(os.listdir(IMAGE_PATH / "zai"))
if img[-4:] == ".gif":
result += image(img, "zai")
else:
@@ -193,7 +193,7 @@ def no_result() -> str:
"我!不!知!道!",
]
)
+ image(random.choice(os.listdir(IMAGE_PATH + "noresult/")), "noresult")
+ image(random.choice(os.listdir(IMAGE_PATH / "noresult")), "noresult")
)
+49 -49
View File
@@ -1,49 +1,49 @@
from nonebot.adapters.cqhttp import MessageSegment
from utils.image_utils import BuildImage
from utils.message_builder import image
from configs.path_config import IMAGE_PATH
from typing import Optional
from configs.config import Config
from utils.http_utils import AsyncHttpx
async def get_data(url: str, params: Optional[dict] = None) -> "Union[dict, str], int":
"""
获取ALAPI数据
:param url: 请求链接
:param params: 参数
"""
if not params:
params = {}
params["token"] = Config.get_config("alapi", "ALAPI_TOKEN")
try:
data = (await AsyncHttpx.get(url, params=params, timeout=5)).json()
if data["code"] == 200:
if not data["data"]:
return "没有搜索到...", 997
return data, 200
else:
return f'发生了错误...code:{data["code"]}', 999
except TimeoutError:
return "超时了....", 998
def gen_wbtop_pic(data: dict) -> MessageSegment:
"""
生成微博热搜图片
:param data: 微博热搜数据
"""
bk = BuildImage(700, 32 * 50 + 280, 700, 32, color="#797979")
wbtop_bk = BuildImage(700, 280, background=f"{IMAGE_PATH}/other/webtop.png")
bk.paste(wbtop_bk)
text_bk = BuildImage(700, 32 * 50, 700, 32, color="#797979")
for i, data in enumerate(data):
title = f"{i+1}. {data['hot_word']}"
hot = data["hot_word_num"]
img = BuildImage(700, 30, font_size=20)
w, h = img.getsize(title)
img.text((10, int((30 - h) / 2)), title)
img.text((580, int((30 - h) / 2)), hot)
text_bk.paste(img)
bk.paste(text_bk, (0, 280))
return image(b64=bk.pic2bs4())
from nonebot.adapters.onebot.v11 import MessageSegment
from utils.image_utils import BuildImage
from utils.message_builder import image
from configs.path_config import IMAGE_PATH
from typing import Optional, Tuple, Union
from configs.config import Config
from utils.http_utils import AsyncHttpx
async def get_data(url: str, params: Optional[dict] = None) -> Tuple[Union[dict, str], int]:
"""
获取ALAPI数据
:param url: 请求链接
:param params: 参数
"""
if not params:
params = {}
params["token"] = Config.get_config("alapi", "ALAPI_TOKEN")
try:
data = (await AsyncHttpx.get(url, params=params, timeout=5)).json()
if data["code"] == 200:
if not data["data"]:
return "没有搜索到...", 997
return data, 200
else:
return f'发生了错误...code:{data["code"]}', 999
except TimeoutError:
return "超时了....", 998
def gen_wbtop_pic(data: dict) -> MessageSegment:
"""
生成微博热搜图片
:param data: 微博热搜数据
"""
bk = BuildImage(700, 32 * 50 + 280, 700, 32, color="#797979")
wbtop_bk = BuildImage(700, 280, background=f"{IMAGE_PATH}/other/webtop.png")
bk.paste(wbtop_bk)
text_bk = BuildImage(700, 32 * 50, 700, 32, color="#797979")
for i, data in enumerate(data):
title = f"{i+1}. {data['hot_word']}"
hot = data["hot_word_num"]
img = BuildImage(700, 30, font_size=20)
w, h = img.getsize(title)
img.text((10, int((30 - h) / 2)), title)
img.text((580, int((30 - h) / 2)), hot)
text_bk.paste(img)
bk.paste(text_bk, (0, 280))
return image(b64=bk.pic2bs4())
+6 -7
View File
@@ -1,7 +1,6 @@
from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
from .data_source import get_data
from nonebot import on_regex
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent
from ._data_source import get_data
from services.log import logger
__zx_plugin_name__ = "网易云热评"
@@ -23,8 +22,8 @@ __plugin_settings__ = {
}
comments_163 = on_command(
"网易云热评", aliases={"网易云评论", "到点了", "12点了"}, priority=5, block=True
comments_163 = on_regex(
"^(网易云热评|网易云评论|到点了|12点了)$", priority=5, block=True
)
@@ -32,7 +31,7 @@ comments_163_url = "https://v2.alapi.cn/api/comment"
@comments_163.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _(event: MessageEvent):
data, code = await get_data(comments_163_url)
if code != 200:
await comments_163.finish(data, at_sender=True)
+5 -6
View File
@@ -1,9 +1,8 @@
from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent
from nonebot.typing import T_State
from nonebot.adapters.onebot.v11 import MessageEvent, Message, GroupMessageEvent
from utils.message_builder import image
from utils.utils import get_message_text
from .data_source import get_data
from nonebot.params import CommandArg
from ._data_source import get_data
from services.log import logger
__zx_plugin_name__ = "b封面"
@@ -32,8 +31,8 @@ cover_url = "https://v2.alapi.cn/api/bilibili/cover"
@cover.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
async def _(event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
params = {"c": msg}
data, code = await get_data(cover_url, params)
if code != 200:
+5 -8
View File
@@ -1,10 +1,7 @@
from nonebot import on_command
from nonebot import on_regex
from services.log import logger
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
from utils.http_utils import AsyncHttpx
from configs.config import Config
from .data_source import get_data
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent
from ._data_source import get_data
__zx_plugin_name__ = "鸡汤"
@@ -28,11 +25,11 @@ __plugin_settings__ = {
url = "https://v2.alapi.cn/api/soul"
jitang = on_command("鸡汤", aliases={"毒鸡汤"}, priority=5, block=True)
jitang = on_regex("^毒?鸡汤$", priority=5, block=True)
@jitang.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _(event: MessageEvent):
try:
data, code = await get_data(url)
if code != 200:
+3 -4
View File
@@ -1,8 +1,7 @@
from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent
from services.log import logger
from .data_source import get_data
from ._data_source import get_data
__zx_plugin_name__ = "古诗"
__plugin_usage__ = """usage:
@@ -27,7 +26,7 @@ poetry_url = "https://v2.alapi.cn/api/shici"
@poetry.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _(event: MessageEvent):
data, code = await get_data(poetry_url)
if code != 200:
await poetry.finish(data, at_sender=True)
+16 -17
View File
@@ -1,11 +1,10 @@
from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent, Message
from nonebot.params import CommandArg
from services.log import logger
from .data_source import get_data, gen_wbtop_pic
from utils.utils import get_message_text, is_number
from ._data_source import get_data, gen_wbtop_pic
from utils.utils import is_number
from configs.path_config import IMAGE_PATH
from utils.message_builder import image
from utils.http_utils import AsyncPlaywright
import asyncio
@@ -38,9 +37,9 @@ wbtop_data = []
@wbtop.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _(event: MessageEvent, arg: Message = CommandArg()):
global wbtop_data
msg = get_message_text(event.json())
msg = arg.extract_plain_text().strip()
if not wbtop_data or not msg:
data, code = await get_data(wbtop_url)
if code != 200:
@@ -57,15 +56,15 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
)
if is_number(msg) and 0 < int(msg) <= 50:
url = wbtop_data[int(msg) - 1]["url"]
try:
await wbtop.send("开始截取数据...")
img = await AsyncPlaywright.screenshot(
url,
f"{IMAGE_PATH}/temp/wbtop_{event.user_id}.png",
"#pl_feedlist_index",
sleep=5
)
await wbtop.send("开始截取数据...")
img = await AsyncPlaywright.screenshot(
url,
f"{IMAGE_PATH}/temp/wbtop_{event.user_id}.png",
"#pl_feedlist_index",
wait_time=5
)
if img:
await wbtop.send(img)
except Exception as e:
logger.error(f"微博热搜截图出错... {type(e)}: {e}")
else:
await wbtop.send("发生了一些错误.....")
+24 -21
View File
@@ -1,6 +1,6 @@
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent, Message
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent, Message
from .data_source import (
add_live_sub,
delete_sub,
@@ -13,10 +13,11 @@ from .data_source import (
)
from models.level_user import LevelUser
from configs.config import Config
from utils.utils import get_message_text, is_number, scheduler, get_bot
from utils.utils import is_number, scheduler, get_bot
from typing import Optional
from services.log import logger
from nonebot import Driver
from nonebot.params import CommandArg, ArgStr
import nonebot
__zx_plugin_name__ = "B站订阅"
@@ -68,18 +69,9 @@ async def _():
sub_manager = SubManager()
@add_sub.args_parser
async def _(bot: Bot, event: MessageEvent, state: T_State):
season_data = state["season_data"]
msg = get_message_text(event.json())
if not is_number(msg) or int(msg) < 1 or int(msg) > len(season_data):
await add_sub.reject("Id必须为数字且在范围内!请重新输入...")
state["id"] = season_data[int(msg) - 1]["media_id"]
@add_sub.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json()).split()
async def _(event: MessageEvent, state: T_State, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip().split()
if len(msg) < 2:
await add_sub.finish("参数不完全,请查看订阅帮助...")
sub_type = msg[0]
@@ -120,11 +112,22 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
state["id"] = int(id_)
@add_sub.got("sub_type")
@add_sub.got("sub_user")
@add_sub.got("id")
async def _(bot: Bot, event: MessageEvent, state: T_State):
sub_type = state["sub_type"]
sub_user = state["sub_user"]
id_ = state["id"]
async def _(
event: MessageEvent,
state: T_State,
id_: str = ArgStr("id"),
sub_type: str = ArgStr("sub_type"),
sub_user: str = ArgStr("sub_user"),
):
if sub_type in ["season", "动漫", "番剧"] and state.get("season_data"):
season_data = state["season_data"]
if not is_number(id_) or int(id_) < 1 or int(id_) > len(season_data):
await add_sub.reject_arg("id", "Id必须为数字且在范围内!请重新输入...")
id_ = season_data[int(id_) - 1]["media_id"]
id_ = int(id_)
if sub_type in ["主播", "直播"]:
await add_sub.send(await add_live_sub(id_, sub_user))
elif sub_type.lower() in ["up", "用户"]:
@@ -141,8 +144,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@del_sub.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
async def _(event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if not is_number(msg):
await del_sub.finish("Id必须为数字!", at_sender=True)
id_ = (
@@ -162,7 +165,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@show_sub_info.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _(event: MessageEvent):
if isinstance(event, GroupMessageEvent):
id_ = f"{event.group_id}"
else:
@@ -208,7 +211,7 @@ async def _():
await sub_manager.reload_sub_data()
sub = await sub_manager.random_sub_data()
if sub:
logger.info(f"Bilibili订阅开始检测:{sub.sub_id}")
logger.debug(f"Bilibili订阅开始检测:{sub.sub_id}")
rst = await get_sub_status(sub.sub_id, sub.sub_type)
await send_sub_msg(rst, sub, bot)
if sub.sub_type == "live":
+27 -25
View File
@@ -8,7 +8,6 @@ from utils.message_builder import image
from bilibili_api.user import User
from bilibili_api import user
from typing import Optional
from pathlib import Path
from configs.path_config import IMAGE_PATH
from datetime import datetime
from utils.browser import get_browser
@@ -20,7 +19,7 @@ import random
bilibili_search_url = "https://api.bilibili.com/x/web-interface/search/all/v2"
dynamic_path = Path(IMAGE_PATH) / "bilibili_sub" / "dynamic"
dynamic_path = IMAGE_PATH / "bilibili_sub" / "dynamic"
dynamic_path.mkdir(exist_ok=True, parents=True)
@@ -193,7 +192,6 @@ async def get_sub_status(id_: int, sub_type: str) -> Optional[str]:
获取订阅状态
:param id_: 订阅 id
:param sub_type: 订阅类型
:return:
"""
try:
if sub_type == "live":
@@ -203,7 +201,7 @@ async def get_sub_status(id_: int, sub_type: str) -> Optional[str]:
elif sub_type == "season":
return await _get_season_status(id_)
except ResponseCodeException:
return "获取信息失败...请检查订阅Id是否存在或稍后再试..."
return f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试..."
# except Exception as e:
# logger.error(f"获取订阅状态发生预料之外的错误 id_:{id_} {type(e)}:{e}")
# return "发生了预料之外的错误..请稍后再试或联系管理员....."
@@ -313,28 +311,32 @@ async def get_user_dynamic(
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
if local_user.dynamic_upload_time < dynamic_upload_time:
page = await browser.new_page()
await page.goto(
f"https://space.bilibili.com/{local_user.uid}/dynamic",
wait_until="networkidle",
timeout=10000,
)
await page.set_viewport_size({"width": 2560, "height": 1080})
# 删除置顶
await page.evaluate(
try:
await page.goto(
f"https://space.bilibili.com/{local_user.uid}/dynamic",
wait_until="networkidle",
timeout=10000,
)
await page.set_viewport_size({"width": 2560, "height": 1080})
# 删除置顶
await page.evaluate(
"""
xs = document.getElementsByClassName('first-card-with-title');
for (x of xs) {
x.remove();
}
"""
xs = document.getElementsByClassName('first-card-with-title');
for (x of xs) {
x.remove();
}
"""
)
card = await page.query_selector(".card")
# 截图并保存
await card.screenshot(
path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
timeout=100000,
)
await page.close()
)
card = await page.query_selector(".card")
# 截图并保存
await card.screenshot(
path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
timeout=100000,
)
except Exception as e:
logger.error(f"B站订阅:获取用户动态 发送错误 {type(e)}:{e}")
finally:
await page.close()
return (
image(
f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
+1 -2
View File
@@ -1,12 +1,11 @@
from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH
from utils.http_utils import AsyncHttpx
from pathlib import Path
from bilibili_api import user
from io import BytesIO
BORDER_PATH = Path(IMAGE_PATH) / "border"
BORDER_PATH = IMAGE_PATH / "border"
BORDER_PATH.mkdir(parents=True, exist_ok=True)
+27 -36
View File
@@ -1,12 +1,12 @@
from nonebot import on_command
from .data_source import get_bt_info
from services.log import logger
from nonebot.typing import T_State
from nonebot.adapters import Bot
from nonebot.adapters.cqhttp import PrivateMessageEvent
from utils.utils import get_message_text
from nonebot.adapters.cqhttp.permission import PRIVATE
from nonebot.adapters.onebot.v11 import PrivateMessageEvent, Message
from nonebot.adapters.onebot.v11.permission import PRIVATE
from asyncio.exceptions import TimeoutError
from utils.utils import is_number
from nonebot.params import CommandArg, ArgStr
from nonebot.typing import T_State
__zx_plugin_name__ = "磁力搜索"
__plugin_usage__ = """
@@ -28,9 +28,7 @@ __plugin_settings__ = {
"limit_superuser": False,
"cmd": ["bt", "磁力搜索", "Bt", "BT"],
}
__plugin_block_limit__ = {
"rst": "您有bt任务正在进行,请等待结束."
}
__plugin_block_limit__ = {"rst": "您有bt任务正在进行,请等待结束."}
__plugin_configs__ = {
"BT_MAX_NUM": {
"value": 10,
@@ -43,44 +41,37 @@ __plugin_configs__ = {
bt = on_command("bt", permission=PRIVATE, priority=5, block=True)
@bt.args_parser
async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
if str(event.get_message()) in ["取消", "算了"]:
await bt.finish("已取消操作..", at_sender=True)
msg = get_message_text(event.json())
if not msg:
await bt.reject("你想搜索什么呢?", at_sender=True)
state["keyword"] = msg
state["page"] = "1"
@bt.handle()
async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
mp = get_message_text(event.json())
if not mp:
return
mp = mp.split()
if len(mp) == 2:
state["keyword"] = mp[0]
state["page"] = mp[1]
async def _(state: T_State, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip().split()
if msg:
keyword = None
page = 1
if n := len(msg):
keyword = msg[0]
if n > 1 and is_number(msg[1]) and int(msg[1]) > 0:
page = int(msg[1])
state["keyword"] = keyword
state["page"] = page
else:
state["keyword"] = mp[0]
state["page"] = "1"
state["page"] = 1
@bt.got("keyword", prompt="虚空磁力?查什么GKD")
async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
keyword = state["keyword"]
page = state["page"]
await bt.send("开始搜索....", at_sender=True)
@bt.got("keyword", prompt="请输入要查询的内容!")
async def _(
event: PrivateMessageEvent,
state: T_State,
keyword: str = ArgStr("keyword"),
page: str = ArgStr("page"),
):
send_flag = False
try:
async for title, itype, create_time, file_size, link in get_bt_info(
async for title, type_, create_time, file_size, link in get_bt_info(
keyword, page
):
await bt.send(
f"标题:{title}\n"
f"类型:{itype}\n"
f"类型:{type_}\n"
f"创建时间:{create_time}\n"
f"文件大小:{file_size}\n"
f"种子:{link}"
+4 -4
View File
@@ -9,10 +9,10 @@ if platform.system() == "Windows":
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
url = "http://www.eclzz.world"
url = "http://www.eclzz.mobi"
async def get_bt_info(keyword: str, page: str):
async def get_bt_info(keyword: str, page: int):
"""
获取资源信息
:param keyword: 关键词
@@ -34,11 +34,11 @@ async def get_bt_info(keyword: str, page: str):
.strip()
)
spans = divs[2].find_all("span")
itype = spans[0].text
type_ = spans[0].text
create_time = spans[1].find("b").text
file_size = spans[2].find("b").text
link = await get_download_link(divs[0].find("a")["href"])
yield title, itype, create_time, file_size, link
yield title, type_, create_time, file_size, link
async def get_download_link(_url: str) -> str:
-57
View File
@@ -1,57 +0,0 @@
from .music_163 import get_song_id, get_song_info
from nonebot.adapters.cqhttp import Bot, Event, GroupMessageEvent
from nonebot.typing import T_State
from services.log import logger
from nonebot import on_command
__zx_plugin_name__ = "点歌"
__plugin_usage__ = """
usage:
在线点歌
指令:
点歌 [歌名]
""".strip()
__plugin_des__ = "为你点播了一首曾经的歌"
__plugin_cmd__ = ["点歌 [歌名]"]
__plugin_type__ = ("一些工具",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["点歌"],
}
music = on_command("点歌", priority=5, block=True)
@music.handle()
async def handle_first_receive(bot: Bot, event: Event, state: T_State):
args = str(event.get_message()).strip()
if args:
state["song_name"] = args
@music.got("song_name", prompt="歌名是?")
async def _(bot: Bot, event: Event, state: T_State):
song = state["song_name"]
song_id = await get_song_id(song)
if not song_id:
await music.finish("没有找到这首歌!", at_sender=True)
for _ in range(3):
song_content = [{"type": "music", "data": {"type": 163, "id": song_id}}]
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 点歌 :{song}"
)
await music.finish(song_content)
else:
await music.finish("网易云繁忙...")
+1 -3
View File
@@ -1,7 +1,5 @@
from nonebot import on_command
from .data_source import Check
from nonebot.adapters.cqhttp import Bot, Event
from nonebot.typing import T_State
from nonebot.rule import to_me
from nonebot.permission import SUPERUSER
from utils.message_builder import image
@@ -29,5 +27,5 @@ check_ = on_command(
@check_.handle()
async def _(bot: Bot, event: Event, state: T_State):
async def _():
await check_.send(image(b64=await check.show()))
+1 -2
View File
@@ -4,7 +4,6 @@ from datetime import datetime
from utils.http_utils import AsyncHttpx
from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH
from pathlib import Path
import asyncio
from services.log import logger
@@ -73,6 +72,6 @@ class Check:
A.transparent(1)
A.text((10, 10), rst)
_x = max(width, height)
bk = BuildImage(_x + 100, _x + 100, background=Path(IMAGE_PATH) / "background" / "check" / "0.jpg")
bk = BuildImage(_x + 100, _x + 100, background=IMAGE_PATH / "background" / "check" / "0.jpg")
bk.paste(A, alpha=True, center_type='center')
return bk.pic2bs4()
+6 -8
View File
@@ -1,5 +1,4 @@
from nonebot.adapters.cqhttp import Bot, MessageEvent
from nonebot.typing import T_State
from nonebot.adapters.onebot.v11 import Bot
from nonebot.permission import SUPERUSER
from nonebot import on_command
from .data_source import check_update, get_latest_version_data
@@ -8,6 +7,7 @@ from utils.utils import scheduler, get_bot
from pathlib import Path
from configs.config import Config
from nonebot.rule import to_me
from nonebot.params import ArgStr
import platform
import os
@@ -45,7 +45,7 @@ restart = on_command(
@update_zhenxun.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _(bot: Bot):
try:
code, error = await check_update(bot)
if error:
@@ -67,14 +67,13 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@restart.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _():
if str(platform.system()).lower() == "windows":
await restart.finish("暂无windows重启脚本...")
@restart.got("flag", prompt="确定是否重启真寻?(重启失败咱们将失去联系,请谨慎!)")
async def _(bot: Bot, event: MessageEvent, state: T_State):
flag = state["flag"]
async def _(flag: str = ArgStr("flag")):
if flag.lower() in ["true", "是", "好", "确定", "确定是"]:
await restart.send("开始重启真寻..请稍等...")
open("is_restart", "w")
@@ -107,8 +106,7 @@ async def _():
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"检测到真寻版本更新\n"
f"当前版本:{_version},最新版本:{latest_version}\n"
f"尝试自动更新...",
f"当前版本:{_version},最新版本:{latest_version}",
)
# try:
# code = await check_update(bot)
+1 -1
View File
@@ -1,4 +1,4 @@
from nonebot.adapters.cqhttp import Bot, Message
from nonebot.adapters.onebot.v11 import Bot, Message
from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH
from utils.message_builder import image
+4 -6
View File
@@ -1,6 +1,6 @@
from nonebot import on_command
from nonebot import on_regex
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent
from nonebot.adapters.onebot.v11 import Bot, MessageEvent
from utils.message_builder import image
from services.log import logger
from utils.manager import withdraw_message_manager
@@ -31,12 +31,10 @@ __plugin_configs__ = {
},
}
coser = on_command(
"cos", aliases={"coser", "括丝", "COS", "Cos", "cOS", "coS"}, priority=5, block=True
)
coser = on_regex("^(cos|COS|coser|括丝)$", priority=5, block=True)
url = "http://iw233.cn/API/cos.php"
url = "https://api.iyk0.com/cos"
@coser.handle()
+19 -20
View File
@@ -1,12 +1,12 @@
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, Message, GroupMessageEvent
from nonebot.permission import SUPERUSER
from utils.utils import get_message_text, is_number, get_message_img
from utils.utils import is_number, get_message_img
from utils.message_builder import image
from utils.message_builder import text as _text
from services.log import logger
from utils.message_builder import at
from nonebot.params import CommandArg
__zx_plugin_name__ = "联系管理员"
@@ -58,10 +58,8 @@ reply = on_command("/t", priority=1, permission=SUPERUSER, block=True)
@dialogue.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
uid = event.user_id
coffee = int(list(bot.config.superusers)[0])
text = get_message_text(event.json())
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
text = arg.extract_plain_text().strip()
img_msg = _text("")
for img in get_message_img(event.json()):
img_msg += image(img)
@@ -76,17 +74,18 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
group_name = (await bot.get_group_info(group_id=event.group_id))[
"group_name"
]
nickname = event.sender.card if event.sender.card else event.sender.nickname
await bot.send_private_msg(
user_id=coffee,
message=_text(
f"*****一份交流报告*****\n"
f"昵称:{nickname}({uid})\n"
f"群聊:{group_name}({group_id})\n"
f"消息:{text}"
nickname = event.sender.card or event.sender.nickname
for coffee in bot.config.superusers:
await bot.send_private_msg(
user_id=int(coffee),
message=_text(
f"*****一份交流报告*****\n"
f"昵称:{nickname}({event.user_id})\n"
f"群聊:{group_name}({group_id})\n"
f"消息:{text}"
)
+ img_msg,
)
+ img_msg,
)
await dialogue.send(
_text(f"您的话已发送至管理员!\n======\n{text}") + img_msg, at_sender=True
)
@@ -99,12 +98,12 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
"msg": _text(text) + img_msg,
}
# print(dialogue_data)
logger.info(f"Q{uid}@群{group_id} 联系管理员:{coffee} text:{text}")
logger.info(f"Q{event.user_id}@群{group_id} 联系管理员:text:{text}")
@reply.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if not msg:
result = "*****待回复消息总览*****\n"
for key in dialogue_data.keys():
Executable → Regular
+171 -391
View File
@@ -1,53 +1,46 @@
from nonebot import on_regex, on_keyword
from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent
from nonebot.permission import SUPERUSER
from nonebot.typing import T_State
from .genshin_handle import (
genshin_draw,
update_genshin_info,
reset_count,
reload_genshin_pool,
)
from .prts_handle import update_prts_info, prts_draw, reload_prts_pool
from .pretty_handle import update_pretty_info, pretty_draw, reload_pretty_pool
from .guardian_handle import update_guardian_info, guardian_draw
from .pcr_handle import update_pcr_info, pcr_draw
from .azur_handle import update_azur_info, azur_draw
from .fgo_handle import update_fgo_info, fgo_draw
from .onmyoji_handle import update_onmyoji_info, onmyoji_draw
from .update_game_info import update_info
from .util import is_number, check_num
from .rule import is_switch
from .config import (
PRTS_FLAG,
PRETTY_FLAG,
GUARDIAN_FLAG,
GENSHIN_FLAG,
PCR_FLAG,
AZUR_FLAG,
FGO_FLAG,
ONMYOJI_FLAG,
)
from .async_update_game_info import async_update_game
import re
import asyncio
from utils.utils import scheduler
from services.log import logger
import traceback
from cn2an import cn2an
from dataclasses import dataclass
from typing import Optional, Set, Tuple
import nonebot
from nonebot import on_regex, on_keyword
from nonebot.adapters.onebot.v11 import MessageEvent
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.params import RegexGroup
from nonebot.permission import SUPERUSER
from nonebot.typing import T_Handler
from nonebot_plugin_apscheduler import scheduler
from .handles.base_handle import BaseHandle
from .handles.azur_handle import AzurHandle
from .handles.fgo_handle import FgoHandle
from .handles.genshin_handle import GenshinHandle
from .handles.guardian_handle import GuardianHandle
from .handles.onmyoji_handle import OnmyojiHandle
from .handles.pcr_handle import PcrHandle
from .handles.pretty_handle import PrettyHandle
from .handles.prts_handle import PrtsHandle
from .config import draw_config
__zx_plugin_name__ = "游戏抽卡"
__plugin_usage__ = """
usage:
模拟赛马娘,原神,明日方舟,坎公骑冠剑,公主连结(国/台),碧蓝航线,FGO,阴阳师进行抽卡
指令:
原神[1-300]抽: 原神常驻池
原神角色[1-300]抽: 原神角色UP池子
原神武器[1-300]抽: 原神武器UP池子
原神[1-180]抽: 原神常驻池
原神角色[1-180]抽: 原神角色UP池子
原神武器[1-180]抽: 原神武器UP池子
重置原神抽卡: 清空当前卡池的抽卡次数[即从0开始计算UP概率]
方舟[1-300]抽: 方舟卡池,当有当期UP时指向UP池
赛马娘[1-200]抽: 赛马娘卡池,当有当期UP时指向UP池
坎公骑冠剑[1-300]抽: 坎公骑冠剑卡池,当有当期UP时指向UP池
pcr/公主连接[1-300]抽: 公主连接卡池
碧蓝航线/碧蓝[重型/轻型/特型][1-300]抽: 碧蓝航线重型/轻型/特型卡池
碧蓝航线/碧蓝[重型/轻型/特型/活动][1-300]抽: 碧蓝航线重型/轻型/特型/活动卡池
fgo[1-300]抽: fgo卡池
阴阳师[1-300]抽: 阴阳师卡池
* 以上指令可以通过 XX一井 来指定最大抽取数量 *
@@ -70,9 +63,9 @@ usage:
""".strip()
__plugin_des__ = "就算是模拟抽卡也不能改变自己是个非酋"
__plugin_cmd__ = [
"原神[1-300]抽",
"原神角色[1-300]抽",
"原神武器[1-300]抽",
"原神[1-180]抽",
"原神角色[1-180]抽",
"原神武器[1-180]抽",
"重置原神抽卡",
"方舟[1-300]抽",
"赛马娘[1-200]抽",
@@ -102,314 +95,137 @@ __plugin_settings__ = {
}
prts = on_regex(
r".*?方舟[1-9|一][0-9]{0,2}[抽|井]", rule=is_switch("prts"), priority=5, block=True
)
prts_update = on_keyword(
{"更新方舟信息", "更新明日方舟信息"}, permission=SUPERUSER, priority=1, block=True
)
prts_up_reload = on_keyword({"重载方舟卡池"}, priority=1, block=True)
@dataclass
class Game:
keywords: Set[str]
handle: BaseHandle
flag: bool
max_count: int = 300 # 一次最大抽卡数
reload_time: Optional[int] = None # 重载UP池时间(小时)
genshin = on_regex(
".*?原神(武器|角色)?池?[1-9|一][0-9]{0,2}[抽|井]",
rule=is_switch("genshin"),
priority=5,
block=True,
)
genshin_update = on_keyword({"更新原神信息"}, permission=SUPERUSER, priority=1, block=True)
genshin_reset = on_keyword({"重置原神抽卡"}, priority=1, block=True)
genshin_up_reload = on_keyword({"重载原神卡池"}, priority=1, block=True)
pretty = on_regex(
".*?马娘卡?[1-9|一][0-9]{0,2}[抽|井]", rule=is_switch("pretty"), priority=5, block=True
)
pretty_update = on_keyword(
{"更新马娘信息", "更新赛马娘信息"}, permission=SUPERUSER, priority=1, block=True
)
pretty_up_reload = on_keyword({"重载赛马娘卡池"}, priority=1, block=True)
guardian = on_regex(
".*?坎公骑冠剑武?器?[1-9|一][0-9]{0,2}[抽|井]",
rule=is_switch("guardian"),
priority=5,
block=True,
)
guardian_update = on_keyword(
{"更新坎公骑冠剑信息"}, permission=SUPERUSER, priority=1, block=True
games = (
Game({"azur", "碧蓝", "碧蓝航线"}, AzurHandle(), draw_config.AZUR_FLAG),
Game({"fgo", "命运冠位指定"}, FgoHandle(), draw_config.FGO_FLAG),
Game(
{"genshin", "原神"},
GenshinHandle(),
draw_config.GENSHIN_FLAG,
max_count=180,
reload_time=18,
),
Game(
{"guardian", "坎公骑冠剑"},
GuardianHandle(),
draw_config.GUARDIAN_FLAG,
reload_time=4,
),
Game({"onmyoji", "阴阳师"}, OnmyojiHandle(), draw_config.ONMYOJI_FLAG),
Game({"pcr", "公主连结", "公主连接", "公主链接", "公主焊接"}, PcrHandle(), draw_config.PCR_FLAG),
Game(
{"pretty", "马娘", "赛马娘"},
PrettyHandle(),
draw_config.PRETTY_FLAG,
max_count=200,
reload_time=4,
),
Game({"prts", "方舟", "明日方舟"}, PrtsHandle(), draw_config.PRTS_FLAG, reload_time=4),
)
pcr = on_regex(
".*?(pcr|公主连结|公主连接|公主链接|公主焊接)[1-9|一][0-9]{0,2}[抽|井]",
rule=is_switch("pcr"),
priority=5,
block=True,
)
pcr_update = on_keyword(
{"更新pcr信息", "更新公主连结信息"}, permission=SUPERUSER, priority=1, block=True
)
azur = on_regex(
".*?碧蓝航?线?(轻型|重型|特型)池?[1-9|一][0-9]{0,2}[抽]",
rule=is_switch("azur"),
priority=5,
block=True,
)
azur_update = on_keyword(
{"更新碧蓝信息", "更新碧蓝航线信息"}, permission=SUPERUSER, priority=1, block=True
)
fgo = on_regex(
".*?fgo[1-9|一][0-9]{0,2}[抽]", rule=is_switch("fgo"), priority=5, block=True
)
fgo_update = on_keyword({"更新fgo信息"}, permission=SUPERUSER, priority=1, block=True)
onmyoji = on_regex(
".*?阴阳师[1-9|一][0-9]{0,2}[抽]", rule=is_switch("onmyoji"), priority=5, block=True
)
onmyoji_update = on_keyword({"更新阴阳师信息"}, permission=SUPERUSER, priority=1, block=True)
@prts.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip()
if msg in ["方舟一井", "方舟1井"]:
num = 300
else:
rmsg = re.search(r".*?方舟(.*)抽", msg)
if rmsg:
num, flag = check_num(rmsg.group(1), 300)
if not flag:
await prts.finish(num, at_sender=True)
else:
return
await prts.send(await prts_draw(int(num)), at_sender=True)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 方舟 {num}抽"
)
@prts_up_reload.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
text = await reload_prts_pool()
await prts_up_reload.finish(Message(f"重载完成!\n{text}"))
@genshin.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip()
rmsg = re.search(r".*?原神(武器|角色)?池?(.*)[抽|井]", msg)
if rmsg:
pool_name = rmsg.group(1)
if pool_name == "武器":
pool_name = "arms"
elif pool_name == "角色":
pool_name = "char"
else:
pool_name = ""
num = rmsg.group(2)
if msg.find("一井") != -1 or msg.find("1井") != -1:
num = 180
else:
num, flag = check_num(num, 180)
if not flag:
await genshin.finish(num, at_sender=True)
else:
return
await genshin.send(
await genshin_draw(event.user_id, int(num), pool_name), at_sender=True
)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 原神 {num}抽"
)
@genshin_up_reload.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
text = await reload_genshin_pool()
await genshin_reset.finish(Message(f"重载成功!\n{text}"))
@genshin_reset.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
reset_count(event.user_id)
await genshin_reset.send("重置了原神抽卡次数", at_sender=True)
@pretty.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip()
if msg.find("1井") != -1 or msg.find("一井") != -1:
num = 200
if msg.find("卡") == -1:
pool_name = "char"
else:
pool_name = "card"
else:
rmsg = re.search(r".*?马娘(.*)抽", msg)
if rmsg:
num = rmsg.group(1)
if num[0] == "卡":
num = num[1:]
pool_name = "card"
def create_matchers():
def draw_handler(game: Game) -> T_Handler:
async def handler(
matcher: Matcher, event: MessageEvent, args: Tuple[str, ...] = RegexGroup()
):
pool_name, num, unit = args
if num == "单":
num = 1
else:
pool_name = "char"
num, flag = check_num(num, 200)
if not flag:
await pretty.finish(num, at_sender=True)
else:
return
await pretty.send(await pretty_draw(int(num), pool_name), at_sender=True)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 赛马娘 {num}抽"
)
try:
num = int(cn2an(num, mode="smart"))
except ValueError:
await matcher.finish("必!须!是!数!字!")
if unit == "井":
num *= game.max_count
if num < 1:
await matcher.finish("虚空抽卡???")
elif num > game.max_count:
await matcher.finish("一井都满不足不了你嘛!快爬开!")
pool_name = (
pool_name.replace("池", "")
.replace("武器", "arms")
.replace("角色", "char")
.replace("卡牌", "card")
.replace("卡", "card")
)
try:
res = await game.handle.draw(num, pool_name=pool_name, user_id=event.user_id)
except:
logger.warning(traceback.format_exc())
await matcher.finish("出错了...")
await matcher.finish(res, at_sender=True)
return handler
def update_handler(game: Game) -> T_Handler:
async def handler(matcher: Matcher):
await game.handle.update_info()
await matcher.finish("更新完成!")
return handler
def reload_handler(game: Game) -> T_Handler:
async def handler(matcher: Matcher):
res = await game.handle.reload_pool()
if res:
await matcher.finish(res)
return handler
def reset_handler(game: Game) -> T_Handler:
async def handler(matcher: Matcher, event: MessageEvent):
if game.handle.reset_count(event.user_id):
await matcher.finish("重置成功!")
return handler
def scheduled_job(game: Game) -> T_Handler:
async def handler():
await game.handle.reload_pool()
return handler
for game in games:
pool_pattern = r"([^\s单0-9零一二三四五六七八九百十]{0,3})"
num_pattern = r"(单|[0-9零一二三四五六七八九百十]{1,3})"
unit_pattern = r"([抽|井|连])"
draw_regex = r".*?(?:{})\s*{}\s*{}\s*{}".format(
"|".join(game.keywords), pool_pattern, num_pattern, unit_pattern
)
update_keywords = {f"更新{keyword}信息" for keyword in game.keywords}
reload_keywords = {f"重载{keyword}卡池" for keyword in game.keywords}
reset_keywords = {f"重置{keyword}抽卡" for keyword in game.keywords}
if game.flag:
on_regex(draw_regex, priority=5, block=True).append_handler(
draw_handler(game)
)
on_keyword(
update_keywords, permission=SUPERUSER, priority=1, block=True
).append_handler(update_handler(game))
on_keyword(reload_keywords, priority=1, block=True).append_handler(
reload_handler(game)
)
on_keyword(reset_keywords, priority=1, block=True).append_handler(
reset_handler(game)
)
if game.reload_time:
scheduler.add_job(
scheduled_job(game), trigger="cron", hour=game.reload_time, minute=1
)
@pretty_up_reload.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
text = await reload_pretty_pool()
await genshin_reset.finish(Message(f"重载成功!\n{text}"))
@guardian.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip()
pool_name = "char"
if msg.find("1井") != -1 or msg.find("一井") != -1:
num = 300
if msg.find("武器") != -1:
pool_name = "arms"
else:
rmsg = re.search(r".*?坎公骑冠剑(.*)抽", msg)
if rmsg:
num = rmsg.group(1)
if num.find("武器") != -1:
pool_name = "arms"
num = num.replace("武器", "")
num, flag = check_num(num, 300)
if not flag:
await guardian.finish(num, at_sender=True)
else:
return
await guardian.send(await guardian_draw(int(num), pool_name), at_sender=True)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 坎公骑冠剑 {num}抽"
)
@pcr.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip()
if msg.find("1井") != -1 or msg.find("一井") != -1:
num = 300
else:
rmsg = re.search(r".*?(pcr|公主连结)(.*)[抽|井]", msg)
if rmsg:
num, flag = check_num(rmsg.group(2), 300)
if not flag:
await pcr.finish(num, at_sender=True)
else:
return
await pcr.send(await pcr_draw(int(num)), at_sender=True)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 公主连结 {num}抽"
)
@azur.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip()
rmsg = re.search(".*?碧蓝航?线?(轻型|重型|特型)池?(.*)[抽]", msg)
if rmsg:
pool_name = rmsg.group(1)
num, flag = check_num(rmsg.group(2), 300)
if not flag:
await azur.finish(num, at_sender=True)
else:
return
await azur.send(await azur_draw(int(num), pool_name), at_sender=True)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 碧蓝航线 {num}抽"
)
@fgo.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip()
rmsg = re.search(".*?fgo(.*)抽", msg)
if rmsg:
num, flag = check_num(rmsg.group(1), 300)
if not flag:
await fgo.finish(num, at_sender=True)
else:
return
await fgo.send(await fgo_draw(int(num)), at_sender=True)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) fgo {num}抽"
)
@onmyoji.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = str(event.get_message()).strip()
rmsg = re.search(".*?阴阳师(.*)抽", msg)
if rmsg:
num, flag = check_num(rmsg.group(1), 300)
if not flag:
await onmyoji.finish(num, at_sender=True)
else:
return
await onmyoji.send(await onmyoji_draw(int(num)), at_sender=True)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 阴阳师 {num}抽"
)
@prts_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_prts_info()
await prts_update.finish("更新完成!")
@genshin_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_genshin_info()
await genshin_update.finish("更新完成!")
@pretty_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_pretty_info()
await genshin_update.finish("更新完成!")
@guardian_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_guardian_info()
await genshin_update.finish("更新完成!")
@pcr_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_pcr_info()
await genshin_update.finish("更新完成!")
@azur_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_azur_info()
await genshin_update.finish("更新完成!")
@fgo_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_fgo_info()
await genshin_update.finish("更新完成!")
@onmyoji_update.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_onmyoji_info()
await genshin_update.finish("更新完成!")
create_matchers()
# 更新资源
@@ -420,57 +236,21 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
)
async def _():
tasks = []
if PRTS_FLAG:
tasks.append(asyncio.ensure_future(update_prts_info()))
if GENSHIN_FLAG:
tasks.append(asyncio.ensure_future(update_genshin_info()))
if PRETTY_FLAG:
tasks.append(asyncio.ensure_future(update_pretty_info()))
if GUARDIAN_FLAG:
tasks.append(asyncio.ensure_future(update_guardian_info()))
if PCR_FLAG:
tasks.append(asyncio.ensure_future(update_pcr_info()))
if AZUR_FLAG:
tasks.append(asyncio.ensure_future(update_azur_info()))
if FGO_FLAG:
tasks.append(asyncio.ensure_future(update_fgo_info()))
if ONMYOJI_FLAG:
tasks.append(asyncio.ensure_future(update_onmyoji_info()))
for game in games:
if game.flag:
tasks.append(asyncio.ensure_future(game.handle.update_info()))
await asyncio.gather(*tasks)
logger.info("draw_card 抽卡自动更新完成...")
# 每天四点重载方舟up卡池
@scheduler.scheduled_job(
"cron",
hour=4,
minute=1,
)
driver = nonebot.get_driver()
@driver.on_startup
async def _():
if PRTS_FLAG:
await reload_prts_pool()
logger.info("自动重载方舟卡池UP成功")
# 每天四点重载赛马娘up卡池
@scheduler.scheduled_job(
"cron",
hour=4,
minute=1,
)
async def _():
if PRETTY_FLAG:
await reload_pretty_pool()
logger.info("自动重载赛马娘UP成功")
# 每天下午六点点重载原神up卡池
@scheduler.scheduled_job(
"cron",
hour=18,
minute=1,
)
async def _():
if PRTS_FLAG:
await reload_genshin_pool()
logger.info("自动重载原神卡池UP成功")
tasks = []
for game in games:
if game.flag:
game.handle.init_data()
if not game.handle.data_exists():
tasks.append(asyncio.ensure_future(game.handle.update_info()))
await asyncio.gather(*tasks)
-390
View File
@@ -1,390 +0,0 @@
from bs4 import BeautifulSoup
from datetime import datetime, timedelta
from .config import DRAW_PATH
from pathlib import Path
from asyncio.exceptions import TimeoutError
from services.log import logger
from utils.http_utils import AsyncHttpx
import re
try:
import ujson as json
except ModuleNotFoundError:
import json
headers = {'User-Agent': '"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)"'}
prts_up_char = Path(DRAW_PATH + "/draw_card_up/prts_up_char.json")
genshin_up_char = Path(DRAW_PATH + "/draw_card_up/genshin_up_char.json")
pretty_up_char = Path(DRAW_PATH + "/draw_card_up/pretty_up_char.json")
guardian_up_char = Path(DRAW_PATH + "/draw_card_up/guardian_up_char.json")
prts_url = "https://ak.hypergryph.com/news.html"
genshin_url = "https://wiki.biligame.com/ys/%E7%A5%88%E6%84%BF"
pretty_url = "https://wiki.biligame.com/umamusume/%E5%85%AC%E5%91%8A"
guardian_url = "https://wiki.biligame.com/gt/%E9%A6%96%E9%A1%B5"
# 是否过时
def is_expired(data: dict):
try:
times = data['time'].split('-')
for i in range(len(times)):
times[i] = str(datetime.now().year) + '-' + times[i].split('日')[0].strip().replace('月', '-')
start_date = datetime.strptime(times[0], '%Y-%m-%d').date()
end_date = datetime.strptime(times[1], '%Y-%m-%d').date()
now = datetime.now().date()
except ValueError:
return False
return not start_date <= now <= end_date
# 检查写入
def check_write(data: dict, up_char_file):
if is_expired(data['char']):
for x in list(data.keys()):
data[x]['title'] = ''
else:
with open(up_char_file, 'w', encoding='utf8') as f:
json.dump(data, f, indent=4, ensure_ascii=False)
if not up_char_file.exists():
with open(up_char_file, 'w', encoding='utf8') as f:
json.dump(data, f, indent=4, ensure_ascii=False)
else:
with open(up_char_file, 'r', encoding='utf8') as f:
old_data = json.load(f)
if is_expired(old_data['char']):
return old_data
else:
with open(up_char_file, 'w', encoding='utf8') as f:
json.dump(data, f, indent=4, ensure_ascii=False)
return data
class PrtsAnnouncement:
def __init__(self):
self.game_name = '明日方舟'
async def _get_announcement_text(self):
text = (await AsyncHttpx.get(prts_url)).text
soup = BeautifulSoup(text, 'lxml')
ol = soup.find('ol', {'class': 'articleList active', 'data-category-key': 'LATEST'})
for li in ol:
itype = li.find('span', {'class': 'articleItemCate'}).text
if itype == '活动':
a = li.find('a')['href']
return (await AsyncHttpx.get(f'https://ak.hypergryph.com{a}')).text
async def update_up_char(self):
prts_up_char.parent.mkdir(parents=True, exist_ok=True)
try:
data = {'char': {'up_char': {'6': {}, '5': {}, '4': {}}, 'title': '', 'time': '', 'pool_img': ''}}
text = await self._get_announcement_text()
soup = BeautifulSoup(text, 'lxml')
content = soup.find('div', {'class': 'article-content'})
contents = [x for x in content.contents if x.text or str(x).find('img') != -1]
start_index = -1
end_index = -1
for i in range(len(contents)):
if str(contents[i]).startswith('<p>'):
r = re.search('(.*)(寻访|复刻).*?开启', contents[i].text)
if r:
if str(contents[i+3].text).find('★') != -1:
img = contents[i-1].find('img')
if img:
data['char']['pool_img'] = img['src']
start_index = i
for j in range(i, len(contents)):
if str(contents[j]).find('注意') != -1:
end_index = j
break
break
contents = contents[start_index: end_index]
title = contents[0].text
data['char']['title'] = title[title.find('【'): title.find('】') + 1]
data['char']['time'] = str(contents[1].text).split(':', maxsplit=1)[1]
for p in contents[2:]:
p = str(p.text)
r = None
if p.find('★') != -1:
if p.find('权值') == -1:
r = re.search(r'.*?:(.*)(占(.*)★.*?的(.*)%)', p)
else:
r = re.search(r'.*?:(.*)(在(.*)★.*?以(.*)倍权值.*?)', p)
star = r.group(2)
if r:
chars = r.group(1)
if chars.find('/') != -1:
chars = chars.strip().split('/')
elif chars.find('\\') != -1:
chars = chars.strip().split('\\')
else:
chars = chars.split('\n')
chars = [x.replace('[限定]', '').strip() for x in chars]
probability = r.group(3)
probability = probability if int(probability) > 10 else f'权{probability}'
for char in chars:
if char.strip():
data['char']['up_char'][star][char.strip()] = probability
except TimeoutError:
logger.warning(f'更新明日方舟UP池信息超时...')
if prts_up_char.exists():
with open(prts_up_char, 'r', encoding='utf8') as f:
data = json.load(f)
except Exception as e:
logger.error(f'更新明日方舟未知错误 e:{e}')
if prts_up_char.exists():
with open(prts_up_char, 'r', encoding='utf8') as f:
data = json.load(f)
return check_write(data, prts_up_char)
class GenshinAnnouncement:
def __init__(self):
self.game_name = '原神'
async def _get_announcement_text(self):
return (await AsyncHttpx.get(genshin_url)).text
async def update_up_char(self):
genshin_up_char.parent.mkdir(exist_ok=True, parents=True)
data = {
'char': {'up_char': {'5': {}, '4': {}}, 'title': '', 'time': '', 'pool_img': ''},
'arms': {'up_char': {'5': {}, '4': {}}, 'title': '', 'time': '', 'pool_img': ''}
}
text = await self._get_announcement_text()
soup = BeautifulSoup(text, 'lxml')
try:
div = soup.find_all('div', {'class': 'row'})[1]
tables = div.find_all('table', {'class': 'wikitable'})
for table in tables:
trs = table.find('tbody').find_all('tr')
pool_img = trs[0].find('th').find('img')
if pool_img['title'].find('角色活动') == -1:
itype = 'arms'
else:
itype = 'char'
try:
data[itype]['pool_img'] = str(pool_img['srcset']).split(' ')[0]
except KeyError:
data[itype]['pool_img'] = pool_img['src']
data[itype]['title'] = str(pool_img['title']).split(f'期{"角色" if itype == "char" else "武器"}')[0][:-3]
data[itype]['time'] = trs[1].find('td').text
if data[itype]['time'][-1] == '\n':
data[itype]['time'] = data[itype]['time'][:-1]
if '版本更新后' in data[itype]['time']:
sp = data[itype]['time'].split('~')
end_time = datetime.strptime(sp[1].strip(), "%Y/%m/%d %H:%M")
start_time = end_time - timedelta(days=20)
data[itype]['time'] = start_time.strftime('%Y/%m/%d') + ' ~ ' + end_time.strftime('%Y/%m/%d')
tmp = ''
for tm in data[itype]['time'].split('~'):
date_time_sp = tm.split('/')
date_time_sp[2] = date_time_sp[2].strip().replace(' ', '日 ')
tmp += date_time_sp[1] + '月' + date_time_sp[2] + ' - '
data[itype]['time'] = tmp[:-2].strip()
for a in trs[2].find('td').find_all('a'):
char_name = a['title']
data[itype]['up_char']['5'][char_name] = "50"
for a in trs[3].find('td').find_all('a'):
char_name = a['title']
data[itype]['up_char']['4'][char_name] = "50"
except TimeoutError:
logger.warning(f'更新原神UP池信息超时...')
if genshin_up_char.exists():
with open(genshin_up_char, 'r', encoding='utf8') as f:
data = json.load(f)
except Exception as e:
logger.error(f'更新原神UP失败,疑似UP池已结束, e:{e}')
if genshin_up_char.exists():
with open(genshin_up_char, 'r', encoding='utf8') as f:
data = json.load(f)
data['char']['title'] = ''
data['arms']['title'] = ''
with open(genshin_up_char, 'w', encoding='utf8') as wf:
json.dump(data, wf, ensure_ascii=False, indent=4)
return data
return check_write(data, genshin_up_char)
class PrettyAnnouncement:
def __init__(self):
self.game_name = '赛马娘'
async def _get_announcement_text(self):
text = (await AsyncHttpx.get(pretty_url)).text
soup = BeautifulSoup(text, 'lxml')
divs = soup.find('div', {'id': 'mw-content-text'}).find('div').find_all('div')
title = " "
for div in divs:
a = div.find('a')
try:
title = a['title']
except (KeyError, TypeError):
continue
if title.find('新角色追加') != -1:
url = a['href']
break
return (await AsyncHttpx.get(f'https://wiki.biligame.com/{url}')).text, title[:-2]
async def update_up_char(self):
pretty_up_char.parent.mkdir(exist_ok=True, parents=True)
data = {
'char': {'up_char': {'3': {}, '2': {}, '1': {}}, 'title': '', 'time': '', 'pool_img': ''},
'card': {'up_char': {'3': {}, '2': {}, '1': {}}, 'title': '', 'time': '', 'pool_img': ''}
}
try:
text, title = await self._get_announcement_text()
soup = BeautifulSoup(text, 'lxml')
context = soup.find('div', {'class': 'toc-sticky'})
if not context:
context = soup.find('div', {'class': 'mw-parser-output'})
data['char']['title'] = title
data['card']['title'] = title
r = re.search(r'(\d{1,2}/\d{1,2} \d{1,2}:\d{1,2} ~ \d{1,2}/\d{1,2} \d{1,2}:\d{1,2})', str(context.text))
if r:
time = str(r.group(1))
else:
logger.error('赛马娘UP无法找到活动日期....取消更新UP池子...')
return check_write(data, pretty_up_char)
time = time.replace('~', '-').replace('/', '月').split(' ')
time = time[0] + '日 ' + time[1] + ' - ' + time[3] + '日 ' + time[4]
data['char']['time'] = time
data['card']['time'] = time
for p in context.find_all('p'):
if str(p).find('当期UP赛马娘') != -1 and str(p).find('■') != -1:
if not data['char']['pool_img']:
try:
data['char']['pool_img'] = p.find('img')['src']
except TypeError:
for center in context.find_all('center'):
try:
img = center.find('img')
if img and str(img['alt']).find('新马娘') != -1 and str(img['alt']).find('总览') == 1:
data['char']['pool_img'] = img['src']
except (TypeError, KeyError):
pass
r = re.findall(r'.*?当期UP赛马娘([\s\S]*)<奖励内容>.*?', str(p))
if r:
for x in r:
x = str(x).split('\n')
for msg in x:
if msg.find('★') != -1:
msg = msg.replace('<br/>', '')
char_name = msg[msg.find('['):].strip()
if (star := len(msg[:msg.find('[')].strip())) == 3:
data['char']['up_char']['3'][char_name] = '70'
elif star == 2:
data['char']['up_char']['2'][char_name] = '70'
elif star == 1:
data['char']['up_char']['1'][char_name] = '70'
if str(p).find('(当期UP对象)') != -1 and str(p).find('赛马娘') == -1 and str(p).find('■') != -1:
# data['card']['pool_img'] = p.find('img')['src']
if not data['char']['pool_img']:
try:
data['char']['pool_img'] = p.find('img')['src']
except TypeError:
for center in context.find_all('center'):
try:
img = center.find('img')
if img and str(img['alt']).find('新卡') != -1 and str(img['alt']).find('总览') == 1:
data['card']['pool_img'] = img['src']
except (TypeError, KeyError):
pass
r = re.search(r'■全?新?支援卡(当期UP对象)([\s\S]*)</p>', str(p))
if r:
rmsg = r.group(1).strip()
rmsg = rmsg.split('<br/>')
rmsg = [x for x in rmsg if x]
for x in rmsg:
x = x.replace('\n', '').replace('・', '')
star = x[:x.find('[')].strip()
char_name = x[x.find('['):].strip()
if star == 'SSR':
data['card']['up_char']['3'][char_name] = '70'
if star == 'SR':
data['card']['up_char']['2'][char_name] = '70'
if star == 'R':
data['card']['up_char']['1'][char_name] = '70'
# 日文->中文
with open(DRAW_PATH + 'pretty_card.json', 'r', encoding='utf8') as f:
all_data = json.load(f)
for star in data['card']['up_char'].keys():
for name in list(data['card']['up_char'][star].keys()):
char_name = name.split(']')[1].strip()
tp_name = name[name.find('['): name.find(']') + 1].strip().replace('[', '【').replace(']', '】')
for x in all_data.keys():
if all_data[x]['名称'].find(tp_name) != -1 and all_data[x]['关联角色'] == char_name:
data['card']['up_char'][star].pop(name)
data['card']['up_char'][star][all_data[x]['中文名']] = '70'
except TimeoutError:
logger.warning(f'更新赛马娘UP池信息超时...')
if pretty_up_char.exists():
with open(pretty_up_char, 'r', encoding='utf8') as f:
data = json.load(f)
except Exception as e:
logger.error(f'赛马娘up更新未知错误 {type(e)}:{e}')
if pretty_up_char.exists():
with open(pretty_up_char, 'r', encoding='utf8') as f:
data = json.load(f)
return check_write(data, pretty_up_char)
class GuardianAnnouncement:
def __init__(self):
self.game_name = '坎公骑冠剑'
async def _get_announcement_text(self):
return (await AsyncHttpx.get(guardian_url)).text
async def update_up_char(self):
data = {
'char': {'up_char': {'3': {}}, 'title': '', 'time': '', 'pool_img': ''},
'arms': {'up_char': {'5': {}}, 'title': '', 'time': '', 'pool_img': ''}
}
try:
text = await self._get_announcement_text()
soup = BeautifulSoup(text, 'lxml')
context = soup.select('div.col-sm-3:nth-child(3) > div:nth-child(2) > div:nth-child(1) '
'> div:nth-child(2) > div:nth-child(3) > font:nth-child(1)')[0]
title = context.find('p').find('b').text
tmp = title.split(',')
time = ''
for msg in tmp:
r = re.search(r'[从|至](.*)(开始|结束)', msg)
if r:
time += r.group(1).strip() + ' - '
time = time[:-3]
title = time.split(' - ')[0] + 'UP卡池'
data['char']['title'] = title
data['arms']['title'] = title
data['char']['time'] = time
data['arms']['time'] = time
start_idx = -1
end_idx = -1
index = 0
divs = context.find_all('div')
for x in divs:
if x.text == '角色':
start_idx = index
if x.text == '武器':
end_idx = index
break
index += 1
for x in divs[start_idx + 1: end_idx]:
name = x.find('p').find_all('a')[-1].text
data['char']['up_char']['3'][name] = '0'
for x in divs[end_idx + 1:]:
name = x.find('p').find_all('a')[-1].text
data['arms']['up_char']['5'][name] = '0'
except TimeoutError:
logger.warning(f'更新坎公骑冠剑UP池信息超时...')
if guardian_up_char.exists():
with open(guardian_up_char, 'r', encoding='utf8') as f:
data = json.load(f)
except Exception as e:
logger.error(f'坎公骑冠剑up更新未知错误 {type(e)}:{e}')
return check_write(data, guardian_up_char)
@@ -1,70 +0,0 @@
import asyncio
import nonebot
import os
from services.log import logger
from .pcr_handle import update_pcr_info, init_pcr_data
from .azur_handle import update_azur_info, init_azur_data
from .prts_handle import update_prts_info, init_prts_data
from .pretty_handle import update_pretty_info, init_pretty_data
from .guardian_handle import update_guardian_info, init_guardian_data
from .genshin_handle import update_genshin_info, init_genshin_data
from .fgo_handle import update_fgo_info, init_fgo_data
from .onmyoji_handle import update_onmyoji_info, init_onmyoji_data
from .config import DRAW_PATH, PRTS_FLAG, PRETTY_FLAG, GUARDIAN_FLAG, PCR_FLAG, AZUR_FLAG, GENSHIN_FLAG, FGO_FLAG, \
ONMYOJI_FLAG
driver: nonebot.Driver = nonebot.get_driver()
@driver.on_startup
async def async_update_game():
tasks = []
init_lst = [init_pcr_data, init_pretty_data, init_azur_data, init_prts_data, init_genshin_data, init_guardian_data,
init_fgo_data, init_onmyoji_data]
if PRTS_FLAG and not os.path.exists(DRAW_PATH + 'prts.json'):
tasks.append(asyncio.ensure_future(update_prts_info()))
init_lst.remove(init_prts_data)
if PRETTY_FLAG and (not os.path.exists(DRAW_PATH + 'pretty.json') or
not os.path.exists(DRAW_PATH + 'pretty_card.json')):
tasks.append(asyncio.ensure_future(update_pretty_info()))
init_lst.remove(init_pretty_data)
if GUARDIAN_FLAG and not os.path.exists(DRAW_PATH + 'guardian.json'):
tasks.append(asyncio.ensure_future(update_guardian_info()))
if PCR_FLAG and not os.path.exists(DRAW_PATH + 'pcr.json'):
tasks.append(asyncio.ensure_future(update_pcr_info()))
init_lst.remove(init_pcr_data)
if GENSHIN_FLAG and (not os.path.exists(DRAW_PATH + 'genshin.json') or
not os.path.exists(DRAW_PATH + 'genshin_arms.json')):
tasks.append(asyncio.ensure_future(update_genshin_info()))
init_lst.remove(init_genshin_data)
if AZUR_FLAG and not os.path.exists(DRAW_PATH + 'azur.json'):
tasks.append(asyncio.ensure_future(update_azur_info()))
init_lst.remove(init_azur_data)
if FGO_FLAG and (not os.path.exists(DRAW_PATH + 'fgo.json') or
not os.path.exists(DRAW_PATH + 'fgo_card.json')):
tasks.append(asyncio.ensure_future(update_fgo_info()))
init_lst.remove(init_fgo_data)
if ONMYOJI_FLAG and not os.path.exists(DRAW_PATH + 'onmyoji.json'):
tasks.append(asyncio.ensure_future(update_onmyoji_info()))
init_lst.remove(init_onmyoji_data)
try:
await asyncio.gather(*tasks)
for func in init_lst:
await func()
except asyncio.exceptions.CancelledError:
logger.info('更新异常:CancelledError,再次更新...')
await async_update_game()
-63
View File
@@ -1,63 +0,0 @@
from nonebot.adapters.cqhttp import MessageSegment
import random
from .update_game_simple_info import update_simple_info
from .util import generate_img, init_star_rst, BaseData, set_list, get_star, max_card, format_card_information
from .config import AZUR_ONE_P, AZUR_TWO_P, AZUR_THREE_P, AZUR_FOUR_P, AZUR_FLAG, DRAW_PATH
from dataclasses import dataclass
from .init_card_pool import init_game_pool
try:
import ujson as json
except ModuleNotFoundError:
import json
ALL_CHAR = []
@dataclass
class AzurChar(BaseData):
itype: str # 舰娘类型
async def azur_draw(count: int, pool_name: str):
# 0 1 2
cnlist = ['金', '紫', '蓝', '白']
star_list = [0, 0, 0, 0]
char_list, char_dict, max_star_list, star_list, max_star_index_list = \
format_card_information(count, star_list, _get_azur_card, pool_name, guaranteed=False)
rst = init_star_rst(star_list, cnlist, max_star_list, max_star_index_list)
if count > 90:
char_list = set_list(char_list)
return MessageSegment.image("base64://" + await generate_img(char_list, 'azur', star_list)) \
+ '\n' + rst[:-1] + '\n' + max_card(char_dict)
async def update_azur_info():
global ALL_CHAR
url = 'https://wiki.biligame.com/blhx/舰娘图鉴'
data, code = await update_simple_info(url, 'azur')
if code == 200:
ALL_CHAR = init_game_pool('azur', data, AzurChar)
async def init_azur_data():
global ALL_CHAR
if AZUR_FLAG:
with open(DRAW_PATH + 'azur.json', 'r', encoding='utf8') as f:
azur_dict = json.load(f)
ALL_CHAR = init_game_pool('azur', azur_dict, AzurChar)
# 抽取卡池
def _get_azur_card(pool_name: str):
global ALL_CHAR
if pool_name == '轻型':
itype = ['驱逐', '轻巡', '维修']
elif pool_name == '重型':
itype = ['重巡', '战列', '战巡', '重炮']
else:
itype = ['维修', '潜艇', '重巡', '轻航', '航母']
star = get_star([4, 3, 2, 1], [AZUR_FOUR_P, AZUR_THREE_P, AZUR_TWO_P, AZUR_ONE_P])
chars = [x for x in ALL_CHAR if x.star == star and x.itype in itype and not x.limited]
return random.choice(chars), 4 - star
Executable → Regular
+123 -367
View File
@@ -1,7 +1,9 @@
import nonebot
from pathlib import Path
from nonebot.log import logger
from pydantic import BaseModel, Extra
from configs.config import Config as AConfig
from configs.path_config import DATA_PATH
from configs.config import Config
try:
import ujson as json
@@ -9,9 +11,115 @@ except ModuleNotFoundError:
import json
DRAW_PATH = DATA_PATH + "/draw_card/"
# 原神
class GenshinConfig(BaseModel, extra=Extra.ignore):
GENSHIN_FIVE_P: float = 0.006
GENSHIN_FOUR_P: float = 0.051
GENSHIN_THREE_P: float = 0.43
GENSHIN_G_FIVE_P: float = 0.016
GENSHIN_G_FOUR_P: float = 0.13
I72_ADD: float = 0.0585
_draw_config = Path(rf"{DRAW_PATH}/draw_card_config/draw_card_config.json")
# 明日方舟
class PrtsConfig(BaseModel, extra=Extra.ignore):
PRTS_SIX_P: float = 0.02
PRTS_FIVE_P: float = 0.08
PRTS_FOUR_P: float = 0.48
PRTS_THREE_P: float = 0.42
# 赛马娘
class PrettyConfig(BaseModel, extra=Extra.ignore):
PRETTY_THREE_P: float = 0.03
PRETTY_TWO_P: float = 0.18
PRETTY_ONE_P: float = 0.79
# 坎公骑冠剑
class GuardianConfig(BaseModel, extra=Extra.ignore):
GUARDIAN_THREE_CHAR_P: float = 0.0275
GUARDIAN_TWO_CHAR_P: float = 0.19
GUARDIAN_ONE_CHAR_P: float = 0.7825
GUARDIAN_THREE_CHAR_UP_P: float = 0.01375
GUARDIAN_THREE_CHAR_OTHER_P: float = 0.01375
GUARDIAN_EXCLUSIVE_ARMS_P: float = 0.03
GUARDIAN_FIVE_ARMS_P: float = 0.03
GUARDIAN_FOUR_ARMS_P: float = 0.09
GUARDIAN_THREE_ARMS_P: float = 0.27
GUARDIAN_TWO_ARMS_P: float = 0.58
GUARDIAN_EXCLUSIVE_ARMS_UP_P: float = 0.01
GUARDIAN_EXCLUSIVE_ARMS_OTHER_P: float = 0.02
# 公主连结
class PcrConfig(BaseModel, extra=Extra.ignore):
PCR_THREE_P: float = 0.025
PCR_TWO_P: float = 0.18
PCR_ONE_P: float = 0.795
PCR_G_THREE_P: float = 0.025
PCR_G_TWO_P: float = 0.975
# 碧蓝航线
class AzurConfig(BaseModel, extra=Extra.ignore):
AZUR_FIVE_P: float = 0.012
AZUR_FOUR_P: float = 0.07
AZUR_THREE_P: float = 0.12
AZUR_TWO_P: float = 0.51
AZUR_ONE_P: float = 0.3
# 命运-冠位指定
class FgoConfig(BaseModel, extra=Extra.ignore):
FGO_SERVANT_FIVE_P: float = 0.01
FGO_SERVANT_FOUR_P: float = 0.03
FGO_SERVANT_THREE_P: float = 0.4
FGO_CARD_FIVE_P: float = 0.04
FGO_CARD_FOUR_P: float = 0.12
FGO_CARD_THREE_P: float = 0.4
# 阴阳师
class OnmyojiConfig(BaseModel, extra=Extra.ignore):
ONMYOJI_SP: float = 0.0025
ONMYOJI_SSR: float = 0.01
ONMYOJI_SR: float = 0.2
ONMYOJI_R: float = 0.7875
class Config(BaseModel, extra=Extra.ignore):
# 开关
PRTS_FLAG: bool = AConfig.get_config("draw_card", "PRTS_FLAG")
GENSHIN_FLAG: bool = AConfig.get_config("draw_card", "GENSHIN_FLAG")
PRETTY_FLAG: bool = AConfig.get_config("draw_card", "PRETTY_FLAG")
GUARDIAN_FLAG: bool = AConfig.get_config("draw_card", "GUARDIAN_FLAG")
PCR_FLAG: bool = AConfig.get_config("draw_card", "PCR_FLAG")
AZUR_FLAG: bool = AConfig.get_config("draw_card", "AZUR_FLAG")
FGO_FLAG: bool = AConfig.get_config("draw_card", "FGO_FLAG")
ONMYOJI_FLAG: bool = AConfig.get_config("draw_card", "ONMYOJI_FLAG")
# 其他配置
PCR_TAI: bool = AConfig.get_config("draw_card", "PCR_TAI")
SEMAPHORE: int = AConfig.get_config("draw_card", "SEMAPHORE")
# 抽卡概率
prts: PrtsConfig = PrtsConfig()
genshin: GenshinConfig = GenshinConfig()
pretty: PrettyConfig = PrettyConfig()
guardian: GuardianConfig = GuardianConfig()
pcr: PcrConfig = PcrConfig()
azur: AzurConfig = AzurConfig()
fgo: FgoConfig = FgoConfig()
onmyoji: OnmyojiConfig = OnmyojiConfig()
driver = nonebot.get_driver()
DRAW_PATH = DATA_PATH / "draw_card"
config_path = DRAW_PATH / "draw_card_config" / "draw_card_config.json"
draw_config: Config = Config()
for game_flag, game_name in zip(
@@ -28,7 +136,7 @@ for game_flag, game_name in zip(
],
["明日方舟", "原神", "赛马娘", "坎公骑冠剑", "公主连结", "碧蓝航线", "命运-冠位指定(FGO)", "阴阳师", "pcr台服卡池"],
):
Config.add_plugin_config(
AConfig.add_plugin_config(
"draw_card",
game_flag,
True,
@@ -36,376 +144,24 @@ for game_flag, game_name in zip(
help_=f"{game_name} 抽卡开关",
default_value=True,
)
Config.add_plugin_config(
AConfig.add_plugin_config(
"draw_card", "SEMAPHORE", 5, help_=f"异步数据下载数量限制", default_value=5
)
PRTS_FLAG = Config.get_config("draw_card", "PRTS_FLAG")
GENSHIN_FLAG = Config.get_config("draw_card", "GENSHIN_FLAG")
PRETTY_FLAG = Config.get_config("draw_card", "PRETTY_FLAG")
GUARDIAN_FLAG = Config.get_config("draw_card", "GUARDIAN_FLAG")
PCR_FLAG = Config.get_config("draw_card", "PCR_FLAG")
AZUR_FLAG = Config.get_config("draw_card", "AZUR_FLAG")
FGO_FLAG = Config.get_config("draw_card", "FGO_FLAG")
ONMYOJI_FLAG = Config.get_config("draw_card", "ONMYOJI_FLAG")
PCR_TAI = Config.get_config("draw_card", "PCR_TAI")
SEMAPHORE = Config.get_config("draw_card", "SEMAPHORE")
# 方舟概率
PRTS_SIX_P = 0.02
PRTS_FIVE_P = 0.08
PRTS_FOUR_P = 0.48
PRTS_THREE_P = 0.42
# 原神概率
GENSHIN_FIVE_P = 0.006
GENSHIN_FOUR_P = 0.051
GENSHIN_THREE_P = 0.43
# 保底概率
GENSHIN_G_FIVE_P = 0.016
GENSHIN_G_FOUR_P = 0.13
# 72抽后增加的概率
I72_ADD = 0.0585
# 赛马娘概率
PRETTY_THREE_P = 0.03
PRETTY_TWO_P = 0.18
PRETTY_ONE_P = 0.79
# 坎公骑冠剑
# 角色概率
GUARDIAN_THREE_CHAR_P = 0.0275
GUARDIAN_TWO_CHAR_P = 0.19
GUARDIAN_ONE_CHAR_P = 0.7825
# UP角色
GUARDIAN_THREE_CHAR_UP_P = 0.01375
GUARDIAN_THREE_CHAR_OTHER_P = 0.01375
# 武器概率
GUARDIAN_EXCLUSIVE_ARMS_P = 0.03
GUARDIAN_FIVE_ARMS_P = 0.03
GUARDIAN_FOUR_ARMS_P = 0.09
GUARDIAN_THREE_ARMS_P = 0.27
GUARDIAN_TWO_ARMS_P = 0.58
# UP武器
GUARDIAN_EXCLUSIVE_ARMS_UP_P = 0.01
GUARDIAN_EXCLUSIVE_ARMS_OTHER_P = 0.02
# PCR
PCR_THREE_P = 0.025
PCR_TWO_P = 0.18
PCR_ONE_P = 0.795
# 保底
PCR_G_THREE_P = 0.025
PCR_G_TWO_P = 0.975
# 碧蓝航线
AZUR_FIVE_P = 0.012
AZUR_FOUR_P = 0.07
AZUR_THREE_P = 0.12
AZUR_TWO_P = 0.51
AZUR_ONE_P = 0.3
# FGO
FGO_SERVANT_FIVE_P = 0.01
FGO_SERVANT_FOUR_P = 0.03
FGO_SERVANT_THREE_P = 0.4
FGO_CARD_FIVE_P = 0.04
FGO_CARD_FOUR_P = 0.12
FGO_CARD_THREE_P = 0.4
# 阴阳师
ONMYOJI_SP = 0.0025
ONMYOJI_SSR = 0.01
ONMYOJI_SR = 0.2
ONMYOJI_R = 0.7875
path_dict = {
"genshin": "原神",
"prts": "明日方舟",
"pretty": "赛马娘",
"guardian": "坎公骑冠剑",
"pcr": "公主连结",
"azur": "碧蓝航线",
"fgo": "命运-冠位指定",
"onmyoji": "阴阳师",
}
driver: nonebot.Driver = nonebot.get_driver()
config_default_data = {
"path_dict": {
"genshin": "原神",
"prts": "明日方舟",
"pretty": "赛马娘",
"guardian": "坎公骑冠剑",
"pcr": "公主连结",
"azur": "碧蓝航线",
"fgo": "命运-冠位指定",
"onmyoji": "阴阳师",
},
"prts": {
"PRTS_SIX_P": 0.02,
"PRTS_FIVE_P": 0.08,
"PRTS_FOUR_P": 0.48,
"PRTS_THREE_P": 0.42,
},
"genshin": {
"GENSHIN_FIVE_P": 0.006,
"GENSHIN_FOUR_P": 0.051,
"GENSHIN_THREE_P": 0.43,
"GENSHIN_G_FIVE_P": 0.13,
"GENSHIN_G_FOUR_P": 0.016,
"I72_ADD": 0.0585,
},
"pretty": {
"PRETTY_THREE_P": 0.03,
"PRETTY_TWO_P": 0.18,
"PRETTY_ONE_P": 0.79,
},
"guardian": {
"GUARDIAN_THREE_CHAR_P": 0.0275,
"GUARDIAN_TWO_CHAR_P": 0.19,
"GUARDIAN_ONE_CHAR_P": 0.7825,
"GUARDIAN_THREE_CHAR_UP_P": 0.01375,
"GUARDIAN_THREE_CHAR_OTHER_P": 0.01375,
"GUARDIAN_EXCLUSIVE_ARMS_P": 0.03,
"GUARDIAN_FIVE_ARMS_P": 0.03,
"GUARDIAN_FOUR_ARMS_P": 0.09,
"GUARDIAN_THREE_ARMS_P": 0.27,
"GUARDIAN_TWO_ARMS_P": 0.58,
"GUARDIAN_EXCLUSIVE_ARMS_UP_P": 0.01,
"GUARDIAN_EXCLUSIVE_ARMS_OTHER_P": 0.02,
},
"pcr": {
"PCR_THREE_P": 0.025,
"PCR_TWO_P": 0.18,
"PCR_ONE_P": 0.795,
},
"azur": {
"AZUR_FIVE_P": 0.012,
"AZUR_FOUR_P": 0.07,
"AZUR_THREE_P": 0.12,
"AZUR_TWO_P": 0.51,
"AZUR_ONE_P": 0.3,
},
"fgo": {
"FGO_SERVANT_FIVE_P": 0.01,
"FGO_SERVANT_FOUR_P": 0.03,
"FGO_SERVANT_THREE_P": 0.4,
"FGO_CARD_FIVE_P": 0.04,
"FGO_CARD_FOUR_P": 0.12,
"FGO_CARD_THREE_P": 0.4,
},
"onmyoji": {
"ONMYOJI_SP": 0.0025,
"ONMYOJI_SSR": 0.01,
"ONMYOJI_SR": 0.2,
"ONMYOJI_R": 0.7875,
},
}
@driver.on_startup
def check_config():
global PRTS_SIX_P, PRTS_FOUR_P, PRTS_FIVE_P, PRTS_THREE_P, GENSHIN_G_FIVE_P, config_default_data, GENSHIN_G_FOUR_P, GENSHIN_FOUR_P, GENSHIN_FIVE_P, I72_ADD, path_dict, PRETTY_THREE_P, PRETTY_ONE_P, PRETTY_TWO_P, GENSHIN_THREE_P, GUARDIAN_THREE_CHAR_P, GUARDIAN_TWO_CHAR_P, GUARDIAN_ONE_CHAR_P, GUARDIAN_THREE_CHAR_UP_P, GUARDIAN_THREE_CHAR_OTHER_P, GUARDIAN_EXCLUSIVE_ARMS_P, GUARDIAN_FIVE_ARMS_P, GUARDIAN_FOUR_ARMS_P, GUARDIAN_THREE_ARMS_P, GUARDIAN_TWO_ARMS_P, GENSHIN_FLAG, PRTS_FLAG, PRETTY_FLAG, GUARDIAN_FLAG, GUARDIAN_EXCLUSIVE_ARMS_UP_P, GUARDIAN_EXCLUSIVE_ARMS_OTHER_P, DRAW_PATH, PCR_THREE_P, PCR_TWO_P, PCR_ONE_P, AZUR_FOUR_P, AZUR_THREE_P, AZUR_TWO_P, AZUR_ONE_P, AZUR_FIVE_P, FGO_CARD_FIVE_P, FGO_CARD_FOUR_P, FGO_CARD_THREE_P, FGO_SERVANT_THREE_P, FGO_SERVANT_FOUR_P, FGO_SERVANT_FIVE_P, ONMYOJI_R, ONMYOJI_SP, ONMYOJI_SSR, ONMYOJI_SR
_draw_config.parent.mkdir(parents=True, exist_ok=True)
try:
data = json.load(open(_draw_config, "r", encoding="utf8"))
except (FileNotFoundError, ValueError):
_draw_config.parent.mkdir(parents=True, exist_ok=True)
global draw_config
draw_config = Config()
if not config_path.exists():
config_path.parent.mkdir(parents=True, exist_ok=True)
draw_config = Config()
logger.warning("draw_card:配置文件不存在,已重新生成配置文件.....")
with config_path.open("w", encoding="utf8") as fp:
json.dump(
config_default_data,
open(_draw_config, "w", encoding="utf8"),
draw_config.dict(),
fp,
indent=4,
ensure_ascii=False,
)
print("draw_card:配置文件不存在或格式错误,已重新生成配置文件.....")
else:
try:
PRTS_SIX_P = float(data["prts"]["PRTS_SIX_P"])
PRTS_FIVE_P = float(data["prts"]["PRTS_FIVE_P"])
PRTS_FOUR_P = float(data["prts"]["PRTS_FOUR_P"])
PRTS_THREE_P = float(data["prts"]["PRTS_THREE_P"])
except KeyError:
data["prts"] = {}
data["prts"]["PRTS_SIX_P"] = config_default_data["prts"]["PRTS_SIX_P"]
data["prts"]["PRTS_FIVE_P"] = config_default_data["prts"]["PRTS_FIVE_P"]
data["prts"]["PRTS_FOUR_P"] = config_default_data["prts"]["PRTS_FOUR_P"]
data["prts"]["PRTS_THREE_P"] = config_default_data["prts"]["PRTS_THREE_P"]
try:
GENSHIN_FIVE_P = float(data["genshin"]["GENSHIN_FIVE_P"])
GENSHIN_FOUR_P = float(data["genshin"]["GENSHIN_FOUR_P"])
GENSHIN_THREE_P = float(data["genshin"]["GENSHIN_THREE_P"])
GENSHIN_G_FIVE_P = float(data["genshin"]["GENSHIN_G_FIVE_P"])
GENSHIN_G_FOUR_P = float(data["genshin"]["GENSHIN_G_FOUR_P"])
I72_ADD = float(data["genshin"]["I72_ADD"])
except KeyError:
data["genshin"] = {}
data["genshin"]["GENSHIN_FIVE_P"] = config_default_data["genshin"][
"GENSHIN_FIVE_P"
]
data["genshin"]["GENSHIN_FOUR_P"] = config_default_data["genshin"][
"GENSHIN_FOUR_P"
]
data["genshin"]["GENSHIN_THREE_P"] = config_default_data["genshin"][
"GENSHIN_THREE_P"
]
data["genshin"]["GENSHIN_G_FIVE_P"] = config_default_data["genshin"][
"GENSHIN_G_FIVE_P"
]
data["genshin"]["GENSHIN_G_FOUR_P"] = config_default_data["genshin"][
"GENSHIN_G_FOUR_P"
]
data["genshin"]["I72_ADD"] = config_default_data["genshin"]["I72_ADD"]
try:
PRETTY_THREE_P = float(data["pretty"]["PRETTY_THREE_P"])
PRETTY_TWO_P = float(data["pretty"]["PRETTY_TWO_P"])
PRETTY_ONE_P = float(data["pretty"]["PRETTY_ONE_P"])
except KeyError:
data["pretty"] = {}
data["pretty"]["PRETTY_THREE_P"] = config_default_data["pretty"][
"PRETTY_THREE_P"
]
data["pretty"]["PRETTY_TWO_P"] = config_default_data["pretty"][
"PRETTY_TWO_P"
]
data["pretty"]["PRETTY_ONE_P"] = config_default_data["pretty"][
"PRETTY_ONE_P"
]
try:
GUARDIAN_THREE_CHAR_P = float(data["guardian"]["GUARDIAN_THREE_CHAR_P"])
GUARDIAN_TWO_CHAR_P = float(data["guardian"]["GUARDIAN_TWO_CHAR_P"])
GUARDIAN_ONE_CHAR_P = float(data["guardian"]["GUARDIAN_ONE_CHAR_P"])
GUARDIAN_THREE_CHAR_UP_P = float(
data["guardian"]["GUARDIAN_THREE_CHAR_UP_P"]
)
GUARDIAN_THREE_CHAR_OTHER_P = float(
data["guardian"]["GUARDIAN_THREE_CHAR_OTHER_P"]
)
GUARDIAN_EXCLUSIVE_ARMS_P = float(
data["guardian"]["GUARDIAN_EXCLUSIVE_ARMS_P"]
)
GUARDIAN_FIVE_ARMS_P = float(data["guardian"]["GUARDIAN_FIVE_ARMS_P"])
GUARDIAN_FOUR_ARMS_P = float(data["guardian"]["GUARDIAN_FOUR_ARMS_P"])
GUARDIAN_THREE_ARMS_P = float(data["guardian"]["GUARDIAN_THREE_ARMS_P"])
GUARDIAN_TWO_ARMS_P = float(data["guardian"]["GUARDIAN_TWO_ARMS_P"])
GUARDIAN_EXCLUSIVE_ARMS_UP_P = float(
data["guardian"]["GUARDIAN_EXCLUSIVE_ARMS_UP_P"]
)
GUARDIAN_EXCLUSIVE_ARMS_OTHER_P = float(
data["guardian"]["GUARDIAN_EXCLUSIVE_ARMS_OTHER_P"]
)
except KeyError:
data["guardian"] = {}
data["guardian"]["GUARDIAN_THREE_CHAR_P"] = config_default_data["guardian"][
"GUARDIAN_THREE_CHAR_P"
]
data["guardian"]["GUARDIAN_TWO_CHAR_P"] = config_default_data["guardian"][
"GUARDIAN_TWO_CHAR_P"
]
data["guardian"]["GUARDIAN_ONE_CHAR_P"] = config_default_data["guardian"][
"GUARDIAN_ONE_CHAR_P"
]
data["guardian"]["GUARDIAN_THREE_CHAR_UP_P"] = config_default_data[
"guardian"
]["GUARDIAN_THREE_CHAR_UP_P"]
data["guardian"]["GUARDIAN_THREE_CHAR_OTHER_P"] = config_default_data[
"guardian"
]["GUARDIAN_THREE_CHAR_OTHER_P"]
data["guardian"]["GUARDIAN_EXCLUSIVE_ARMS_P"] = config_default_data[
"guardian"
]["GUARDIAN_EXCLUSIVE_ARMS_P"]
data["guardian"]["GUARDIAN_FIVE_ARMS_P"] = config_default_data["guardian"][
"GUARDIAN_FIVE_ARMS_P"
]
data["guardian"]["GUARDIAN_FOUR_ARMS_P"] = config_default_data["guardian"][
"GUARDIAN_FOUR_ARMS_P"
]
data["guardian"]["GUARDIAN_THREE_ARMS_P"] = config_default_data["guardian"][
"GUARDIAN_THREE_ARMS_P"
]
data["guardian"]["GUARDIAN_TWO_ARMS_P"] = config_default_data["guardian"][
"GUARDIAN_TWO_ARMS_P"
]
data["guardian"]["GUARDIAN_EXCLUSIVE_ARMS_UP_P"] = config_default_data[
"guardian"
]["GUARDIAN_EXCLUSIVE_ARMS_UP_P"]
data["guardian"]["GUARDIAN_EXCLUSIVE_ARMS_OTHER_P"] = config_default_data[
"guardian"
]["GUARDIAN_EXCLUSIVE_ARMS_OTHER_P"]
try:
PCR_THREE_P = float(data["pcr"]["PCR_THREE_P"])
PCR_TWO_P = float(data["pcr"]["PCR_TWO_P"])
PCR_ONE_P = float(data["pcr"]["PCR_ONE_P"])
except KeyError:
data["pcr"] = {}
data["pcr"]["PCR_THREE_P"] = config_default_data["pcr"]["PCR_THREE_P"]
data["pcr"]["PCR_TWO_P"] = config_default_data["pcr"]["PCR_TWO_P"]
data["pcr"]["PCR_ONE_P"] = config_default_data["pcr"]["PCR_ONE_P"]
try:
AZUR_FIVE_P = float(data["azur"]["AZUR_FIVE_P"])
AZUR_FOUR_P = float(data["azur"]["AZUR_FOUR_P"])
AZUR_THREE_P = float(data["azur"]["AZUR_THREE_P"])
AZUR_TWO_P = float(data["azur"]["AZUR_TWO_P"])
AZUR_ONE_P = float(data["azur"]["AZUR_ONE_P"])
except KeyError:
data["azur"] = {}
data["azur"]["AZUR_FIVE_P"] = config_default_data["azur"]["AZUR_FIVE_P"]
data["azur"]["AZUR_FOUR_P"] = config_default_data["azur"]["AZUR_FOUR_P"]
data["azur"]["AZUR_THREE_P"] = config_default_data["azur"]["AZUR_THREE_P"]
data["azur"]["AZUR_TWO_P"] = config_default_data["azur"]["AZUR_TWO_P"]
data["azur"]["AZUR_ONE_P"] = config_default_data["azur"]["AZUR_ONE_P"]
try:
FGO_SERVANT_FIVE_P = float(data["fgo"]["FGO_SERVANT_FIVE_P"])
FGO_SERVANT_FOUR_P = float(data["fgo"]["FGO_SERVANT_FOUR_P"])
FGO_SERVANT_THREE_P = float(data["fgo"]["FGO_SERVANT_THREE_P"])
FGO_CARD_FIVE_P = float(data["fgo"]["FGO_CARD_FIVE_P"])
FGO_CARD_FOUR_P = float(data["fgo"]["FGO_CARD_FOUR_P"])
FGO_CARD_THREE_P = float(data["fgo"]["FGO_CARD_THREE_P"])
except KeyError:
data["fgo"] = {}
data["fgo"]["FGO_SERVANT_FIVE_P"] = config_default_data["fgo"][
"FGO_SERVANT_FIVE_P"
]
data["fgo"]["FGO_SERVANT_FOUR_P"] = config_default_data["fgo"][
"FGO_SERVANT_FOUR_P"
]
data["fgo"]["FGO_SERVANT_THREE_P"] = config_default_data["fgo"][
"FGO_SERVANT_THREE_P"
]
data["fgo"]["FGO_CARD_FIVE_P"] = config_default_data["fgo"][
"FGO_CARD_FIVE_P"
]
data["fgo"]["FGO_CARD_FOUR_P"] = config_default_data["fgo"][
"FGO_CARD_FOUR_P"
]
data["fgo"]["FGO_CARD_THREE_P"] = config_default_data["fgo"][
"FGO_CARD_THREE_P"
]
try:
ONMYOJI_SP = float(data["onmyoji"]["ONMYOJI_SP"])
ONMYOJI_SSR = float(data["onmyoji"]["ONMYOJI_SSR"])
ONMYOJI_SR = float(data["onmyoji"]["ONMYOJI_SR"])
ONMYOJI_R = float(data["onmyoji"]["ONMYOJI_R"])
except KeyError:
data["onmyoji"] = {}
data["onmyoji"]["ONMYOJI_SP"] = config_default_data["onmyoji"]["ONMYOJI_SP"]
data["onmyoji"]["ONMYOJI_SSR"] = config_default_data["onmyoji"][
"ONMYOJI_SSR"
]
data["onmyoji"]["ONMYOJI_SR"] = config_default_data["onmyoji"]["ONMYOJI_SR"]
data["onmyoji"]["ONMYOJI_R"] = config_default_data["onmyoji"]["ONMYOJI_R"]
json.dump(
data, open(_draw_config, "w", encoding="utf8"), indent=4, ensure_ascii=False
)
+169
View File
@@ -0,0 +1,169 @@
from typing import Optional
from pydantic import BaseModel
import time
class BaseUserCount(BaseModel):
count: int = 1 # 当前抽卡次数
time_: int = time.time() # 抽卡时间,当超过一定时间时将重置抽卡次数
timeout: int = 60 # 超时时间60秒
class DrawCountManager:
"""
抽卡统计保底
"""
def __init__(
self, game_draw_count_rule: tuple, star2name: tuple, max_draw_count: int
):
"""
初始化保底统计
例如:DrawCountManager((10, 90, 180), ("4", "5", "5"))
抽卡保底需要的次数和返回的对应名称,例如星级等
:param game_draw_count_rule:抽卡规则
:param star2name:星级对应的名称
:param max_draw_count:最大累计抽卡次数,当下次单次抽卡超过该次数时将会清空数据
"""
# 只有保底
self._data = {}
self._guarantee_tuple = game_draw_count_rule
self._star2name = star2name
self._max_draw_count = max_draw_count
def increase(self, key: int, value: int = 1):
"""
用户抽卡次数加1
"""
if self._data.get(key) is None:
self._data[key] = BaseUserCount()
else:
self._data[key].count += 1
def get_max_guarantee(self):
"""
获取最大保底抽卡次数
"""
return self._guarantee_tuple[-1]
def get_user_count(self, key: int) -> int:
"""
获取当前抽卡次数
"""
return self._data[key].count
def update_time(self, key: int):
"""
更新抽卡时间
"""
self._data[key].time_ = time.time()
def reset(self, key: int):
"""
清空记录
"""
del self._data[key]
class GenshinCountManager(DrawCountManager):
class UserCount(BaseUserCount):
five_index: int = 0 # 获取五星时的抽卡次数
four_index: int = 0 # 获取四星时的抽卡次数
is_up: bool = False # 下次五星是否必定为up
def increase(self, key: int, value: int = 1):
"""
用户抽卡次数加1
"""
if self._data.get(key) is None:
self._data[key] = self.UserCount()
else:
self._data[key].count += 1
def set_is_up(self, key: int, value: bool):
"""
设置下次是否必定up
"""
self._data[key].is_up = value
def is_up(self, key: int) -> bool:
"""
判断该次保底是否必定为up
"""
return self._data[key].is_up
def get_user_five_index(self, key: int) -> int:
"""
获取用户上次获取五星的次数
"""
return self._data[key].five_index
def get_user_four_index(self, key: int) -> int:
"""
获取用户上次获取四星的次数
"""
return self._data[key].four_index
def mark_five_index(self, key: int):
"""
标记用户该次次数为五星
"""
self._data[key].five_index = self._data[key].count
def mark_four_index(self, key: int):
"""
标记用户该次次数为四星
"""
self._data[key].four_index = self._data[key].count
def check_timeout(self, key: int):
"""
检查用户距离上次抽卡是否超时
"""
if key in self._data.keys() and self._is_timeout(key):
del self._data[key]
def check_count(self, key: int, count: int):
"""
检查用户该次抽卡次数累计是否超过最大限制次数
"""
if (
key in self._data.keys()
and self._data[key].count + count > self._max_draw_count
):
del self._data[key]
def _is_timeout(self, key: int) -> bool:
return time.time() - self._data[key].time_ > self._data[key].timeout
def get_user_guarantee_count(self, key: int) -> int:
return (
self.get_max_guarantee()
- (
(
self._data[key].count % self.get_max_guarantee()
if self._data[key].count > 0
else 0
)
- self._data[key].five_index
)
) % self.get_max_guarantee() or self.get_max_guarantee()
def check(self, key: int) -> Optional[int]:
"""
是否保底
"""
# print(self._data)
user: GenshinCountManager.UserCount = self._data[key]
if user.count - user.five_index == 90:
user.five_index = 90
return 5
if user.count - user.four_index == 10:
user.four_index = user.count
return 4
return None
-112
View File
@@ -1,112 +0,0 @@
from nonebot.adapters.cqhttp import MessageSegment
import random
from .update_game_requests_info import update_requests_info
from .util import generate_img, init_star_rst, BaseData, set_list, get_star, max_card
from .config import FGO_CARD_FOUR_P, FGO_CARD_FIVE_P, FGO_CARD_THREE_P, FGO_SERVANT_THREE_P, \
FGO_SERVANT_FIVE_P, FGO_SERVANT_FOUR_P, FGO_FLAG, DRAW_PATH
from dataclasses import dataclass
from .init_card_pool import init_game_pool
try:
import ujson as json
except ModuleNotFoundError:
import json
ALL_CHAR = []
ALL_CARD = []
@dataclass
class FgoChar(BaseData):
pass
async def fgo_draw(count: int):
# 0 1 2
cnlist = ['★★★★★', '★★★★', '★★★']
obj_list, obj_dict, max_star_list, star_list, max_star_index_list = _format_card_information(count)
rst = init_star_rst(star_list, cnlist, max_star_list, max_star_index_list)
if count > 90:
obj_list = set_list(obj_list)
return MessageSegment.image("base64://" + await generate_img(obj_list, 'fgo', star_list)) \
+ '\n' + rst[:-1] + '\n' + max_card(obj_dict)
async def update_fgo_info():
global ALL_CHAR, ALL_CARD
data, code = await update_requests_info('fgo')
if code == 200:
ALL_CHAR = init_game_pool('fgo', data, FgoChar)
data, code = await update_requests_info('fgo_card')
if code == 200:
ALL_CARD = init_game_pool('fgo_card', data, FgoChar)
async def init_fgo_data():
global ALL_CHAR, ALL_CARD
if FGO_FLAG:
with open(DRAW_PATH + 'fgo.json', 'r', encoding='utf8') as f:
fgo_dict = json.load(f)
ALL_CHAR = init_game_pool('fgo', fgo_dict, FgoChar)
with open(DRAW_PATH + 'fgo_card.json', 'r', encoding='utf8') as f:
fgo_dict = json.load(f)
ALL_CARD = init_game_pool('fgo', fgo_dict, FgoChar)
# 抽取卡池
def _get_fgo_card(mode: int = 1):
global ALL_CHAR, ALL_CARD
if mode == 1:
star = get_star([8, 7, 6, 5, 4, 3], [FGO_SERVANT_FIVE_P, FGO_SERVANT_FOUR_P, FGO_SERVANT_THREE_P,
FGO_CARD_FIVE_P, FGO_CARD_FOUR_P, FGO_CARD_THREE_P])
elif mode == 2:
star = get_star([5, 4], [FGO_CARD_FIVE_P, FGO_CARD_FOUR_P])
else:
star = get_star([8, 7, 6], [FGO_SERVANT_FIVE_P, FGO_SERVANT_FOUR_P, FGO_SERVANT_THREE_P])
if star > 5:
itype = 'servant'
star -= 3
chars = [x for x in ALL_CHAR if x.star == star if not x.limited]
else:
itype = 'card'
chars = [x for x in ALL_CARD if x.star == star if not x.limited]
return random.choice(chars), 5 - star, itype
# 整理数据
def _format_card_information(count: int):
max_star_lst = [] # 获取的最高星级角色列表
max_index_lst = [] # 获取最高星级角色的次数
star_list = [0, 0, 0]
obj_list = [] # 获取所有角色
obj_dict = {} # 获取角色次数字典
servant_count = 0 # 保底计算
card_count = 0 # 保底计算
for i in range(count):
servant_count += 1
card_count += 1
# 四星卡片保底
if card_count == 9:
obj, code, itype = _get_fgo_card(2)
# 三星从者保底
elif servant_count == 10:
obj, code, itype = _get_fgo_card(3)
_count = 0
# 普通抽
else:
obj, code, itype = _get_fgo_card()
star_list[code] += 1
if itype == 'card' and code < 2:
card_count = 0
if itype == 'servant':
servant_count = 0
if code == 0:
max_star_lst.append(obj.name)
max_index_lst.append(i)
try:
obj_dict[obj.name] += 1
except KeyError:
obj_dict[obj.name] = 1
obj_list.append(obj)
return obj_list, obj_dict, max_star_lst, star_list, max_index_lst
-277
View File
@@ -1,277 +0,0 @@
import os
from nonebot.adapters.cqhttp import MessageSegment, Message
import random
from .update_game_info import update_info
from .util import (
generate_img,
init_star_rst,
BaseData,
set_list,
get_star,
init_up_char,
)
from .config import (
GENSHIN_FIVE_P,
GENSHIN_FOUR_P,
GENSHIN_G_FIVE_P,
GENSHIN_G_FOUR_P,
GENSHIN_THREE_P,
I72_ADD,
DRAW_PATH,
GENSHIN_FLAG,
)
from dataclasses import dataclass
from .init_card_pool import init_game_pool
from .announcement import GenshinAnnouncement
try:
import ujson as json
except ModuleNotFoundError:
import json
announcement = GenshinAnnouncement()
genshin_five = {}
genshin_count = {}
genshin_pl_count = {}
ALL_CHAR = []
ALL_ARMS = []
UP_CHAR = []
UP_ARMS = []
_CURRENT_CHAR_POOL_TITLE = ""
_CURRENT_ARMS_POOL_TITLE = ""
POOL_IMG = ""
@dataclass
class GenshinChar(BaseData):
pass
async def genshin_draw(user_id: int, count: int, pool_name: str):
# 0 1 2
cnlist = ["★★★★★", "★★★★", "★★★"]
(
char_list,
five_list,
five_index_list,
char_dict,
star_list,
) = _format_card_information(count, user_id, pool_name)
temp = ""
title = ""
up_type = []
up_list = []
if pool_name == "char" and _CURRENT_CHAR_POOL_TITLE:
up_type = UP_CHAR
title = _CURRENT_CHAR_POOL_TITLE
elif pool_name == "arms" and _CURRENT_ARMS_POOL_TITLE:
up_type = UP_ARMS
title = _CURRENT_ARMS_POOL_TITLE
tmp = ""
if up_type:
for x in up_type:
for operator in x.operators:
up_list.append(operator)
if x.star == 5:
tmp += f'五星UP:{" ".join(x.operators)} \n'
elif x.star == 4:
tmp += f'四星UP:{" ".join(x.operators)}'
rst = init_star_rst(star_list, cnlist, five_list, five_index_list, up_list)
pool_info = f"当前up池:{title}\n{tmp}" if title else ""
if count > 90:
char_list = set_list(char_list)
return (
pool_info
+ "\n"
+ MessageSegment.image(
"base64://" + await generate_img(char_list, "genshin", star_list)
)
+ "\n"
+ rst[:-1]
+ temp[:-1]
+ f'\n距离保底发还剩 {90 - genshin_count[user_id] if genshin_count.get(user_id) else "^"} 抽'
+ "\n【五星:0.6%,四星:5.1%\n第72抽开始五星概率每抽加0.585%】"
)
async def update_genshin_info():
global ALL_CHAR, ALL_ARMS
url = "https://wiki.biligame.com/ys/角色筛选"
data, code = await update_info(url, "genshin")
if code == 200:
ALL_CHAR = init_game_pool("genshin", data, GenshinChar)
url = "https://wiki.biligame.com/ys/武器图鉴"
data, code = await update_info(
url,
"genshin_arms",
[
"头像",
"名称",
"类型",
"稀有度.alt",
"获取途径",
"初始基础属性1",
"初始基础属性2",
"攻击力(MAX)",
"副属性(MAX)",
"技能",
],
)
if code == 200:
ALL_ARMS = init_game_pool("genshin_arms", data, GenshinChar)
await _genshin_init_up_char()
async def init_genshin_data():
global ALL_CHAR, ALL_ARMS
if GENSHIN_FLAG:
if not os.path.exists(DRAW_PATH + "genshin.json") or not os.path.exists(
DRAW_PATH + "genshin_arms.json"
):
await update_genshin_info()
else:
with open(DRAW_PATH + "genshin.json", "r", encoding="utf8") as f:
genshin_dict = json.load(f)
with open(DRAW_PATH + "genshin_arms.json", "r", encoding="utf8") as f:
genshin_ARMS_dict = json.load(f)
ALL_CHAR = init_game_pool("genshin", genshin_dict, GenshinChar)
ALL_ARMS = init_game_pool("genshin_arms", genshin_ARMS_dict, GenshinChar)
await _genshin_init_up_char()
# 抽取卡池
def _get_genshin_card(mode: int = 1, pool_name: str = "", add: float = 0.0):
global ALL_ARMS, ALL_CHAR, UP_ARMS, UP_CHAR, _CURRENT_ARMS_POOL_TITLE, _CURRENT_CHAR_POOL_TITLE
if mode == 1:
star = get_star(
[5, 4, 3], [GENSHIN_FIVE_P + add, GENSHIN_FOUR_P, GENSHIN_THREE_P]
)
elif mode == 2:
star = get_star([5, 4], [GENSHIN_G_FIVE_P + add, GENSHIN_G_FOUR_P])
else:
star = 5
if pool_name == "char":
data_lst = UP_CHAR
flag = _CURRENT_CHAR_POOL_TITLE
itype_all_lst = ALL_CHAR + [
x for x in ALL_ARMS if x.star == star and x.star < 5
]
elif pool_name == "arms":
data_lst = UP_ARMS
flag = _CURRENT_ARMS_POOL_TITLE
itype_all_lst = ALL_ARMS + [
x for x in ALL_CHAR if x.star == star and x.star < 5
]
else:
data_lst = ""
flag = ""
itype_all_lst = ""
all_lst = ALL_ARMS + ALL_CHAR
# 是否UP
try:
if flag and star > 3 and pool_name:
# 获取up角色列表
up_char_lst = [x.operators for x in data_lst if x.star == star][0]
print(up_char_lst)
# 成功获取up角色
if random.random() < 0.5:
up_char_name = random.choice(up_char_lst)
acquire_char = [x for x in all_lst if x.name == up_char_name][0]
else:
# 无up
all_char_lst = [
x
for x in itype_all_lst
if x.star == star and x.name not in up_char_lst and not x.limited
]
acquire_char = random.choice(all_char_lst)
else:
chars = [x for x in all_lst if x.star == star and not x.limited]
acquire_char = random.choice(chars)
except IndexError:
chars = [x for x in all_lst if x.star == star and not x.limited]
acquire_char = random.choice(chars)
return acquire_char, 5 - star
def _format_card_information(_count: int, user_id, pool_name):
char_list = []
star_list = [0, 0, 0]
five_index_list = []
five_list = []
five_dict = {}
_start_add_count = 72 if pool_name == "char" else 62
_x = 90 if pool_name == "char" else 80 # 保底
add = 0.0
if genshin_count.get(user_id) and _count <= _x:
f_count = genshin_count[user_id]
else:
f_count = 0
if genshin_pl_count.get(user_id) and _count <= _x:
count = genshin_pl_count[user_id]
else:
count = 0
for i in range(_count):
count += 1
f_count += 1
# 十连保底
if count == 10 and f_count != _x:
if f_count >= _start_add_count:
add += I72_ADD
char, code = _get_genshin_card(2, pool_name, add=add)
count = 0
# 大保底
elif f_count == _x:
char, code = _get_genshin_card(3, pool_name)
else:
if f_count >= _start_add_count:
add += I72_ADD
char, code = _get_genshin_card(pool_name=pool_name, add=add)
if code == 1:
count = 0
star_list[code] += 1
if code == 0:
if _count <= _x:
genshin_five[user_id] = f_count
add = 0.0
f_count = 0
five_list.append(char.name)
five_index_list.append(i)
try:
five_dict[char.name] += 1
except KeyError:
five_dict[char.name] = 1
char_list.append(char)
if _count <= _x:
genshin_count[user_id] = f_count
genshin_pl_count[user_id] = count
return char_list, five_list, five_index_list, five_dict, star_list
def reset_count(user_id: int):
genshin_count[user_id] = 0
genshin_pl_count[user_id] = 0
# 获取up和概率
async def _genshin_init_up_char():
global _CURRENT_CHAR_POOL_TITLE, _CURRENT_ARMS_POOL_TITLE, UP_CHAR, UP_ARMS, POOL_IMG
(
_CURRENT_CHAR_POOL_TITLE,
_CURRENT_ARMS_POOL_TITLE,
POOL_IMG,
UP_CHAR,
UP_ARMS,
) = await init_up_char(announcement)
async def reload_genshin_pool():
await _genshin_init_up_char()
return Message(
f"当前UP池子:{_CURRENT_CHAR_POOL_TITLE} & {_CURRENT_ARMS_POOL_TITLE} {POOL_IMG}"
)
-160
View File
@@ -1,160 +0,0 @@
import os
from nonebot.adapters.cqhttp import MessageSegment, Message
from .update_game_info import update_info
from .announcement import GuardianAnnouncement
from .util import init_star_rst, generate_img, max_card, BaseData,\
set_list, get_star, format_card_information, init_up_char
import random
from .config import DRAW_PATH, GUARDIAN_ONE_CHAR_P, GUARDIAN_TWO_CHAR_P, GUARDIAN_THREE_CHAR_P, \
GUARDIAN_THREE_CHAR_UP_P, GUARDIAN_TWO_ARMS_P, GUARDIAN_FIVE_ARMS_P, GUARDIAN_THREE_CHAR_OTHER_P, \
GUARDIAN_FOUR_ARMS_P, GUARDIAN_THREE_ARMS_P, GUARDIAN_EXCLUSIVE_ARMS_P, GUARDIAN_EXCLUSIVE_ARMS_UP_P, \
GUARDIAN_EXCLUSIVE_ARMS_OTHER_P, GUARDIAN_FLAG
from dataclasses import dataclass
from .init_card_pool import init_game_pool
try:
import ujson as json
except ModuleNotFoundError:
import json
announcement = GuardianAnnouncement()
ALL_CHAR = []
ALL_ARMS = []
_CURRENT_CHAR_POOL_TITLE = ''
_CURRENT_ARMS_POOL_TITLE = ''
UP_CHAR = []
UP_ARMS = []
POOL_IMG = ''
@dataclass
class GuardianChar(BaseData):
pass
@dataclass
class GuardianArms(BaseData):
pass
async def guardian_draw(count: int, pool_name):
if pool_name == 'arms':
cnlist = ['★★★★★', '★★★★', '★★★', '★★']
star_list = [0, 0, 0, 0]
else:
cnlist = ['★★★', '★★', '★']
star_list = [0, 0, 0]
title = ''
up_type = []
up_list = []
if pool_name == 'char' and _CURRENT_CHAR_POOL_TITLE:
up_type = UP_CHAR
title = _CURRENT_CHAR_POOL_TITLE
elif pool_name == 'arms' and _CURRENT_ARMS_POOL_TITLE:
up_type = UP_ARMS
title = _CURRENT_ARMS_POOL_TITLE
tmp = ''
if up_type:
for x in up_type:
for operator in x.operators:
up_list.append(operator)
if pool_name == 'char':
if x.star == 3:
tmp += f'三星UP:{" ".join(x.operators)} \n'
else:
if x.star == 5:
tmp += f'五星UP:{" ".join(x.operators)}'
obj_list, obj_dict, max_list, star_list, max_index_list = format_card_information(count, star_list,
_get_guardian_card, pool_name)
rst = init_star_rst(star_list, cnlist, max_list, max_index_list, up_list)
pool_info = f'当前up池:{title}\n{tmp}' if title else ''
if count > 90:
obj_list = set_list(obj_list)
return pool_info + '\n' + MessageSegment.image(
"base64://" + await generate_img(obj_list, 'guardian', star_list)) \
+ '\n' + rst[:-1] + '\n' + max_card(obj_dict)
async def update_guardian_info():
global ALL_CHAR, ALL_ARMS
url = 'https://wiki.biligame.com/gt/英雄筛选表'
data, code = await update_info(url, 'guardian')
if code == 200:
ALL_CHAR = init_game_pool('guardian', data, GuardianChar)
url = 'https://wiki.biligame.com/gt/武器'
tmp, code_1 = await update_info(url, 'guardian_arms')
url = 'https://wiki.biligame.com/gt/盾牌'
data, code_2 = await update_info(url, 'guardian_arms')
if code_1 == 200 and code_2 == 200:
data.update(tmp)
ALL_ARMS = init_game_pool('guardian_arms', data, GuardianArms)
await _guardian_init_up_char()
async def init_guardian_data():
global ALL_CHAR, ALL_ARMS
if GUARDIAN_FLAG:
if not os.path.exists(DRAW_PATH + 'guardian.json') or not os.path.exists(DRAW_PATH + 'guardian_arms.json'):
await update_guardian_info()
else:
with open(DRAW_PATH + 'guardian.json', 'r', encoding='utf8') as f:
guardian_char_dict = json.load(f)
with open(DRAW_PATH + 'guardian_arms.json', 'r', encoding='utf8') as f:
guardian_arms_dict = json.load(f)
ALL_CHAR = init_game_pool('guardian', guardian_char_dict, GuardianChar)
ALL_ARMS = init_game_pool('guardian_arms', guardian_arms_dict, GuardianArms)
await _guardian_init_up_char()
# 抽取卡池
def _get_guardian_card(pool_name: str = '', mode: int = 1):
global ALL_ARMS, ALL_CHAR, UP_ARMS, UP_CHAR, _CURRENT_ARMS_POOL_TITLE, _CURRENT_CHAR_POOL_TITLE
if pool_name == 'char':
if mode == 1:
star = get_star([3, 2, 1], [GUARDIAN_THREE_CHAR_P, GUARDIAN_TWO_CHAR_P, GUARDIAN_ONE_CHAR_P])
else:
star = get_star([3, 2], [GUARDIAN_THREE_CHAR_P, GUARDIAN_TWO_CHAR_P])
up_lst = UP_CHAR
flag = _CURRENT_CHAR_POOL_TITLE
_max_star = 3
all_data = ALL_CHAR
else:
if mode == 1:
star = get_star([5, 4, 3, 2], [GUARDIAN_FIVE_ARMS_P, GUARDIAN_FOUR_ARMS_P,
GUARDIAN_THREE_ARMS_P, GUARDIAN_TWO_ARMS_P])
else:
star = get_star([5, 4], [GUARDIAN_FIVE_ARMS_P, GUARDIAN_FOUR_ARMS_P])
up_lst = UP_ARMS
flag = _CURRENT_ARMS_POOL_TITLE
_max_star = 5
all_data = ALL_ARMS
# 是否UP
if flag and star == _max_star and pool_name:
# 获取up角色列表
up_char_lst = [x.operators for x in up_lst if x.star == star][0]
# 成功获取up角色
if random.random() < 0.5:
up_char_name = random.choice(up_char_lst)
acquire_char = [x for x in all_data if x.name == up_char_name][0]
else:
# 无up
all_char_lst = [x for x in all_data if x.star == star and x.name not in up_char_lst and not x.limited]
acquire_char = random.choice(all_char_lst)
else:
chars = [x for x in all_data if x.star == star and not x.limited]
acquire_char = random.choice(chars)
return acquire_char, _max_star - star
# 获取up和概率
async def _guardian_init_up_char():
global _CURRENT_CHAR_POOL_TITLE, _CURRENT_ARMS_POOL_TITLE, UP_CHAR, UP_ARMS, POOL_IMG
_CURRENT_CHAR_POOL_TITLE, _CURRENT_ARMS_POOL_TITLE, POOL_IMG, UP_CHAR, UP_ARMS = await init_up_char(announcement)
async def reload_guardian_pool():
await _guardian_init_up_char()
return Message(f'当前UP池子:{_CURRENT_CHAR_POOL_TITLE} & {_CURRENT_ARMS_POOL_TITLE}')
+317
View File
@@ -0,0 +1,317 @@
import random
import dateparser
from lxml import etree
from typing import List, Optional
from urllib.parse import unquote
from pydantic import ValidationError
from nonebot.log import logger
from nonebot.adapters.onebot.v11 import Message
from utils.message_builder import image
from .base_handle import BaseHandle, BaseData, UpEvent as _UpEvent, UpChar as _UpChar
from ..config import draw_config
from ..util import remove_prohibited_str, cn2py
from utils.image_utils import BuildImage
import asyncio
try:
import ujson as json
except ModuleNotFoundError:
import json
class AzurChar(BaseData):
type_: str # 舰娘类型
@property
def star_str(self) -> str:
return ["白", "蓝", "紫", "金"][self.star - 1]
class UpChar(_UpChar):
type_: str # 舰娘类型
class UpEvent(_UpEvent):
up_char: List[UpChar] # up对象
class AzurHandle(BaseHandle[AzurChar]):
def __init__(self):
super().__init__("azur", "碧蓝航线")
self.max_star = 4
self.config = draw_config.azur
self.ALL_CHAR: List[AzurChar] = []
self.UP_EVENT: Optional[UpEvent] = None
def get_card(self, pool_name: str, **kwargs) -> AzurChar:
if pool_name == "轻型":
type_ = ["驱逐", "轻巡", "维修"]
elif pool_name == "重型":
type_ = ["重巡", "战列", "战巡", "重炮"]
else:
type_ = ["维修", "潜艇", "重巡", "轻航", "航母"]
up_pool_flag = pool_name == "活动"
# Up
up_ship = [x for x in self.UP_EVENT.up_char if x.zoom > 0]
# print(up_ship)
acquire_char = None
if up_ship and up_pool_flag:
up_zoom = [(0, up_ship[0].zoom / 100)]
# 初始化概率
cur_ = up_ship[0].zoom / 100
for i in range(len(up_ship)):
try:
up_zoom.append((cur_, cur_ + up_ship[i+1].zoom / 100))
cur_ += up_ship[i+1].zoom / 100
except IndexError:
pass
rand = random.random()
# 抽取up
for i, zoom in enumerate(up_zoom):
if zoom[0] <= rand <= zoom[1]:
try:
acquire_char = [x for x in self.ALL_CHAR if x.name == up_ship[i].name][0]
except IndexError:
pass
# 没有up或者未抽取到up
if not acquire_char:
star = self.get_star(
[4, 3, 2, 1],
[
self.config.AZUR_FOUR_P,
self.config.AZUR_THREE_P,
self.config.AZUR_TWO_P,
self.config.AZUR_ONE_P,
],
)
acquire_char = random.choice([
x
for x in self.ALL_CHAR
if x.star == star and x.type_ in type_ and not x.limited
])
return acquire_char
# async def draw(self, count: int, **kwargs) -> Message:
# return await asyncio.get_event_loop().run_in_executor(None, self._draw, count)
async def draw(self, count: int, **kwargs) -> Message:
index2card = self.get_cards(count, **kwargs)
cards = [card[0] for card in index2card]
up_list = [x.name for x in self.UP_EVENT.up_char] if self.UP_EVENT.up_char else []
result = self.format_result(index2card, **{**kwargs, "up_list": up_list})
return image(b64=self.generate_img(cards).pic2bs4()) + result
def generate_card_img(self, card: AzurChar) -> BuildImage:
sep_w = 5
sep_t = 5
sep_b = 20
w = 100
h = 100
bg = BuildImage(w + sep_w * 2, h + sep_t + sep_b, font="msyh.ttf")
frame_path = self.img_path / f"{card.star}_star.png"
frame = BuildImage(w, h, background=frame_path)
img_path = self.img_path / f"{cn2py(card.name)}.png"
img = BuildImage(w, h, background=img_path)
# 加圆角
img.circle_corner(6)
bg.paste(img, (sep_w, sep_t), alpha=True)
bg.paste(frame, (sep_w, sep_t), alpha=True)
bg.circle_corner(6)
# 加名字
text = card.name[:6] + "..." if len(card.name) > 7 else card.name
text_w, text_h = bg.getsize(text)
bg.text(
(sep_w + (w - text_w) / 2, h + sep_t + (sep_b - text_h) / 2),
text,
fill=["#808080", "#3b8bff", "#8000ff", "#c90", "#ee494c"][card.star - 1],
)
return bg
def _init_data(self):
self.ALL_CHAR = [
AzurChar(
name=value["名称"],
star=int(value["星级"]),
limited="可以建造" not in value["获取途径"],
type_=value["类型"],
)
for value in self.load_data().values()
]
self.load_up_char()
def load_up_char(self):
try:
data = self.load_data(f"draw_card_up/{self.game_name}_up_char.json")
self.UP_EVENT = UpEvent.parse_obj(data.get("char", {}))
except ValidationError:
logger.warning(f"{self.game_name}_up_char 解析出错")
def dump_up_char(self):
if self.UP_EVENT:
data = {"char": json.loads(self.UP_EVENT.json())}
self.dump_data(data, f"draw_card_up/{self.game_name}_up_char.json")
async def _update_info(self):
info = {}
# 更新图鉴
url = "https://wiki.biligame.com/blhx/舰娘图鉴"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
return
dom = etree.HTML(result, etree.HTMLParser())
contents = dom.xpath(
"//div[@class='resp-tabs-container']/div[@class='resp-tab-content']"
)
for index, content in enumerate(contents):
char_list = content.xpath("./table/tbody/tr[2]/td/div/div/div/div")
for char in char_list:
try:
name = char.xpath("./a/@title")[0]
frame = char.xpath("./div/a/img/@alt")[0]
avatar = char.xpath("./a/img/@srcset")[0]
except IndexError:
continue
member_dict = {
"名称": remove_prohibited_str(name),
"头像": unquote(str(avatar).split(" ")[-2]),
"星级": self.parse_star(frame),
"类型": self.parse_type(index),
}
info[member_dict["名称"]] = member_dict
# 更新额外信息
for key in info.keys():
url = f"https://wiki.biligame.com/blhx/{key}"
result = await self.get_url(url)
if not result:
info[key]["获取途径"] = []
logger.warning(f"{self.game_name_cn} 获取额外信息错误 {key}")
continue
try:
dom = etree.HTML(result, etree.HTMLParser())
time = dom.xpath(
"//table[@class='wikitable sv-general']/tbody[1]/tr[4]/td[2]//text()"
)[0]
sources = []
if "无法建造" in time:
sources.append("无法建造")
elif "活动已关闭" in time:
sources.append("活动限定")
else:
sources.append("可以建造")
info[key]["获取途径"] = sources
except IndexError:
info[key]["获取途径"] = []
logger.warning(f"{self.game_name_cn} 获取额外信息错误 {key}")
self.dump_data(info)
logger.info(f"{self.game_name_cn} 更新成功")
# 下载头像
for value in info.values():
await self.download_img(value["头像"], value["名称"])
# 下载头像框
idx = 1
BLHX_URL = "https://patchwiki.biligame.com/images/blhx"
for url in [
"/1/15/pxho13xsnkyb546tftvh49etzdh74cf.png",
"/a/a9/k8t7nx6c8pan5vyr8z21txp45jxeo66.png",
"/a/a5/5whkzvt200zwhhx0h0iz9qo1kldnidj.png",
"/a/a2/ptog1j220x5q02hytpwc8al7f229qk9.png",
"/6/6d/qqv5oy3xs40d3055cco6bsm0j4k4gzk.png",
]:
await self.download_img(BLHX_URL + url, f"{idx}_star")
idx += 1
await self.update_up_char()
@staticmethod
def parse_star(star: str) -> int:
if star in ["舰娘头像外框普通.png", "舰娘头像外框白色.png"]:
return 1
elif star in ["舰娘头像外框稀有.png", "舰娘头像外框蓝色.png"]:
return 2
elif star in ["舰娘头像外框精锐.png", "舰娘头像外框紫色.png"]:
return 3
elif star in ["舰娘头像外框超稀有.png", "舰娘头像外框金色.png"]:
return 4
elif star in ["舰娘头像外框海上传奇.png", "舰娘头像外框彩色.png"]:
return 5
elif star in [
"舰娘头像外框最高方案.png",
"舰娘头像外框决战方案.png",
"舰娘头像外框超稀有META.png",
"舰娘头像外框精锐META.png",
]:
return 6
else:
return 6
@staticmethod
def parse_type(index: int) -> str:
azur_types = [
"驱逐",
"轻巡",
"重巡",
"超巡",
"战巡",
"战列",
"航母",
"航站",
"轻航",
"重炮",
"维修",
"潜艇",
"运输",
]
try:
return azur_types[index]
except IndexError:
return azur_types[0]
async def update_up_char(self):
url = "https://wiki.biligame.com/blhx/游戏活动表"
result = await self.get_url(url)
if not result:
logger.warning(f"{self.game_name_cn}获取活动表出错")
return
try:
dom = etree.HTML(result, etree.HTMLParser())
dd = dom.xpath("//div[@class='timeline2']/dl/dd/a")[0]
url = "https://wiki.biligame.com" + dd.xpath("./@href")[0]
title = dd.xpath("string(.)")
result = await self.get_url(url)
if not result:
logger.warning(f"{self.game_name_cn}获取活动页面出错")
return
dom = etree.HTML(result, etree.HTMLParser())
timer = dom.xpath("//span[@class='eventTimer']")[0]
start_time = dateparser.parse(timer.xpath("./@data-start")[0])
end_time = dateparser.parse(timer.xpath("./@data-end")[0])
ships = dom.xpath("//table[@class='shipinfo']")
up_chars = []
for ship in ships:
name = ship.xpath("./tbody/tr/td[2]/p/a/@title")[0]
type_ = ship.xpath("./tbody/tr/td[2]/p/small/text()")[0] # 舰船类型
try:
p = float(str(ship.xpath(".//sup/text()")[0]).strip("%"))
except IndexError:
p = 0
star = self.parse_star(
ship.xpath("./tbody/tr/td[1]/div/div/div/a/img/@alt")[0]
)
up_chars.append(UpChar(name=name, star=star, limited=False, zoom=p, type_=type_))
self.UP_EVENT = UpEvent(
title=title,
pool_img="",
start_time=start_time,
end_time=end_time,
up_char=up_chars,
)
self.dump_up_char()
except Exception as e:
logger.warning(f"{self.game_name_cn}UP更新出错 {type(e)}:{e}")
async def _reload_pool(self) -> Optional[Message]:
await self.update_up_char()
self.load_up_char()
if self.UP_EVENT:
return Message(f"重载成功!\n当前活动:{self.UP_EVENT.title}")
+298
View File
@@ -0,0 +1,298 @@
import math
import random
import aiohttp
import asyncio
import aiofiles
from PIL import Image
from datetime import datetime
from pydantic import BaseModel, Extra
from asyncio.exceptions import TimeoutError
from typing import Dict, List, Optional, TypeVar, Generic, Tuple
from nonebot.adapters.onebot.v11 import Message
from configs.path_config import IMAGE_PATH
from utils.message_builder import image
from nonebot.log import logger
import asyncio
try:
import ujson as json
except ModuleNotFoundError:
import json
from utils.image_utils import BuildImage
from ..config import DRAW_PATH, draw_config
from ..util import cn2py, circled_number
class BaseData(BaseModel, extra=Extra.ignore):
name: str # 名字
star: int # 星级
limited: bool # 限定
def __eq__(self, other: "BaseData"):
return self.name == other.name
def __hash__(self):
return hash(self.name)
@property
def star_str(self) -> str:
return "".join(["★" for _ in range(self.star)])
class UpChar(BaseData):
zoom: float # up提升倍率
class UpEvent(BaseModel):
title: str # up池标题
pool_img: str # up池封面
start_time: Optional[datetime] # 开始时间
end_time: Optional[datetime] # 结束时间
up_char: List[UpChar] # up对象
TC = TypeVar("TC", bound="BaseData")
class BaseHandle(Generic[TC]):
def __init__(self, game_name: str, game_name_cn: str, game_card_color: str = "#ffffff"):
self.game_name = game_name
self.game_name_cn = game_name_cn
self.max_star = 1 # 最大星级
self.data_path = DRAW_PATH
self.img_path = IMAGE_PATH / f"draw_card/{self.game_name}"
self.up_path = DRAW_PATH / "draw_card_up"
self.img_path.mkdir(parents=True, exist_ok=True)
self.up_path.mkdir(parents=True, exist_ok=True)
self.data_files: List[str] = [f"{self.game_name}.json"]
self.game_card_color: str = game_card_color
async def draw(self, count: int, **kwargs) -> Message:
return await asyncio.get_event_loop().run_in_executor(None, self._draw, count)
def _draw(self, count: int, **kwargs) -> Message:
index2card = self.get_cards(count, **kwargs)
cards = [card[0] for card in index2card]
result = self.format_result(index2card)
return image(b64=self.generate_img(cards).pic2bs4()) + result
# 抽取卡池
def get_card(self, **kwargs) -> TC:
raise NotImplementedError
def get_cards(self, count: int, **kwargs) -> List[Tuple[TC, int]]:
return [(self.get_card(**kwargs), i) for i in range(count)]
# 获取星级
@staticmethod
def get_star(star_list: List[int], probability_list: List[float]) -> int:
return random.choices(star_list, weights=probability_list, k=1)[0]
def format_result(self, index2card: List[Tuple[TC, int]], **kwargs) -> str:
card_list = [card[0] for card in index2card]
results = [
self.format_star_result(card_list, **kwargs),
self.format_max_star(index2card, **kwargs),
self.format_max_card(card_list, **kwargs),
]
results = [rst for rst in results if rst]
return "\n".join(results)
def format_star_result(self, card_list: List[TC], **kwargs) -> str:
star_dict: Dict[str, int] = {} # 记录星级及其次数
card_list_sorted = sorted(card_list, key=lambda c: c.star, reverse=True)
for card in card_list_sorted:
try:
star_dict[card.star_str] += 1
except KeyError:
star_dict[card.star_str] = 1
rst = ""
for star_str, count in star_dict.items():
rst += f"[{star_str}×{count}] "
return rst.strip()
def format_max_star(
self, card_list: List[Tuple[TC, int]], up_list: List[str] = [], **kwargs
) -> str:
up_list = up_list or kwargs.get("up_list", [])
rst = ""
for card, index in card_list:
if card.star == self.max_star:
if card.name in up_list:
rst += f"第 {index} 抽获取UP {card.name}\n"
else:
rst += f"第 {index} 抽获取 {card.name}\n"
return rst.strip()
def format_max_card(self, card_list: List[TC], **kwargs) -> str:
card_dict: Dict[TC, int] = {} # 记录卡牌抽取次数
for card in card_list:
try:
card_dict[card] += 1
except KeyError:
card_dict[card] = 1
max_count = max(card_dict.values())
max_card = list(card_dict.keys())[list(card_dict.values()).index(max_count)]
if max_count <= 1:
return ""
return f"抽取到最多的是{max_card.name},共抽取了{max_count}次"
def generate_img(
self,
cards: List[TC],
num_per_line: int = 5,
max_per_line: Tuple[int, int] = (40, 10),
) -> BuildImage:
"""
生成统计图片
:param cards: 卡牌列表
:param num_per_line: 单行角色显示数量
:param max_per_line: 当card_list超过一定数值时,更改单行数量
"""
if len(cards) > max_per_line[0]:
num_per_line = max_per_line[1]
if len(cards) > 90:
card_dict: Dict[TC, int] = {} # 记录卡牌抽取次数
for card in cards:
try:
card_dict[card] += 1
except KeyError:
card_dict[card] = 1
card_list = list(card_dict)
num_list = list(card_dict.values())
else:
card_list = cards
num_list = [1] * len(cards)
card_imgs: List[BuildImage] = []
for card, num in zip(card_list, num_list):
card_img = self.generate_card_img(card)
# 数量 > 1 时加数字上标
if num > 1:
label = circled_number(num)
label_w = int(min(card_img.w, card_img.h) / 7)
label = label.resize(
(
int(label_w * label.width / label.height),
label_w,
),
Image.ANTIALIAS,
)
card_img.paste(label, alpha=True)
card_imgs.append(card_img)
img_w = card_imgs[0].w
img_h = card_imgs[0].h
if len(card_imgs) < num_per_line:
w = img_w * len(card_imgs)
else:
w = img_w * num_per_line
h = img_h * math.ceil(len(card_imgs) / num_per_line)
img = BuildImage(w, h, img_w, img_h, color=self.game_card_color)
for card_img in card_imgs:
img.paste(card_img)
return img
def generate_card_img(self, card: TC) -> BuildImage:
img = str(self.img_path / f"{cn2py(card.name)}.png")
return BuildImage(100, 100, background=img)
def load_data(self, filename: str = "") -> dict:
if not filename:
filename = f"{self.game_name}.json"
filepath = self.data_path / filename
if not filepath.exists():
return {}
with filepath.open("r", encoding="utf8") as f:
return json.load(f)
def dump_data(self, data: dict, filename: str = ""):
if not filename:
filename = f"{self.game_name}.json"
filepath = self.data_path / filename
with filepath.open("w", encoding="utf8") as f:
json.dump(data, f, ensure_ascii=False, indent=4)
def data_exists(self) -> bool:
for file in self.data_files:
if not (self.data_path / file).exists():
return False
return True
def _init_data(self):
raise NotImplementedError
def init_data(self):
try:
self._init_data()
except Exception as e:
logger.warning(f"{self.game_name_cn} 导入角色数据错误:{type(e)}:{e}")
async def _update_info(self):
raise NotImplementedError
def client(self) -> aiohttp.ClientSession:
headers = {
"User-Agent": '"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)"'
}
return aiohttp.ClientSession(headers=headers)
async def update_info(self):
try:
async with asyncio.Semaphore(draw_config.SEMAPHORE):
async with self.client() as session:
self.session = session
await self._update_info()
except Exception as e:
logger.warning(f"{self.game_name_cn} 更新数据错误:{type(e)}:{e}")
self.init_data()
async def get_url(self, url: str) -> str:
result = ""
retry = 5
for i in range(retry):
try:
async with self.session.get(url, timeout=10) as response:
result = await response.text()
break
except TimeoutError:
logger.warning(f"访问 {url} 超时, 重试 {i + 1}/{retry}")
await asyncio.sleep(1)
return result
async def download_img(self, url: str, name: str) -> bool:
img_path = self.img_path / f"{cn2py(name)}.png"
if img_path.exists():
return True
try:
async with self.session.get(url, timeout=10) as response:
async with aiofiles.open(str(img_path), "wb") as f:
await f.write(await response.read())
return True
except TimeoutError:
logger.warning(f"下载 {self.game_name_cn} 图片超时,名称:{name},url:{url}")
return False
except:
logger.warning(f"下载 {self.game_name_cn} 链接错误,名称:{name},url:{url}")
return False
async def _reload_pool(self) -> Optional[Message]:
return None
async def reload_pool(self) -> Optional[Message]:
try:
async with self.client() as session:
self.session = session
return await self._reload_pool()
except Exception as e:
logger.warning(f"{self.game_name_cn} 重载UP池错误:{type(e)}:{e}")
def reset_count(self, user_id: int) -> bool:
return False
+221
View File
@@ -0,0 +1,221 @@
import random
from lxml import etree
from typing import List, Tuple
from PIL import ImageDraw
from nonebot.log import logger
try:
import ujson as json
except ModuleNotFoundError:
import json
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 FgoData(BaseData):
pass
class FgoChar(FgoData):
pass
class FgoCard(FgoData):
pass
class FgoHandle(BaseHandle[FgoData]):
def __init__(self):
super().__init__("fgo", "命运-冠位指定")
self.data_files.append("fgo_card.json")
self.max_star = 5
self.config = draw_config.fgo
self.ALL_CHAR: List[FgoChar] = []
self.ALL_CARD: List[FgoCard] = []
def get_card(self, mode: int = 1) -> FgoData:
if mode == 1:
star = self.get_star(
[8, 7, 6, 5, 4, 3],
[
self.config.FGO_SERVANT_FIVE_P,
self.config.FGO_SERVANT_FOUR_P,
self.config.FGO_SERVANT_THREE_P,
self.config.FGO_CARD_FIVE_P,
self.config.FGO_CARD_FOUR_P,
self.config.FGO_CARD_THREE_P,
],
)
elif mode == 2:
star = self.get_star(
[5, 4], [self.config.FGO_CARD_FIVE_P, self.config.FGO_CARD_FOUR_P]
)
else:
star = self.get_star(
[8, 7, 6],
[
self.config.FGO_SERVANT_FIVE_P,
self.config.FGO_SERVANT_FOUR_P,
self.config.FGO_SERVANT_THREE_P,
],
)
if star > 5:
star -= 3
chars = [x for x in self.ALL_CHAR if x.star == star and not x.limited]
else:
chars = [x for x in self.ALL_CARD if x.star == star and not x.limited]
return random.choice(chars)
def get_cards(self, count: int, **kwargs) -> List[Tuple[FgoData, int]]:
card_list = [] # 获取所有角色
servant_count = 0 # 保底计算
card_count = 0 # 保底计算
for i in range(count):
servant_count += 1
card_count += 1
if card_count == 9: # 四星卡片保底
mode = 2
elif servant_count == 10: # 三星从者保底
mode = 3
else: # 普通抽
mode = 1
card = self.get_card(mode)
if isinstance(card, FgoCard) and card.star > self.max_star - 2:
card_count = 0
if isinstance(card, FgoChar):
servant_count = 0
card_list.append((card, i + 1))
return card_list
def generate_card_img(self, card: FgoData) -> BuildImage:
sep_w = 5
sep_t = 5
sep_b = 20
w = 128
h = 140
bg = BuildImage(w + sep_w * 2, h + sep_t + sep_b)
img_path = str(self.img_path / f"{cn2py(card.name)}.png")
img = BuildImage(w, h, background=img_path)
bg.paste(img, (sep_w, sep_t), alpha=True)
# 加名字
text = card.name[:6] + "..." if len(card.name) > 7 else card.name
font = load_font(fontsize=16)
text_w, text_h = font.getsize(text)
draw = ImageDraw.Draw(bg.markImg)
draw.text(
(sep_w + (w - text_w) / 2, h + sep_t + (sep_b - text_h) / 2),
text,
font=font,
fill="gray",
)
return bg
def _init_data(self):
self.ALL_CHAR = [
FgoChar(
name=value["名称"],
star=int(value["星级"]),
limited=True
if not ("圣晶石召唤" in value["入手方式"] or "圣晶石召唤(Story卡池)" in value["入手方式"])
else False,
)
for value in self.load_data().values()
]
self.ALL_CARD = [
FgoCard(name=value["名称"], star=int(value["星级"]), limited=False)
for value in self.load_data("fgo_card.json").values()
]
async def _update_info(self):
# fgo.json
fgo_info = {}
for i in range(500):
url = f"http://fgo.vgtime.com/servant/ajax?card=&wd=&ids=&sort=12777&o=desc&pn={i}"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} page {i} 出错")
continue
fgo_data = json.loads(result)
if int(fgo_data["nums"]) <= 0:
break
for x in fgo_data["data"]:
name = remove_prohibited_str(x["name"])
member_dict = {
"id": x["id"],
"card_id": x["charid"],
"头像": x["icon"],
"名称": remove_prohibited_str(x["name"]),
"职阶": x["classes"],
"星级": int(x["star"]),
"hp": x["lvmax4hp"],
"atk": x["lvmax4atk"],
"card_quick": x["cardquick"],
"card_arts": x["cardarts"],
"card_buster": x["cardbuster"],
"宝具": x["tprop"],
}
fgo_info[name] = member_dict
# 更新额外信息
for key in fgo_info.keys():
url = f'http://fgo.vgtime.com/servant/{fgo_info[key]["id"]}'
result = await self.get_url(url)
if not result:
fgo_info[key]["入手方式"] = ["圣晶石召唤"]
logger.warning(f"{self.game_name_cn} 获取额外信息错误 {key}")
continue
try:
dom = etree.HTML(result, etree.HTMLParser())
obtain = dom.xpath(
"//table[contains(string(.),'入手方式')]/tr[8]/td[3]/text()"
)[0]
obtain = str(obtain).strip()
if "限时活动免费获取 活动结束后无法获得" in obtain:
obtain = ["活动获取"]
elif "非限时UP无法获得" in obtain:
obtain = ["限时召唤"]
else:
if "&" in obtain:
obtain = obtain.split("&")
else:
obtain = obtain.split(" ")
obtain = [s.strip() for s in obtain if s.strip()]
fgo_info[key]["入手方式"] = obtain
except IndexError:
fgo_info[key]["入手方式"] = ["圣晶石召唤"]
logger.warning(f"{self.game_name_cn} 获取额外信息错误 {key}")
self.dump_data(fgo_info)
logger.info(f"{self.game_name_cn} 更新成功")
# fgo_card.json
fgo_card_info = {}
for i in range(500):
url = f"http://fgo.vgtime.com/equipment/ajax?wd=&ids=&sort=12958&o=desc&pn={i}"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn}卡牌 page {i} 出错")
continue
fgo_data = json.loads(result)
if int(fgo_data["nums"]) <= 0:
break
for x in fgo_data["data"]:
name = remove_prohibited_str(x["name"])
member_dict = {
"id": x["id"],
"card_id": x["equipid"],
"头像": x["icon"],
"名称": name,
"星级": int(x["star"]),
"hp": x["lvmax_hp"],
"atk": x["lvmax_atk"],
"skill_e": str(x["skill_e"]).split("<br />")[:-1],
}
fgo_card_info[name] = member_dict
self.dump_data(fgo_card_info, "fgo_card.json")
logger.info(f"{self.game_name_cn} 卡牌更新成功")
# 下载头像
for value in fgo_info.values():
await self.download_img(value["头像"], value["名称"])
for value in fgo_card_info.values():
await self.download_img(value["头像"], value["名称"])
+429
View File
@@ -0,0 +1,429 @@
import random
import dateparser
from lxml import etree
from PIL import Image, ImageDraw
from urllib.parse import unquote
from typing import List, Optional, Tuple
from pydantic import ValidationError
from datetime import datetime, timedelta
from nonebot.adapters.onebot.v11 import Message
from utils.message_builder import image
from nonebot.log import logger
import asyncio
try:
import ujson as json
except ModuleNotFoundError:
import json
from .base_handle import BaseHandle, BaseData, UpChar, UpEvent
from ..config import draw_config
from ..count_manager import GenshinCountManager
from ..util import remove_prohibited_str, cn2py, load_font
from utils.image_utils import BuildImage
class GenshinData(BaseData):
pass
class GenshinChar(GenshinData):
pass
class GenshinArms(GenshinData):
pass
class GenshinHandle(BaseHandle[GenshinData]):
def __init__(self):
super().__init__("genshin", "原神", "#ebebeb")
self.data_files.append("genshin_arms.json")
self.max_star = 5
self.config = draw_config.genshin
self.ALL_CHAR: List[GenshinData] = []
self.ALL_ARMS: List[GenshinData] = []
self.UP_CHAR: Optional[UpEvent] = None
self.UP_ARMS: Optional[UpEvent] = None
self.count_manager = GenshinCountManager((10, 90), ("4", "5"), 180)
# 抽取卡池
def get_card(
self, pool_name: str, mode: int = 1, add: float = 0.0, is_up: bool = False
):
"""
mode 1:普通抽 2:四星保底 3:五星保底
"""
if mode == 1:
star = self.get_star(
[5, 4, 3],
[
self.config.GENSHIN_FIVE_P + add,
self.config.GENSHIN_FOUR_P,
self.config.GENSHIN_THREE_P,
],
)
elif mode == 2:
star = self.get_star(
[5, 4],
[self.config.GENSHIN_G_FIVE_P + add, self.config.GENSHIN_G_FOUR_P],
)
else:
star = 5
if pool_name == "char":
up_event = self.UP_CHAR
all_list = self.ALL_CHAR + [
x for x in self.ALL_ARMS if x.star == star and x.star < 5
]
elif pool_name == "arms":
up_event = self.UP_ARMS
all_list = self.ALL_ARMS + [
x for x in self.ALL_CHAR if x.star == star and x.star < 5
]
else:
up_event = None
all_list = self.ALL_ARMS + self.ALL_CHAR
acquire_char = None
# 是否UP
if up_event and star > 3:
# 获取up角色列表
up_list = [x.name for x in up_event.up_char if x.star == star]
# 成功获取up角色
if random.random() < 0.5 or is_up:
up_name = random.choice(up_list)
try:
acquire_char = [x for x in all_list if x.name == up_name][0]
except IndexError:
pass
if not acquire_char:
chars = [x for x in all_list if x.star == star and not x.limited]
acquire_char = random.choice(chars)
return acquire_char
def get_cards(
self, count: int, user_id: int, pool_name: str
) -> List[Tuple[GenshinData, int]]:
card_list = [] # 获取角色列表
add = 0.0
count_manager = self.count_manager
count_manager.check_timeout(user_id) # 检查上次抽卡次数是否超时
count_manager.check_count(user_id, count) # 检查次数累计
pool = self.UP_CHAR if pool_name == "char" else self.UP_ARMS
for i in range(count):
count_manager.increase(user_id)
star = count_manager.check(user_id) # 是否有四星或五星保底
if (
count_manager.get_user_count(user_id)
- count_manager.get_user_five_index(user_id)
) % count_manager.get_max_guarantee() >= 72:
add += draw_config.genshin.I72_ADD
if star:
if star == 4:
card = self.get_card(pool_name, 2, add=add)
else:
card = self.get_card(
pool_name, 3, add, count_manager.is_up(user_id)
)
else:
card = self.get_card(pool_name, 1, add, count_manager.is_up(user_id))
# print(f"{count_manager.get_user_count(user_id)}:",
# count_manager.get_user_five_index(user_id), star, card.star, add)
# 四星角色
if card.star == 4:
count_manager.mark_four_index(user_id)
# 五星角色
elif card.star == self.max_star:
add = 0
count_manager.mark_five_index(user_id) # 记录五星保底
count_manager.mark_four_index(user_id) # 记录四星保底
if pool and card.name in [
x.name for x in pool.up_char if x.star == self.max_star
]:
count_manager.set_is_up(user_id, True)
else:
count_manager.set_is_up(user_id, False)
card_list.append((card, count_manager.get_user_count(user_id)))
count_manager.update_time(user_id)
return card_list
def generate_card_img(self, card: GenshinData) -> BuildImage:
sep_w = 10
sep_h = 5
frame_w = 112
frame_h = 132
img_w = 106
img_h = 106
bg = BuildImage(frame_w + sep_w * 2, frame_h + sep_h * 2, color="#EBEBEB")
frame_path = str(self.img_path / "avatar_frame.png")
frame = Image.open(frame_path)
# 加名字
text = card.name
font = load_font(fontsize=14)
text_w, text_h = font.getsize(text)
draw = ImageDraw.Draw(frame)
draw.text(
((frame_w - text_w) / 2, frame_h - 15 - text_h / 2),
text,
font=font,
fill="gray",
)
img_path = str(self.img_path / f"{cn2py(card.name)}.png")
img = BuildImage(img_w, img_h, background=img_path)
if isinstance(card, GenshinArms):
# 武器卡背景不是透明的,切去上方两个圆弧
r = 12
circle = Image.new("L", (r * 2, r * 2), 0)
alpha = Image.new("L", img.size, 255)
alpha.paste(circle, (-r - 3, -r - 3)) # 左上角
alpha.paste(circle, (img_h - r + 3, -r - 3)) # 右上角
img.markImg.putalpha(alpha)
star_path = str(self.img_path / f"{card.star}_star.png")
star = Image.open(star_path)
bg.paste(frame, (sep_w, sep_h), alpha=True)
bg.paste(img, (sep_w + 3, sep_h + 3), alpha=True)
bg.paste(star, (sep_w + int((frame_w - star.width) / 2), sep_h - 6), alpha=True)
return bg
def format_pool_info(self, pool_name: str) -> str:
info = ""
up_event = None
if pool_name == "char":
up_event = self.UP_CHAR
elif pool_name == "arms":
up_event = self.UP_ARMS
if up_event:
star5_list = [x.name for x in up_event.up_char if x.star == 5]
star4_list = [x.name for x in up_event.up_char if x.star == 4]
if star5_list:
info += f"五星UP:{' '.join(star5_list)}\n"
if star4_list:
info += f"四星UP:{' '.join(star4_list)}\n"
info = f"当前up池:{up_event.title}\n{info}"
return info.strip()
async def draw(self, count: int, user_id: int, pool_name: str = "", **kwargs) -> Message:
return await asyncio.get_event_loop().run_in_executor(None, self._draw, count, user_id, pool_name)
def _draw(self, count: int, user_id: int, pool_name: str = "", **kwargs) -> Message:
index2cards = self.get_cards(count, user_id, pool_name)
cards = [card[0] for card in index2cards]
up_event = None
if pool_name == "char":
up_event = self.UP_CHAR
elif pool_name == "arms":
up_event = self.UP_ARMS
up_list = [x.name for x in up_event.up_char] if up_event else []
result = self.format_star_result(cards)
result += (
"\n" + max_star_str
if (max_star_str := self.format_max_star(index2cards, up_list=up_list))
else ""
)
result += f"\n距离保底发还剩 {self.count_manager.get_user_guarantee_count(user_id)} 抽"
# result += "\n【五星:0.6%,四星:5.1%,第72抽开始五星概率每抽加0.585%】"
pool_info = self.format_pool_info(pool_name)
img = self.generate_img(cards)
bk = BuildImage(img.w, img.h + 50, font_size=20, color="#ebebeb")
bk.paste(img)
bk.text((0, img.h + 10), "【五星:0.6%,四星:5.1%,第72抽开始五星概率每抽加0.585%】")
return pool_info + image(b64=bk.pic2bs4()) + result
def _init_data(self):
self.ALL_CHAR = [
GenshinChar(
name=value["名称"],
star=int(value["星级"]),
limited=value["常驻/限定"] == "限定UP",
)
for key, value in self.load_data().items()
if "旅行者" not in key
]
self.ALL_ARMS = [
GenshinArms(
name=value["名称"],
star=int(value["星级"]),
limited="祈愿" not in value["获取途径"],
)
for value in self.load_data("genshin_arms.json").values()
]
self.load_up_char()
def load_up_char(self):
try:
data = self.load_data(f"draw_card_up/{self.game_name}_up_char.json")
self.UP_CHAR = UpEvent.parse_obj(data.get("char", {}))
self.UP_ARMS = UpEvent.parse_obj(data.get("arms", {}))
except ValidationError:
logger.warning(f"{self.game_name}_up_char 解析出错")
def dump_up_char(self):
if self.UP_CHAR and self.UP_ARMS:
data = {
"char": json.loads(self.UP_CHAR.json()),
"arms": json.loads(self.UP_ARMS.json()),
}
self.dump_data(data, f"draw_card_up/{self.game_name}_up_char.json")
async def _update_info(self):
# genshin.json
char_info = {}
url = "https://wiki.biligame.com/ys/角色筛选"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
else:
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath("//table[@id='CardSelectTr']/tbody/tr")
for char in char_list:
try:
name = char.xpath("./td[1]/a/@title")[0]
avatar = char.xpath("./td[1]/a/img/@srcset")[0]
star = char.xpath("./td[3]/text()")[0]
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar).split(" ")[-2]),
"名称": remove_prohibited_str(name),
"星级": int(str(star).strip()[:1]),
}
char_info[member_dict["名称"]] = member_dict
# 更新额外信息
for key in char_info.keys():
result = await self.get_url(f"https://wiki.biligame.com/ys/{key}")
if not result:
char_info[key]["常驻/限定"] = "未知"
logger.warning(f"{self.game_name_cn} 获取额外信息错误 {key}")
continue
try:
dom = etree.HTML(result, etree.HTMLParser())
limit = dom.xpath(
"//table[contains(string(.),'常驻/限定')]/tbody/tr[6]/td/text()"
)[0]
char_info[key]["常驻/限定"] = str(limit).strip()
except IndexError:
char_info[key]["常驻/限定"] = "未知"
logger.warning(f"{self.game_name_cn} 获取额外信息错误 {key}")
self.dump_data(char_info)
logger.info(f"{self.game_name_cn} 更新成功")
# genshin_arms.json
arms_info = {}
url = "https://wiki.biligame.com/ys/武器图鉴"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
else:
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath("//table[@id='CardSelectTr']/tbody/tr")
for char in char_list:
try:
name = char.xpath("./td[1]/a/@title")[0]
avatar = char.xpath("./td[1]/a/img/@srcset")[0]
star = char.xpath("./td[4]/img/@alt")[0]
sources = str(char.xpath("./td[5]/text()")[0]).split(",")
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar).split(" ")[-2]),
"名称": remove_prohibited_str(name),
"星级": int(str(star).strip()[:1]),
"获取途径": [s.strip() for s in sources if s.strip()],
}
arms_info[member_dict["名称"]] = member_dict
self.dump_data(arms_info, "genshin_arms.json")
logger.info(f"{self.game_name_cn} 武器更新成功")
# 下载头像
for value in char_info.values():
await self.download_img(value["头像"], value["名称"])
for value in arms_info.values():
await self.download_img(value["头像"], value["名称"])
# 下载星星
idx = 1
YS_URL = "https://patchwiki.biligame.com/images/ys"
for url in [
"/1/13/7xzg7tgf8dsr2hjpmdbm5gn9wvzt2on.png",
"/b/bc/sd2ige6d7lvj7ugfumue3yjg8gyi0d1.png",
"/e/ec/l3mnhy56pyailhn3v7r873htf2nofau.png",
"/9/9c/sklp02ffk3aqszzvh8k1c3139s0awpd.png",
"/c/c7/qu6xcndgj6t14oxvv7yz2warcukqv1m.png",
]:
await self.download_img(YS_URL + url, f"{idx}_star")
idx += 1
# 下载头像框
await self.download_img(
YS_URL + "/2/2e/opbcst4xbtcq0i4lwerucmosawn29ti.png", f"avatar_frame"
)
await self.update_up_char()
async def update_up_char(self):
url = "https://wiki.biligame.com/ys/祈愿"
result = await self.get_url(url)
if not result:
logger.warning(f"{self.game_name_cn}获取祈愿页面出错")
return
dom = etree.HTML(result, etree.HTMLParser())
tables = dom.xpath(
"//div[@class='mw-parser-output']/div[@class='row']/div/table[@class='wikitable']/tbody"
)
if not tables or len(tables) < 2:
logger.warning(f"{self.game_name_cn}获取活动祈愿出错")
return
try:
for index, table in enumerate(tables):
title = table.xpath("./tr[1]/th/img/@title")[0]
title = str(title).split("」")[0] + "」" if "」" in title else title
pool_img = str(table.xpath("./tr[1]/th/img/@srcset")[0]).split(" ")[-2]
time = table.xpath("./tr[2]/td/text()")[0]
star5_list = table.xpath("./tr[3]/td/a/@title")
star4_list = table.xpath("./tr[4]/td/a/@title")
start, end = str(time).split("~")
start_time = dateparser.parse(start)
end_time = dateparser.parse(end)
if not start_time and end_time:
start_time = end_time - timedelta(days=20)
if start_time and end_time and start_time <= datetime.now() <= end_time:
up_event = UpEvent(
title=title,
pool_img=pool_img,
start_time=start_time,
end_time=end_time,
up_char=[
UpChar(name=name, star=5, limited=False, zoom=50)
for name in star5_list
]
+ [
UpChar(name=name, star=4, limited=False, zoom=50)
for name in star4_list
],
)
if index == 0:
self.UP_CHAR = up_event
elif index == 1:
self.UP_ARMS = up_event
if self.UP_CHAR and self.UP_ARMS:
self.dump_up_char()
logger.info(
f"成功获取{self.game_name_cn}当前up信息...当前up池: {self.UP_CHAR.title} & {self.UP_ARMS.title}"
)
except Exception as e:
logger.warning(f"{self.game_name_cn}UP更新出错 {type(e)}:{e}")
def reset_count(self, user_id: int) -> bool:
self.count_manager.reset(user_id)
return True
async def _reload_pool(self) -> Optional[Message]:
await self.update_up_char()
self.load_up_char()
if self.UP_CHAR and self.UP_ARMS:
return Message(
Message.template("重载成功!\n当前UP池子:{} & {}{:image}{:image}").format(
self.UP_CHAR.title,
self.UP_ARMS.title,
self.UP_CHAR.pool_img,
self.UP_ARMS.pool_img,
)
)
@@ -0,0 +1,400 @@
import re
import random
import dateparser
from lxml import etree
from PIL import ImageDraw
from datetime import datetime
from urllib.parse import unquote
from typing import List, Optional, Tuple
from pydantic import ValidationError
from nonebot.adapters.onebot.v11 import Message
from utils.message_builder import image
from nonebot.log import logger
import asyncio
try:
import ujson as json
except ModuleNotFoundError:
import json
from .base_handle import BaseHandle, BaseData, UpChar, UpEvent
from ..config import draw_config
from ..util import remove_prohibited_str, cn2py, load_font
from utils.image_utils import BuildImage
class GuardianData(BaseData):
pass
class GuardianChar(GuardianData):
pass
class GuardianArms(GuardianData):
pass
class GuardianHandle(BaseHandle[GuardianData]):
def __init__(self):
super().__init__("guardian", "坎公骑冠剑")
self.data_files.append("guardian_arms.json")
self.config = draw_config.guardian
self.ALL_CHAR: List[GuardianChar] = []
self.ALL_ARMS: List[GuardianArms] = []
self.UP_CHAR: Optional[UpEvent] = None
self.UP_ARMS: Optional[UpEvent] = None
def get_card(self, pool_name: str, mode: int = 1) -> GuardianData:
if pool_name == "char":
if mode == 1:
star = self.get_star(
[3, 2, 1],
[
self.config.GUARDIAN_THREE_CHAR_P,
self.config.GUARDIAN_TWO_CHAR_P,
self.config.GUARDIAN_ONE_CHAR_P,
],
)
else:
star = self.get_star(
[3, 2],
[
self.config.GUARDIAN_THREE_CHAR_P,
self.config.GUARDIAN_TWO_CHAR_P,
],
)
up_event = self.UP_CHAR
self.max_star = 3
all_data = self.ALL_CHAR
else:
if mode == 1:
star = self.get_star(
[5, 4, 3, 2],
[
self.config.GUARDIAN_FIVE_ARMS_P,
self.config.GUARDIAN_FOUR_ARMS_P,
self.config.GUARDIAN_THREE_ARMS_P,
self.config.GUARDIAN_TWO_ARMS_P,
],
)
else:
star = self.get_star(
[5, 4],
[
self.config.GUARDIAN_FIVE_ARMS_P,
self.config.GUARDIAN_FOUR_ARMS_P,
],
)
up_event = self.UP_ARMS
self.max_star = 5
all_data = self.ALL_ARMS
acquire_char = None
# 是否UP
if up_event and star == self.max_star and pool_name:
# 获取up角色列表
up_list = [x.name for x in up_event.up_char if x.star == star]
# 成功获取up角色
if random.random() < 0.5:
up_name = random.choice(up_list)
try:
acquire_char = [x for x in all_data if x.name == up_name][0]
except IndexError:
pass
if not acquire_char:
chars = [x for x in all_data if x.star == star and not x.limited]
acquire_char = random.choice(chars)
return acquire_char
def get_cards(self, count: int, pool_name: str) -> List[Tuple[GuardianData, int]]:
card_list = []
card_count = 0 # 保底计算
for i in range(count):
card_count += 1
# 十连保底
if card_count == 10:
card = self.get_card(pool_name, 2)
card_count = 0
else:
card = self.get_card(pool_name, 1)
if card.star > self.max_star - 2:
card_count = 0
card_list.append((card, i + 1))
return card_list
def format_pool_info(self, pool_name: str) -> str:
info = ""
up_event = self.UP_CHAR if pool_name == "char" else self.UP_ARMS
if up_event:
if pool_name == "char":
up_list = [x.name for x in up_event.up_char if x.star == 3]
info += f'三星UP:{" ".join(up_list)}\n'
else:
up_list = [x.name for x in up_event.up_char if x.star == 5]
info += f'五星UP:{" ".join(up_list)}\n'
info = f"当前up池:{up_event.title}\n{info}"
return info.strip()
async def draw(self, count: int, pool_name: str, **kwargs) -> Message:
return await asyncio.get_event_loop().run_in_executor(None, self._draw, count, pool_name)
def _draw(self, count: int, pool_name: str, **kwargs) -> Message:
index2card = self.get_cards(count, pool_name)
cards = [card[0] for card in index2card]
up_event = self.UP_CHAR if pool_name == "char" else self.UP_ARMS
up_list = [x.name for x in up_event.up_char] if up_event else []
result = self.format_result(index2card, up_list=up_list)
pool_info = self.format_pool_info(pool_name)
return pool_info + image(b64=self.generate_img(cards).pic2bs4()) + result
def generate_card_img(self, card: GuardianData) -> BuildImage:
sep_w = 1
sep_h = 1
block_w = 170
block_h = 90
img_w = 90
img_h = 90
if isinstance(card, GuardianChar):
block_color = "#2e2923"
font_color = "#e2ccad"
star_w = 90
star_h = 30
star_name = f"{card.star}_star.png"
frame_path = ""
else:
block_color = "#EEE4D5"
font_color = "#A65400"
star_w = 45
star_h = 45
star_name = f"{card.star}_star_rank.png"
frame_path = str(self.img_path / "avatar_frame.png")
bg = BuildImage(block_w + sep_w * 2, block_h + sep_h * 2, color="#F6F4ED")
block = BuildImage(block_w, block_h, color=block_color)
star_path = str(self.img_path / star_name)
star = BuildImage(star_w, star_h, background=star_path)
img_path = str(self.img_path / f"{cn2py(card.name)}.png")
img = BuildImage(img_w, img_h, background=img_path)
block.paste(img, (0, 0), alpha=True)
if frame_path:
frame = BuildImage(img_w, img_h, background=frame_path)
block.paste(frame, (0, 0), alpha=True)
block.paste(
star,
(int((block_w + img_w - star_w) / 2), block_h - star_h - 30),
alpha=True,
)
# 加名字
text = card.name[:4] + "..." if len(card.name) > 5 else card.name
font = load_font(fontsize=14)
text_w, _ = font.getsize(text)
draw = ImageDraw.Draw(block.markImg)
draw.text(
((block_w + img_w - text_w) / 2, 55),
text,
font=font,
fill=font_color,
)
bg.paste(block, (sep_w, sep_h))
return bg
def _init_data(self):
self.ALL_CHAR = [
GuardianChar(name=value["名称"], star=int(value["星级"]), limited=False)
for value in self.load_data().values()
]
self.ALL_ARMS = [
GuardianArms(name=value["名称"], star=int(value["星级"]), limited=False)
for value in self.load_data("guardian_arms.json").values()
]
self.load_up_char()
def load_up_char(self):
try:
data = self.load_data(f"draw_card_up/{self.game_name}_up_char.json")
self.UP_CHAR = UpEvent.parse_obj(data.get("char", {}))
self.UP_ARMS = UpEvent.parse_obj(data.get("arms", {}))
except ValidationError:
logger.warning(f"{self.game_name}_up_char 解析出错")
def dump_up_char(self):
if self.UP_CHAR and self.UP_ARMS:
data = {
"char": json.loads(self.UP_CHAR.json()),
"arms": json.loads(self.UP_ARMS.json()),
}
self.dump_data(data, f"draw_card_up/{self.game_name}_up_char.json")
async def _update_info(self):
# guardian.json
guardian_info = {}
url = "https://wiki.biligame.com/gt/英雄筛选表"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
else:
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath("//table[@id='CardSelectTr']/tbody/tr")
for char in char_list:
try:
name = char.xpath("./td[1]/a/@title")[0]
avatar = char.xpath("./td[1]/a/img/@src")[0]
star = char.xpath("./td[1]/span/img/@alt")[0]
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar)),
"名称": remove_prohibited_str(name),
"星级": int(str(star).split(" ")[0].replace("Rank", "")),
}
guardian_info[member_dict["名称"]] = member_dict
self.dump_data(guardian_info)
logger.info(f"{self.game_name_cn} 更新成功")
# guardian_arms.json
guardian_arms_info = {}
url = "https://wiki.biligame.com/gt/武器"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 武器出错")
else:
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath(
"//div[@class='resp-tabs-container']/div[1]/div/table[2]/tbody/tr"
)
for char in char_list:
try:
name = char.xpath("./td[2]/a/@title")[0]
avatar = char.xpath("./td[1]/div/div/div/a/img/@src")[0]
star = char.xpath("./td[3]/text()")[0]
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar)),
"名称": remove_prohibited_str(name),
"星级": int(str(star).strip()),
}
guardian_arms_info[member_dict["名称"]] = member_dict
self.dump_data(guardian_arms_info, "guardian_arms.json")
logger.info(f"{self.game_name_cn} 武器更新成功")
url = "https://wiki.biligame.com/gt/盾牌"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 盾牌出错")
else:
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath(
"//div[@class='resp-tabs-container']/div[2]/div/table[1]/tbody/tr"
)
for char in char_list:
try:
name = char.xpath("./td[2]/a/@title")[0]
avatar = char.xpath("./td[1]/div/div/div/a/img/@src")[0]
star = char.xpath("./td[3]/text()")[0]
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar)),
"名称": remove_prohibited_str(name),
"星级": int(str(star).strip()),
}
guardian_arms_info[member_dict["名称"]] = member_dict
self.dump_data(guardian_arms_info, "guardian_arms.json")
logger.info(f"{self.game_name_cn} 盾牌更新成功")
# 下载头像
for value in guardian_info.values():
await self.download_img(value["头像"], value["名称"])
for value in guardian_arms_info.values():
await self.download_img(value["头像"], value["名称"])
# 下载星星
idx = 1
GT_URL = "https://patchwiki.biligame.com/images/gt"
for url in [
"/4/4b/ardr3bi2yf95u4zomm263tc1vke6i3i.png",
"/5/55/6vow7lh76gzus6b2g9cfn325d1sugca.png",
"/b/b9/du8egrd2vyewg0cuyra9t8jh0srl0ds.png",
]:
await self.download_img(GT_URL + url, f"{idx}_star")
idx += 1
# 另一种星星
idx = 1
for url in [
"/6/66/4e2tfa9kvhfcbikzlyei76i9crva145.png",
"/1/10/r9ihsuvycgvsseyneqz4xs22t53026m.png",
"/7/7a/o0k86ru9k915y04azc26hilxead7xp1.png",
"/c/c9/rxz99asysz0rg391j3b02ta09mnpa7v.png",
"/2/2a/sfxz0ucv1s6ewxveycz9mnmrqs2rw60.png",
]:
await self.download_img(GT_URL + url, f"{idx}_star_rank")
idx += 1
# 头像框
await self.download_img(
GT_URL + "/8/8e/ogbqslbhuykjhnc8trtoa0p0nhfzohs.png", f"avatar_frame"
)
await self.update_up_char()
async def update_up_char(self):
url = "https://wiki.biligame.com/gt/首页"
result = await self.get_url(url)
if not result:
logger.warning(f"{self.game_name_cn}获取公告出错")
return
try:
dom = etree.HTML(result, etree.HTMLParser())
announcement = dom.xpath(
"//div[@class='mw-parser-output']/div/div[3]/div[2]/div/div[2]/div[3]"
)[0]
title = announcement.xpath("./font/p/b/text()")[0]
match = re.search(r"从(.*?)开始.*?至(.*?)结束", title)
if not match:
logger.warning(f"{self.game_name_cn}找不到UP时间")
return
start, end = match.groups()
start_time = dateparser.parse(start.replace("月", "/").replace("日", ""))
end_time = dateparser.parse(end.replace("月", "/").replace("日", ""))
if not (start_time and end_time) or not (
start_time <= datetime.now() <= end_time
):
return
divs = announcement.xpath("./font/div")
char_index = 0
arms_index = 0
for index, div in enumerate(divs):
if div.xpath("string(.)") == "角色":
char_index = index
elif div.xpath("string(.)") == "武器":
arms_index = index
chars = divs[char_index + 1 : arms_index]
arms = divs[arms_index + 1 :]
up_chars = []
up_arms = []
for char in chars:
name = char.xpath("./p/a/@title")[0]
up_chars.append(UpChar(name=name, star=3, limited=False, zoom=0))
for arm in arms:
name = arm.xpath("./p/a/@title")[0]
up_arms.append(UpChar(name=name, star=5, limited=False, zoom=0))
self.UP_CHAR = UpEvent(
title=title,
pool_img="",
start_time=start_time,
end_time=end_time,
up_char=up_chars,
)
self.UP_ARMS = UpEvent(
title=title,
pool_img="",
start_time=start_time,
end_time=end_time,
up_char=up_arms,
)
self.dump_up_char()
logger.info(f"成功获取{self.game_name_cn}当前up信息...当前up池: {title}")
except Exception as e:
logger.warning(f"{self.game_name_cn}UP更新出错 {type(e)}:{e}")
async def _reload_pool(self) -> Optional[Message]:
await self.update_up_char()
self.load_up_char()
if self.UP_CHAR and self.UP_ARMS:
return Message(f"重载成功!\n当前UP池子:{self.UP_CHAR.title}")
+179
View File
@@ -0,0 +1,179 @@
import random
from lxml import etree
from typing import List, Tuple
from nonebot.log import logger
from PIL import Image, ImageDraw
from PIL.Image import Image as IMG
try:
import ujson as json
except ModuleNotFoundError:
import json
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 OnmyojiChar(BaseData):
@property
def star_str(self) -> str:
return ["N", "R", "SR", "SSR", "SP"][self.star - 1]
class OnmyojiHandle(BaseHandle[OnmyojiChar]):
def __init__(self):
super().__init__("onmyoji", "阴阳师")
self.max_star = 5
self.config = draw_config.onmyoji
self.ALL_CHAR: List[OnmyojiChar] = []
def get_card(self, **kwargs) -> OnmyojiChar:
star = self.get_star(
[5, 4, 3, 2],
[
self.config.ONMYOJI_SP,
self.config.ONMYOJI_SSR,
self.config.ONMYOJI_SR,
self.config.ONMYOJI_R,
],
)
chars = [x for x in self.ALL_CHAR if x.star == star and not x.limited]
return random.choice(chars)
def format_max_star(self, card_list: List[Tuple[OnmyojiChar, int]]) -> str:
rst = ""
for card, index in card_list:
if card.star == self.max_star:
rst += f"第 {index} 抽获取SP {card.name}\n"
elif card.star == self.max_star - 1:
rst += f"第 {index} 抽获取SSR {card.name}\n"
return rst.strip()
@staticmethod
def star_label(star: int) -> IMG:
text, color1, color2 = [
("N", "#7E7E82", "#F5F6F7"),
("R", "#014FA8", "#37C6FD"),
("SR", "#6E0AA4", "#E94EFD"),
("SSR", "#E5511D", "#FAF905"),
("SP", "#FA1F2D", "#FFBBAF"),
][star - 1]
w = 200
h = 110
# 制作渐变色图片
base = Image.new("RGBA", (w, h), color1)
top = Image.new("RGBA", (w, h), color2)
mask = Image.new("L", (w, h))
mask_data = []
for y in range(h):
mask_data.extend([int(255 * (y / h))] * w)
mask.putdata(mask_data)
base.paste(top, (0, 0), mask)
# 透明图层
font = load_font("gorga.otf", 100)
alpha = Image.new("L", (w, h))
draw = ImageDraw.Draw(alpha)
draw.text((20, -30), text, fill="white", font=font)
base.putalpha(alpha)
# stroke
bg = Image.new("RGBA", (w, h))
draw = ImageDraw.Draw(bg)
draw.text(
(20, -30),
text,
font=font,
fill="gray",
stroke_width=3,
stroke_fill="gray",
)
bg.paste(base, (0, 0), base)
return bg
def generate_img(self, card_list: List[OnmyojiChar]) -> BuildImage:
return super().generate_img(card_list, num_per_line=10)
def generate_card_img(self, card: OnmyojiChar) -> BuildImage:
bg = BuildImage(73, 240, color="#F1EFE9")
img_path = str(self.img_path / f"{cn2py(card.name)}_mark_btn.png")
img = BuildImage(0, 0, background=img_path)
img = Image.open(img_path).convert("RGBA")
label = self.star_label(card.star).resize((60, 33), Image.ANTIALIAS)
bg.paste(img, (0, 0), alpha=True)
bg.paste(label, (0, 135), alpha=True)
font = load_font("msyh.ttf", 16)
draw = ImageDraw.Draw(bg.markImg)
text = "\n".join([t for t in card.name[:4]])
_, text_h = font.getsize_multiline(text, spacing=0)
draw.text(
(40, 150 + (90 - text_h) / 2), text, font=font, fill="gray", spacing=0
)
return bg
def _init_data(self):
self.ALL_CHAR = [
OnmyojiChar(
name=value["名称"],
star=["N", "R", "SR", "SSR", "SP"].index(value["星级"]) + 1,
limited=True
if key
in [
"奴良陆生",
"卖药郎",
"鬼灯",
"阿香",
"蜜桃&芥子",
"犬夜叉",
"杀生丸",
"桔梗",
"朽木露琪亚",
"黑崎一护",
"灶门祢豆子",
"灶门炭治郎",
]
else False,
)
for key, value in self.load_data().items()
]
async def _update_info(self):
info = {}
url = "https://yys.res.netease.com/pc/zt/20161108171335/js/app/all_shishen.json?v74="
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
return
data = json.loads(result)
for x in data:
name = remove_prohibited_str(x["name"])
member_dict = {
"id": x["id"],
"名称": name,
"星级": x["level"],
}
info[name] = member_dict
# logger.info(f"{name} is update...")
# 更新头像
for key in info.keys():
url = f'https://yys.163.com/shishen/{info[key]["id"]}.html'
result = await self.get_url(url)
if not result:
info[key]["头像"] = ""
continue
try:
dom = etree.HTML(result, etree.HTMLParser())
avatar = dom.xpath("//div[@class='pic_wrap']/img/@src")[0]
avatar = "https:" + avatar
info[key]["头像"] = avatar
except IndexError:
info[key]["头像"] = ""
logger.warning(f"{self.game_name_cn} 获取头像错误 {key}")
self.dump_data(info)
logger.info(f"{self.game_name_cn} 更新成功")
# 下载头像
for value in info.values():
await self.download_img(value["头像"], value["名称"])
# 下载书签形式的头像
url = f"https://yys.res.netease.com/pc/zt/20161108171335/data/mark_btn/{value['id']}.png"
await self.download_img(url, value["名称"] + "_mark_btn")
+147
View File
@@ -0,0 +1,147 @@
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 PcrChar(BaseData):
pass
class PcrHandle(BaseHandle[PcrChar]):
def __init__(self):
super().__init__("pcr", "公主连结")
self.max_star = 3
self.config = draw_config.pcr
self.ALL_CHAR: List[PcrChar] = []
def get_card(self, mode: int = 1) -> PcrChar:
if mode == 2:
star = self.get_star(
[3, 2], [self.config.PCR_G_THREE_P, self.config.PCR_G_TWO_P]
)
else:
star = self.get_star(
[3, 2, 1],
[self.config.PCR_THREE_P, self.config.PCR_TWO_P, self.config.PCR_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[PcrChar, 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: PcrChar) -> BuildImage:
sep_w = 5
sep_h = 5
star_h = 15
img_w = 90
img_h = 90
font_h = 20
bg = BuildImage(img_w + sep_w * 2, img_h + font_h + sep_h * 2, color="#EFF2F5")
star_path = str(self.img_path / "star.png")
star = BuildImage(star_h, star_h, background=star_path)
img_path = str(self.img_path / f"{cn2py(card.name)}.png")
img = BuildImage(img_w, img_h, background=img_path)
bg.paste(img, (sep_w, sep_h), alpha=True)
for i in range(card.star):
bg.paste(star, (sep_w + img_w - star_h * (i + 1), sep_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 = [
PcrChar(
name=value["名称"],
star=int(value["星级"]),
limited=True if "(" in key else False,
)
for key, value in self.load_data().items()
]
async def _update_info(self):
info = {}
if draw_config.PCR_TAI:
url = "https://wiki.biligame.com/pcr/角色图鉴"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
return
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath(
"//div[@class='resp-tab-content']/div[@class='unit-icon']"
)
for char in char_list:
try:
name = char.xpath("./a/@title")[0]
avatar = char.xpath("./a/img/@srcset")[0]
star = len(char.xpath("./div[1]/img"))
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar).split(" ")[-2]),
"名称": remove_prohibited_str(name),
"星级": star,
}
info[member_dict["名称"]] = member_dict
else:
url = "https://wiki.biligame.com/pcr/角色筛选表"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
return
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath("//table[@id='CardSelectTr']/tbody/tr")
for char in char_list:
try:
name = char.xpath("./td[1]/a/@title")[0]
avatar = char.xpath("./td[1]/a/img/@srcset")[0]
star = char.xpath("./td[4]/text()")[0]
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar).split(" ")[-2]),
"名称": remove_prohibited_str(name),
"星级": int(str(star).strip()),
}
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/pcr/0/02/s75ys2ecqhu2xbdw1wf1v9ccscnvi5g.png",
"star",
)
+426
View File
@@ -0,0 +1,426 @@
import re
import random
import dateparser
from lxml import etree
from PIL import ImageDraw
from bs4 import BeautifulSoup
from datetime import datetime
from urllib.parse import unquote
from typing import List, Optional, Tuple
from pydantic import ValidationError
from nonebot.adapters.onebot.v11 import Message
from utils.message_builder import image
from nonebot.log import logger
import asyncio
try:
import ujson as json
except ModuleNotFoundError:
import json
from .base_handle import BaseHandle, BaseData, UpChar, UpEvent
from ..config import draw_config
from ..util import remove_prohibited_str, cn2py, load_font
from utils.image_utils import BuildImage
class PrettyData(BaseData):
pass
class PrettyChar(PrettyData):
pass
class PrettyCard(PrettyData):
@property
def star_str(self) -> str:
return ["R", "SR", "SSR"][self.star - 1]
class PrettyHandle(BaseHandle[PrettyData]):
def __init__(self):
super().__init__("pretty", "赛马娘", "#eff2f5")
self.data_files.append("pretty_card.json")
self.max_star = 3
self.config = draw_config.pretty
self.ALL_CHAR: List[PrettyChar] = []
self.ALL_CARD: List[PrettyCard] = []
self.UP_CHAR: Optional[UpEvent] = None
self.UP_CARD: Optional[UpEvent] = None
def get_card(self, pool_name: str, mode: int = 1) -> PrettyData:
if mode == 1:
star = self.get_star(
[3, 2, 1],
[
self.config.PRETTY_THREE_P,
self.config.PRETTY_TWO_P,
self.config.PRETTY_ONE_P,
],
)
else:
star = self.get_star(
[3, 2], [self.config.PRETTY_THREE_P, self.config.PRETTY_TWO_P]
)
up_pool = None
if pool_name == "char":
up_pool = self.UP_CHAR
all_list = self.ALL_CHAR
else:
up_pool = self.UP_CARD
all_list = self.ALL_CARD
all_char = [x for x in all_list if x.star == star and not x.limited]
acquire_char = None
# 有UP池子
if up_pool and star in [x.star for x in up_pool.up_char]:
up_list = [x.name for x in up_pool.up_char if x.star == star]
# 抽到UP
if random.random() < 1 / len(all_char) * (0.7 / 0.1385):
up_name = random.choice(up_list)
try:
acquire_char = [x for x in all_list if x.name == up_name][0]
except IndexError:
pass
if not acquire_char:
acquire_char = random.choice(all_char)
return acquire_char
def get_cards(self, count: int, pool_name: str) -> List[Tuple[PrettyData, int]]:
card_list = []
card_count = 0 # 保底计算
for i in range(count):
card_count += 1
# 十连保底
if card_count == 10:
card = self.get_card(pool_name, 2)
card_count = 0
else:
card = self.get_card(pool_name, 1)
if card.star > self.max_star - 2:
card_count = 0
card_list.append((card, i + 1))
return card_list
def format_pool_info(self, pool_name: str) -> str:
info = ""
up_event = self.UP_CHAR if pool_name == "char" else self.UP_CARD
if up_event:
star3_list = [x.name for x in up_event.up_char if x.star == 3]
star2_list = [x.name for x in up_event.up_char if x.star == 2]
star1_list = [x.name for x in up_event.up_char if x.star == 1]
if star3_list:
if pool_name == "char":
info += f'三星UP:{" ".join(star3_list)}\n'
else:
info += f'SSR UP:{" ".join(star3_list)}\n'
if star2_list:
if pool_name == "char":
info += f'二星UP:{" ".join(star2_list)}\n'
else:
info += f'SR UP:{" ".join(star2_list)}\n'
if star1_list:
if pool_name == "char":
info += f'一星UP:{" ".join(star1_list)}\n'
else:
info += f'R UP:{" ".join(star1_list)}\n'
info = f"当前up池:{up_event.title}\n{info}"
return info.strip()
async def draw(self, count: int, pool_name: str, **kwargs) -> Message:
return await asyncio.get_event_loop().run_in_executor(None, self._draw, count, pool_name)
def _draw(self, count: int, pool_name: str, **kwargs) -> Message:
pool_name = "char" if not pool_name else pool_name
index2card = self.get_cards(count, pool_name)
cards = [card[0] for card in index2card]
up_event = self.UP_CHAR if pool_name == "char" else self.UP_CARD
up_list = [x.name for x in up_event.up_char] if up_event else []
result = self.format_result(index2card, up_list=up_list)
pool_info = self.format_pool_info(pool_name)
return pool_info + image(b64=self.generate_img(cards).pic2bs4()) + result
def generate_card_img(self, card: PrettyData) -> BuildImage:
if isinstance(card, PrettyChar):
star_h = 30
img_w = 200
img_h = 219
font_h = 50
bg = BuildImage(img_w, img_h + font_h, color="#EFF2F5")
star_path = str(self.img_path / "star.png")
star = BuildImage(star_h, star_h, background=star_path)
img_path = str(self.img_path / f"{cn2py(card.name)}.png")
img = BuildImage(img_w, img_h, background=img_path)
star_w = star_h * card.star
for i in range(card.star):
bg.paste(star, (int((img_w - star_w) / 2) + star_h * i, 0), alpha=True)
bg.paste(img, (0, 0), alpha=True)
# 加名字
text = card.name[:5] + "..." if len(card.name) > 6 else card.name
font = load_font(fontsize=30)
text_w, _ = font.getsize(text)
draw = ImageDraw.Draw(bg.markImg)
draw.text(
((img_w - text_w) / 2, img_h),
text,
font=font,
fill="gray",
)
return bg
else:
sep_w = 10
img_w = 200
img_h = 267
font_h = 75
bg = BuildImage(img_w + sep_w * 2, img_h + font_h, color="#EFF2F5")
label_path = str(self.img_path / f"{card.star}_label.png")
label = BuildImage(40, 40, background=label_path)
img_path = str(self.img_path / f"{cn2py(card.name)}.png")
img = BuildImage(img_w, img_h, background=img_path)
bg.paste(img, (sep_w, 0), alpha=True)
bg.paste(label, (30, 3), alpha=True)
# 加名字
text = ""
texts = []
font = load_font(fontsize=25)
for t in card.name:
if font.getsize(text + t)[0] > 190:
texts.append(text)
text = ""
if len(texts) >= 2:
texts[-1] += "..."
break
else:
text += t
if text:
texts.append(text)
text = "\n".join(texts)
text_w, _ = font.getsize_multiline(text)
draw = ImageDraw.Draw(bg.markImg)
draw.text(
((img_w - text_w) / 2, img_h),
text,
font=font,
align="center",
fill="gray",
)
return bg
def _init_data(self):
self.ALL_CHAR = [
PrettyChar(
name=value["名称"],
star=int(value["初始星级"]),
limited=False,
)
for value in self.load_data().values()
]
self.ALL_CARD = [
PrettyCard(
name=value["中文名"],
star=["R", "SR", "SSR"].index(value["稀有度"]) + 1,
limited=True if "卡池" not in value["获取方式"] else False,
)
for value in self.load_data("pretty_card.json").values()
]
self.load_up_char()
def load_up_char(self):
try:
data = self.load_data(f"draw_card_up/{self.game_name}_up_char.json")
self.UP_CHAR = UpEvent.parse_obj(data.get("char", {}))
self.UP_CARD = UpEvent.parse_obj(data.get("card", {}))
except ValidationError:
logger.warning(f"{self.game_name}_up_char 解析出错")
def dump_up_char(self):
if self.UP_CHAR and self.UP_CARD:
data = {
"char": json.loads(self.UP_CHAR.json()),
"card": json.loads(self.UP_CARD.json()),
}
self.dump_data(data, f"draw_card_up/{self.game_name}_up_char.json")
async def _update_info(self):
# pretty.json
pretty_info = {}
url = "https://wiki.biligame.com/umamusume/赛马娘图鉴"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
else:
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath("//table[@id='CardSelectTr']/tbody/tr")
for char in char_list:
try:
name = char.xpath("./td[1]/a/@title")[0]
avatar = char.xpath("./td[1]/a/img/@srcset")[0]
star = len(char.xpath("./td[3]/img"))
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar).split(" ")[-2]),
"名称": remove_prohibited_str(name),
"初始星级": star,
}
pretty_info[member_dict["名称"]] = member_dict
self.dump_data(pretty_info)
logger.info(f"{self.game_name_cn} 更新成功")
# pretty_card.json
pretty_card_info = {}
url = "https://wiki.biligame.com/umamusume/支援卡图鉴"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 卡牌出错")
else:
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath("//table[@id='CardSelectTr']/tbody/tr")
for char in char_list:
try:
name = char.xpath("./td[1]/div/a/@title")[0]
name_cn = char.xpath("./td[3]/a/text()")[0]
avatar = char.xpath("./td[1]/div/a/img/@srcset")[0]
star = str(char.xpath("./td[5]/text()")[0]).strip()
sources = str(char.xpath("./td[7]/text()")[0]).strip()
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar).split(" ")[-2]),
"名称": remove_prohibited_str(name),
"中文名": remove_prohibited_str(name_cn),
"稀有度": star,
"获取方式": [sources] if sources else [],
}
pretty_card_info[member_dict["中文名"]] = member_dict
self.dump_data(pretty_card_info, "pretty_card.json")
logger.info(f"{self.game_name_cn} 卡牌更新成功")
# 下载头像
for value in pretty_info.values():
await self.download_img(value["头像"], value["名称"])
for value in pretty_card_info.values():
await self.download_img(value["头像"], value["中文名"])
# 下载星星
PRETTY_URL = "https://patchwiki.biligame.com/images/umamusume"
await self.download_img(
PRETTY_URL + "/1/13/e1hwjz4vmhtvk8wlyb7c0x3ld1s2ata.png", "star"
)
# 下载稀有度标志
idx = 1
for url in [
"/f/f7/afqs7h4snmvovsrlifq5ib8vlpu2wvk.png",
"/3/3b/d1jmpwrsk4irkes1gdvoos4ic6rmuht.png",
"/0/06/q23szwkbtd7pfkqrk3wcjlxxt9z595o.png",
]:
await self.download_img(PRETTY_URL + url, f"{idx}_label")
idx += 1
await self.update_up_char()
async def update_up_char(self):
announcement_url = "https://wiki.biligame.com/umamusume/公告"
result = await self.get_url(announcement_url)
if not result:
logger.warning(f"{self.game_name_cn}获取公告出错")
return
dom = etree.HTML(result, etree.HTMLParser())
announcements = dom.xpath("//div[@id='mw-content-text']/div/div/span/a")
title = ""
url = ""
for announcement in announcements:
try:
title = announcement.xpath("./@title")[0]
url = "https://wiki.biligame.com/" + announcement.xpath("./@href")[0]
if re.match(r".*?\d{8}$", title) or re.match(
r"^\d{1,2}月\d{1,2}日.*?", title
):
break
except IndexError:
continue
if not title:
logger.warning(f"{self.game_name_cn}未找到新UP公告")
return
result = await self.get_url(url)
if not result:
logger.warning(f"{self.game_name_cn}获取UP公告出错")
return
try:
start_time = None
end_time = None
char_img = ""
card_img = ""
up_chars = []
up_cards = []
soup = BeautifulSoup(result, "lxml")
heads = soup.find_all("span", {"class": "mw-headline"})
for head in heads:
if "时间" in head.text:
time = head.find_next("p").text.split("\n")[0]
if "~" in time:
start, end = time.split("~")
start_time = dateparser.parse(start)
end_time = dateparser.parse(end)
elif "赛马娘" in head.text:
char_img = head.find_next("a", {"class": "image"}).find("img")[
"src"
]
lines = str(head.find_next("p").text).split("\n")
chars = [
line
for line in lines
if "★" in line and "(" in line and ")" in line
]
for char in chars:
star = char.count("★")
name = re.split(r"[()]", char)[-2].strip()
up_chars.append(
UpChar(name=name, star=star, limited=False, zoom=70)
)
elif "支援卡" in head.text:
card_img = head.find_next("a", {"class": "image"}).find("img")[
"src"
]
lines = str(head.find_next("p").text).split("\n")
cards = [
line
for line in lines
if "R" in line and "(" in line and ")" in line
]
for card in cards:
star = 3 if "SSR" in card else 2 if "SR" in card else 1
name = re.split(r"[()]", card)[-2].strip()
up_cards.append(
UpChar(name=name, star=star, limited=False, zoom=70)
)
if start_time and end_time:
if start_time <= datetime.now() <= end_time:
self.UP_CHAR = UpEvent(
title=title,
pool_img=char_img,
start_time=start_time,
end_time=end_time,
up_char=up_chars,
)
self.UP_CARD = UpEvent(
title=title,
pool_img=card_img,
start_time=start_time,
end_time=end_time,
up_char=up_cards,
)
self.dump_up_char()
logger.info(f"成功获取{self.game_name_cn}当前up信息...当前up池: {title}")
except Exception as e:
logger.warning(f"{self.game_name_cn}UP更新出错 {type(e)}:{e}")
async def _reload_pool(self) -> Optional[Message]:
await self.update_up_char()
self.load_up_char()
if self.UP_CHAR and self.UP_CARD:
return Message(
Message.template("重载成功!\n当前UP池子:{}{:image}{:image}").format(
self.UP_CHAR.title,
self.UP_CHAR.pool_img,
self.UP_CARD.pool_img,
)
)
+292
View File
@@ -0,0 +1,292 @@
import re
import random
import dateparser
from lxml import etree
from PIL import ImageDraw
from bs4 import BeautifulSoup
from datetime import datetime
from urllib.parse import unquote
from typing import List, Optional, Tuple
from pydantic import ValidationError
from nonebot.adapters.onebot.v11 import Message
from utils.message_builder import image
from nonebot.log import logger
import asyncio
try:
import ujson as json
except ModuleNotFoundError:
import json
from .base_handle import BaseHandle, BaseData, UpChar, UpEvent
from ..config import draw_config
from ..util import remove_prohibited_str, cn2py, load_font
from utils.image_utils import BuildImage
class Operator(BaseData):
recruit_only: bool # 公招限定
event_only: bool # 活动获得干员
# special_only: bool # 升变/异格干员
class PrtsHandle(BaseHandle[Operator]):
def __init__(self):
super().__init__("prts", "明日方舟", "#eff2f5")
self.max_star = 6
self.config = draw_config.prts
self.ALL_OPERATOR: List[Operator] = []
self.UP_EVENT: Optional[UpEvent] = None
def get_card(self, add: float) -> Operator:
star = self.get_star(
[6, 5, 4, 3],
[
self.config.PRTS_SIX_P + add,
self.config.PRTS_FIVE_P,
self.config.PRTS_FOUR_P,
self.config.PRTS_THREE_P,
],
)
all_operators = [
x
for x in self.ALL_OPERATOR
if x.star == star and not any([x.limited, x.event_only, x.recruit_only])
]
acquire_operator = None
if self.UP_EVENT:
up_operators = [x for x in self.UP_EVENT.up_char if x.star == star]
# UPs
try:
zooms = [x.zoom for x in up_operators]
zoom_sum = sum(zooms)
if random.random() < zoom_sum:
up_name = random.choices(up_operators, weights=zooms, k=1)[0].name
acquire_operator = [
x for x in self.ALL_OPERATOR if x.name == up_name
][0]
except IndexError:
pass
if not acquire_operator:
acquire_operator = random.choice(all_operators)
return acquire_operator
def get_cards(self, count: int) -> List[Tuple[Operator, int]]:
card_list = [] # 获取所有角色
add = 0.0
count_idx = 0
for i in range(count):
count_idx += 1
card = self.get_card(add)
if card.star == self.max_star:
add = 0.0
count_idx = 0
elif count_idx > 50:
add += 0.02
card_list.append((card, i + 1))
return card_list
def format_pool_info(self) -> str:
info = ""
if self.UP_EVENT:
star6_list = [x.name for x in self.UP_EVENT.up_char if x.star == 6]
star5_list = [x.name for x in self.UP_EVENT.up_char if x.star == 5]
star4_list = [x.name for x in self.UP_EVENT.up_char if x.star == 4]
if star6_list:
info += f"六星UP:{' '.join(star6_list)}\n"
if star5_list:
info += f"五星UP:{' '.join(star5_list)}\n"
if star4_list:
info += f"四星UP:{' '.join(star4_list)}\n"
info = f"当前up池: {self.UP_EVENT.title}\n{info}"
return info.strip()
async def draw(self, count: int, **kwargs) -> Message:
return await asyncio.get_event_loop().run_in_executor(None, self._draw, count)
def _draw(self, count: int, **kwargs) -> Message:
index2card = self.get_cards(count)
cards = [card[0] for card in self.get_cards(count)]
up_list = [x.name for x in self.UP_EVENT.up_char] if self.UP_EVENT else []
result = self.format_result(index2card, up_list=up_list)
pool_info = self.format_pool_info()
return pool_info + image(b64=self.generate_img(cards).pic2bs4()) + result
def generate_card_img(self, card: Operator) -> BuildImage:
sep_w = 5
sep_h = 5
star_h = 15
img_w = 120
img_h = 120
font_h = 20
bg = BuildImage(img_w + sep_w * 2, img_h + font_h + sep_h * 2, color="#EFF2F5")
star_path = str(self.img_path / "star.png")
star = BuildImage(star_h, star_h, background=star_path)
img_path = str(self.img_path / f"{cn2py(card.name)}.png")
img = BuildImage(img_w, img_h, background=img_path)
bg.paste(img, (sep_w, sep_h), alpha=True)
for i in range(card.star):
bg.paste(star, (sep_w + img_w - 5 - star_h * (i + 1), sep_h), alpha=True)
# 加名字
text = card.name[:7] + "..." if len(card.name) > 8 else card.name
font = load_font(fontsize=16)
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_OPERATOR = [
Operator(
name=value["名称"],
star=int(value["星级"]),
limited="干员寻访" not in value["获取途径"],
recruit_only=True
if "干员寻访" not in value["获取途径"] and "公开招募" in value["获取途径"]
else False,
event_only=True if "活动获取" in value["获取途径"] else False,
)
for key, value in self.load_data().items()
if "阿米娅" not in key
]
self.load_up_char()
def load_up_char(self):
try:
data = self.load_data(f"draw_card_up/{self.game_name}_up_char.json")
self.UP_EVENT = UpEvent.parse_obj(data.get("char", {}))
except ValidationError:
logger.warning(f"{self.game_name}_up_char 解析出错")
def dump_up_char(self):
if self.UP_EVENT:
data = {"char": json.loads(self.UP_EVENT.json())}
self.dump_data(data, f"draw_card_up/{self.game_name}_up_char.json")
async def _update_info(self):
info = {}
url = "https://wiki.biligame.com/arknights/干员数据表"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
return
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath("//table[@id='CardSelectTr']/tbody/tr")
for char in char_list:
try:
avatar = char.xpath("./td[1]/div/div/div/a/img/@srcset")[0]
name = char.xpath("./td[2]/a/text()")[0]
star = char.xpath("./td[5]/text()")[0]
sources = str(char.xpath("./td[8]/text()")[0]).split("\n")
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar).split(" ")[-2]),
"名称": remove_prohibited_str(str(name).strip()),
"星级": int(str(star).strip()),
"获取途径": [s for s in sources if s],
}
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/pcr/0/02/s75ys2ecqhu2xbdw1wf1v9ccscnvi5g.png",
"star",
)
await self.update_up_char()
async def update_up_char(self):
announcement_url = "https://ak.hypergryph.com/news.html"
result = await self.get_url(announcement_url)
if not result:
logger.warning(f"{self.game_name_cn}获取公告出错")
return
dom = etree.HTML(result, etree.HTMLParser())
activity_urls = dom.xpath(
"//ol[@class='articleList' and @data-category-key='ACTIVITY']/li/a/@href"
)
start_time = None
end_time = None
up_chars = []
pool_img = ""
title = ""
for activity_url in activity_urls:
activity_url = f"https://ak.hypergryph.com{activity_url}"
result = await self.get_url(activity_url)
if not result:
logger.warning(f"{self.game_name_cn}获取公告 {activity_url} 出错")
continue
soup = BeautifulSoup(result, "lxml")
contents = soup.find_all("p")
for index, content in enumerate(contents):
if re.search("(.*)(寻访|复刻).*?开启", content.text):
title = content.text
if "【" in title and "】" in title:
title = re.split(r"[【】]", title)[1]
lines = [str(contents[index + i + 1].text) for i in range(5)]
time = ""
chars: List[str] = []
for line in lines:
match = re.search(
r"(\d{1,2}月\d{1,2}日.*?-.*?\d{1,2}月\d{1,2}日.*?$)", line
)
if match:
time = match.group(1)
if "★" in line:
chars.append(line)
if not time:
continue
start, end = time.replace("月", "/").replace("日", "").split("-")[:2]
start_time = dateparser.parse(start)
end_time = dateparser.parse(end)
pool_img = content.find_previous("img")["src"]
for char in chars:
star = char.split("(")[0].count("★")
name = re.split(r"[:(]", char)[1]
names = name.split("/") if "/" in name else [name]
names = [name.replace("[限定]", "").strip() for name in names]
if "权值" in char:
match = re.search(r"(在.*?以.*?(\d+).*?倍权值.*?)", char)
else:
match = re.search(r"(占.*?的.*?(\d+).*?%)", char)
zoom = 1
if match:
zoom = float(match.group(1))
zoom = zoom / 100 if zoom > 10 else zoom
for name in names:
up_chars.append(
UpChar(name=name, star=star, limited=False, zoom=zoom)
)
break
if title and start_time and end_time:
if start_time <= datetime.now() <= end_time:
self.UP_EVENT = UpEvent(
title=title,
pool_img=pool_img,
start_time=start_time,
end_time=end_time,
up_char=up_chars,
)
self.dump_up_char()
logger.info(f"成功获取{self.game_name_cn}当前up信息...当前up池: {title}")
break
async def _reload_pool(self) -> Optional[Message]:
await self.update_up_char()
self.load_up_char()
if self.UP_EVENT:
return f"重载成功!\n当前UP池子:{self.UP_EVENT.title}" + image(
self.UP_EVENT.pool_img
)
-137
View File
@@ -1,137 +0,0 @@
from typing import Any
from .config import DATA_PATH
from utils.utils import is_number
from pathlib import Path
from services.log import logger
try:
import ujson as json
except ModuleNotFoundError:
import json
def init_game_pool(game: str, data: dict, Operator: Any):
tmp_lst = []
if game == 'prts':
for key in data.keys():
limited = False
recruit_only = False
event_only = False
if '限定寻访' in data[key]['获取途径']:
limited = True
if '干员寻访' not in data[key]['获取途径'] and '公开招募' in data[key]['获取途径']:
recruit_only = True
if '活动获取' in data[key]['获取途径']:
event_only = True
if '干员寻访' not in data[key]['获取途径'] and '凭证交易所' == data[key]['获取途径'][0]:
limited = True
if '干员寻访' not in data[key]['获取途径'] and '信用累计奖励' == data[key]['获取途径'][0]:
limited = True
if key.find('阿米娅') != -1:
continue
try:
tmp_lst.append(Operator(name=key, star=int(data[key]['星级']),
limited=limited, recruit_only=recruit_only, event_only=event_only))
except Exception as e:
logger.warning(f"明日方舟导入角色 {key} 数据错误:{type(e)}:{e}")
if game == 'genshin':
for key in data.keys():
if key.find('旅行者') != -1:
continue
limited = False
if data[key]['常驻/限定'] == '限定UP':
limited = True
try:
tmp_lst.append(Operator(name=key, star=int(data[key]['稀有度'][:1]), limited=limited))
except Exception as e:
logger.warning(f"原神导入角色 {key} 数据错误:{type(e)}:{e}")
if game == 'genshin_arms':
for key in data.keys():
if data[key]['获取途径'].find('祈愿') != -1:
limited = False
if data[key]['获取途径'].find('限定祈愿') != -1:
limited = True
try:
tmp_lst.append(Operator(name=key, star=int(data[key]['稀有度'][:1]), limited=limited))
except Exception as e:
logger.warning(f"原神导入武器 {key} 数据错误:{type(e)}:{e}")
if game == 'pretty':
for key in data.keys():
try:
tmp_lst.append(Operator(name=key, star=data[key]['初始星级'], limited=False))
except Exception as e:
logger.warning(f"赛马娘导入角色 {key} 数据错误:{type(e)}:{e}")
if game == 'pretty_card':
for key in data.keys():
limited = False
if '卡池' not in data[key]['获取方式']:
limited = True
if not data[key]['获取方式']:
limited = False
try:
tmp_lst.append(Operator(name=data[key]['中文名'], star=len(data[key]['稀有度']), limited=limited))
except Exception as e:
logger.warning(f"赛马娘导入卡片 {key} 数据错误:{type(e)}:{e}")
if game in ['guardian', 'guardian_arms']:
for key in data.keys():
tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), limited=False))
if game == 'pcr':
for key in data.keys():
limited = False
if key.find('(') != -1:
limited = True
try:
tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), limited=limited))
except Exception as e:
logger.warning(f"公主连接导入角色 {key} 数据错误:{type(e)}:{e}")
if game == 'azur':
for key in data.keys():
if is_number(data[key]['星级']):
limited = False
if '可以建造' not in data[key]['获取途径']:
limited = True
try:
tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']),
limited=limited, itype=data[key]['类型']))
except Exception as e:
logger.warning(f"碧蓝航线导入角色 {key} 数据错误:{type(e)}:{e}")
if game in ['fgo', 'fgo_card']:
for key in data.keys():
limited = False
try:
if "圣晶石召唤" not in data[key]['入手方式'] and "圣晶石召唤(Story卡池)" not in data[key]['入手方式']:
limited = True
except KeyError:
pass
try:
tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), limited=limited))
except Exception as e:
logger.warning(f"FGO导入角色 {key} 数据错误:{type(e)}:{e}")
if game == 'onmyoji':
for key in data.keys():
limited = False
if key in ['奴良陆生', '卖药郎', '鬼灯', '阿香', '蜜桃&芥子', '犬夜叉', '杀生丸', '桔梗', '朽木露琪亚', '黑崎一护',
'灶门祢豆子', '灶门炭治郎']:
limited = True
try:
tmp_lst.append(Operator(name=data[key]['名称'], star=data[key]['星级'], limited=limited))
except Exception as e:
logger.warning(f"阴阳师导入角色 {key} 数据错误:{type(e)}:{e}")
# print(tmp_lst)
char_name_lst = [x.name for x in tmp_lst]
up_char_file = Path(f'{DATA_PATH}/draw_card/draw_card_up/{game.split("_")[0]}_up_char.json')
if up_char_file.exists():
data = json.load(open(up_char_file, 'r', encoding='utf8'))
if len(game.split('_')) == 1:
key = 'char'
else:
key = list(data.keys())[1]
for x in data[key]['up_char']:
for char in data[key]['up_char'][x]:
if char not in char_name_lst:
if game.find('prts') != -1:
tmp_lst.append(Operator(name=char, star=int(x),
recruit_only=False, event_only=False, limited=False))
else:
tmp_lst.append(Operator(name=char, star=int(x), limited=False))
return tmp_lst

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