mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-09-29 00:32:06 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0ab78340a | ||
|
|
93539be492 | ||
|
|
c551e21766 | ||
|
|
ed342af97d | ||
|
|
17b87a2cb1 | ||
|
|
69ca02834d | ||
|
|
6cb4099a2e | ||
|
|
e75a4966b2 | ||
|
|
d55cba49bd | ||
|
|
0215abac30 | ||
|
|
2dcac5f009 | ||
|
|
0d8c64f7d1 | ||
|
|
55626645fb | ||
|
|
f7925fa259 | ||
|
|
c814ae08b1 | ||
|
|
24666368f7 | ||
|
|
a77e85b62d | ||
|
|
6319c7fce1 | ||
|
|
a45b95d5d4 | ||
|
|
188f6f0f99 | ||
|
|
a72f126d8f | ||
|
|
33eb1b83a8 | ||
|
|
2d9ebe2e6d | ||
|
|
262b098dec | ||
|
|
79d88065aa | ||
|
|
0f59895c3b | ||
|
|
610d588e34 | ||
|
|
f99abb2f98 | ||
|
|
b2b7560d05 | ||
|
|
07c16e5238 | ||
|
|
1741ea7d26 | ||
|
|
b5ce4fab31 | ||
|
|
062e4c2062 | ||
|
|
0adb66a8bf | ||
|
|
2aad517112 | ||
|
|
14d1512b1e | ||
|
|
3f533516af | ||
|
|
f1354308fd | ||
|
|
c06248e46b | ||
|
|
9c8eaed938 | ||
|
|
2081302e32 | ||
|
|
678fc19f2d | ||
|
|
adc2757e8e | ||
|
|
82a1261371 | ||
|
|
067da9dc57 |
@@ -142,7 +142,6 @@ test.py
|
||||
server_ip.py
|
||||
member_activity_handle.py
|
||||
Yu-Gi-Oh/
|
||||
search_image/
|
||||
black_word/
|
||||
csgo/
|
||||
fantasy_card/
|
||||
|
||||
@@ -32,6 +32,12 @@
|
||||
## 普通帮助图片
|
||||

|
||||
|
||||
## 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
@@ -1 +1 @@
|
||||
__version__: v0.1.4.2
|
||||
__version__: v0.1.5.0
|
||||
@@ -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,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)} 条管理员命令')
|
||||
|
||||
|
||||
|
||||
@@ -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..")
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import nonebot
|
||||
|
||||
nonebot.load_plugins("basic_plugins/chat_history")
|
||||
@@ -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))
|
||||
@@ -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):
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
"^[[_task\|(.*)]]", str(data["message"]["text"]).strip()
|
||||
"^[[_task\|(.*)]]",
|
||||
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)
|
||||
|
||||
@@ -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必须为纯数字!")
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
@@ -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))
|
||||
|
||||
@@ -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
@@ -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("发生了一些错误.....")
|
||||
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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}"
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
@@ -144,7 +144,6 @@ class Setu(db.Model):
|
||||
return _tmp_local_id
|
||||
return -1
|
||||
|
||||
|
||||
@classmethod
|
||||
async def update_setu_data(
|
||||
cls,
|
||||
|
||||
@@ -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
|
||||
@@ -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_="前端管理密码"
|
||||
)
|
||||
@@ -0,0 +1,4 @@
|
||||
from .group import *
|
||||
from .plugins import *
|
||||
from .request import *
|
||||
from .system import *
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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"},
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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,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
|
||||
|
||||
@@ -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
@@ -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_))
|
||||
|
||||
Reference in New Issue
Block a user