Compare commits

..
45 Commits
Author SHA1 Message Date
HibiKier b0ab78340a Update chat_message_handle.py 2022-04-26 15:16:26 +08:00
HibiKier 93539be492 update v0.1.5.0 2022-04-26 14:45:04 +08:00
HibiKier c551e21766 Update __version__ 2022-04-12 00:51:37 +08:00
HibiKier ed342af97d Update README.md 2022-04-12 00:51:25 +08:00
HibiKier 17b87a2cb1 Update README.md 2022-04-12 00:50:55 +08:00
HibiKier 69ca02834d Update __init__.py 2022-04-12 00:50:19 +08:00
HibiKier 6cb4099a2e Update README.md 2022-04-10 22:57:36 +08:00
HibiKier e75a4966b2 Update __init__.py 2022-04-10 22:57:05 +08:00
HibiKier d55cba49bd Update data_source.py 2022-04-10 22:31:07 +08:00
HibiKier 0215abac30 update v0.1.4.7 2022-04-10 22:19:50 +08:00
HibiKier 2dcac5f009 Update README.md 2022-04-09 18:09:22 +08:00
HibiKier 0d8c64f7d1 Merge pull request #144 from AkashiCoin/main
fix: 更新问题,戳一戳图片路径问题
2022-04-09 18:08:49 +08:00
AkashiCoin 55626645fb fix: 更新问题,戳一戳图片路径问题 2022-04-09 14:56:30 +08:00
HibiKier f7925fa259 update genshin.query_role 2022-04-08 01:09:29 +08:00
HibiKier c814ae08b1 Update README.md 2022-04-06 18:52:01 +08:00
HibiKier 24666368f7 Merge pull request #143 from yajiwa/main
update search_type
2022-04-06 18:50:59 +08:00
yajiwa a77e85b62d fix bug 2022-04-06 13:17:08 +08:00
yajiwa 6319c7fce1 fix 2022-04-05 22:38:43 +08:00
yajiwa a45b95d5d4 update search_type 2022-04-05 22:15:47 +08:00
HibiKier 188f6f0f99 Update README.md 2022-04-05 19:42:20 +08:00
HibiKier a72f126d8f fix web_ui api 2022-04-05 13:14:29 +08:00
HibiKier 33eb1b83a8 Update README.md 2022-04-04 23:52:24 +08:00
HibiKier 2d9ebe2e6d Merge pull request #141 from yajiwa/main
Update word_bank plus
2022-04-04 23:50:34 +08:00
yajiwa 262b098dec Merge branch 'HibiKier:main' into main 2022-04-04 22:04:27 +08:00
HibiKier 79d88065aa Update README.md 2022-04-04 20:56:02 +08:00
HibiKier 0f59895c3b update v0.1.4.5 2022-04-04 20:33:37 +08:00
yajiwa 610d588e34 Merge branch 'zhenxun_yajiwa' 2022-04-03 20:17:08 +08:00
yajiwa f99abb2f98 fix config 2022-04-03 20:11:00 +08:00
yajiwa b2b7560d05 Update word_bank_plus 2022-04-03 20:05:18 +08:00
yajiwa 07c16e5238 Merge pull request #1 from yajiwa/zhenxun_yajiwa
Update Modify the entry
2022-04-01 23:56:57 +08:00
yajiwa 1741ea7d26 Update Modify the entry 2022-04-01 23:54:40 +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
62 changed files with 2593 additions and 679 deletions
-1
View File
@@ -142,7 +142,6 @@ test.py
server_ip.py
member_activity_handle.py
Yu-Gi-Oh/
search_image/
black_word/
csgo/
fantasy_card/
+78 -6
View File
@@ -32,6 +32,12 @@
## 普通帮助图片
![x](https://github.com/HibiKier/zhenxun_bot/blob/0.0.8.2/docs_image/3238573864-836268675-E2FFBB2AC143EAF4DDDF150438508721.png)
## Web UI
[zhenxun_bot_webui](https://github.com/HibiKier/zhenxun_bot_webui)
## 一键安装脚本
[zhenxun_bot-deploy](https://github.com/AkashiCoin/zhenxun_bot-deploy)
## 提供符合真寻标准的插件仓库
[AkashiCoin/nonebot_plugins_zhenxun_bot](https://github.com/AkashiCoin/nonebot_plugins_zhenxun_bot)
@@ -236,6 +242,73 @@ __Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
## 更新
### 2022/4/26
* 修复了群白名单无法正确添加
* 优化了管理员帮助图片,背景图层将位于最下层
* 修复了树脂140时不断提醒(未测试
* 新增了消息记录的消息排行
* WebUI新增CPU,内存,磁盘监控
* WebUI新增资源文件夹统计可视化
### 2022/4/12
* 修复b了命令私聊出错
### 2022/4/10 \[v0.1.4.7]
* 新增消息记录模块
* 丰富处理请求操作提示
* web ui新增配置项修改
### 2022/4/9
* fix: 更新问题,戳一戳图片路径问题 [@pull/144](https://github.com/HibiKier/zhenxun_bot/pull/144)
### 2022/4/8
* 修复原神玩家查询
### 2022/4/6
* update search_type [@pull/143](https://github.com/HibiKier/zhenxun_bot/pull/143)
### 2022/4/5 \[v0.1.4.6]
* 修复web修改插件后帮助图片生成错误
### 2022/4/4 \[v0.1.4.5]
* 替换了bt搜索URL
* 优化使用playwright的相关代码
* 原神玩家查询新增层岩巨渊探索
* 修复原神便笺角色头像黑框
* 修复同意群聊请求错误
* 提供webui方面的api
* 新增web-ui(前端简易管理页面插件)插件
### 2022/3/21
* 修复statistics_handle.py乱码
### 2022/3/18 \[v0.1.4.4]
* 修复戳一戳无法功能关闭与ban禁用
* 新增图片搜索 search_image
### 2022/3/7
* 优化增删权限插件
### 2022/3/6
* 修复树脂提醒无法开启
* 修复p搜图片路径错误
### 2022/3/3 \[v0.1.4.3]
* 修复手动同意群聊请求依旧退出
### 2022/3/1 \[v0.1.4.2]
* 0.1.4内容
@@ -288,7 +361,6 @@ __Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
* 适配nonebot.beta2
* 删除图片搜索 nonebot_plugin_picsearcher
* 新增图片搜索 search_image
* 替换cos api
* 原神签到树脂提醒新增绑定群里,在某群绑定uid就会在某群发送提醒信息(有好友则私聊,需要重新绑定uid
* 修改update_info.json
@@ -297,15 +369,15 @@ __Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
* 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
### 2022/2/11
* 修复pix不使用反代无法下载图片
### 2021/2/10 \[v0.1.1]
### 2022/2/10 \[v0.1.1]
* 修复购买道具出错
### 2021/2/9 \[v0.1]
### 2022/2/9 \[v0.1]
* 新增原神自动签到和手动签到
* 新增原神树脂提醒
@@ -315,7 +387,7 @@ __Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
* 修复修改商品时限制时间出错
* 修复超时商品依旧可以被购买
### 2021/1/16 \[v0.0.9.0]
### 2022/1/16 \[v0.0.9.0]
* Ai提供文本敏感词过滤器
* 疫情插件适配新版腾讯API
@@ -334,7 +406,7 @@ __Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
* “send_success_msg”(发送成功的交互信息->即:使用道具 {name} {num} 次成功)
* “_max_num_limit”(该道具单次使用的最多个数,默认1)
### 2021/1/5 \[v0.0.8.2]
### 2022/1/5 \[v0.0.8.2]
* 提供金币消费hook,可在plugins2settings.yaml中配置该功能需要消费的金币
* 商店插件将作为内置插件移动至basic_plugins
+1 -1
View File
@@ -1 +1 @@
__version__: v0.1.4.2
__version__: v0.1.5.0
+11 -5
View File
@@ -24,11 +24,17 @@ def switch_rule(event: Event) -> 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()
+5 -8
View File
@@ -5,7 +5,6 @@ from utils.utils import get_matchers
from utils.manager import group_manager
from nonebot.adapters.onebot.v11 import Bot
from nonebot import Driver
import asyncio
import nonebot
@@ -27,12 +26,10 @@ async def create_help_image():
"""
创建管理员帮助图片
"""
await asyncio.get_event_loop().run_in_executor(
None, _create_help_image
)
await _create_help_image()
def _create_help_image():
async def _create_help_image():
"""
创建管理员帮助图片
"""
@@ -85,9 +82,9 @@ def _create_help_image():
height = len(help_str.split("\n")) * 33
A = BuildImage(width, height, font_size=24)
_background = BuildImage(width, height, background=background)
A.text((150, 110), help_str)
A.paste(_background, alpha=True)
A.save(admin_help_image)
await A.apaste(_background, alpha=True)
await A.atext((150, 110), help_str)
await A.asave(admin_help_image)
logger.info(f'已成功加载 {len(_plugin_name_list)} 条管理员命令')
+40 -26
View File
@@ -1,15 +1,17 @@
from nonebot.adapters.onebot.v11 import GroupMessageEvent, PrivateMessageEvent, MessageEvent, Message, Bot
from nonebot.params import CommandArg, Command
from nonebot import on_command
from typing import Tuple
from configs.config import NICKNAME, Config
from models.ban_user import BanUser
from models.level_user import LevelUser
from typing import Tuple
from utils.utils import get_message_at, is_number
from configs.config import NICKNAME, Config
from nonebot import on_command
from nonebot.adapters.onebot.v11 import (Bot, GroupMessageEvent, Message,
MessageEvent, PrivateMessageEvent)
from nonebot.params import Command, CommandArg
from nonebot.permission import SUPERUSER
from .data_source import parse_ban_time, a_ban
from services.log import logger
from utils.utils import get_message_at, is_number
from .data_source import a_ban, parse_ban_time
__zx_plugin_name__ = "封禁Ban用户 [Admin]"
__plugin_usage__ = """
@@ -34,19 +36,19 @@ usage:
示例:b了 1234567
示例:.ban 12345567
""".strip()
__plugin_des__ = '你被逮捕了!丢进小黑屋!'
__plugin_cmd__ = ['.ban [at] ?[小时] ?[分钟]', '.unban [at]', 'b了 [at] [_superuser]']
__plugin_des__ = "你被逮捕了!丢进小黑屋!"
__plugin_cmd__ = [".ban [at] ?[小时] ?[分钟]", ".unban [at]", "b了 [at] [_superuser]"]
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"admin_level": Config.get_config("ban", "BAN_LEVEL"),
"cmd": ['.ban', '.unban', 'ban', 'unban']
"cmd": [".ban", ".unban", "ban", "unban"],
}
__plugin_configs__ = {
"BAN_LEVEL [LEVEL]": {
"value": 5,
"help": "ban/unban所需要的管理员权限等级",
"default_value": 5
"default_value": 5,
}
}
@@ -58,18 +60,23 @@ ban = on_command(
block=True,
)
super_ban = on_command('b了', permission=SUPERUSER, priority=5, block=True)
super_ban = on_command("b了", permission=SUPERUSER, priority=5, block=True)
@ban.handle()
async def _(bot: Bot, event: GroupMessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
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'] or user_name['nickname']
user_name = user_name["card"] or user_name["nickname"]
msg = arg.extract_plain_text().strip()
time = parse_ban_time(msg)
if isinstance(time, str):
@@ -108,7 +115,12 @@ async def _(bot: Bot, event: GroupMessageEvent, cmd: Tuple[str, ...] = Command()
@ban.handle()
async def _(bot: Bot, event: PrivateMessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
async def _(
bot: Bot,
event: PrivateMessageEvent,
cmd: Tuple[str, ...] = Command(),
arg: Message = CommandArg(),
):
cmd = cmd[0]
msg = arg.extract_plain_text().strip()
if msg:
@@ -123,34 +135,36 @@ async def _(bot: Bot, event: PrivateMessageEvent, cmd: Tuple[str, ...] = Command
result = await a_ban(qq, time, str(qq), event, 9)
else:
if await BanUser.unban(qq):
logger.info(
f"USER {event.user_id} 将 USER {qq} 解禁"
)
logger.info(f"USER {event.user_id} 将 USER {qq} 解禁")
result = f"已经把 {qq} 从黑名单中删除了!"
else:
result = f"{qq} 不在黑名单!"
await ban.send(result)
else:
await ban.finish('qq号必须是数字!\n格式:.ban [qq] [hour]? [minute]?', at_sender=True)
await ban.finish(
"qq号必须是数字!\n格式:.ban [qq] [hour]? [minute]?", at_sender=True
)
@super_ban.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
user_name = ""
if isinstance(event, GroupMessageEvent):
qq = get_message_at(event.json())
if qq:
qq = qq[0]
user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq)
user_name = user["card"] or user["nickname"]
else:
qq = arg.extract_plain_text().strip()
if not is_number(qq):
await super_ban.finish("对象qq必须为纯数字...")
qq = [qq]
qq = int(qq)
user_name = 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'] 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的对象,可以使用at或者指定qq..')
await super_ban.send("需要添加被super ban的对象,可以使用at或者指定qq..")
+3
View File
@@ -0,0 +1,3 @@
import nonebot
nonebot.load_plugins("basic_plugins/chat_history")
+6
View File
@@ -0,0 +1,6 @@
from nonebot.adapters.onebot.v11 import Event, MessageEvent
from configs.config import Config
def rule(event: Event) -> bool:
return Config.get_config("chat_history", "FLAG") and isinstance(event, MessageEvent)
@@ -0,0 +1,38 @@
from configs.config import Config
from models.chat_history import ChatHistory
from nonebot import on_message
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
from ._rule import rule
__zx_plugin_name__ = "消息存储 [Hidden]"
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
Config.add_plugin_config(
"chat_history", "FLAG", True, help_="是否开启消息自从存储", name="消息存储", default_value=True
)
chat_history = on_message(rule=rule, priority=1, block=False)
@chat_history.handle()
async def _(event: MessageEvent):
if isinstance(event, GroupMessageEvent):
await ChatHistory.add_chat_msg(
event.user_id, event.group_id, str(event.get_message())
)
else:
await ChatHistory.add_chat_msg(event.user_id, None, str(event.get_message()))
# @test.handle()
# async def _(event: MessageEvent):
# print(await ChatHistory.get_user_msg(event.user_id, "private"))
# print(await ChatHistory.get_user_msg_count(event.user_id, "private"))
# print(await ChatHistory.get_user_msg(event.user_id, "group"))
# print(await ChatHistory.get_user_msg_count(event.user_id, "group"))
# print(await ChatHistory.get_group_msg(event.group_id))
# print(await ChatHistory.get_group_msg_count(event.group_id))
@@ -0,0 +1,108 @@
from datetime import datetime, timedelta
import pytz
from models.chat_history import ChatHistory
from models.group_member_info import GroupInfoUser
from nonebot import on_regex
from nonebot.adapters.onebot.v11 import GroupMessageEvent
from nonebot.params import RegexGroup
from utils.image_utils import BuildImage, text2image
from utils.utils import is_number
from utils.message_builder import image
from typing import Tuple, Any
__zx_plugin_name__ = "消息统计"
__plugin_usage__ = """
usage:
发言记录统计
regex:(周|月)?消息排行(des|DES)?(n=[0-9]{1,2})?
指令:
消息统计?(des)?(n=?)
周消息统计?(des)?(n=?)
月消息统计?(des)?(n=?)
示例:
消息统计
消息统计des
消息统计DESn=15
消息统计n=15
""".strip()
__plugin_des__ = "发言消息排行"
__plugin_cmd__ = [
"消息统计",
"周消息统计",
"月消息统计"
]
__plugin_type__ = ("数据统计", 1)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"cmd": ["消息统计"],
}
msg_handler = on_regex(r"(周|月)?消息统计(des|DES)?(n=[0-9]{1,2})?", priority=5, block=True)
@msg_handler.handle()
async def _(event: GroupMessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
gid = event.group_id
date_scope = None
date, order, num = reg_group
num = num.split("=")[-1] if num else 10
if num and is_number(num) and 10 < int(num) < 50:
num = int(num)
if date in ["周"]:
date_scope = (datetime.now() - timedelta(days=7), datetime.now())
elif date in ["月"]:
date_scope = (datetime.now() - timedelta(days=30), datetime.now())
if rank_data := await ChatHistory.get_group_msg_rank(
gid, num, order or "DESC", date_scope
):
name = "昵称:\n\n"
num_str = "发言次数:\n\n"
idx = 1
for uid, num in rank_data:
try:
user_name = (await GroupInfoUser.get_member_info(uid, gid)).user_name
except AttributeError:
user_name = uid
name += f"\t{idx}.{user_name} \n\n"
num_str += f"\t{num}\n\n"
idx += 1
name_img = await text2image(name.strip(), padding=10, color="#f9f6f2")
num_img = await text2image(num_str.strip(), padding=10, color="#f9f6f2")
if not date_scope:
if date_scope := await ChatHistory.get_group_first_msg_datetime(gid):
date_scope = date_scope.astimezone(
pytz.timezone("Asia/Shanghai")
).replace(microsecond=0)
else:
date_scope = datetime.now().replace(microsecond=0)
date_str = f"日期:{date_scope} - 至今"
else:
date_str = f"日期:{date_scope[0].replace(microsecond=0)} - {date_scope[1].replace(microsecond=0)}"
date_w = BuildImage(0, 0, font_size=15).getsize(date_str)[0]
img_w = date_w if date_w > name_img.w + num_img.w else name_img.w + num_img.w
A = BuildImage(
img_w + 15,
num_img.h + 30,
color="#f9f6f2",
font="CJGaoDeGuo.otf",
font_size=15,
)
await A.atext((10, 10), date_str)
await A.apaste(name_img, (0, 30))
await A.apaste(num_img, (name_img.w, 30))
await msg_handler.send(image(b64=A.pic2bs4()))
# @test.handle()
# async def _(event: MessageEvent):
# print(await ChatHistory.get_user_msg(event.user_id, "private"))
# print(await ChatHistory.get_user_msg_count(event.user_id, "private"))
# print(await ChatHistory.get_user_msg(event.user_id, "group"))
# print(await ChatHistory.get_user_msg_count(event.user_id, "group"))
# print(await ChatHistory.get_group_msg(event.group_id))
# print(await ChatHistory.get_group_msg_count(event.group_id))
+4
View File
@@ -2,6 +2,10 @@ 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):
+10 -9
View File
@@ -13,7 +13,12 @@ from utils.manager import (
plugins2block_manager,
plugins2count_manager,
)
from ._utils import set_block_limit_false, status_message_manager
from ._utils import (
set_block_limit_false,
status_message_manager,
ignore_rst_module,
other_limit_plugins,
)
from nonebot.typing import T_State
from typing import Optional
from nonebot.adapters.onebot.v11 import (
@@ -37,12 +42,10 @@ _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):
async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
module = matcher.plugin_name
plugins2info_dict = plugins2settings_manager.get_data()
# 功能的金币检测 #######################################
@@ -64,7 +67,7 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
await BagUser.spend_gold(event.user_id, event.group_id, cost_gold)
try:
if (
(not isinstance(event, MessageEvent) and module != "poke")
(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 (
@@ -88,7 +91,7 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
except AttributeError:
pass
# 群黑名单检测 群总开关检测
if isinstance(event, GroupMessageEvent) or matcher.plugin_name == "poke":
if isinstance(event, GroupMessageEvent) or matcher.plugin_name == other_limit_plugins:
try:
if (
group_manager.get_group_level(event.group_id) < 0
@@ -145,9 +148,7 @@ 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(
+22 -14
View File
@@ -4,13 +4,16 @@ from nonebot.adapters.onebot.v11.exception import ActionFailed
from nonebot.typing import T_State
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)
+13 -3
View File
@@ -10,14 +10,24 @@ async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
r = None
if (
(
(api == "send_msg" and data["message_type"] == "group")
(api == "send_msg" and data.get("message_type") == "group")
or api == "send_group_msg"
)
and (
(r := re.search("^\[\[_task\|(.*)]]", str(data["message"]["text"]).strip()))
(
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;", str(data["message"]["text"]).strip()
"^&#91;&#91;_task\|(.*)&#93;&#93;",
data["message"].strip()
if isinstance(data["message"], str)
else str(data["message"]["text"]).strip(),
)
)
)
@@ -74,7 +74,7 @@ def init_plugins_settings(data_path: str):
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)
+21 -15
View File
@@ -82,15 +82,16 @@ async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandAr
if is_number(id_):
id_ = int(id_)
if cmd[:2] == "同意":
if await requests_manager.approve(bot, id_, "private"):
await friend_handle.send("同意好友请求成功..")
else:
await friend_handle.send("同意好友请求失败,可能是未找到此id的请求..")
flag = await requests_manager.approve(bot, id_, "private")
else:
if await requests_manager.refused(bot, id_, "private"):
await friend_handle.send("拒绝好友请求成功..")
else:
await friend_handle.send("拒绝好友请求失败,可能是未找到此id的请求..")
flag = await requests_manager.refused(bot, id_, "private")
if flag == 1:
await friend_handle.send(f"{cmd[:2]}好友请求失败,该请求已失效..")
requests_manager.delete_request(id_, "private")
elif flag == 2:
await friend_handle.send(f"{cmd[:2]}好友请求失败,未找到此id的请求..")
else:
await friend_handle.send(f"{cmd[:2]}好友请求成功!")
else:
await friend_handle.send("id必须为纯数字!")
@@ -99,12 +100,12 @@ async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandAr
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
cmd = cmd[0]
id_ = arg.extract_plain_text().strip()
flag = None
if is_number(id_):
id_ = int(id_)
if cmd[:2] == "同意":
rid = await requests_manager.approve(bot, id_, "group")
rid = requests_manager.get_group_id(id_)
if rid:
await friend_handle.send("同意群聊请求成功..")
if await GroupInfo.get_group_info(rid):
await GroupInfo.set_group_flag(rid, 1)
else:
@@ -116,13 +117,18 @@ async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandAr
group_info["member_count"],
1
)
flag = await requests_manager.approve(bot, id_, "group")
else:
await friend_handle.send("同意群聊请求失败,可能是未找到此id的请求..")
await friend_handle.send("同意群聊请求失败,未找到此id的请求..")
else:
if await requests_manager.refused(bot, id_, "group"):
await friend_handle.send("拒绝群聊请求成功..")
else:
await friend_handle.send("拒绝群聊请求失败,可能是未找到此id的请求..")
flag = await requests_manager.refused(bot, id_, "group")
if flag == 1:
await friend_handle.send(f"{cmd[:2]}群聊请求失败,该请求已失效..")
requests_manager.delete_request(id_, "group")
elif flag == 2:
await friend_handle.send(f"{cmd[:2]}群聊请求失败,未找到此id的请求..")
else:
await friend_handle.send(f"{cmd[:2]}群聊请求成功!")
else:
await friend_handle.send("id必须为纯数字!")
+1 -1
View File
@@ -136,7 +136,7 @@ async def _():
@manager_group_whitelist.handle()
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
cmd = cmd[0]
msg = arg.extract_plain_text().strip()
msg = arg.extract_plain_text().strip().split()
all_group = [
g["group_id"] for g in await bot.get_group_list()
]
@@ -1,7 +1,7 @@
from nonebot import on_command
from nonebot.permission import SUPERUSER
from models.level_user import LevelUser
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message
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
@@ -37,41 +37,49 @@ super_cmd = on_command(
@super_cmd.handle()
async def _(bot: Bot, event: GroupMessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
cmd = cmd[0]
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 = arg.extract_plain_text().strip().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 cmd[: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 = "删除管理成功!"
@@ -82,7 +90,10 @@ async def _(bot: Bot, event: GroupMessageEvent, cmd: Tuple[str, ...] = Command()
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:
+19 -3
View File
@@ -11,14 +11,19 @@ class ConfigsManager:
def __init__(self, file: Path):
self._data: dict = {}
self._simple_data: dict = {}
self._admin_level_data = []
self._simple_file = Path() / "configs" / "config.yaml"
if file:
file.parent.mkdir(exist_ok=True, parents=True)
self.file = file
_yaml = YAML()
if file.exists():
_yaml = YAML()
with open(file, "r", encoding="utf8") as f:
self._data = _yaml.load(f)
if self._simple_file.exists():
with open(self._simple_file, "r", encoding="utf8") as f:
self._simple_data = _yaml.load(f)
def add_plugin_config(
self,
@@ -71,6 +76,7 @@ class ConfigsManager:
"""
if module in self._data.keys():
del self._data[module]
self.save()
def set_config(self, module: str, key: str, value: str):
"""
@@ -80,8 +86,10 @@ class ConfigsManager:
:param value: 值
"""
if module in self._data.keys():
if self._data[module].get(key) is not None:
if self._data[module].get(key) is not None and self._data[module][key] != value:
self._data[module][key]["value"] = value
self._simple_data[module][key] = value
self.save()
def set_help(self, module: str, key: str, help_: str):
"""
@@ -93,6 +101,7 @@ class ConfigsManager:
if module in self._data.keys():
if self._data[module].get(key) is not None:
self._data[module][key]["help"] = help_
self.save()
def set_default_value(self, module: str, key: str, value: str):
"""
@@ -104,6 +113,7 @@ class ConfigsManager:
if module in self._data.keys():
if self._data[module].get(key) is not None:
self._data[module][key]["default_value"] = value
self.save()
def get_config(self, module: str, key: str, default: Optional[Any] = None) -> Optional[Any]:
"""
@@ -142,11 +152,17 @@ class ConfigsManager:
if key in self._data.keys():
return self._data[key]
def save(self, path: Union[str, Path] = None):
def save(self, path: Union[str, Path] = None, save_simple_data: bool = False):
"""
保存数据
:param path: 路径
:param save_simple_data: 同时保存至config.yaml
"""
if save_simple_data:
with open(self._simple_file, "w", encoding="utf8") as f:
yaml.dump(
self._simple_data, f, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True
)
path = path if path else self.file
with open(path, "w", encoding="utf8") as f:
yaml.dump(
+214
View File
@@ -0,0 +1,214 @@
from datetime import datetime, timedelta
from typing import List, Literal, Optional, Tuple, Union
from services.db_context import db
class ChatHistory(db.Model):
__tablename__ = "chat_history"
id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False)
group_id = db.Column(db.BigInteger())
text = db.Column(db.Text())
create_time = db.Column(db.DateTime(timezone=True), nullable=False)
@classmethod
async def add_chat_msg(cls, user_qq: int, group_id: Optional[int], text: str):
await cls.create(
user_qq=user_qq, group_id=group_id, text=text, create_time=datetime.now()
)
@classmethod
async def get_user_msg(
cls,
uid: int,
msg_type: Optional[Literal["private", "group"]],
days: Optional[int] = None,
) -> List["ChatHistory"]:
"""
说明:
获取用户消息
参数:
:param uid: 用户qq
:param msg_type: 消息类型,私聊或群聊
:param days: 限制日期
"""
return await cls._get_msg(uid, None, "user", msg_type, days).gino.all()
@classmethod
async def get_group_user_msg(
cls,
uid: int,
gid: int,
limit: int = 10,
date_scope: Tuple[datetime, datetime] = None,
) -> List["ChatHistory"]:
"""
说明:
获取群聊指定用户聊天记录
参数:
:param uid: qq
:param gid: 群号
:param limit: 获取数量
:param date_scope: 日期范围,默认None为全搜索
"""
return (
await cls._get_msg(uid, gid, "group", days=date_scope)
.limit(limit)
.gino.all()
)
@classmethod
async def get_group_user_msg_count(cls, uid: int, gid: int) -> Optional[int]:
"""
说明:
查询群聊指定用户的聊天记录数量
参数:
:param uid: qq
:param gid: 群号
"""
if x := await db.first(
db.text(
f"SELECT COUNT(id) as sum FROM public.chat_history WHERE user_qq = {uid} AND group_id = {gid}"
)
):
return x[0]
return None
@classmethod
async def get_group_msg_rank(
cls,
gid: int,
limit: int = 10,
order: str = "DESC",
date_scope: Optional[Tuple[datetime, datetime]] = None,
) -> Optional[Tuple[int, int]]:
"""
说明:
获取排行数据
参数:
:param gid: 群号
:param limit: 获取数量
:param order: 排序类型,desc,des
:param date_scope: 日期范围
"""
sql = f"SELECT user_qq, COUNT(id) as sum FROM public.chat_history WHERE group_id = {gid} "
if date_scope:
sql += f"AND create_time BETWEEN '{date_scope[0]}' AND '{date_scope[1]}' "
sql += f"GROUP BY user_qq ORDER BY sum {order if order and order.upper() != 'DES' else ''} LIMIT {limit}"
print(sql)
return await db.all(db.text(sql))
@classmethod
async def get_group_first_msg_datetime(cls, gid: int) -> Optional[datetime]:
"""
说明:
获取群第一条记录消息时间
参数:
:param gid:
"""
if (
msg := await cls.query.where(cls.group_id == gid)
.order_by(cls.create_time)
.gino.first()
):
return msg.create_time
return None
@classmethod
async def get_user_msg_count(
cls,
uid: int,
msg_type: Optional[Literal["private", "group"]],
days: Optional[int] = None,
) -> int:
"""
说明:
获取用户消息数量
参数:
:param uid: 用户qq
:param msg_type: 消息类型,私聊或群聊
:param days: 限制日期
"""
return (
await cls._get_msg(uid, None, "user", msg_type, days, True).gino.first()
)[0]
@classmethod
async def get_group_msg(
cls,
gid: int,
days: Optional[int] = None,
) -> List["ChatHistory"]:
"""
说明:
获取群聊消息
参数:
:param gid: 用户qq
:param days: 限制日期
"""
return await cls._get_msg(None, gid, "group", None, days).gino.all()
@classmethod
async def get_group_msg_count(
cls,
gid: int,
days: Optional[int] = None,
) -> List["ChatHistory"]:
"""
说明:
获取群聊消息数量
参数:
:param gid: 用户qq
:param days: 限制日期
"""
return (await cls._get_msg(None, gid, "group", None, days, True).gino.first())[
0
]
@classmethod
def _get_msg(
cls,
uid: Optional[int],
gid: Optional[int],
type_: Literal["user", "group"],
msg_type: Optional[Literal["private", "group"]] = None,
days: Optional[Union[int, Tuple[datetime, datetime]]] = None,
is_select_count: bool = False,
):
"""
说明:
获取消息查询query
参数:
:param uid: 用户qq
:param gid: 群号
:param type_: 类型,私聊或群聊
:param msg_type: 消息类型,用户或群聊
:param days: 限制日期
"""
if is_select_count:
setattr(ChatHistory, "count", db.func.count(cls.id).label("count"))
query = cls.select("count")
else:
query = cls.query
if type_ == "user":
query = query.where(cls.user_qq == uid)
if msg_type == "private":
query = query.where(cls.group_id == None)
elif msg_type == "group":
query = query.where(cls.group_id != None)
else:
query = query.where(cls.group_id == gid)
if uid:
query = query.where(cls.user_qq == uid)
if days:
if isinstance(days, int):
query = query.where(
cls.create_time >= datetime.now() - timedelta(days=days)
)
elif isinstance(days, tuple):
query = query.where(cls.create_time >= days[0]).where(
cls.create_time <= days[1]
)
return query
+1
View File
@@ -76,6 +76,7 @@ async def _(bot: Bot, event: MessageEvent):
f"问题:{msg} ---- 回答:{result}"
)
if result:
result = str(result)
for t in Config.get_config("ai", "TEXT_FILTER"):
result = result.replace(t, "*")
await ai.finish(Message(result))
+1 -1
View File
@@ -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
+10 -10
View File
@@ -56,15 +56,15 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
)
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("发生了一些错误.....")
+1 -1
View File
@@ -211,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":
+25 -21
View File
@@ -311,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 -1
View File
@@ -9,7 +9,7 @@ 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: int):
@@ -73,6 +73,7 @@ async def check_update(bot: Bot) -> 'int, str':
message=f"检测真寻已更新,当前版本:{_version},最新版本:{latest_version}\n" f"开始更新.....",
)
logger.info(f"开始下载真寻最新版文件....")
tar_gz_url = (await AsyncHttpx.get(tar_gz_url)).headers.get('Location')
if await AsyncHttpx.download_file(tar_gz_url, zhenxun_latest_tar_gz):
logger.info("下载真寻最新版文件完成....")
error = await asyncio.get_event_loop().run_in_executor(
+1 -1
View File
@@ -132,4 +132,4 @@ async def get_fudu_img_hash(url, group_id):
logger.warning(f"复读下载图片失败...")
except Exception as e:
logger.warning(f"复读读取图片Hash出错 {type(e)}:{e}")
return ""
return ""
@@ -254,10 +254,9 @@ class Genshin(db.Model):
query = cls.query.where(cls.today_query_uid.contains(str(uid)))
x = await query.gino.first()
if x:
await cls._add_query_uid(uid, uid)
return x.cookie
for u in [
x for x in await cls.query.order_by(db.func.random()).gino.all() if x.cookie
]:
for u in await cls.query.where(cls.cookie != "").order_by(db.func.random()).gino.all():
if not u.today_query_uid or len(u.today_query_uid[:-1].split()) < 30:
await cls._add_query_uid(uid, u.uid)
return u.cookie
@@ -356,7 +355,7 @@ class Genshin(db.Model):
"""
query = cls.query.where(cls.uid == cookie_uid).with_for_update()
user = await query.gino.first()
await user.update(today_query_uid=cls.today_query_uid + f"{uid} ").apply()
await user.update(today_query_uid=user.today_query_uid + f"{uid} ").apply()
@classmethod
async def _get_user_data(
@@ -23,17 +23,17 @@ async def _():
g_list = await Genshin.get_all_auto_sign_user()
for u in g_list:
if u.auto_sign_time:
date = await Genshin.random_sign_time(u.uid)
scheduler.add_job(
_sign,
"date",
run_date=date.replace(microsecond=0),
id=f"genshin_auto_sign_{u.uid}_{u.user_qq}_0",
args=[u.user_qq, u.uid, 0],
)
logger.info(
f"genshin_sign add_job:USER:{u.user_qq} UID:{u.uid} " f"{date} 原神自动签到"
)
if date := await Genshin.random_sign_time(u.uid):
scheduler.add_job(
_sign,
"date",
run_date=date.replace(microsecond=0),
id=f"genshin_auto_sign_{u.uid}_{u.user_qq}_0",
args=[u.user_qq, u.uid, 0],
)
logger.info(
f"genshin_sign add_job:USER:{u.user_qq} UID:{u.uid} " f"{date} 原神自动签到"
)
def add_job(user_id: int, uid: int, date: datetime):
@@ -200,7 +200,7 @@ def _parse_data_and_draw(
file_name = x["avatar_side_icon"].split("_")[-1]
role_avatar = memo_path / "role_avatar" / file_name
_ava_img = BuildImage(75, 75, background=role_avatar)
_ava_img.circle()
# _ava_img.circle()
if x["status"] == "Finished":
msg = "探索完成"
font_color = (146, 188, 63)
@@ -218,10 +218,10 @@ def _parse_data_and_draw(
a_circle.circle()
b_circle = BuildImage(47, 47)
b_circle.circle()
a_circle.paste(b_circle, (4, 4), alpha=True)
_circle_bk.paste(a_circle, (4, 4), alpha=True)
a_circle.paste(b_circle, (4, 4), True)
_circle_bk.paste(a_circle, (4, 4), True)
_bk.paste(_circle_bk, (25, 0), True, center_type="by_height")
_bk.paste(_circle_bk, (25, 0), True, "by_height")
_bk.paste(_ava_img, (19, -13), True)
_bk.text((100, 0), msg, font_color, "by_height")
_bk.circle_corner(20)
@@ -296,9 +296,10 @@ def get_country_data_image(world_data_dict: Dict) -> BuildImage:
画出国家探索供奉等图像
:param world_data_dict: 国家数据字典
"""
region = BuildImage(790, 267 * len(world_data_dict), color="#F9F6F2")
# 层岩巨渊 和 地下矿区 算一个
region = BuildImage(790, 267 * (len(world_data_dict) - 1), color="#F9F6F2")
height = 0
for country in ["蒙德", "龙脊雪山", "璃月", "稻妻", "渊下宫"]:
for country in ["蒙德", "龙脊雪山", "璃月", "层岩巨渊", "稻妻", "渊下宫"]:
x = BuildImage(790, 250, color="#3A4467")
logo = BuildImage(180, 180, background=image_path / "logo" / f"{country}.png")
tmp_bk = BuildImage(770, 230, color="#606779")
@@ -320,6 +321,25 @@ def get_country_data_image(world_data_dict: Dict) -> BuildImage:
f"Lv.{world_data_dict[country]['level']}",
fill=(255, 255, 255),
)
elif country in ["层岩巨渊"]:
content_bk.text((300, 20), "层岩巨渊探索", fill=(239, 211, 114))
content_bk.text(
(570, 20),
f"{world_data_dict['层岩巨渊']['exploration_percentage'] / 10}%",
fill=(255, 255, 255),
)
content_bk.text((300, 85), "地下矿区探索", fill=(239, 211, 114))
content_bk.text(
(570, 85),
f"{world_data_dict['层岩巨渊·地下矿区']['exploration_percentage'] / 10}%",
fill=(255, 255, 255),
)
content_bk.text((300, 150), "流明石触媒", fill=(239, 211, 114))
content_bk.text(
(570, 150),
f"LV.{world_data_dict['层岩巨渊·地下矿区']['offerings'][0]['level']}",
fill=(255, 255, 255),
)
elif country in ["龙脊雪山"]:
content_bk.text((300, 40), "探索", fill=(239, 211, 114))
content_bk.text(
@@ -55,7 +55,7 @@ async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command()):
scheduler.remove_job(f"genshin_resin_remind_{uid}_{event.user_id}")
except JobLookupError:
pass
if cmd == "开":
if cmd[0] == "开":
await Genshin.set_resin_remind(uid, True)
add_job(event.user_id, uid)
logger.info(
@@ -5,6 +5,7 @@ from apscheduler.jobstores.base import ConflictingIdError
from nonebot import Driver
from .._models import Genshin
from datetime import datetime, timedelta
from apscheduler.jobstores.base import JobLookupError
from services.log import logger
from nonebot.plugin import require
from configs.config import Config
@@ -19,6 +20,9 @@ driver: Driver = nonebot.get_driver()
get_memo = require("query_memo").get_memo
global_map = {}
class UserManager:
def __init__(self, max_error_count: int = 3):
self._data = []
@@ -108,6 +112,11 @@ async def _():
def add_job(user_id: int, uid: int):
# 移除
try:
scheduler.remove_job(f"genshin_resin_remind_{uid}_{user_id}")
except JobLookupError:
pass
date = datetime.now(pytz.timezone("Asia/Shanghai")) + timedelta(seconds=30)
try:
scheduler.add_job(
@@ -140,8 +149,8 @@ async def _remind(user_id: int, uid: str):
if current_resin < max_resin:
user_manager.remove(uid)
user_manager.remove_overflow(uid)
if max_resin - 40 <= current_resin <= max_resin - 20:
next_time = now + timedelta(minutes=(max_resin - 20 - current_resin) * 8, seconds=10)
if max_resin - 40 < current_resin <= max_resin - 20:
next_time = now + timedelta(minutes=(max_resin - 20 - current_resin + 1) * 8, seconds=10)
elif current_resin < max_resin:
next_time = now + timedelta(minutes=(max_resin - current_resin) * 8, seconds=10)
elif current_resin == max_resin:
@@ -183,6 +192,7 @@ async def _remind(user_id: int, uid: str):
user_manager.remove_error_count(uid)
await Genshin.set_user_resin_recovery_time(int(uid), next_time)
scheduler.add_job(
_remind,
_remind,
"date",
run_date=next_time,
+1 -1
View File
@@ -84,7 +84,7 @@ async def _(event: MessageEvent, state: T_State, pid: str = Arg("pid")):
f"pid:{pid}\n"
f"author:{author}\n"
f"author_id:{author_id}\n"
f'{image(f"pid_search_{event.user_id}.png", "temp")}'
f'{image(TEMP_PATH / f"pid_search_{event.user_id}.png")}'
f"{tmp}"
)
)
+2 -2
View File
@@ -64,8 +64,8 @@ async def _poke_event(event: PokeNotifyEvent):
rand = random.random()
if rand <= 0.3:
path = random.choice(["luoli", "meitu"])
index = random.randint(0, len(os.listdir(IMAGE_PATH / path)))
result = f"id:{index}" + image(f"{index}.jpg", path)
index = random.randint(0, len(os.listdir(IMAGE_PATH / "image_management" / path)))
result = f"id:{index}" + image(f"{index}.jpg", "image_management/" + path)
await poke_.send(result)
logger.info(f"USER {event.user_id} 戳了戳我 回复: {result} \n {result}")
elif 0.3 < rand < 0.6:
+91
View File
@@ -0,0 +1,91 @@
from nonebot.plugin import on_command
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent, Message
from nonebot.typing import T_State
from services.log import logger
from utils.utils import get_message_img
from utils.message_builder import custom_forward_msg
from nonebot.params import CommandArg, Arg, ArgStr, Depends
from .saucenao import get_saucenao_image
__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": ["识图"],
}
__plugin_configs__ = {
"MAX_FIND_IMAGE_COUNT": {"value": 3, "help": "识图返回的最大结果数", "default_value": 3},
"API_KEY": {
"value": None,
"help": "Saucenao的API_KEY,通过 https://saucenao.com/user.php?page=search-api 注册获取",
},
}
search_image = on_command("识图", block=True, priority=5)
async def get_image_info(mod: str, url: str):
if mod == "saucenao":
return await get_saucenao_image(url)
def parse_image(key: str):
async def _key_parser(
state: T_State, img: Message = Arg(key)
):
if not get_message_img(img):
await search_image.reject_arg(key, "请发送要识别的图片!")
state[key] = img
return _key_parser
@search_image.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if msg:
state["mod"] = msg
else:
state["mod"] = "saucenao"
if get_message_img(event.json()):
state["img"] = event.message
@search_image.got("img", prompt="图来!", parameterless=[Depends(parse_image("img"))])
async def _(
bot: Bot,
event: MessageEvent,
state: T_State,
mod: str = ArgStr("mod"),
img: Message = Arg("img"),
):
img = get_message_img(img)[0]
await search_image.send("开始处理图片...")
msg = await get_image_info(mod, img)
if isinstance(msg, str):
await search_image.finish(msg, at_sender=True)
if isinstance(event, GroupMessageEvent):
await bot.send_group_forward_msg(
group_id=event.group_id, messages=custom_forward_msg(msg, bot.self_id)
)
else:
for m in msg[1:]:
await search_image.send(m)
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 识图:" + img
)
+50
View File
@@ -0,0 +1,50 @@
from utils.http_utils import AsyncHttpx
from configs.config import Config
from configs.path_config import TEMP_PATH
from utils.message_builder import image
from typing import Union, List
import random
API_URL_SAUCENAO = "https://saucenao.com/search.php"
API_URL_ASCII2D = "https://ascii2d.net/search/url/"
API_URL_IQDB = "https://iqdb.org/"
async def get_saucenao_image(url: str) -> Union[str, List[str]]:
api_key = Config.get_config("search_image", "API_KEY")
if not api_key:
return "Saucenao 缺失API_KEY!"
params = {
"output_type": 2,
"api_key": api_key,
"testmode": 1,
"numres": 6,
"db": 999,
"url": url,
}
data = (await AsyncHttpx.post(API_URL_SAUCENAO, params=params)).json()
if data["header"]["status"] != 0:
return "Saucenao识图失败.."
data = data["results"]
data = (
data
if len(data) < Config.get_config("search_image", "MAX_FIND_IMAGE_COUNT")
else data[: Config.get_config("search_image", "MAX_FIND_IMAGE_COUNT")]
)
msg_list = []
index = random.randint(0, 10000)
if await AsyncHttpx.download_file(
url, TEMP_PATH / f"saucenao_search_{index}.jpg"
):
msg_list.append(image(TEMP_PATH / f"saucenao_search_{index}.jpg"))
for info in data:
similarity = info["header"]["similarity"]
tmp = f"相似度:{similarity}%\n"
for x in info["data"].keys():
if x != "ext_urls":
tmp += f"{x}:{info['data'][x]}\n"
if "source" not in info["data"].keys():
tmp += f'source:{info["data"]["ext_urls"][0]}\n'
msg_list.append(tmp[:-1])
return msg_list
-1
View File
@@ -144,7 +144,6 @@ class Setu(db.Model):
return _tmp_local_id
return -1
@classmethod
async def update_setu_data(
cls,
+279 -280
View File
@@ -1,280 +1,279 @@
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, MessageEvent, Message
from models.group_info import GroupInfo
from configs.path_config import DATA_PATH, IMAGE_PATH
from nonebot.params import CommandArg, Command
from utils.image_utils import BuildMat
from utils.message_builder import image
from utils.manager import plugins2settings_manager
from typing import Tuple
import asyncio
import os
try:
import ujson as json
except ModuleNotFoundError:
import json
__zx_plugin_name__ = "功能调用统计可视化"
__plugin_usage__ = """
usage:
功能调用统计可视化
指令:
功能调用统计
日功能调用统计
周功能调用统计 ?[功能]
月功能调用统计 ?[功能]
我的功能调用统计
我的日功能调用统计 ?[功能]
我的周功能调用统计 ?[功能]
我的月功能调用统计 ?[功能]
""".strip()
__plugin_superuser_usage__ = """
usage:
功能调用统计可视化
指令:
全局功能调用统计
全局日功能调用统计
全局周功能调用统计 ?[功能]
全局月功能调用统计 ?[功能]
""".strip()
__plugin_des__ = "功能调用统计可视化"
__plugin_cmd__ = [
"功能调用统计",
"全局功能调用统计 [_superuser]",
"全局日功能调用统计 [_superuser]",
"全局周功能调用统计 ?[功能] [_superuser]",
"全局月功能调用统计 ?[功能] [_superuser]",
"周功能调用统计 ?[功能]",
"月功能调用统计 ?[功能]",
"我的功能调用统计",
"我的日功能调用统计 ?[功能]",
"我的周功能调用统计 ?[功能]",
"我的月功能调用统计 ?[功能]",
]
__plugin_type__ = ("功能调用统计可视化", 1)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["功能调用统计"],
}
statistics = on_command(
"功能调用统计",
aliases={
"全局功能调用统计",
"全局日功能调用统计",
"全局周功能调用统计",
"全局月功能调用统计",
"日功能调用统计",
"周功能调用统计",
"月功能调用统计",
"我的功能调用统计",
"我的日功能调用统计",
"我的周功能调用统计",
"我的月功能调用统计",
},
priority=5,
block=True,
)
statistics_group_file = DATA_PATH / "statistics" / "_prefix_count.json"
statistics_user_file = DATA_PATH / "statistics" / "_prefix_user_count.json"
@statistics.handle()
async def _(bot: Bot, event: MessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if cmd[0][:2] == "全局":
if str(event.user_id) in bot.config.superusers:
data: dict = json.load(open(statistics_group_file, "r", encoding="utf8"))
if cmd[0][2] == '日':
_type = 'day_statistics'
elif cmd[0][2] == '周':
_type = 'week_statistics'
elif cmd[0][2] == '月':
_type = 'month_statistics'
else:
_type = 'total_statistics'
tmp_dict = {}
data = data[_type]
if _type in ["day_statistics", "total_statistics"]:
for key in data['total']:
tmp_dict[key] = data['total'][key]
else:
for group in data.keys():
if group != 'total':
for day in data[group].keys():
for plugin_name in data[group][day].keys():
if data[group][day][plugin_name] is not None:
if tmp_dict.get(plugin_name) is None:
tmp_dict[plugin_name] = 1
else:
tmp_dict[plugin_name] += data[group][day][plugin_name]
bar_graph = await init_bar_graph(tmp_dict, cmd[0])
await asyncio.get_event_loop().run_in_executor(None, bar_graph.gen_graph)
await statistics.finish(image(b64=bar_graph.pic2bs4()))
return
if cmd[0][:2] == "我的":
_type = "user"
key = str(event.user_id)
cmd = list(cmd)
cmd[0] = cmd[0][2:]
if not statistics_user_file.exists():
await statistics.finish("统计文件不存在...", at_sender=True)
else:
if not isinstance(event, GroupMessageEvent):
await statistics.finish("请在群内调用此功能...")
_type = "group"
key = str(event.group_id)
if not statistics_group_file.exists():
await statistics.finish("统计文件不存在...", at_sender=True)
plugin = ""
if cmd[0][0] == "日":
arg = "day_statistics"
elif cmd[0][0] == "周":
arg = "week_statistics"
elif cmd[0][0] == "月":
arg = "month_statistics"
else:
arg = "total_statistics"
if msg:
plugin = plugins2settings_manager.get_plugin_module(msg)
if not plugin:
if arg not in ["day_statistics", "total_statistics"]:
await statistics.finish("未找到此功能的调用...", at_sender=True)
if _type == "group":
data: dict = json.load(open(statistics_group_file, "r", encoding="utf8"))
if not data[arg].get(str(event.group_id)):
await statistics.finish("该群统计数据不存在...", at_sender=True)
else:
data: dict = json.load(open(statistics_user_file, "r", encoding="utf8"))
if not data[arg].get(str(event.user_id)):
await statistics.finish("该用户统计数据不存在...", at_sender=True)
day_index = data["day_index"]
data = data[arg][key]
if _type == "group":
name = await GroupInfo.get_group_info(event.group_id)
name = name.group_name if name else str(event.group_id)
else:
name = event.sender.card or event.sender.nickname
img = await generate_statistics_img(data, arg, name, plugin, day_index)
await statistics.send(image(b64=img))
async def generate_statistics_img(
data: dict, arg: str, name: str, plugin: str, day_index: int
):
try:
plugin = plugins2settings_manager.get_plugin_data(plugin)['cmd'][0]
except (KeyError, IndexError):
pass
bar_graph = None
if arg == "day_statistics":
bar_graph = await init_bar_graph(data, f"{name} 日功能调用统计")
elif arg == "week_statistics":
if plugin:
current_week = day_index % 7
week_lst = []
if current_week == 0:
week_lst = [1, 2, 3, 4, 5, 6, 7]
else:
for i in range(current_week + 1, 7):
week_lst.append(str(i))
for i in range(current_week + 1):
week_lst.append(str(i))
count = []
for i in range(7):
if int(week_lst[i]) == 7:
try:
count.append(data[str(0)][plugin])
except KeyError:
count.append(0)
else:
try:
count.append(data[str(week_lst[i])][plugin])
except KeyError:
count.append(0)
week_lst = ["7" if i == "0" else i for i in week_lst]
bar_graph = BuildMat(
y=count,
mat_type="line",
title=f"{name} 周 {plugin} 功能调用统计【为7天统计】",
x_index=week_lst,
display_num=True,
background=[
f"{IMAGE_PATH}/background/create_mat/{x}"
for x in os.listdir(f"{IMAGE_PATH}/background/create_mat")
],
bar_color=["*"],
)
else:
bar_graph = await init_bar_graph(update_data(data), f"{name} 周功能调用统计【为7天统计】")
elif arg == "month_statistics":
if plugin:
day_index = day_index % 30
day_lst = []
for i in range(day_index + 1, 30):
day_lst.append(i)
for i in range(day_index + 1):
day_lst.append(i)
count = [data[str(day_lst[i])][plugin] for i in range(30)]
day_lst = [str(x + 1) for x in day_lst]
bar_graph = BuildMat(
y=count,
mat_type="line",
title=f"{name} 月 {plugin} 功能调用统计【为30天统计】",
x_index=day_lst,
display_num=True,
background=[
f"{IMAGE_PATH}/background/create_mat/{x}"
for x in os.listdir(f"{IMAGE_PATH}/background/create_mat")
],
bar_color=["*"],
)
else:
bar_graph = await init_bar_graph(update_data(data), f"{name} 月功能调用统计【为30天统计】")
elif arg == "total_statistics":
bar_graph = await init_bar_graph(data, f"{name} 功能调用统计")
await asyncio.get_event_loop().run_in_executor(None, bar_graph.gen_graph)
return bar_graph.pic2bs4()
async def init_bar_graph(data: dict, title: str) -> BuildMat:
return await asyncio.get_event_loop().run_in_executor(None, _init_bar_graph, data, title)
def _init_bar_graph(data: dict, title: str) -> BuildMat:
bar_graph = BuildMat(
y=[data[x] for x in data.keys() if data[x] != 0],
mat_type="barh",
title=title,
x_index=[x for x in data.keys() if data[x] != 0],
display_num=True,
background=[
f"{IMAGE_PATH}/background/create_mat/{x}"
for x in os.listdir(f"{IMAGE_PATH}/background/create_mat")
],
bar_color=["*"],
)
return bar_graph
def update_data(data: dict):
tmp_dict = {}
for day in data.keys():
for plugin_name in data[day].keys():
# print(f'{day}:{plugin_name} = {data[day][plugin_name]}')
if data[day][plugin_name] is not None:
if tmp_dict.get(plugin_name) is None:
tmp_dict[plugin_name] = 1
else:
tmp_dict[plugin_name] += data[day][plugin_name]
return tmp_dict
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, MessageEvent, Message
from models.group_info import GroupInfo
from configs.path_config import DATA_PATH, IMAGE_PATH
from nonebot.params import CommandArg, Command
from utils.image_utils import BuildMat
from utils.message_builder import image
from utils.manager import plugins2settings_manager
from typing import Tuple
import asyncio
import os
try:
import ujson as json
except ModuleNotFoundError:
import json
__zx_plugin_name__ = "功能调用统计可视化"
__plugin_usage__ = """
usage:
功能调用统计可视化
指令:
功能调用统计
日功能调用统计
周功能调用统计 ?[功能]
月功能调用统计 ?[功能]
我的功能调用统计
我的日功能调用统计 ?[功能]
我的周功能调用统计 ?[功能]
我的月功能调用统计 ?[功能]
""".strip()
__plugin_superuser_usage__ = """
usage:
功能调用统计可视化
指令:
全局功能调用统计
全局日功能调用统计
全局周功能调用统计 ?[功能]
全局月功能调用统计 ?[功能]
""".strip()
__plugin_des__ = "功能调用统计可视化"
__plugin_cmd__ = [
"功能调用统计",
"全局功能调用统计 [_superuser]",
"全局日功能调用统计 [_superuser]",
"全局周功能调用统计 ?[功能] [_superuser]",
"全局月功能调用统计 ?[功能] [_superuser]",
"周功能调用统计 ?[功能]",
"月功能调用统计 ?[功能]",
"我的功能调用统计",
"我的日功能调用统计 ?[功能]",
"我的周功能调用统计 ?[功能]",
"我的月功能调用统计 ?[功能]",
]
__plugin_type__ = ("数据统计", 1)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["功能调用统计"],
}
statistics = on_command(
"功能调用统计",
aliases={
"全局功能调用统计",
"全局日功能调用统计",
"全局周功能调用统计",
"全局月功能调用统计",
"日功能调用统计",
"周功能调用统计",
"月功能调用统计",
"我的功能调用统计",
"我的日功能调用统计",
"我的周功能调用统计",
"我的月功能调用统计",
},
priority=5,
block=True,
)
statistics_group_file = DATA_PATH / "statistics" / "_prefix_count.json"
statistics_user_file = DATA_PATH / "statistics" / "_prefix_user_count.json"
@statistics.handle()
async def _(bot: Bot, event: MessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if cmd[0][:2] == "全局":
if str(event.user_id) in bot.config.superusers:
data: dict = json.load(open(statistics_group_file, "r", encoding="utf8"))
if cmd[0][2] == '日':
_type = 'day_statistics'
elif cmd[0][2] == '周':
_type = 'week_statistics'
elif cmd[0][2] == '月':
_type = 'month_statistics'
else:
_type = 'total_statistics'
tmp_dict = {}
data = data[_type]
if _type in ["day_statistics", "total_statistics"]:
for key in data['total']:
tmp_dict[key] = data['total'][key]
else:
for group in data.keys():
if group != 'total':
for day in data[group].keys():
for plugin_name in data[group][day].keys():
if data[group][day][plugin_name] is not None:
if tmp_dict.get(plugin_name) is None:
tmp_dict[plugin_name] = 1
else:
tmp_dict[plugin_name] += data[group][day][plugin_name]
bar_graph = await init_bar_graph(tmp_dict, cmd[0])
await asyncio.get_event_loop().run_in_executor(None, bar_graph.gen_graph)
await statistics.finish(image(b64=bar_graph.pic2bs4()))
return
if cmd[0][:2] == "我的":
_type = "user"
key = str(event.user_id)
cmd = list(cmd)
cmd[0] = cmd[0][2:]
if not statistics_user_file.exists():
await statistics.finish("统计文件不存在...", at_sender=True)
else:
if not isinstance(event, GroupMessageEvent):
await statistics.finish("请在群内调用此功能...")
_type = "group"
key = str(event.group_id)
if not statistics_group_file.exists():
await statistics.finish("统计文件不存在...", at_sender=True)
plugin = ""
if cmd[0][0] == "日":
arg = "day_statistics"
elif cmd[0][0] == "周":
arg = "week_statistics"
elif cmd[0][0] == "月":
arg = "month_statistics"
else:
arg = "total_statistics"
if msg:
plugin = plugins2settings_manager.get_plugin_module(msg)
if not plugin:
if arg not in ["day_statistics", "total_statistics"]:
await statistics.finish("未找到此功能的调用...", at_sender=True)
if _type == "group":
data: dict = json.load(open(statistics_group_file, "r", encoding="utf8"))
if not data[arg].get(str(event.group_id)):
await statistics.finish("该群统计数据不存在...", at_sender=True)
else:
data: dict = json.load(open(statistics_user_file, "r", encoding="utf8"))
if not data[arg].get(str(event.user_id)):
await statistics.finish("该用户统计数据不存在...", at_sender=True)
day_index = data["day_index"]
data = data[arg][key]
if _type == "group":
name = await GroupInfo.get_group_info(event.group_id)
name = name.group_name if name else str(event.group_id)
else:
name = event.sender.card or event.sender.nickname
img = await generate_statistics_img(data, arg, name, plugin, day_index)
await statistics.send(image(b64=img))
async def generate_statistics_img(
data: dict, arg: str, name: str, plugin: str, day_index: int
):
try:
plugin = plugins2settings_manager.get_plugin_data(plugin)['cmd'][0]
except (KeyError, IndexError):
pass
bar_graph = None
if arg == "day_statistics":
bar_graph = await init_bar_graph(data, f"{name} 日功能调用统计")
elif arg == "week_statistics":
if plugin:
current_week = day_index % 7
week_lst = []
if current_week == 0:
week_lst = [1, 2, 3, 4, 5, 6, 7]
else:
for i in range(current_week + 1, 7):
week_lst.append(str(i))
for i in range(current_week + 1):
week_lst.append(str(i))
count = []
for i in range(7):
if int(week_lst[i]) == 7:
try:
count.append(data[str(0)][plugin])
except KeyError:
count.append(0)
else:
try:
count.append(data[str(week_lst[i])][plugin])
except KeyError:
count.append(0)
week_lst = ["7" if i == "0" else i for i in week_lst]
bar_graph = BuildMat(
y=count,
mat_type="line",
title=f"{name} 周 {plugin} 功能调用统计【为7天统计】",
x_index=week_lst,
display_num=True,
background=[
f"{IMAGE_PATH}/background/create_mat/{x}"
for x in os.listdir(f"{IMAGE_PATH}/background/create_mat")
],
bar_color=["*"],
)
else:
bar_graph = await init_bar_graph(update_data(data), f"{name} 周功能调用统计【为7天统计】")
elif arg == "month_statistics":
if plugin:
day_index = day_index % 30
day_lst = []
for i in range(day_index + 1, 30):
day_lst.append(i)
for i in range(day_index + 1):
day_lst.append(i)
count = [data[str(day_lst[i])][plugin] for i in range(30)]
day_lst = [str(x + 1) for x in day_lst]
bar_graph = BuildMat(
y=count,
mat_type="line",
title=f"{name} 月 {plugin} 功能调用统计【为30天统计】",
x_index=day_lst,
display_num=True,
background=[
f"{IMAGE_PATH}/background/create_mat/{x}"
for x in os.listdir(f"{IMAGE_PATH}/background/create_mat")
],
bar_color=["*"],
)
else:
bar_graph = await init_bar_graph(update_data(data), f"{name} 月功能调用统计【为30天统计】")
elif arg == "total_statistics":
bar_graph = await init_bar_graph(data, f"{name} 功能调用统计")
await asyncio.get_event_loop().run_in_executor(None, bar_graph.gen_graph)
return bar_graph.pic2bs4()
async def init_bar_graph(data: dict, title: str) -> BuildMat:
return await asyncio.get_event_loop().run_in_executor(None, _init_bar_graph, data, title)
def _init_bar_graph(data: dict, title: str) -> BuildMat:
bar_graph = BuildMat(
y=[data[x] for x in data.keys() if data[x] != 0],
mat_type="barh",
title=title,
x_index=[x for x in data.keys() if data[x] != 0],
display_num=True,
background=[
f"{IMAGE_PATH}/background/create_mat/{x}"
for x in os.listdir(f"{IMAGE_PATH}/background/create_mat")
],
bar_color=["*"],
)
return bar_graph
def update_data(data: dict):
tmp_dict = {}
for day in data.keys():
for plugin_name in data[day].keys():
# print(f'{day}:{plugin_name} = {data[day][plugin_name]}')
if data[day][plugin_name] is not None:
if tmp_dict.get(plugin_name) is None:
tmp_dict[plugin_name] = 1
else:
tmp_dict[plugin_name] += data[day][plugin_name]
return tmp_dict
+20
View File
@@ -0,0 +1,20 @@
from configs.config import Config as gConfig
from .api import *
from .auth import *
gConfig.add_plugin_config(
"web-ui",
"username",
"admin",
name="web-ui",
help_="前端管理用户名"
)
gConfig.add_plugin_config(
"web-ui",
"password",
None,
name="web-ui",
help_="前端管理密码"
)
+4
View File
@@ -0,0 +1,4 @@
from .group import *
from .plugins import *
from .request import *
from .system import *
+61
View File
@@ -0,0 +1,61 @@
from pydantic.error_wrappers import ValidationError
from services.log import logger
from utils.manager import group_manager
from utils.utils import get_bot
from ..auth import Depends, User, token_to_user
from ..config import *
@app.get("/webui/group")
async def _(user: User = Depends(token_to_user)) -> Result:
"""
获取群信息
"""
group_list_result = []
group_info = {}
if bot := get_bot():
group_list = await bot.get_group_list()
for g in group_list:
group_info[g["group_id"]] = Group(**g)
group_data = group_manager.get_data()
for group_id in group_data["group_manager"]:
try:
task_list = []
data = group_data["group_manager"][group_id]
for tn, status in data["group_task_status"].items():
task_list.append(
Task(
**{
"name": tn,
"nameZh": group_manager.get_task_data().get(tn) or tn,
"status": status,
}
)
)
data["task"] = task_list
if x := group_info.get(int(group_id)):
data["group"] = x
else:
continue
try:
group_list_result.append(GroupResult(**data))
except ValidationError:
pass
except Exception as e:
logger.error(f"WEB_UI /webui/group 发生错误 {type(e)}:{e}")
return Result(code=200, data=group_list_result)
@app.post("/webui/group")
async def _(group: GroupResult, user: User = Depends(token_to_user)) -> Result:
"""
修改群信息
"""
group_id = group.group.group_id
group_manager.set_group_level(group_id, group.level)
if group.status:
group_manager.turn_on_group_bot_status(group_id)
else:
group_manager.shutdown_group_bot_status(group_id)
return Result(code=200)
+142
View File
@@ -0,0 +1,142 @@
from configs.config import Config
from services.log import logger
from utils.manager import (plugins2block_manager, plugins2cd_manager,
plugins2count_manager, plugins2settings_manager,
plugins_manager)
from utils.utils import get_matchers
from ..auth import Depends, User, token_to_user
from ..config import *
plugin_name_list = None
@app.get("/webui/plugins")
def _(type_: Optional[str], user: User = Depends(token_to_user)) -> Result:
"""
获取插件列表
:param type_: 类型 normal, superuser, hidden, admin
"""
global plugin_name_list
if not plugin_name_list:
plugin_name_list = [x.plugin_name for x in get_matchers()]
plugin_list = []
plugin_data = plugins_manager.get_data()
for model in plugin_data:
if model in plugin_name_list:
try:
data = plugin_data.get(model)
data["model"] = model
plugin_name = data.get("plugin_name")
if (
(type_ == "hidden" and "[hidden]" not in plugin_name.lower())
or (type_ == "admin" and "[admin]" not in plugin_name.lower())
or (
type_ == "superuser"
and "[superuser]" not in plugin_name.lower()
)
):
continue
if type_ == "normal" and (
"[hidden]" in plugin_name.lower()
or "[admin]" in plugin_name.lower()
or "[superuser]" in plugin_name.lower()
):
continue
data = {"model": model}
if x := plugin_data.get(model):
if not x.get("status") and x.get("block_type") in [
"group",
"private",
"all",
]:
x["block_type"] = (
"群聊"
if x["block_type"] == "group"
else "私聊"
if x["block_type"] == "private"
else "全部"
)
data["plugin_manager"] = PluginManager(**x)
if x := plugins2settings_manager.get(model):
if x.get("cmd") and isinstance(x.get("cmd"), list):
x["cmd"] = ",".join(x["cmd"])
data["plugin_settings"] = PluginSettings(**x)
if x := plugins2cd_manager.get(model):
data["cd_limit"] = CdLimit(**x)
if x := plugins2block_manager.get(model):
data["block_limit"] = BlockLimit(**x)
if x := plugins2count_manager.get(model):
data["count_limit"] = CountLimit(**x)
if x := Config.get(model):
id_ = 0
tmp = []
for key in x.keys():
tmp.append(
PluginConfig(
**{
"key": key,
"help_": x[key].get("help"),
"id": id_,
**x[key],
}
)
)
id_ += 1
data["plugin_config"] = tmp
plugin_list.append(Plugin(**data))
except Exception as e:
logger.error(
f"WEB_UI GET /webui/plugins model:{model} 发生错误 {type(e)}:{e}"
)
return Result(
code=500,
data=f"WEB_UI GET /webui/plugins model:{model} 发生错误 {type(e)}:{e}",
)
return Result(code=200, data=plugin_list)
@app.post("/webui/plugins")
def _(plugin: Plugin, user: User = Depends(token_to_user)) -> Result:
"""
修改插件信息
:param plugin: 插件内容
"""
try:
if plugin.plugin_config:
for c in plugin.plugin_config:
if str(c.value).lower() in ["true", "false"] and (
c.default_value is None or isinstance(c.default_value, bool)
):
c.value = True if str(c.value).lower() == "true" else False
elif isinstance(
Config.get_config(plugin.model, c.key, c.value), int
) or isinstance(c.default_value, int):
c.value = int(c.value)
elif isinstance(
Config.get_config(plugin.model, c.key, c.value), float
) or isinstance(c.default_value, float):
c.value = float(c.value)
elif isinstance(c.value, str) and (
isinstance(Config.get_config(plugin.model, c.key, c.value), list)
or isinstance(c.default_value, list)
):
c.value = c.value.split(",")
Config.set_config(plugin.model, c.key, c.value)
Config.save(None, True)
else:
if plugin.plugin_settings:
for key, value in plugin.plugin_settings:
plugins2settings_manager.set_module_data(plugin.model, key, value)
if plugin.plugin_manager:
for key, value in plugin.plugin_manager:
plugins_manager.set_module_data(plugin.model, key, value)
except Exception as e:
logger.error(
f"WEB_UI POST /webui/plugins model:{plugin.model} 发生错误 {type(e)}:{e}"
)
return Result(
code=500,
data=f"WEB_UI POST /webui/plugins model:{plugin.model} 发生错误 {type(e)}:{e}",
)
return Result(code=200)
+65
View File
@@ -0,0 +1,65 @@
from utils.manager import requests_manager
from ..auth import token_to_user, Depends, User
from utils.utils import get_bot
from models.group_info import GroupInfo
from ..config import *
@app.get("/webui/request")
def _(type_: Optional[str], user: User = Depends(token_to_user)) -> Result:
req_data = requests_manager.get_data()
req_list = []
if type_ in ["group", "private"]:
req_data = req_data[type_]
for x in req_data:
req_data[x]["oid"] = x
req_list.append(RequestResult(**req_data[x]))
req_list.reverse()
return Result(code=200, data=req_list)
@app.delete("/webui/request")
def _(type_: Optional[str], user: User = Depends(token_to_user)) -> Result:
"""
清空请求
:param type_: 类型
"""
requests_manager.clear(type_)
return Result(code=200)
@app.post("/webui/request")
async def _(parma: RequestParma, user: User = Depends(token_to_user)) -> Result:
"""
操作请求
:param parma: 参数
"""
result = "ok"
flag = 3
if bot := get_bot():
if parma.handle == "approve":
if parma.type == "group":
rid = requests_manager.get_group_id(parma.id)
if await GroupInfo.get_group_info(rid):
await GroupInfo.set_group_flag(rid, 1)
else:
group_info = await bot.get_group_info(group_id=rid)
await GroupInfo.add_group_info(
rid,
group_info["group_name"],
group_info["max_member_count"],
group_info["member_count"],
1,
)
flag = await requests_manager.approve(bot, parma.id, parma.type)
elif parma.handle == "refuse":
flag = await requests_manager.refused(bot, parma.id, parma.type)
elif parma.handle == "delete":
requests_manager.delete_request(parma.id, parma.type)
if parma.handle != "delete":
if flag == 1:
result = "该请求已失效"
requests_manager.delete_request(parma.id, parma.type)
elif flag == 2:
result = "未找到此Id"
return Result(code=200, data=result)
+219
View File
@@ -0,0 +1,219 @@
import asyncio
import os
from pathlib import Path
import psutil
import ujson as json
from configs.path_config import (
DATA_PATH,
FONT_PATH,
IMAGE_PATH,
LOG_PATH,
RECORD_PATH,
TEMP_PATH,
TEXT_PATH,
)
from services.log import logger
from utils.http_utils import AsyncHttpx
from ..auth import Depends, User, token_to_user
from ..config import *
CPU_DATA_PATH = DATA_PATH / "system" / "cpu.json"
MEMORY_DATA_PATH = DATA_PATH / "system" / "memory.json"
DISK_DATA_PATH = DATA_PATH / "system" / "disk.json"
CPU_DATA_PATH.parent.mkdir(exist_ok=True, parents=True)
cpu_data = {"data": []}
memory_data = {"data": []}
disk_data = {"data": []}
@app.get("/webui/system")
async def _(user: User = Depends(token_to_user)) -> Result:
return await get_system_data()
@app.get("/webui/system/status")
async def _(user: User = Depends(token_to_user)) -> Result:
return Result(
code=200,
data=await asyncio.get_event_loop().run_in_executor(None, _get_system_status),
)
@app.get("/webui/system/disk")
async def _(type_: Optional[str] = None, user: User = Depends(token_to_user)) -> Result:
return Result(
code=200,
data=await asyncio.get_event_loop().run_in_executor(
None, _get_system_disk, type_
),
)
@app.get("/webui/system/statusList")
async def _(user: User = Depends(token_to_user)) -> Result:
global cpu_data, memory_data, disk_data
await asyncio.get_event_loop().run_in_executor(None, _get_system_status)
cpu_rst = cpu_data["data"][-10:] if len(cpu_data["data"]) > 10 else cpu_data["data"]
memory_rst = (
memory_data["data"][-10:]
if len(memory_data["data"]) > 10
else memory_data["data"]
)
disk_rst = (
disk_data["data"][-10:] if len(disk_data["data"]) > 10 else disk_data["data"]
)
return Result(
code=200,
data=SystemStatusList(
cpu_data=cpu_rst,
memory_data=memory_rst,
disk_data=disk_rst,
),
)
async def get_system_data():
"""
说明:
获取系统信息,资源文件大小,网络状态等
"""
baidu = 200
google = 200
try:
await AsyncHttpx.get("https://www.baidu.com/", timeout=5)
except Exception as e:
logger.warning(f"访问BaiDu失败... {type(e)}: {e}")
baidu = 404
try:
await AsyncHttpx.get("https://www.google.com/", timeout=5)
except Exception as e:
logger.warning(f"访问Google失败... {type(e)}: {e}")
google = 404
network = SystemNetwork(baidu=baidu, google=google)
disk = await asyncio.get_event_loop().run_in_executor(None, _get_system_disk)
status = await asyncio.get_event_loop().run_in_executor(None, _get_system_status)
return Result(
code=200,
data=SystemResult(
status=status,
network=network,
disk=disk,
check_time=datetime.now().replace(microsecond=0),
),
)
def _get_system_status() -> SystemStatus:
"""
说明:
获取系统信息等
"""
cpu = psutil.cpu_percent()
memory = psutil.virtual_memory().percent
disk = psutil.disk_usage("/").percent
save_system_data(cpu, memory, disk)
return SystemStatus(
cpu=cpu,
memory=memory,
disk=disk,
check_time=datetime.now().replace(microsecond=0),
)
def _get_system_disk(
type_: Optional[str],
) -> Union[SystemFolderSize, Dict[str, Union[float, datetime]]]:
"""
说明:
获取资源文件大小等
"""
if not type_:
disk = SystemFolderSize(
font_dir_size=_get_dir_size(FONT_PATH) / 1024 / 1024,
image_dir_size=_get_dir_size(IMAGE_PATH) / 1024 / 1024,
text_dir_size=_get_dir_size(TEXT_PATH) / 1024 / 1024,
record_dir_size=_get_dir_size(RECORD_PATH) / 1024 / 1024,
temp_dir_size=_get_dir_size(TEMP_PATH) / 1024 / 102,
data_dir_size=_get_dir_size(DATA_PATH) / 1024 / 1024,
log_dir_size=_get_dir_size(LOG_PATH) / 1024 / 1024,
check_time=datetime.now().replace(microsecond=0),
)
return disk
else:
if type_ == "image":
dir_path = IMAGE_PATH
elif type_ == "font":
dir_path = FONT_PATH
elif type_ == "text":
dir_path = TEXT_PATH
elif type_ == "record":
dir_path = RECORD_PATH
elif type_ == "data":
dir_path = DATA_PATH
elif type_ == "temp":
dir_path = TEMP_PATH
else:
dir_path = LOG_PATH
dir_map = {}
other_file_size = 0
for file in os.listdir(dir_path):
file = Path(dir_path / file)
if file.is_dir():
dir_map[file.name] = _get_dir_size(file) / 1024 / 1024
else:
other_file_size += os.path.getsize(file) / 1024 / 1024
dir_map["其他文件"] = other_file_size
dir_map["check_time"] = datetime.now().replace(microsecond=0)
return dir_map
def _get_dir_size(dir_path: Path) -> float:
"""
说明:
获取文件夹大小
参数:
:param dir_path: 文件夹路径
"""
size = 0
for root, dirs, files in os.walk(dir_path):
size += sum([os.path.getsize(os.path.join(root, name)) for name in files])
return size
def save_system_data(cpu: float, memory: float, disk: float):
"""
说明:
保存一些系统信息
参数:
:param cpu: cpu
:param memory: memory
:param disk: disk
"""
global cpu_data, memory_data, disk_data
if CPU_DATA_PATH.exists() and not cpu_data["data"]:
with open(CPU_DATA_PATH, "r") as f:
cpu_data = json.load(f)
if MEMORY_DATA_PATH.exists() and not memory_data["data"]:
with open(MEMORY_DATA_PATH, "r") as f:
memory_data = json.load(f)
if DISK_DATA_PATH.exists() and not disk_data["data"]:
with open(DISK_DATA_PATH, "r") as f:
disk_data = json.load(f)
now = str(datetime.now().time().replace(microsecond=0))
cpu_data["data"].append({"time": now, "data": cpu})
memory_data["data"].append({"time": now, "data": memory})
disk_data["data"].append({"time": now, "data": disk})
if len(cpu_data["data"]) > 50:
cpu_data["data"] = cpu_data["data"][-50:]
if len(memory_data["data"]) > 50:
memory_data["data"] = memory_data["data"][-50:]
if len(disk_data["data"]) > 50:
disk_data["data"] = disk_data["data"][-50:]
with open(CPU_DATA_PATH, "w") as f:
json.dump(cpu_data, f, indent=4, ensure_ascii=False)
with open(MEMORY_DATA_PATH, "w") as f:
json.dump(memory_data, f, indent=4, ensure_ascii=False)
with open(DISK_DATA_PATH, "w") as f:
json.dump(disk_data, f, indent=4, ensure_ascii=False)
+89
View File
@@ -0,0 +1,89 @@
from datetime import datetime, timedelta
from typing import Optional
from starlette import status
from fastapi import Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
from configs.config import Config
from jose import JWTError, jwt
import nonebot
app = nonebot.get_app()
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="webui/login")
class User(BaseModel):
username: str
password: str
class Token(BaseModel):
access_token: str
token_type: str
# USER_LIST = [
# User(username="admin", password="123")
# ]
def get_user(uname: str) -> Optional[User]:
username = Config.get_config("web-ui", "username")
password = Config.get_config("web-ui", "password")
if username and password and uname == username:
return User(username=username, password=password)
form_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
def create_token(user: User, expires_delta: Optional[timedelta] = None):
expire = datetime.utcnow() + expires_delta or timedelta(minutes=15)
return jwt.encode(
claims={"sub": user.username, "exp": expire},
key=SECRET_KEY,
algorithm=ALGORITHM
)
@app.post("/webui/login")
async def login_get_token(form_data: OAuth2PasswordRequestForm = Depends()):
user: User = get_user(form_data.username)
if not user or user.password != form_data.password:
raise form_exception
access_token = create_token(user=user, expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
return {"access_token": access_token, "token_type": "bearer"}
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
def token_to_user(token: str = Depends(oauth2_scheme)):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username, expire = payload.get("sub"), payload.get("exp")
user = get_user(username)
if user is None:
raise JWTError
except JWTError:
raise credentials_exception
return user
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8080)
+212
View File
@@ -0,0 +1,212 @@
from typing import Optional, List, Any, Union, Dict
from pydantic import BaseModel
from fastapi.middleware.cors import CORSMiddleware
from datetime import datetime
import nonebot
app = nonebot.get_app()
origins = ["http://localhost"]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
class CdLimit(BaseModel):
"""
Cd 限制
"""
cd: int
status: bool
check_type: str
limit_type: str
rst: Optional[str]
class BlockLimit(BaseModel):
"""
Block限制
"""
status: bool
check_type: str
limit_type: str
rst: Optional[str]
class CountLimit(BaseModel):
"""
Count限制
"""
max_count: int
status: bool
limit_type: bool
rst: Optional[str]
class PluginManager(BaseModel):
"""
插件信息
"""
plugin_name: str # 插件名称
status: Optional[bool] # 插件状态
error: Optional[bool] # 加载状态
version: Optional[float] # 版本
author: Optional[str] # 作者
block_type: Optional[str] # 禁用类型
class PluginSettings(BaseModel):
"""
插件基本设置
"""
level: Optional[int] # 群权限等级
default_status: Optional[bool] # 默认开关
limit_superuser: Optional[bool] # 是否限制超级用户
cmd: Optional[str] # cmd别名
cost_gold: Optional[int] # 花费金币限制
plugin_type: Optional[List[Union[str, int]]] # 帮助类型
class PluginConfig(BaseModel):
"""
插件配置项
"""
id: int
key: str
value: Optional[Any]
help_: Optional[str]
default_value: Optional[Any]
class Plugin(BaseModel):
"""
插件
"""
model: str # 模块
plugin_settings: Optional[PluginSettings]
plugin_manager: Optional[PluginManager]
plugin_config: Optional[List[PluginConfig]]
cd_limit: Optional[CdLimit]
block_limit: Optional[BlockLimit]
count_limit: Optional[CountLimit]
class Group(BaseModel):
"""
群组信息
"""
group_id: int
group_name: str
member_count: int
max_member_count: int
class Task(BaseModel):
"""
被动技能
"""
name: str
nameZh: str
status: bool
class GroupResult(BaseModel):
"""
群组返回数据
"""
group: Group
level: int
status: bool
close_plugins: List[str]
task: List[Task]
class RequestResult(BaseModel):
"""
好友/群组请求管理
"""
oid: str
id: int
flag: str
nickname: Optional[str]
level: Optional[int]
sex: Optional[str]
age: Optional[int]
from_: Optional[str]
comment: Optional[str]
invite_group: Optional[int]
group_name: Optional[str]
class RequestParma(BaseModel):
"""
操作请求接收数据
"""
id: int
handle: str
type: str
class SystemStatus(BaseModel):
"""
系统状态
"""
cpu: int
memory: int
disk: int
check_time: datetime
class SystemNetwork(BaseModel):
"""
系统网络状态
"""
baidu: int
google: int
class SystemFolderSize(BaseModel):
"""
资源文件占比
"""
font_dir_size: float
image_dir_size: float
text_dir_size: float
record_dir_size: float
temp_dir_size: float
data_dir_size: float
log_dir_size: float
check_time: datetime
class SystemStatusList(BaseModel):
"""
状态记录
"""
cpu_data: List[Dict[str, Union[float, str]]]
memory_data: List[Dict[str, Union[float, str]]]
disk_data: List[Dict[str, Union[float, str]]]
class SystemResult(BaseModel):
"""
系统api返回
"""
status: SystemStatus
network: SystemNetwork
disk: SystemFolderSize
check_time: datetime
class Result(BaseModel):
"""
总体返回
"""
code: int
data: Any
+21 -3
View File
@@ -1,7 +1,6 @@
from configs.config import Config
import nonebot
Config.add_plugin_config(
"word_bank",
"WORD_BANK_LEVEL [LEVEL]",
@@ -11,6 +10,25 @@ Config.add_plugin_config(
default_value=5
)
Config.add_plugin_config(
"word_bank",
"WORD_BANK_FUZZY",
False,
help_="模糊匹配",
default_value=False
)
Config.add_plugin_config(
"word_bank",
"WORD_BANK_KEY",
True,
help_="关键字匹配",
default_value=True
)
Config.add_plugin_config(
"word_bank",
"WORD_BANK_MIX",
25,
help_="查看词条时图片内最多显示条数",
default_value=25
)
nonebot.load_plugins("plugins/word_bank")
+10 -14
View File
@@ -5,7 +5,6 @@ from typing import Union
class WordBankBuilder:
def __init__(self, user_id: int, group_id: int, problem: str):
self._data = {
"user_id": user_id,
"group_id": group_id,
@@ -29,24 +28,21 @@ class WordBankBuilder:
"""
self._data["answer"] = answer
async def save(self):
async def save(self,search_type):
user_id = self._data["user_id"]
group_id = self._data["group_id"]
problem = self._data["problem"]
answer = self._data["answer"]
placeholder = self._data.get("placeholder")
await WordBank.add_problem_answer(user_id, group_id, problem, answer, placeholder)
return await WordBank.add_problem_answer(user_id, group_id, search_type,problem, answer, placeholder)
async def update(self, index):
user_id = self._data["user_id"]
group_id = self._data["group_id"]
problem = self._data["problem"]
answer = self._data["answer"]
placeholder = self._data.get("placeholder")
return await WordBank.update_problem_answer(user_id, group_id, problem, answer, index, placeholder)
def __str__(self):
return str(self._data)
+20 -11
View File
@@ -5,36 +5,40 @@ from configs.path_config import DATA_PATH
from nonebot.adapters.onebot.v11 import GroupMessageEvent
from utils.utils import get_message_text
from nonebot import on_message
from models.group_member_info import GroupInfoUser
__zx_plugin_name__ = "词库问答回复操作 [Hidden]"
data_dir = DATA_PATH / "word_bank"
data_dir.mkdir(parents=True, exist_ok=True)
message_handle = on_message(priority=5, block=True, rule=check)
message_handle = on_message(priority=6, block=True, rule=check)
@message_handle.handle()
async def _(event: GroupMessageEvent):
path = data_dir / f"{event.group_id}"
q = await WordBank.check(
event.group_id, get_message_text(event.json()), event.is_tome()
)
await message_handle.send(await get_one_answer(event, q.format, q.answer))
# 处理单条回答
async def get_one_answer(event, format, _answer, all=1):
path = data_dir / f"{event.group_id}"
placeholder_list = (
[
(x.split("<_s>")[0], x.split("<_s>")[1])
for x in q.format.split("<format>")[:-1]
for x in format.split("<format>")[:-1]
]
if q.format
if format
else []
)
answer = ""
_a = q.answer
_a = _answer
if not placeholder_list:
answer = _a
return answer
else:
for idx, placeholder in placeholder_list:
if placeholder.endswith("jpg"):
@@ -42,6 +46,11 @@ async def _(event: GroupMessageEvent):
path / placeholder
)
else:
answer += _a[: _a.find(f"[__placeholder_{idx}]")] + at(placeholder)
_a = _a[_a.find(f"[__placeholder_{idx}]") + len(f"[__placeholder_{idx}]") :]
await message_handle.send(answer)
if all == 1:
answer += _a[: _a.find(f"[__placeholder_{idx}]")] + at(placeholder)
else:
q = await GroupInfoUser.get_member_info(
int(placeholder), event.group_id)
answer += _a[: _a.find(f"[__placeholder_{idx}]")] + "@" + q.user_name
_a = _a[_a.find(f"[__placeholder_{idx}]") + len(f"[__placeholder_{idx}]"):]
return answer + _a
+121 -39
View File
@@ -5,6 +5,7 @@ from pathlib import Path
from configs.path_config import DATA_PATH
import re
import random
from configs.config import Config
class WordBank(db.Model):
@@ -21,17 +22,19 @@ class WordBank(db.Model):
@classmethod
async def add_problem_answer(
cls,
user_id: int,
group_id: Optional[int],
problem: str,
answer: str,
format_: Optional[List[Tuple[int, Union[int, str]]]],
cls,
user_id: int,
group_id: Optional[int],
search_type: [int],
problem: str,
answer: str,
format_: Optional[List[Tuple[int, Union[int, str]]]],
) -> bool:
"""
添加或新增一个问答
:param user_id: 用户id
:param group_id: 群号
:search_type: 问题类型,
:param problem: 问题
:param answer: 回答
:param format_: 格式化数据
@@ -42,12 +45,12 @@ class WordBank(db.Model):
for x, y in format_:
_str += f"{x}<_s>{y}<format>"
return await cls._problem_answer_handle(
user_id, group_id, problem, "add", answer=answer, format_=_str
user_id, group_id, problem, "add", search_type=search_type, answer=answer, format_=_str
)
@classmethod
async def delete_problem_answer(
cls, user_id: int, group_id: Optional[int], problem: str, index: Optional[int]
cls, user_id: int, group_id: Optional[int], problem: str, index: Optional[int]
) -> str:
"""
删除某问题一个或全部回答
@@ -60,9 +63,35 @@ class WordBank(db.Model):
user_id, group_id, problem, "delete", index=index
)
@classmethod
async def update_problem_answer(
cls,
user_id: int,
group_id: Optional[int],
problem: str,
answer: str,
index: Optional[int],
format_: Optional[List[Tuple[int, Union[int, str]]]],
) -> str:
"""
修改某问题一个或全部回答
:param user_id: 用户id
:param group_id: 群号
:param problem: 问题
:param index: 回答下标
"""
_str = None
if format_:
_str = ""
for x, y in format_:
_str += f"{x}<_s>{y}<format>"
return await cls._problem_answer_handle(
user_id, group_id, problem, "update", answer=answer, index=index, format_=_str
)
@classmethod
async def get_problem_answer(
cls, user_id: int, group_id: Optional[int], problem: str
cls, user_id: int, group_id: Optional[int], problem: str
) -> List[str]:
"""
获取问题的所有回答
@@ -87,7 +116,8 @@ class WordBank(db.Model):
q = await cls.query.where(
(cls.group_id == group_id) & (cls.problem == problem)
).gino.all()
return [x.answer for x in q] if q else None
return [(x.answer, x.format) for x in q] if q else None
@classmethod
async def get_group_all_problem(cls, group_id: int) -> List[str]:
@@ -128,22 +158,45 @@ class WordBank(db.Model):
return x
return None
else:
q = await cls.query.where(
(cls.group_id == group_id) & (cls.problem == problem)
).gino.all()
if problem:
FUZZY = Config.get_config("word_bank", "WORD_BANK_FUZZY")
KEY = Config.get_config("word_bank", "WORD_BANK_KEY")
q = await cls.query.where(
(cls.group_id == group_id) & (cls.problem == problem)
).gino.all()
if KEY and FUZZY:
q_fuzzy = await cls.query.where(
(cls.group_id == group_id) & (cls.search_type == 2) & (
cls.problem.contains(f'{problem}'))).gino.all()
q_key = await cls.query.where((cls.group_id == group_id) & (cls.search_type == 1)).gino.all()
q_key = [x for x in q_key if str(x.problem) in (problem)]
q += q_fuzzy + q_key
elif FUZZY:
q_fuzzy = await cls.query.where(
(cls.group_id == group_id) & (cls.search_type == 2) & (
cls.problem.contains(f'{problem}'))).gino.all()
q += q_fuzzy
elif KEY:
q_key = await cls.query.where((cls.group_id == group_id) & (cls.search_type == 1)).gino.all()
q_key = [x for x in q_key if str(x.problem) in (problem)]
q += q_key
else:
return None
return random.choice(q) if q else None
@classmethod
async def _problem_answer_handle(
cls,
user_id: int,
group_id: Optional[int],
problem: str,
type_: str,
*,
answer: Optional[str] = None,
index: Optional[int] = None,
format_: Optional[str] = None,
cls,
user_id: int,
group_id: Optional[int],
problem: str,
type_: str,
*,
search_type: [int] = 0,
answer: Optional[str] = None,
index: Optional[int] = None,
format_: Optional[str] = None,
) -> Union[List[Union[str, Tuple[str, str]]], bool, str]:
"""
添加或新增一个问答
@@ -166,27 +219,27 @@ class WordBank(db.Model):
else:
q = cls.query.where((cls.user_qq == user_id) & (cls.problem == problem))
if type_ == "add":
q = await q.where(cls.answer == answer).gino.all()
if not q or ".jpg" in format_:
await cls.create(
user_qq=user_id,
group_id=group_id,
problem=problem,
answer=answer,
format=format_,
create_time=datetime.now().date(),
update_time=datetime.now().date(),
)
q = await q.where((cls.answer == answer) & (cls.search_type == search_type)).gino.all()
try:
if not q or ".jpg" in format_:
await cls.create(
user_qq=user_id,
group_id=group_id,
search_type=search_type,
problem=problem,
answer=answer,
format=format_,
create_time=datetime.now().replace(microsecond=0),
update_time=datetime.now().replace(microsecond=0),
)
except:
return False
return True
elif type_ == "delete":
q = await q.with_for_update().gino.all()
if q:
path = DATA_PATH / "word_bank" / f"{group_id}"
if index is not None:
_q = [x.problem for x in q]
_q.sort()
prob = _q[index]
index = [x.problem for x in q].index(prob)
q = [q[index]]
answer = "\n".join([x.answer for x in q])
for x in q:
@@ -199,14 +252,43 @@ class WordBank(db.Model):
if _path.exists():
_path.unlink()
await cls.delete.where(
(cls.problem == problem)
(cls.update_time == x.update_time)
& (cls.problem == problem)
& (cls.answer == x.answer)
& (cls.group_id == group_id)
).gino.status()
return answer
elif type_ == "update":
new_format = format_
new_answer = answer
q = await q.with_for_update().gino.all()
if q:
path = DATA_PATH / "word_bank" / f"{group_id}"
if index is not None:
q = [q[index]]
else:
q = [q[0]]
for x in q:
format_ = x.format
if format_:
for sp in format_.split("<format>")[:-1]:
_, image_name = sp.split("<_s>")
if image_name.endswith("jpg"):
_path = path / image_name
if _path.exists():
_path.unlink()
await cls.update.values(answer=new_answer,
format=new_format,
update_time=datetime.now().replace(microsecond=0), ).where(
(cls.problem == problem)
& (cls.answer == x.answer)
& (cls.group_id == group_id)
& (cls.group_id == group_id)
& (cls.update_time == x.update_time)
).gino.status()
return True
elif type_ == "get":
q = await q.gino.all()
if q:
return [(x.answer, x.format.split("<format>")[:-1]) for x in q]
return False
+184 -73
View File
@@ -4,9 +4,9 @@ from services.log import logger
from configs.path_config import DATA_PATH
from utils.http_utils import AsyncHttpx
from ._data_source import WordBankBuilder
from configs.config import Config
from utils.message_builder import image
from utils.image_utils import text2image
from .message_handle import get_one_answer
from .model import WordBank
from nonebot.adapters.onebot.v11 import (
Bot,
@@ -18,6 +18,7 @@ from nonebot import on_command
import random
import os
import re
from configs.config import NICKNAME, Config
__zx_plugin_name__ = "词库问答 [Admin]"
__plugin_usage__ = """
@@ -25,45 +26,51 @@ usage:
对指定问题的随机回答,对相同问题可以设置多个不同回答
删除词条后每个词条的id可能会变化,请查看后再删除
指令:
添加词条问...答...:添加问答词条,可重复添加相同问题的不同回答
添加词条 ?[模糊/关键字]...答...:添加问答词条,可重复添加相同问题的不同回答
删除词条 [问题/下标] ?[下标]:删除指定词条指定或全部回答
修改词条 [问题/下标] ?[下标/新回答] [新回答]:修改指定词条指定回答默认修改为第一条
查看词条 ?[问题/下标]:查看全部词条或对应词条回答
示例:添加词条问谁是萝莉答是我
示例:删除词条 谁是萝莉
示例:删除词条 谁是萝莉 0
示例:删除词条 id:0
示例:修改词条 谁是萝莉 是你
示例:修改词条 谁是萝莉 0 是你
示例:修改词条 id:0 是你
示例:查看词条
示例:查看词条 谁是萝莉
示例:查看词条 id:0
""".strip()
__plugin_des__ = "自定义词条内容随机回复"
__plugin_cmd__ = [
"添加词条问...答..",
"添加词条 ?[模糊/关键字]问...答..",
"删除词条 [问题/下标] ?[下标]",
"修改词条 [问题/下标] ?[下标/新回答] [新回答]",
"查看词条 ?[问题/下标]",
]
__plugin_version__ = 0.1
__plugin_version__ = 0.2
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"admin_level": Config.get_config("word_bank", "WORD_BANK_LEVEL"),
"cmd": ["词库问答", "添加词条", "删除词条", "查看词条"],
"admin_level": Config.get_config("word_bank", "WORD_BANK_LEVEL [LEVEL]"),
"cmd": ["词库问答", "添加词条", "删除词条", "修改词条", "查看词条"],
}
data_dir = DATA_PATH / "word_bank"
data_dir.mkdir(parents=True, exist_ok=True)
add_word = on_command("添加词条", priority=5, block=True)
delete_word = on_command("删除词条", priority=5, block=True)
update_word = on_command("修改词条", priority=5, block=True)
show_word = on_command("显示词条", aliases={"查看词条"}, priority=5, block=True)
@add_word.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State, arg: Message = CommandArg()):
msg = str(arg)
r = re.search(r"^问(.+)\s?答([\s\S]*)", msg)
r = re.search(r"问(.+)\s?答([\s\S]*)", msg)
if not r:
await add_word.finish("未检测到词条问题...")
problem = r.group(1).strip()
@@ -75,10 +82,177 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State, arg: Message = C
idx = 0
for n in bot.config.nickname:
if n and problem.startswith(n):
_problem = f"[_to_me|{n}]" + problem[len(n) :]
_problem = f"[_to_me|{n}]" + problem[len(n):]
break
else:
_problem = problem
search_type = 0
if re.search("^关键字(.*)", msg):
search_type = 1
elif re.search("^模糊(.*)", msg):
search_type = 2
_builder = await get__builder(event, _problem, answer, idx)
if await _builder.save(search_type):
logger.info(f"已保存词条 问:{problem} 答:{msg}")
await add_word.send(f"已保存词条:{problem}")
else:
await delete_word.send("保存失败,可能是回答重复")
@delete_word.handle()
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if not msg:
await delete_word.finish("此命令之后需要跟随指定词条,通过“显示词条“查看")
index = None
_sp_msg = msg.split()
if len(_sp_msg) > 1:
if is_number(_sp_msg[-1]):
index = int(_sp_msg[-1])
msg = " ".join(_sp_msg[:-1])
problem = msg
if problem.startswith("id:"):
x = problem.split(":")[-1]
if not is_number(x) or int(x) < 0:
await delete_word.finish("id必须为数字且符合规范!")
p = await WordBank.get_group_all_problem(event.group_id)
if p:
problem = p[int(x)]
try:
if answer := await WordBank.delete_problem_answer(
event.user_id, event.group_id, problem, index
):
await delete_word.send(f"删除词条成功:{problem}\n回答:\n{answer}")
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 删除词条: {problem}"
)
else:
await delete_word.send(f"删除词条:{problem} 失败,可能该词条不存在")
except IndexError:
await delete_word.send("指定下标错误...请通过查看词条来确定..")
@update_word.handle()
async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
msg = str(arg)
if not msg:
await update_word.finish("此命令之后需要跟随指定词条,通过“显示词条“查看")
index = None
new_answer = None
problem = None
_sp_msg = msg.split()
len_msg = len(_sp_msg)
if 1 < len_msg:
problem = "".join(_sp_msg[0])
if len_msg == 3:
if is_number(_sp_msg[1]):
index = int(_sp_msg[1])
new_answer = "".join(_sp_msg[2:])
else:
new_answer = "".join(_sp_msg[1:])
else:
await update_word.finish("此命令之后需要跟随修改内容")
idx = 0
for n in bot.config.nickname:
if n and problem.startswith(n):
_problem = f"[_to_me|{n}]" + problem[len(n):]
break
else:
_problem = problem
_builder = await get__builder(event, _problem, new_answer, idx)
try:
if await _builder.update(index):
await update_word.send(f"修改词条成功:{problem}")
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 修改词条: {problem}"
)
else:
await update_word.send(f"修改词条:{problem} 失败,可能该词条不存在")
except IndexError:
await update_word.send("指定下标错误...请通过查看词条来确定..")
@show_word.handle()
async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if not msg:
_problem_list = await WordBank.get_group_all_problem(event.group_id)
if not _problem_list:
await show_word.finish("该群未收录任何词条..")
_problem_list = [f"\t{i}. {x}" for i, x in enumerate(_problem_list)]
long_problem_list = len(_problem_list)
max_line = Config.get_config("word_bank", "WORD_BANK_MIX")
if long_problem_list > max_line:
pic_list = []
mes_list = []
img_nu = long_problem_list // max_line
one_msg = "该群已收录的词条:"
await show_word.send(one_msg)
for i in range(img_nu + 1):
if _problem_list:
one_img = image(
b64=(await text2image("\n".join(_problem_list[:max_line]),
padding=10,
color="#f9f6f2",
)).pic2bs4()
)
if img_nu > 2:
pic_list.append(one_img)
else:
await show_word.send(one_img)
del _problem_list[:max_line]
if pic_list:
for img in pic_list:
data = {
"type": "node",
"data": {"name": f"{NICKNAME}", "uin": f"{bot.self_id}", "content": img},
}
mes_list.append(data)
await bot.send_group_forward_msg(group_id=event.group_id, messages=mes_list)
else:
await show_word.send(
image(
b64=(await text2image(
"该群已收录的词条:\n\n" + "\n".join(_problem_list),
padding=10,
color="#f9f6f2",
)).pic2bs4()
)
)
else:
_answer_list = await WordBank.get_group_all_answer(event.group_id, msg)
if not _answer_list:
await show_word.send("未收录该词条...")
else:
# 解析图片和@
_answer_img_nu_list = [await get_one_answer(event, format, answer, 0) for answer, format in _answer_list]
word_nu = len(_answer_img_nu_list)
img_nu = 0
answer = f"词条 {msg} 回答:"
for i, x, in enumerate(_answer_img_nu_list):
r = re.findall(rf"\[CQ:image,file=", str(x))
if r:
img_nu += len(r)
answer += "\n" + f"{i}." + x
if (img_nu > 2 and word_nu > 5) or word_nu > 10 or img_nu > 4:
data = {
"type": "node",
"data": {"name": f"{NICKNAME}", "uin": f"{bot.self_id}", "content": answer},
}
await bot.send_group_forward_msg(group_id=event.group_id, messages=data)
else:
await show_word.send(answer)
# await show_word.send(f"词条 {msg} 回答:\n" + "\n".join(_answer_list))
async def get__builder(event, _problem, answer, idx):
(data_dir / f"{event.group_id}").mkdir(exist_ok=True, parents=True)
_builder = WordBankBuilder(event.user_id, event.group_id, _problem)
for at_ in get_message_at(event.json()):
@@ -108,67 +282,4 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State, arg: Message = C
_builder.set_placeholder(idx, f"__placeholder_{rand}_{idx}.jpg")
idx += 1
_builder.set_answer(answer)
await _builder.save()
logger.info(f"已保存词条 问:{problem} 答:{msg}")
await add_word.send(f"已保存词条:{problem}")
@delete_word.handle()
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if not msg:
await delete_word.finish("此命令之后需要跟随指定词条,通过“显示词条“查看")
index = None
_sp_msg = msg.split()
if len(_sp_msg) > 1:
if is_number(_sp_msg[-1]):
index = int(_sp_msg[-1])
msg = " ".join(_sp_msg[:-1])
problem = msg
if problem.startswith("id:"):
x = problem.split(":")[-1]
if not is_number(x) or int(x) < 0:
await delete_word.finish("id必须为数字且符合规范!")
p = await WordBank.get_group_all_problem(event.group_id)
if p:
problem = p[int(x)]
try:
if answer := await WordBank.delete_problem_answer(
event.user_id, event.group_id, problem, index
):
await delete_word.send(f"删除词条成功:{problem}\n回答:\n{answer}")
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 删除词条: {problem}"
)
else:
await delete_word.send(f"删除词条:{problem} 失败,可能该词条不存在")
except IndexError:
await delete_word.send("指定下标错误...请通过查看词条来确定..")
@show_word.handle()
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if not msg:
_problem_list = await WordBank.get_group_all_problem(event.group_id)
if not _problem_list:
await show_word.finish("该群未收录任何词条..")
_problem_list = [f"\t{i}. {x}" for i, x in enumerate(_problem_list)]
await show_word.send(
image(
b64=(await text2image(
"该群已收录的词条:\n\n" + "\n".join(_problem_list),
padding=10,
color="#f9f6f2",
)).pic2bs4()
)
)
else:
_answer_list = await WordBank.get_group_all_answer(event.group_id, msg)
if not _answer_list:
await show_word.send("未收录该词条...")
else:
_answer_list = [f"{i}. {x}" for i, x in enumerate(_answer_list)]
await show_word.send(f"词条 {msg} 回答:\n" + "\n".join(_answer_list))
return _builder
Generated
+150 -24
View File
@@ -377,6 +377,26 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "tsinghua"
[[package]]
name = "ecdsa"
version = "0.17.0"
description = "ECDSA cryptographic signature library (pure python)"
category = "main"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
[package.dependencies]
six = ">=1.9.0"
[package.extras]
gmpy = ["gmpy"]
gmpy2 = ["gmpy2"]
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "tsinghua"
[[package]]
name = "fastapi"
version = "0.73.0"
@@ -520,14 +540,14 @@ reference = "tsinghua"
[[package]]
name = "httptools"
version = "0.2.0"
version = "0.4.0"
description = "A collection of framework independent HTTP protocol utils."
category = "main"
optional = false
python-versions = "*"
python-versions = ">=3.5.0"
[package.extras]
test = ["Cython (==0.29.22)"]
test = ["Cython (>=0.29.24,<0.30.0)"]
[package.source]
type = "legacy"
@@ -915,6 +935,19 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "tsinghua"
[[package]]
name = "pyasn1"
version = "0.4.8"
description = "ASN.1 types and codecs"
category = "main"
optional = false
python-versions = "*"
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "tsinghua"
[[package]]
name = "pydantic"
version = "1.9.0"
@@ -1023,6 +1056,45 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "tsinghua"
[[package]]
name = "python-jose"
version = "3.3.0"
description = "JOSE implementation in Python"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
ecdsa = "!=0.15"
pyasn1 = "*"
rsa = "*"
[package.extras]
cryptography = ["cryptography (>=3.4.0)"]
pycrypto = ["pycrypto (>=2.6.0,<2.7.0)", "pyasn1"]
pycryptodome = ["pycryptodome (>=3.3.1,<4.0.0)", "pyasn1"]
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "tsinghua"
[[package]]
name = "python-multipart"
version = "0.0.5"
description = "A streaming multipart parser for Python"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
six = ">=1.4.0"
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "tsinghua"
[[package]]
name = "pytz"
version = "2021.3"
@@ -1130,6 +1202,22 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "tsinghua"
[[package]]
name = "rsa"
version = "4.8"
description = "Pure-Python RSA implementation"
category = "main"
optional = false
python-versions = ">=3.6,<4"
[package.dependencies]
pyasn1 = ">=0.1.3"
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "tsinghua"
[[package]]
name = "ruamel.yaml"
version = "0.17.21"
@@ -1403,7 +1491,7 @@ reference = "tsinghua"
[[package]]
name = "uvicorn"
version = "0.17.5"
version = "0.17.6"
description = "The lightning-fast ASGI server."
category = "main"
optional = false
@@ -1414,7 +1502,7 @@ asgiref = ">=3.4.0"
click = ">=7.0"
colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""}
h11 = ">=0.8"
httptools = {version = ">=0.2.0,<0.4.0", optional = true, markers = "extra == \"standard\""}
httptools = {version = ">=0.4.0", optional = true, markers = "extra == \"standard\""}
python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""}
PyYAML = {version = ">=5.1", optional = true, markers = "extra == \"standard\""}
uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""}
@@ -1422,7 +1510,7 @@ watchgod = {version = ">=0.6", optional = true, markers = "extra == \"standard\"
websockets = {version = ">=10.0", optional = true, markers = "extra == \"standard\""}
[package.extras]
standard = ["websockets (>=10.0)", "httptools (>=0.2.0,<0.4.0)", "watchgod (>=0.6)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"]
standard = ["websockets (>=10.0)", "httptools (>=0.4.0)", "watchgod (>=0.6)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"]
[package.source]
type = "legacy"
@@ -1522,7 +1610,7 @@ reference = "tsinghua"
[metadata]
lock-version = "1.1"
python-versions = "^3.8"
content-hash = "2521c3cd7346f1f1b3b22276fa71ca9680447f85fc13fd23ede0e33d6f942423"
content-hash = "a467c2f5d871d65cd76363a2839023a38bde9b4aacd37f3019dca6fe0f199723"
[metadata.files]
aiofiles = [
@@ -1764,6 +1852,10 @@ dateparser = [
{file = "dateparser-1.1.0-py2.py3-none-any.whl", hash = "sha256:fec344db1f73d005182e214c0ff27313c748bbe0c1638ce9d48a809ddfdab2a0"},
{file = "dateparser-1.1.0.tar.gz", hash = "sha256:faa2b97f51f3b5ff1ba2f17be90de2b733fb6191f89b4058787473e8202f3044"},
]
ecdsa = [
{file = "ecdsa-0.17.0-py2.py3-none-any.whl", hash = "sha256:5cf31d5b33743abe0dfc28999036c849a69d548f994b535e527ee3cb7f3ef676"},
{file = "ecdsa-0.17.0.tar.gz", hash = "sha256:b9f500bb439e4153d0330610f5d26baaf18d17b8ced1bc54410d189385ea68aa"},
]
fastapi = [
{file = "fastapi-0.73.0-py3-none-any.whl", hash = "sha256:f0a618aff5f6942862f2d3f20f39b1c037e33314d1b8207fd1c3a2cca76dfd8c"},
{file = "fastapi-0.73.0.tar.gz", hash = "sha256:dcfee92a7f9a72b5d4b7ca364bd2b009f8fc10d95ed5769be20e94f39f7e5a15"},
@@ -1846,21 +1938,40 @@ httpcore = [
{file = "httpcore-0.14.7.tar.gz", hash = "sha256:7503ec1c0f559066e7e39bc4003fd2ce023d01cf51793e3c173b864eb456ead1"},
]
httptools = [
{file = "httptools-0.2.0-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:79dbc21f3612a78b28384e989b21872e2e3cf3968532601544696e4ed0007ce5"},
{file = "httptools-0.2.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:78d03dd39b09c99ec917d50189e6743adbfd18c15d5944392d2eabda688bf149"},
{file = "httptools-0.2.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:a23166e5ae2775709cf4f7ad4c2048755ebfb272767d244e1a96d55ac775cca7"},
{file = "httptools-0.2.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3ab1f390d8867f74b3b5ee2a7ecc9b8d7f53750bd45714bf1cb72a953d7dfa77"},
{file = "httptools-0.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a7594f9a010cdf1e16a58b3bf26c9da39bbf663e3b8d46d39176999d71816658"},
{file = "httptools-0.2.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:01b392a166adcc8bc2f526a939a8aabf89fe079243e1543fd0e7dc1b58d737cb"},
{file = "httptools-0.2.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:80ffa04fe8c8dfacf6e4cef8277347d35b0442c581f5814f3b0cf41b65c43c6e"},
{file = "httptools-0.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d5682eeb10cca0606c4a8286a3391d4c3c5a36f0c448e71b8bd05be4e1694bfb"},
{file = "httptools-0.2.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:a289c27ccae399a70eacf32df9a44059ca2ba4ac444604b00a19a6c1f0809943"},
{file = "httptools-0.2.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:813871f961edea6cb2fe312f2d9b27d12a51ba92545380126f80d0de1917ea15"},
{file = "httptools-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:cc9be041e428c10f8b6ab358c6b393648f9457094e1dcc11b4906026d43cd380"},
{file = "httptools-0.2.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b08d00d889a118f68f37f3c43e359aab24ee29eb2e3fe96d64c6a2ba8b9d6557"},
{file = "httptools-0.2.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:fd3b8905e21431ad306eeaf56644a68fdd621bf8f3097eff54d0f6bdf7262065"},
{file = "httptools-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:200fc1cdf733a9ff554c0bb97a4047785cfaad9875307d6087001db3eb2b417f"},
{file = "httptools-0.2.0.tar.gz", hash = "sha256:94505026be56652d7a530ab03d89474dc6021019d6b8682281977163b3471ea0"},
{file = "httptools-0.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcddfe70553be717d9745990dfdb194e22ee0f60eb8f48c0794e7bfeda30d2d5"},
{file = "httptools-0.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1ee0b459257e222b878a6c09ccf233957d3a4dcb883b0847640af98d2d9aac23"},
{file = "httptools-0.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceafd5e960b39c7e0d160a1936b68eb87c5e79b3979d66e774f0c77d4d8faaed"},
{file = "httptools-0.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fdb9f9ed79bc6f46b021b3319184699ba1a22410a82204e6e89c774530069683"},
{file = "httptools-0.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:abe829275cdd4174b4c4e65ad718715d449e308d59793bf3a931ee1bf7e7b86c"},
{file = "httptools-0.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7af6bdbd21a2a25d6784f6d67f44f5df33ef39b6159543b9f9064d365c01f919"},
{file = "httptools-0.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5d1fe6b6661022fd6cac541f54a4237496b246e6f1c0a6b41998ee08a1135afe"},
{file = "httptools-0.4.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:48e48530d9b995a84d1d89ae6b3ec4e59ea7d494b150ac3bbc5e2ac4acce92cd"},
{file = "httptools-0.4.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a113789e53ac1fa26edf99856a61e4c493868e125ae0dd6354cf518948fbbd5c"},
{file = "httptools-0.4.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8e2eb957787cbb614a0f006bfc5798ff1d90ac7c4dd24854c84edbdc8c02369e"},
{file = "httptools-0.4.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:7ee9f226acab9085037582c059d66769862706e8e8cd2340470ceb8b3850873d"},
{file = "httptools-0.4.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:701e66b59dd21a32a274771238025d58db7e2b6ecebbab64ceff51b8e31527ae"},
{file = "httptools-0.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6a1a7dfc1f9c78a833e2c4904757a0f47ce25d08634dd2a52af394eefe5f9777"},
{file = "httptools-0.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:903f739c9fb78dab8970b0f3ea51f21955b24b45afa77b22ff0e172fc11ef111"},
{file = "httptools-0.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54bbd295f031b866b9799dd39cb45deee81aca036c9bff9f58ca06726f6494f1"},
{file = "httptools-0.4.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3194f6d6443befa8d4db16c1946b2fc428a3ceb8ab32eb6f09a59f86104dc1a0"},
{file = "httptools-0.4.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cd1295f52971097f757edfbfce827b6dbbfb0f7a74901ee7d4933dff5ad4c9af"},
{file = "httptools-0.4.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:20a45bcf22452a10fa8d58b7dbdb474381f6946bf5b8933e3662d572bc61bae4"},
{file = "httptools-0.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d1f27bb0f75bef722d6e22dc609612bfa2f994541621cd2163f8c943b6463dfe"},
{file = "httptools-0.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7f7bfb74718f52d5ed47d608d507bf66d3bc01d4a8b3e6dd7134daaae129357b"},
{file = "httptools-0.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a522d12e2ddbc2e91842ffb454a1aeb0d47607972c7d8fc88bd0838d97fb8a2a"},
{file = "httptools-0.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2db44a0b294d317199e9f80123e72c6b005c55b625b57fae36de68670090fa48"},
{file = "httptools-0.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c286985b5e194ca0ebb2908d71464b9be8f17cc66d6d3e330e8d5407248f56ad"},
{file = "httptools-0.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3a4e165ca6204f34856b765d515d558dc84f1352033b8721e8d06c3e44930c3"},
{file = "httptools-0.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:72aa3fbe636b16d22e04b5a9d24711b043495e0ecfe58080addf23a1a37f3409"},
{file = "httptools-0.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:9967d9758df505975913304c434cb9ab21e2c609ad859eb921f2f615a038c8de"},
{file = "httptools-0.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f72b5d24d6730035128b238decdc4c0f2104b7056a7ca55cf047c106842ec890"},
{file = "httptools-0.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:29bf97a5c532da9c7a04de2c7a9c31d1d54f3abd65a464119b680206bbbb1055"},
{file = "httptools-0.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98993805f1e3cdb53de4eed02b55dcc953cdf017ba7bbb2fd89226c086a6d855"},
{file = "httptools-0.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d9b90bf58f3ba04e60321a23a8723a1ff2a9377502535e70495e5ada8e6e6722"},
{file = "httptools-0.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1a99346ebcb801b213c591540837340bdf6fd060a8687518d01c607d338b7424"},
{file = "httptools-0.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:645373c070080e632480a3d251d892cb795be3d3a15f86975d0f1aca56fd230d"},
{file = "httptools-0.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:34d2903dd2a3dd85d33705b6fde40bf91fc44411661283763fd0746723963c83"},
{file = "httptools-0.4.0.tar.gz", hash = "sha256:2c9a930c378b3d15d6b695fb95ebcff81a7395b4f9775c4f10a076beb0b2c1ff"},
]
httpx = [
{file = "httpx-0.22.0-py3-none-any.whl", hash = "sha256:e35e83d1d2b9b2a609ef367cc4c1e66fd80b750348b20cc9e19d1952fc2ca3f6"},
@@ -2228,6 +2339,10 @@ psutil = [
{file = "psutil-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:7d190ee2eaef7831163f254dc58f6d2e2a22e27382b936aab51c835fc080c3d3"},
{file = "psutil-5.9.0.tar.gz", hash = "sha256:869842dbd66bb80c3217158e629d6fceaecc3a3166d3d1faee515b05dd26ca25"},
]
pyasn1 = [
{file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"},
{file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"},
]
pydantic = [
{file = "pydantic-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cb23bcc093697cdea2708baae4f9ba0e972960a835af22560f6ae4e7e47d33f5"},
{file = "pydantic-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1d5278bd9f0eee04a44c712982343103bba63507480bfd2fc2790fa70cd64cf4"},
@@ -2288,6 +2403,13 @@ python-dotenv = [
{file = "python-dotenv-0.19.2.tar.gz", hash = "sha256:a5de49a31e953b45ff2d2fd434bbc2670e8db5273606c1e737cc6b93eff3655f"},
{file = "python_dotenv-0.19.2-py2.py3-none-any.whl", hash = "sha256:32b2bdc1873fd3a3c346da1c6db83d0053c3c62f28f1f38516070c4c8971b1d3"},
]
python-jose = [
{file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"},
{file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"},
]
python-multipart = [
{file = "python-multipart-0.0.5.tar.gz", hash = "sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"},
]
pytz = [
{file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"},
{file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"},
@@ -2450,6 +2572,10 @@ rfc3986 = [
{file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
{file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
]
rsa = [
{file = "rsa-4.8-py3-none-any.whl", hash = "sha256:95c5d300c4e879ee69708c428ba566c59478fd653cc3a22243eeb8ed846950bb"},
{file = "rsa-4.8.tar.gz", hash = "sha256:5c6bd9dc7a543b7fe4304a631f8a8a3b674e2bbfc49c2ae96200cdbe55df6b17"},
]
"ruamel.yaml" = [
{file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"},
{file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"},
@@ -2638,8 +2764,8 @@ urllib3 = [
{file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"},
]
uvicorn = [
{file = "uvicorn-0.17.5-py3-none-any.whl", hash = "sha256:8adddf629b79857b48b999ae1b14d6c92c95d4d7840bd86461f09bee75f1653e"},
{file = "uvicorn-0.17.5.tar.gz", hash = "sha256:c04a9c069111489c324f427501b3840d306c6b91a77b00affc136a840a3f45f1"},
{file = "uvicorn-0.17.6-py3-none-any.whl", hash = "sha256:19e2a0e96c9ac5581c01eb1a79a7d2f72bb479691acd2b8921fce48ed5b961a6"},
{file = "uvicorn-0.17.6.tar.gz", hash = "sha256:5180f9d059611747d841a4a4c4ab675edf54c8489e97f96d0583ee90ac3bfc23"},
]
uvloop = [
{file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d"},
+2
View File
@@ -38,6 +38,8 @@ black = "^22.1.0"
pypinyin = "^0.46.0"
dateparser = "^1.1.0"
cn2an = "^0.5.16"
python-jose = "^3.3.0"
python-multipart = "^0.0.5"
[tool.poetry.dev-dependencies]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 88 KiB

+6 -2
View File
@@ -14,16 +14,20 @@ _browser: Optional[Browser] = None
async def init(**kwargs) -> Optional[Browser]:
global _browser
if platform.system() == "Windows":
return None
try:
global _browser
browser = await async_playwright().start()
_browser = await browser.chromium.launch(**kwargs)
return _browser
except NotImplementedError:
logger.warning("win环境下 初始化playwright失败,相关功能将被限制....")
return None
except Exception as e:
logger.warning(f"启动chromium发生错误 {type(e)}:{e}")
if _browser:
await _browser.close()
return None
async def get_browser(**kwargs) -> Browser:
+10 -6
View File
@@ -295,7 +295,7 @@ class AsyncPlaywright:
path: Union[Path, str],
element: Union[str, List[str]],
*,
sleep: Optional[int] = None,
wait_time: Optional[int] = None,
viewport_size: Dict[str, int] = None,
wait_until: Optional[
Literal["domcontentloaded", "load", "networkidle"]
@@ -311,7 +311,7 @@ class AsyncPlaywright:
:param url: 网址
:param path: 存储路径
:param element: 元素选择
:param sleep: 延迟截取
:param wait_time: 等待截取超时时间
:param viewport_size: 窗口大小
:param wait_until: 等待类型
:param timeout: 超时限制
@@ -325,14 +325,18 @@ class AsyncPlaywright:
try:
page = await cls.goto(url, wait_until=wait_until, **kwargs)
await page.set_viewport_size(viewport_size)
if sleep:
await asyncio.sleep(sleep)
if isinstance(element, str):
card = await page.query_selector(element)
if wait_time:
card = await page.wait_for_selector(element, timeout=wait_time)
else:
card = await page.query_selector(element)
else:
card = page
for e in element:
card = await card.query_selector(e)
if wait_time:
card = await card.wait_for_selector(e, timeout=wait_time)
else:
card = await card.query_selector(e)
await card.screenshot(path=path, timeout=timeout, type=type_)
return image(path)
except Exception as e:
+18 -16
View File
@@ -1,17 +1,18 @@
import asyncio
from configs.path_config import IMAGE_PATH, FONT_PATH
from PIL import Image, ImageFile, ImageDraw, ImageFont, ImageFilter
from imagehash import ImageHash
from io import BytesIO
from matplotlib import pyplot as plt
from typing import Tuple, Optional, Union, List, Literal
from pathlib import Path
from math import ceil
import random
import cv2
import base64
import imagehash
import random
import re
from io import BytesIO
from math import ceil
from pathlib import Path
from typing import List, Literal, Optional, Tuple, Union
import cv2
import imagehash
from configs.path_config import FONT_PATH, IMAGE_PATH
from imagehash import ImageHash
from matplotlib import pyplot as plt
from PIL import Image, ImageDraw, ImageFile, ImageFilter, ImageFont
ImageFile.LOAD_TRUNCATED_IMAGES = True
Image.MAX_IMAGE_PIXELS = None
@@ -65,9 +66,7 @@ def compressed_image(
"""
in_file = IMAGE_PATH / in_file if isinstance(in_file, str) else in_file
if out_file:
out_file = (
IMAGE_PATH / out_file if isinstance(out_file, str) else out_file
)
out_file = IMAGE_PATH / out_file if isinstance(out_file, str) else out_file
else:
out_file = in_file
h, w, d = cv2.imread(str(in_file.absolute())).shape
@@ -673,9 +672,11 @@ class BuildImage:
ellipse_box = [0, 0, r2 - 2, r2 - 2]
mask = Image.new(
size=[int(dim * antialias) for dim in self.markImg.size],
mode='L', color='black')
mode="L",
color="black",
)
draw = ImageDraw.Draw(mask)
for offset, fill in (width / -2.0, 'black'), (width / 2.0, 'white'):
for offset, fill in (width / -2.0, "black"), (width / 2.0, "white"):
left, top = [(value + offset) * antialias for value in ellipse_box[:2]]
right, bottom = [(value - offset) * antialias for value in ellipse_box[2:]]
draw.ellipse([left, top, right, bottom], fill=fill)
@@ -1490,6 +1491,7 @@ async def text2image(
height = 0
_tmp = BuildImage(0, 0, font_size=font_size)
for x in text.split("\n"):
x = x if x.strip() else "正"
w, h = _tmp.getsize(x)
height += h
width = width if width > w else w
+2 -1
View File
@@ -2,6 +2,7 @@ from typing import Union, Optional
from pathlib import Path
from ruamel.yaml import YAML
import ujson as json
import copy
yaml = YAML(typ="safe")
@@ -47,7 +48,7 @@ class StaticData:
del self._data[key]
def get_data(self) -> dict:
return self._data
return copy.deepcopy(self._data)
def save(self, path: Union[str, Path] = None):
path = path if path else self.file
+38 -12
View File
@@ -2,7 +2,7 @@ from utils.manager.data_class import StaticData
from nonebot.adapters.onebot.v11 import Bot
from nonebot.adapters.onebot.v11.exception import ActionFailed
from services.log import logger
from typing import Optional
from typing import Optional, Literal
from utils.image_utils import BuildImage
from utils.utils import get_user_avatar
from pathlib import Path
@@ -75,7 +75,17 @@ class RequestManager(StaticData):
break
self.save()
async def approve(self, bot: Bot, id_: int, type_: str) -> Optional[int]:
def get_group_id(self, id_: int) -> Optional[int]:
"""
通过id获取群号
:param id_: id
"""
data = self._data["group"].get(str(id_))
if data:
return data["invite_group"]
return None
async def approve(self, bot: Bot, id_: int, type_: str) -> int:
"""
同意请求
:param bot: Bot
@@ -93,13 +103,28 @@ class RequestManager(StaticData):
"""
return await self._set_add_request(bot, id_, type_, False)
def clear(self):
def clear(self, type_: Optional[str] = None): # type_: Optional[Literal["group", "private"]] = None
"""
清空所有请求信息,无视请求
:param type_: 类型
"""
self._data = {"private": {}, "group": {}}
if type_:
self._data[type_] = {}
else:
self._data = {"private": {}, "group": {}}
self.save()
def delete_request(self, id_: int, type_: str): # type_: Literal["group", "private"]
"""
删除请求
:param id_: id
:param type_: 类型
"""
id_ = str(id_)
if self._data[type_].get(id_):
del self._data[type_][id_]
self.save()
def set_group_name(self, group_name: str, group_id: int):
"""
设置群聊名称
@@ -207,7 +232,7 @@ class RequestManager(StaticData):
async def _set_add_request(
self, bot: Bot, id_: int, type_: str, approve: bool
) -> Optional[int]:
) -> int:
"""
处理请求
:param bot: Bot
@@ -216,7 +241,7 @@ class RequestManager(StaticData):
:param approve: 是否同意
"""
id_ = str(id_)
if id_ in self._data[type_]:
if id_ in self._data[type_].keys():
try:
if type_ == "private":
await bot.set_friend_add_request(
@@ -235,12 +260,13 @@ class RequestManager(StaticData):
f"同意{self._data[type_][id_]['nickname']}({self._data[type_][id_]['id']})"
f"的{'好友' if type_ == 'private' else '入群'}请求失败了..."
)
return None
logger.info(
f"同意{self._data[type_][id_]['nickname']}({self._data[type_][id_]['id']})"
f"的{'好友' if type_ == 'private' else '入群'}请求..."
)
return 1 # flag失效
else:
logger.info(
f"{'同意' if approve else '拒绝'}{self._data[type_][id_]['nickname']}({self._data[type_][id_]['id']})"
f"的{'好友' if type_ == 'private' else '入群'}请求..."
)
del self._data[type_][id_]
self.save()
return rid
return None
return 2 # 未找到id
+46 -12
View File
@@ -1,16 +1,16 @@
from configs.path_config import IMAGE_PATH, RECORD_PATH
from nonebot.adapters.onebot.v11.message import MessageSegment
from configs.config import NICKNAME
from services.log import logger
from typing import Union, List
from pathlib import Path
import os
from typing import List, Union
from configs.config import NICKNAME
from configs.path_config import IMAGE_PATH, RECORD_PATH
from nonebot.adapters.onebot.v11.message import MessageSegment, Message
from services.log import logger
def image(
file: Union[str, Path, bytes] = None,
path: str = None,
b64: str = None,
file: Union[str, Path, bytes] = None,
path: str = None,
b64: str = None,
) -> Union[MessageSegment, str]:
"""
说明:
@@ -63,7 +63,9 @@ def record(voice_name: str, path: str = None) -> MessageSegment or str:
if len(voice_name.split(".")) == 1:
voice_name += ".mp3"
file = (
Path(RECORD_PATH) / path / voice_name if path else Path(RECORD_PATH) / voice_name
Path(RECORD_PATH) / path / voice_name
if path
else Path(RECORD_PATH) / voice_name
)
if "http" in voice_name:
return MessageSegment.record(voice_name)
@@ -96,7 +98,7 @@ def contact_user(qq: int) -> MessageSegment:
def share(
url: str, title: str, content: str = None, image_url: str = None
url: str, title: str, content: str = None, image_url: str = None
) -> MessageSegment:
"""
说明:
@@ -155,7 +157,7 @@ def music(type_: str, id_: int) -> MessageSegment:
def custom_forward_msg(
msg_list: List[str], uin: Union[int, str], name: str = f"这里是{NICKNAME}"
msg_list: List[str], uin: Union[int, str], name: str = f"这里是{NICKNAME}"
) -> List[dict]:
"""
生成自定义合并消息
@@ -176,3 +178,35 @@ def custom_forward_msg(
}
mes_list.append(data)
return mes_list
class MessageBuilder:
"""
MessageSegment构建工具
"""
def __init__(self, msg: Union[str, MessageSegment, Message]):
if msg:
if isinstance(msg, str):
self._msg = text(msg)
else:
self._msg = msg
else:
self._msg = text("")
def text(self, msg: str):
return MessageBuilder(self._msg + text(msg))
def image(
self,
file: Union[str, Path, bytes] = None,
path: str = None,
b64: str = None,
):
return MessageBuilder(self._msg + image(file, path, b64))
def at(self, qq: int):
return MessageBuilder(self._msg + at(qq))
def face(self, id_: int):
return MessageBuilder(self._msg + face(id_))