Compare commits

...
27 Commits
Author SHA1 Message Date
HibiKier d55cba49bd Update data_source.py 2022-04-10 22:31:07 +08:00
HibiKier 0215abac30 update v0.1.4.7 2022-04-10 22:19:50 +08:00
HibiKier 2dcac5f009 Update README.md 2022-04-09 18:09:22 +08:00
HibiKier 0d8c64f7d1 Merge pull request #144 from AkashiCoin/main
fix: 更新问题,戳一戳图片路径问题
2022-04-09 18:08:49 +08:00
AkashiCoin 55626645fb fix: 更新问题,戳一戳图片路径问题 2022-04-09 14:56:30 +08:00
HibiKier f7925fa259 update genshin.query_role 2022-04-08 01:09:29 +08:00
HibiKier c814ae08b1 Update README.md 2022-04-06 18:52:01 +08:00
HibiKier 24666368f7 Merge pull request #143 from yajiwa/main
update search_type
2022-04-06 18:50:59 +08:00
yajiwa a77e85b62d fix bug 2022-04-06 13:17:08 +08:00
yajiwa 6319c7fce1 fix 2022-04-05 22:38:43 +08:00
yajiwa a45b95d5d4 update search_type 2022-04-05 22:15:47 +08:00
HibiKier 188f6f0f99 Update README.md 2022-04-05 19:42:20 +08:00
HibiKier a72f126d8f fix web_ui api 2022-04-05 13:14:29 +08:00
HibiKier 33eb1b83a8 Update README.md 2022-04-04 23:52:24 +08:00
HibiKier 2d9ebe2e6d Merge pull request #141 from yajiwa/main
Update word_bank plus
2022-04-04 23:50:34 +08:00
yajiwa 262b098dec Merge branch 'HibiKier:main' into main 2022-04-04 22:04:27 +08:00
HibiKier 79d88065aa Update README.md 2022-04-04 20:56:02 +08:00
HibiKier 0f59895c3b update v0.1.4.5 2022-04-04 20:33:37 +08:00
yajiwa 610d588e34 Merge branch 'zhenxun_yajiwa' 2022-04-03 20:17:08 +08:00
yajiwa f99abb2f98 fix config 2022-04-03 20:11:00 +08:00
yajiwa b2b7560d05 Update word_bank_plus 2022-04-03 20:05:18 +08:00
yajiwa 07c16e5238 Merge pull request #1 from yajiwa/zhenxun_yajiwa
Update Modify the entry
2022-04-01 23:56:57 +08:00
yajiwa 1741ea7d26 Update Modify the entry 2022-04-01 23:54:40 +08:00
HibiKier b5ce4fab31 Merge pull request #135 from Sakuracio/main
fix GB18030 to UTF-8.
2022-03-28 00:04:32 +08:00
MangataAkihi 062e4c2062 fix GB18030 to UTF-8. 2022-03-26 23:43:21 +08:00
MangataAkihi 0adb66a8bf fix GB18030 to UTF-8. 2022-03-26 23:43:06 +08:00
HibiKier 2aad517112 fix statistics_handle 2022-03-21 18:43:06 +08:00
39 changed files with 1390 additions and 258 deletions
+47 -5
View File
@@ -32,6 +32,12 @@
## 普通帮助图片 ## 普通帮助图片
![x](https://github.com/HibiKier/zhenxun_bot/blob/0.0.8.2/docs_image/3238573864-836268675-E2FFBB2AC143EAF4DDDF150438508721.png) ![x](https://github.com/HibiKier/zhenxun_bot/blob/0.0.8.2/docs_image/3238573864-836268675-E2FFBB2AC143EAF4DDDF150438508721.png)
## Web UI
[zhenxun_bot_webui](https://github.com/HibiKier/zhenxun_bot_webui)
## 一键安装脚本
[zhenxun_bot-deploy](https://github.com/AkashiCoin/zhenxun_bot-deploy)
## 提供符合真寻标准的插件仓库 ## 提供符合真寻标准的插件仓库
[AkashiCoin/nonebot_plugins_zhenxun_bot](https://github.com/AkashiCoin/nonebot_plugins_zhenxun_bot) [AkashiCoin/nonebot_plugins_zhenxun_bot](https://github.com/AkashiCoin/nonebot_plugins_zhenxun_bot)
@@ -236,6 +242,42 @@ __Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
## 更新 ## 更新
### 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] ### 2022/3/18 \[v0.1.4.4]
* 修复戳一戳无法功能关闭与ban禁用 * 修复戳一戳无法功能关闭与ban禁用
@@ -314,15 +356,15 @@ __Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
* epic restful 替换 [@pull/119](https://github.com/HibiKier/zhenxun_bot/pull/119) * epic restful 替换 [@pull/119](https://github.com/HibiKier/zhenxun_bot/pull/119)
* fix: 修复远古时期残留的epic推送问题 [@pull/122](https://github.com/HibiKier/zhenxun_bot/pull/122) * fix: 修复远古时期残留的epic推送问题 [@pull/122](https://github.com/HibiKier/zhenxun_bot/pull/122)
### 2021/2/11 ### 2022/2/11
* 修复pix不使用反代无法下载图片 * 修复pix不使用反代无法下载图片
### 2021/2/10 \[v0.1.1] ### 2022/2/10 \[v0.1.1]
* 修复购买道具出错 * 修复购买道具出错
### 2021/2/9 \[v0.1] ### 2022/2/9 \[v0.1]
* 新增原神自动签到和手动签到 * 新增原神自动签到和手动签到
* 新增原神树脂提醒 * 新增原神树脂提醒
@@ -332,7 +374,7 @@ __Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
* 修复修改商品时限制时间出错 * 修复修改商品时限制时间出错
* 修复超时商品依旧可以被购买 * 修复超时商品依旧可以被购买
### 2021/1/16 \[v0.0.9.0] ### 2022/1/16 \[v0.0.9.0]
* Ai提供文本敏感词过滤器 * Ai提供文本敏感词过滤器
* 疫情插件适配新版腾讯API * 疫情插件适配新版腾讯API
@@ -351,7 +393,7 @@ __Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
* “send_success_msg”(发送成功的交互信息->即:使用道具 {name} {num} 次成功) * “send_success_msg”(发送成功的交互信息->即:使用道具 {name} {num} 次成功)
* “_max_num_limit”(该道具单次使用的最多个数,默认1) * “_max_num_limit”(该道具单次使用的最多个数,默认1)
### 2021/1/5 \[v0.0.8.2] ### 2022/1/5 \[v0.0.8.2]
* 提供金币消费hook,可在plugins2settings.yaml中配置该功能需要消费的金币 * 提供金币消费hook,可在plugins2settings.yaml中配置该功能需要消费的金币
* 商店插件将作为内置插件移动至basic_plugins * 商店插件将作为内置插件移动至basic_plugins
+1 -1
View File
@@ -1 +1 @@
__version__: v0.1.4.4 __version__: v0.1.4.7
+11 -5
View File
@@ -24,11 +24,17 @@ def switch_rule(event: Event) -> bool:
_data = plugins2settings_manager.get_data() _data = plugins2settings_manager.get_data()
for key in _data: for key in _data:
try: try:
for x in _data[key]["cmd"]: if isinstance(_data[key]["cmd"], list):
cmd.append(f"开启{x}") for x in _data[key]["cmd"]:
cmd.append(f"关闭{x}") cmd.append(f"开启{x}")
cmd.append(f"开启 {x}") 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: except KeyError:
pass pass
msg = get_message_text(event.json()).split() msg = get_message_text(event.json()).split()
+37
View File
@@ -0,0 +1,37 @@
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
from models.chat_history import ChatHistory
from ._rule import rule
from configs.config import Config
from nonebot import on_message
Config.add_plugin_config(
"chat_history",
"FLAG",
True,
help_="是否开启消息自从存储",
name="消息存储",
default_value=True
)
chat_history = on_message(rule=rule)
# test = on_command("aa")
@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))
+6
View File
@@ -0,0 +1,6 @@
from nonebot.adapters.onebot.v11 import Event, MessageEvent
from configs.config import Config
def rule(event: Event) -> bool:
return Config.get_config("chat_history", "FLAG") and isinstance(event, MessageEvent)
@@ -74,7 +74,7 @@ def init_plugins_settings(data_path: str):
if plugin_settings.get('cost_gold') is None: if plugin_settings.get('cost_gold') is None:
plugin_settings['cost_gold'] = 0 plugin_settings['cost_gold'] = 0
if ( if (
plugin_settings["cmd"] is not None plugin_settings.get("cmd") is not None
and plugin_name not in plugin_settings["cmd"] and plugin_name not in plugin_settings["cmd"]
): ):
plugin_settings["cmd"].append(plugin_name) plugin_settings["cmd"].append(plugin_name)
+20 -15
View File
@@ -82,15 +82,16 @@ async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandAr
if is_number(id_): if is_number(id_):
id_ = int(id_) id_ = int(id_)
if cmd[:2] == "同意": if cmd[:2] == "同意":
if await requests_manager.approve(bot, id_, "private"): flag = await requests_manager.approve(bot, id_, "private")
await friend_handle.send("同意好友请求成功..")
else:
await friend_handle.send("同意好友请求失败,可能是未找到此id的请求..")
else: else:
if await requests_manager.refused(bot, id_, "private"): flag = await requests_manager.refused(bot, id_, "private")
await friend_handle.send("拒绝好友请求成功..") if flag == 1:
else: await friend_handle.send(f"{cmd[:2]}好友请求失败,该请求已失效..")
await friend_handle.send("拒绝好友请求失败,可能是未找到此id的请求..") 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: else:
await friend_handle.send("id必须为纯数字!") 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()): async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
cmd = cmd[0] cmd = cmd[0]
id_ = arg.extract_plain_text().strip() id_ = arg.extract_plain_text().strip()
flag = None
if is_number(id_): if is_number(id_):
id_ = int(id_) id_ = int(id_)
if cmd[:2] == "同意": if cmd[:2] == "同意":
rid = requests_manager.get_group_id(id_) rid = requests_manager.get_group_id(id_)
if rid: if rid:
await friend_handle.send("同意群聊请求成功..")
if await GroupInfo.get_group_info(rid): if await GroupInfo.get_group_info(rid):
await GroupInfo.set_group_flag(rid, 1) await GroupInfo.set_group_flag(rid, 1)
else: else:
@@ -116,14 +117,18 @@ async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandAr
group_info["member_count"], group_info["member_count"],
1 1
) )
await requests_manager.approve(bot, id_, "group") flag = await requests_manager.approve(bot, id_, "group")
else: else:
await friend_handle.send("同意群聊请求失败,可能是未找到此id的请求..") await friend_handle.send("同意群聊请求失败,未找到此id的请求..")
else: else:
if await requests_manager.refused(bot, id_, "group"): flag = await requests_manager.refused(bot, id_, "group")
await friend_handle.send("拒绝群聊请求成功..") if flag == 1:
else: await friend_handle.send(f"{cmd[:2]}群聊请求失败,该请求已失效..")
await friend_handle.send("拒绝群聊请求失败,可能是未找到此id的请求..") 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: else:
await friend_handle.send("id必须为纯数字!") await friend_handle.send("id必须为纯数字!")
+19 -3
View File
@@ -11,14 +11,19 @@ class ConfigsManager:
def __init__(self, file: Path): def __init__(self, file: Path):
self._data: dict = {} self._data: dict = {}
self._simple_data: dict = {}
self._admin_level_data = [] self._admin_level_data = []
self._simple_file = Path() / "configs" / "config.yaml"
if file: if file:
file.parent.mkdir(exist_ok=True, parents=True) file.parent.mkdir(exist_ok=True, parents=True)
self.file = file self.file = file
_yaml = YAML()
if file.exists(): if file.exists():
_yaml = YAML()
with open(file, "r", encoding="utf8") as f: with open(file, "r", encoding="utf8") as f:
self._data = _yaml.load(f) self._data = _yaml.load(f)
if self._simple_file.exists():
with open(self._simple_file, "r", encoding="utf8") as f:
self._simple_data = _yaml.load(f)
def add_plugin_config( def add_plugin_config(
self, self,
@@ -71,6 +76,7 @@ class ConfigsManager:
""" """
if module in self._data.keys(): if module in self._data.keys():
del self._data[module] del self._data[module]
self.save()
def set_config(self, module: str, key: str, value: str): def set_config(self, module: str, key: str, value: str):
""" """
@@ -80,8 +86,10 @@ class ConfigsManager:
:param value: 值 :param value: 值
""" """
if module in self._data.keys(): if module in self._data.keys():
if self._data[module].get(key) is not None: if self._data[module].get(key) is not None and self._data[module][key] != value:
self._data[module][key]["value"] = value self._data[module][key]["value"] = value
self._simple_data[module][key] = value
self.save()
def set_help(self, module: str, key: str, help_: str): def set_help(self, module: str, key: str, help_: str):
""" """
@@ -93,6 +101,7 @@ class ConfigsManager:
if module in self._data.keys(): if module in self._data.keys():
if self._data[module].get(key) is not None: if self._data[module].get(key) is not None:
self._data[module][key]["help"] = help_ self._data[module][key]["help"] = help_
self.save()
def set_default_value(self, module: str, key: str, value: str): def set_default_value(self, module: str, key: str, value: str):
""" """
@@ -104,6 +113,7 @@ class ConfigsManager:
if module in self._data.keys(): if module in self._data.keys():
if self._data[module].get(key) is not None: if self._data[module].get(key) is not None:
self._data[module][key]["default_value"] = value self._data[module][key]["default_value"] = value
self.save()
def get_config(self, module: str, key: str, default: Optional[Any] = None) -> Optional[Any]: def get_config(self, module: str, key: str, default: Optional[Any] = None) -> Optional[Any]:
""" """
@@ -142,11 +152,17 @@ class ConfigsManager:
if key in self._data.keys(): if key in self._data.keys():
return self._data[key] 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 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 path = path if path else self.file
with open(path, "w", encoding="utf8") as f: with open(path, "w", encoding="utf8") as f:
yaml.dump( yaml.dump(
+123
View File
@@ -0,0 +1,123 @@
from datetime import datetime, timedelta
from typing import List, Literal, Optional
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_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"]],
days: Optional[int],
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 days:
query = query.where(
cls.create_time >= datetime.now() - timedelta(days=days)
)
return query
+1
View File
@@ -76,6 +76,7 @@ async def _(bot: Bot, event: MessageEvent):
f"问题:{msg} ---- 回答:{result}" f"问题:{msg} ---- 回答:{result}"
) )
if result: if result:
result = str(result)
for t in Config.get_config("ai", "TEXT_FILTER"): for t in Config.get_config("ai", "TEXT_FILTER"):
result = result.replace(t, "*") result = result.replace(t, "*")
await ai.finish(Message(result)) await ai.finish(Message(result))
+1 -1
View File
@@ -57,7 +57,7 @@ async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str)
if random.random() < 0.2: if random.random() < 0.2:
if nickname.find("大人") == -1: if nickname.find("大人") == -1:
nickname += "大~人~" nickname += "大~人~"
rst = rst.replace("小主人", nickname).replace("小朋友", nickname) rst = str(rst).replace("小主人", nickname).replace("小朋友", nickname)
ai_message_manager.add_result(user_id, rst) ai_message_manager.add_result(user_id, rst)
return rst return rst
+10 -10
View File
@@ -56,15 +56,15 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
) )
if is_number(msg) and 0 < int(msg) <= 50: if is_number(msg) and 0 < int(msg) <= 50:
url = wbtop_data[int(msg) - 1]["url"] url = wbtop_data[int(msg) - 1]["url"]
try: await wbtop.send("开始截取数据...")
await wbtop.send("开始截取数据...") img = await AsyncPlaywright.screenshot(
img = await AsyncPlaywright.screenshot( url,
url, f"{IMAGE_PATH}/temp/wbtop_{event.user_id}.png",
f"{IMAGE_PATH}/temp/wbtop_{event.user_id}.png", "#pl_feedlist_index",
"#pl_feedlist_index", wait_time=5
sleep=5 )
) if img:
await wbtop.send(img) await wbtop.send(img)
except Exception as e: else:
logger.error(f"微博热搜截图出错... {type(e)}: {e}")
await wbtop.send("发生了一些错误.....") await wbtop.send("发生了一些错误.....")
+1 -1
View File
@@ -211,7 +211,7 @@ async def _():
await sub_manager.reload_sub_data() await sub_manager.reload_sub_data()
sub = await sub_manager.random_sub_data() sub = await sub_manager.random_sub_data()
if sub: 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) rst = await get_sub_status(sub.sub_id, sub.sub_type)
await send_sub_msg(rst, sub, bot) await send_sub_msg(rst, sub, bot)
if sub.sub_type == "live": if sub.sub_type == "live":
+25 -21
View File
@@ -311,28 +311,32 @@ async def get_user_dynamic(
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"] dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
if local_user.dynamic_upload_time < dynamic_upload_time: if local_user.dynamic_upload_time < dynamic_upload_time:
page = await browser.new_page() page = await browser.new_page()
await page.goto( try:
f"https://space.bilibili.com/{local_user.uid}/dynamic", await page.goto(
wait_until="networkidle", f"https://space.bilibili.com/{local_user.uid}/dynamic",
timeout=10000, wait_until="networkidle",
) timeout=10000,
await page.set_viewport_size({"width": 2560, "height": 1080}) )
# 删除置顶 await page.set_viewport_size({"width": 2560, "height": 1080})
await page.evaluate( # 删除置顶
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) { card = await page.query_selector(".card")
x.remove(); # 截图并保存
} await card.screenshot(
""" path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
) timeout=100000,
card = await page.query_selector(".card") )
# 截图并保存 except Exception as e:
await card.screenshot( logger.error(f"B站订阅:获取用户动态 发送错误 {type(e)}:{e}")
path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg", finally:
timeout=100000, await page.close()
)
await page.close()
return ( return (
image( image(
f"{local_user.sub_id}_{dynamic_upload_time}.jpg", f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
+1 -1
View File
@@ -9,7 +9,7 @@ if platform.system() == "Windows":
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) 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): 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"开始更新.....", message=f"检测真寻已更新,当前版本:{_version},最新版本:{latest_version}\n" f"开始更新.....",
) )
logger.info(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): if await AsyncHttpx.download_file(tar_gz_url, zhenxun_latest_tar_gz):
logger.info("下载真寻最新版文件完成....") logger.info("下载真寻最新版文件完成....")
error = await asyncio.get_event_loop().run_in_executor( error = await asyncio.get_event_loop().run_in_executor(
@@ -200,7 +200,7 @@ def _parse_data_and_draw(
file_name = x["avatar_side_icon"].split("_")[-1] file_name = x["avatar_side_icon"].split("_")[-1]
role_avatar = memo_path / "role_avatar" / file_name role_avatar = memo_path / "role_avatar" / file_name
_ava_img = BuildImage(75, 75, background=role_avatar) _ava_img = BuildImage(75, 75, background=role_avatar)
_ava_img.circle() # _ava_img.circle()
if x["status"] == "Finished": if x["status"] == "Finished":
msg = "探索完成" msg = "探索完成"
font_color = (146, 188, 63) font_color = (146, 188, 63)
@@ -218,10 +218,10 @@ def _parse_data_and_draw(
a_circle.circle() a_circle.circle()
b_circle = BuildImage(47, 47) b_circle = BuildImage(47, 47)
b_circle.circle() b_circle.circle()
a_circle.paste(b_circle, (4, 4), alpha=True) a_circle.paste(b_circle, (4, 4), True)
_circle_bk.paste(a_circle, (4, 4), alpha=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.paste(_ava_img, (19, -13), True)
_bk.text((100, 0), msg, font_color, "by_height") _bk.text((100, 0), msg, font_color, "by_height")
_bk.circle_corner(20) _bk.circle_corner(20)
@@ -296,9 +296,11 @@ def get_country_data_image(world_data_dict: Dict) -> BuildImage:
画出国家探索供奉等图像 画出国家探索供奉等图像
:param world_data_dict: 国家数据字典 :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 height = 0
for country in ["蒙德", "龙脊雪山", "璃月", "稻妻", "渊下宫"]: print(world_data_dict)
for country in ["蒙德", "龙脊雪山", "璃月", "层岩巨渊", "稻妻", "渊下宫"]:
x = BuildImage(790, 250, color="#3A4467") x = BuildImage(790, 250, color="#3A4467")
logo = BuildImage(180, 180, background=image_path / "logo" / f"{country}.png") logo = BuildImage(180, 180, background=image_path / "logo" / f"{country}.png")
tmp_bk = BuildImage(770, 230, color="#606779") tmp_bk = BuildImage(770, 230, color="#606779")
@@ -320,6 +322,25 @@ def get_country_data_image(world_data_dict: Dict) -> BuildImage:
f"Lv.{world_data_dict[country]['level']}", f"Lv.{world_data_dict[country]['level']}",
fill=(255, 255, 255), 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 ["龙脊雪山"]: elif country in ["龙脊雪山"]:
content_bk.text((300, 40), "探索", fill=(239, 211, 114)) content_bk.text((300, 40), "探索", fill=(239, 211, 114))
content_bk.text( content_bk.text(
+2 -2
View File
@@ -64,8 +64,8 @@ async def _poke_event(event: PokeNotifyEvent):
rand = random.random() rand = random.random()
if rand <= 0.3: if rand <= 0.3:
path = random.choice(["luoli", "meitu"]) path = random.choice(["luoli", "meitu"])
index = random.randint(0, len(os.listdir(IMAGE_PATH / path))) index = random.randint(0, len(os.listdir(IMAGE_PATH / "image_management" / path)))
result = f"id:{index}" + image(f"{index}.jpg", path) result = f"id:{index}" + image(f"{index}.jpg", "image_management/" + path)
await poke_.send(result) await poke_.send(result)
logger.info(f"USER {event.user_id} 戳了戳我 回复: {result} \n {result}") logger.info(f"USER {event.user_id} 戳了戳我 回复: {result} \n {result}")
elif 0.3 < rand < 0.6: elif 0.3 < rand < 0.6:
@@ -139,7 +139,7 @@ async def update_setu_img(flag: bool = False):
f"--> /{path}/{image.local_id}.jpg" f"--> /{path}/{image.local_id}.jpg"
) )
os.rename( os.rename(
TEMP_PATH / f"/{image.local_id}.jpg", TEMP_PATH / f"{image.local_id}.jpg",
path / f"{image.local_id}.jpg", path / f"{image.local_id}.jpg",
) )
except FileNotFoundError: except FileNotFoundError:
Binary file not shown.
+20
View File
@@ -0,0 +1,20 @@
from configs.config import Config as gConfig
from .api import *
from .auth import *
gConfig.add_plugin_config(
"web-ui",
"username",
"admin",
name="web-ui",
help_="前端管理用户名"
)
gConfig.add_plugin_config(
"web-ui",
"password",
None,
name="web-ui",
help_="前端管理密码"
)
+3
View File
@@ -0,0 +1,3 @@
from .group import *
from .plugins import *
from .request import *
+61
View File
@@ -0,0 +1,61 @@
from pydantic.error_wrappers import ValidationError
from services.log import logger
from utils.manager import group_manager
from utils.utils import get_bot
from ..auth import Depends, User, token_to_user
from ..config import *
@app.get("/webui/group")
async def _(user: User = Depends(token_to_user)) -> Result:
"""
获取群信息
"""
group_list_result = []
group_info = {}
if bot := get_bot():
group_list = await bot.get_group_list()
for g in group_list:
group_info[g["group_id"]] = Group(**g)
group_data = group_manager.get_data()
for group_id in group_data["group_manager"]:
try:
task_list = []
data = group_data["group_manager"][group_id]
for tn, status in data["group_task_status"].items():
task_list.append(
Task(
**{
"name": tn,
"nameZh": group_manager.get_task_data().get(tn) or tn,
"status": status,
}
)
)
data["task"] = task_list
if x := group_info.get(int(group_id)):
data["group"] = x
else:
continue
try:
group_list_result.append(GroupResult(**data))
except ValidationError:
pass
except Exception as e:
logger.error(f"WEB_UI /webui/group 发生错误 {type(e)}:{e}")
return Result(code=200, data=group_list_result)
@app.post("/webui/group")
async def _(group: GroupResult, user: User = Depends(token_to_user)) -> Result:
"""
修改群信息
"""
group_id = group.group.group_id
group_manager.set_group_level(group_id, group.level)
if group.status:
group_manager.turn_on_group_bot_status(group_id)
else:
group_manager.shutdown_group_bot_status(group_id)
return Result(code=200)
+142
View File
@@ -0,0 +1,142 @@
from configs.config import Config
from services.log import logger
from utils.manager import (plugins2block_manager, plugins2cd_manager,
plugins2count_manager, plugins2settings_manager,
plugins_manager)
from utils.utils import get_matchers
from ..auth import Depends, User, token_to_user
from ..config import *
plugin_name_list = None
@app.get("/webui/plugins")
def _(type_: Optional[str], user: User = Depends(token_to_user)) -> Result:
"""
获取插件列表
:param type_: 类型 normal, superuser, hidden, admin
"""
global plugin_name_list
if not plugin_name_list:
plugin_name_list = [x.plugin_name for x in get_matchers()]
plugin_list = []
plugin_data = plugins_manager.get_data()
for model in plugin_data:
if model in plugin_name_list:
try:
data = plugin_data.get(model)
data["model"] = model
plugin_name = data.get("plugin_name")
if (
(type_ == "hidden" and "[hidden]" not in plugin_name.lower())
or (type_ == "admin" and "[admin]" not in plugin_name.lower())
or (
type_ == "superuser"
and "[superuser]" not in plugin_name.lower()
)
):
continue
if type_ == "normal" and (
"[hidden]" in plugin_name.lower()
or "[admin]" in plugin_name.lower()
or "[superuser]" in plugin_name.lower()
):
continue
data = {"model": model}
if x := plugin_data.get(model):
if not x.get("status") and x.get("block_type") in [
"group",
"private",
"all",
]:
x["block_type"] = (
"群聊"
if x["block_type"] == "group"
else "私聊"
if x["block_type"] == "private"
else "全部"
)
data["plugin_manager"] = PluginManager(**x)
if x := plugins2settings_manager.get(model):
if x.get("cmd") and isinstance(x.get("cmd"), list):
x["cmd"] = ",".join(x["cmd"])
data["plugin_settings"] = PluginSettings(**x)
if x := plugins2cd_manager.get(model):
data["cd_limit"] = CdLimit(**x)
if x := plugins2block_manager.get(model):
data["block_limit"] = BlockLimit(**x)
if x := plugins2count_manager.get(model):
data["count_limit"] = CountLimit(**x)
if x := Config.get(model):
id_ = 0
tmp = []
for key in x.keys():
tmp.append(
PluginConfig(
**{
"key": key,
"help_": x[key].get("help"),
"id": id_,
**x[key],
}
)
)
id_ += 1
data["plugin_config"] = tmp
plugin_list.append(Plugin(**data))
except Exception as e:
logger.error(
f"WEB_UI GET /webui/plugins model:{model} 发生错误 {type(e)}:{e}"
)
return Result(
code=500,
data=f"WEB_UI GET /webui/plugins model:{model} 发生错误 {type(e)}:{e}",
)
return Result(code=200, data=plugin_list)
@app.post("/webui/plugins")
def _(plugin: Plugin, user: User = Depends(token_to_user)) -> Result:
"""
修改插件信息
:param plugin: 插件内容
"""
try:
if plugin.plugin_config:
for c in plugin.plugin_config:
if str(c.value).lower() in ["true", "false"] and (
c.default_value is None or isinstance(c.default_value, bool)
):
c.value = True if str(c.value).lower() == "true" else False
elif isinstance(
Config.get_config(plugin.model, c.key, c.value), int
) or isinstance(c.default_value, int):
c.value = int(c.value)
elif isinstance(
Config.get_config(plugin.model, c.key, c.value), float
) or isinstance(c.default_value, float):
c.value = float(c.value)
elif isinstance(c.value, str) and (
isinstance(Config.get_config(plugin.model, c.key, c.value), list)
or isinstance(c.default_value, list)
):
c.value = c.value.split(",")
Config.set_config(plugin.model, c.key, c.value)
Config.save(None, True)
else:
if plugin.plugin_settings:
for key, value in plugin.plugin_settings:
plugins2settings_manager.set_module_data(plugin.model, key, value)
if plugin.plugin_manager:
for key, value in plugin.plugin_manager:
plugins_manager.set_module_data(plugin.model, key, value)
except Exception as e:
logger.error(
f"WEB_UI POST /webui/plugins model:{plugin.model} 发生错误 {type(e)}:{e}"
)
return Result(
code=500,
data=f"WEB_UI POST /webui/plugins model:{plugin.model} 发生错误 {type(e)}:{e}",
)
return Result(code=200)
+65
View File
@@ -0,0 +1,65 @@
from utils.manager import requests_manager
from ..auth import token_to_user, Depends, User
from utils.utils import get_bot
from models.group_info import GroupInfo
from ..config import *
@app.get("/webui/request")
def _(type_: Optional[str], user: User = Depends(token_to_user)) -> Result:
req_data = requests_manager.get_data()
req_list = []
if type_ in ["group", "private"]:
req_data = req_data[type_]
for x in req_data:
req_data[x]["oid"] = x
req_list.append(RequestResult(**req_data[x]))
req_list.reverse()
return Result(code=200, data=req_list)
@app.delete("/webui/request")
def _(type_: Optional[str], user: User = Depends(token_to_user)) -> Result:
"""
清空请求
:param type_: 类型
"""
requests_manager.clear(type_)
return Result(code=200)
@app.post("/webui/request")
async def _(parma: RequestParma, user: User = Depends(token_to_user)) -> Result:
"""
操作请求
:param parma: 参数
"""
result = "ok"
flag = 3
if bot := get_bot():
if parma.handle == "approve":
if parma.type == "group":
rid = requests_manager.get_group_id(parma.id)
if await GroupInfo.get_group_info(rid):
await GroupInfo.set_group_flag(rid, 1)
else:
group_info = await bot.get_group_info(group_id=rid)
await GroupInfo.add_group_info(
rid,
group_info["group_name"],
group_info["max_member_count"],
group_info["member_count"],
1,
)
flag = await requests_manager.approve(bot, parma.id, parma.type)
elif parma.handle == "refuse":
flag = await requests_manager.refused(bot, parma.id, parma.type)
elif parma.handle == "delete":
requests_manager.delete_request(parma.id, parma.type)
if parma.handle != "delete":
if flag == 1:
result = "该请求已失效"
requests_manager.delete_request(parma.id, parma.type)
elif flag == 2:
result = "未找到此Id"
return Result(code=200, data=result)
+89
View File
@@ -0,0 +1,89 @@
from datetime import datetime, timedelta
from typing import Optional
from starlette import status
from fastapi import Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
from configs.config import Config
from jose import JWTError, jwt
import nonebot
app = nonebot.get_app()
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="webui/login")
class User(BaseModel):
username: str
password: str
class Token(BaseModel):
access_token: str
token_type: str
# USER_LIST = [
# User(username="admin", password="123")
# ]
def get_user(uname: str) -> Optional[User]:
username = Config.get_config("web-ui", "username")
password = Config.get_config("web-ui", "password")
if username and password and uname == username:
return User(username=username, password=password)
form_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
def create_token(user: User, expires_delta: Optional[timedelta] = None):
expire = datetime.utcnow() + expires_delta or timedelta(minutes=15)
return jwt.encode(
claims={"sub": user.username, "exp": expire},
key=SECRET_KEY,
algorithm=ALGORITHM
)
@app.post("/webui/login")
async def login_get_token(form_data: OAuth2PasswordRequestForm = Depends()):
user: User = get_user(form_data.username)
if not user or user.password != form_data.password:
raise form_exception
access_token = create_token(user=user, expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
return {"access_token": access_token, "token_type": "bearer"}
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
def token_to_user(token: str = Depends(oauth2_scheme)):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username, expire = payload.get("sub"), payload.get("exp")
user = get_user(username)
if user is None:
raise JWTError
except JWTError:
raise credentials_exception
return user
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8080)
+121
View File
@@ -0,0 +1,121 @@
from typing import Optional, List, Any, Union
from pydantic import BaseModel
from fastapi.middleware.cors import CORSMiddleware
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: int
status: bool
check_type: str
limit_type: str
rst: Optional[str]
class BlockLimit(BaseModel):
status: bool
check_type: str
limit_type: str
rst: Optional[str]
class CountLimit(BaseModel):
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 Result(BaseModel):
code: int
data: Any
+21 -3
View File
@@ -1,7 +1,6 @@
from configs.config import Config from configs.config import Config
import nonebot import nonebot
Config.add_plugin_config( Config.add_plugin_config(
"word_bank", "word_bank",
"WORD_BANK_LEVEL [LEVEL]", "WORD_BANK_LEVEL [LEVEL]",
@@ -11,6 +10,25 @@ Config.add_plugin_config(
default_value=5 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") nonebot.load_plugins("plugins/word_bank")
+10 -14
View File
@@ -5,7 +5,6 @@ from typing import Union
class WordBankBuilder: class WordBankBuilder:
def __init__(self, user_id: int, group_id: int, problem: str): def __init__(self, user_id: int, group_id: int, problem: str):
self._data = { self._data = {
"user_id": user_id, "user_id": user_id,
"group_id": group_id, "group_id": group_id,
@@ -29,24 +28,21 @@ class WordBankBuilder:
""" """
self._data["answer"] = answer self._data["answer"] = answer
async def save(self): async def save(self,search_type):
user_id = self._data["user_id"] user_id = self._data["user_id"]
group_id = self._data["group_id"] group_id = self._data["group_id"]
problem = self._data["problem"] problem = self._data["problem"]
answer = self._data["answer"] answer = self._data["answer"]
placeholder = self._data.get("placeholder") 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): def __str__(self):
return str(self._data) return str(self._data)
+20 -11
View File
@@ -5,36 +5,40 @@ from configs.path_config import DATA_PATH
from nonebot.adapters.onebot.v11 import GroupMessageEvent from nonebot.adapters.onebot.v11 import GroupMessageEvent
from utils.utils import get_message_text from utils.utils import get_message_text
from nonebot import on_message from nonebot import on_message
from models.group_member_info import GroupInfoUser
__zx_plugin_name__ = "词库问答回复操作 [Hidden]" __zx_plugin_name__ = "词库问答回复操作 [Hidden]"
data_dir = DATA_PATH / "word_bank" data_dir = DATA_PATH / "word_bank"
data_dir.mkdir(parents=True, exist_ok=True) data_dir.mkdir(parents=True, exist_ok=True)
message_handle = on_message(priority=6, block=True, rule=check)
message_handle = on_message(priority=5, block=True, rule=check)
@message_handle.handle() @message_handle.handle()
async def _(event: GroupMessageEvent): async def _(event: GroupMessageEvent):
path = data_dir / f"{event.group_id}"
q = await WordBank.check( q = await WordBank.check(
event.group_id, get_message_text(event.json()), event.is_tome() 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 = ( placeholder_list = (
[ [
(x.split("<_s>")[0], x.split("<_s>")[1]) (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 [] else []
) )
answer = "" answer = ""
_a = q.answer _a = _answer
if not placeholder_list: if not placeholder_list:
answer = _a answer = _a
return answer
else: else:
for idx, placeholder in placeholder_list: for idx, placeholder in placeholder_list:
if placeholder.endswith("jpg"): if placeholder.endswith("jpg"):
@@ -42,6 +46,11 @@ async def _(event: GroupMessageEvent):
path / placeholder path / placeholder
) )
else: else:
answer += _a[: _a.find(f"[__placeholder_{idx}]")] + at(placeholder) if all == 1:
_a = _a[_a.find(f"[__placeholder_{idx}]") + len(f"[__placeholder_{idx}]") :] answer += _a[: _a.find(f"[__placeholder_{idx}]")] + at(placeholder)
await message_handle.send(answer) else:
q = await GroupInfoUser.get_member_info(
int(placeholder), event.group_id)
answer += _a[: _a.find(f"[__placeholder_{idx}]")] + "@" + q.user_name
_a = _a[_a.find(f"[__placeholder_{idx}]") + len(f"[__placeholder_{idx}]"):]
return answer + _a
+121 -39
View File
@@ -5,6 +5,7 @@ from pathlib import Path
from configs.path_config import DATA_PATH from configs.path_config import DATA_PATH
import re import re
import random import random
from configs.config import Config
class WordBank(db.Model): class WordBank(db.Model):
@@ -21,17 +22,19 @@ class WordBank(db.Model):
@classmethod @classmethod
async def add_problem_answer( async def add_problem_answer(
cls, cls,
user_id: int, user_id: int,
group_id: Optional[int], group_id: Optional[int],
problem: str, search_type: [int],
answer: str, problem: str,
format_: Optional[List[Tuple[int, Union[int, str]]]], answer: str,
format_: Optional[List[Tuple[int, Union[int, str]]]],
) -> bool: ) -> bool:
""" """
添加或新增一个问答 添加或新增一个问答
:param user_id: 用户id :param user_id: 用户id
:param group_id: 群号 :param group_id: 群号
:search_type: 问题类型,
:param problem: 问题 :param problem: 问题
:param answer: 回答 :param answer: 回答
:param format_: 格式化数据 :param format_: 格式化数据
@@ -42,12 +45,12 @@ class WordBank(db.Model):
for x, y in format_: for x, y in format_:
_str += f"{x}<_s>{y}<format>" _str += f"{x}<_s>{y}<format>"
return await cls._problem_answer_handle( 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 @classmethod
async def delete_problem_answer( 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: ) -> str:
""" """
删除某问题一个或全部回答 删除某问题一个或全部回答
@@ -60,9 +63,35 @@ class WordBank(db.Model):
user_id, group_id, problem, "delete", index=index 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 @classmethod
async def get_problem_answer( 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]: ) -> List[str]:
""" """
获取问题的所有回答 获取问题的所有回答
@@ -87,7 +116,8 @@ class WordBank(db.Model):
q = await cls.query.where( q = await cls.query.where(
(cls.group_id == group_id) & (cls.problem == problem) (cls.group_id == group_id) & (cls.problem == problem)
).gino.all() ).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 @classmethod
async def get_group_all_problem(cls, group_id: int) -> List[str]: async def get_group_all_problem(cls, group_id: int) -> List[str]:
@@ -128,22 +158,45 @@ class WordBank(db.Model):
return x return x
return None return None
else: else:
q = await cls.query.where( if problem:
(cls.group_id == group_id) & (cls.problem == problem) FUZZY = Config.get_config("word_bank", "WORD_BANK_FUZZY")
).gino.all() 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 return random.choice(q) if q else None
@classmethod @classmethod
async def _problem_answer_handle( async def _problem_answer_handle(
cls, cls,
user_id: int, user_id: int,
group_id: Optional[int], group_id: Optional[int],
problem: str, problem: str,
type_: str, type_: str,
*, *,
answer: Optional[str] = None, search_type: [int] = 0,
index: Optional[int] = None, answer: Optional[str] = None,
format_: Optional[str] = None, index: Optional[int] = None,
format_: Optional[str] = None,
) -> Union[List[Union[str, Tuple[str, str]]], bool, str]: ) -> Union[List[Union[str, Tuple[str, str]]], bool, str]:
""" """
添加或新增一个问答 添加或新增一个问答
@@ -166,27 +219,27 @@ class WordBank(db.Model):
else: else:
q = cls.query.where((cls.user_qq == user_id) & (cls.problem == problem)) q = cls.query.where((cls.user_qq == user_id) & (cls.problem == problem))
if type_ == "add": if type_ == "add":
q = await q.where(cls.answer == answer).gino.all() q = await q.where((cls.answer == answer) & (cls.search_type == search_type)).gino.all()
if not q or ".jpg" in format_: try:
await cls.create( if not q or ".jpg" in format_:
user_qq=user_id, await cls.create(
group_id=group_id, user_qq=user_id,
problem=problem, group_id=group_id,
answer=answer, search_type=search_type,
format=format_, problem=problem,
create_time=datetime.now().date(), answer=answer,
update_time=datetime.now().date(), format=format_,
) create_time=datetime.now().replace(microsecond=0),
update_time=datetime.now().replace(microsecond=0),
)
except:
return False
return True return True
elif type_ == "delete": elif type_ == "delete":
q = await q.with_for_update().gino.all() q = await q.with_for_update().gino.all()
if q: if q:
path = DATA_PATH / "word_bank" / f"{group_id}" path = DATA_PATH / "word_bank" / f"{group_id}"
if index is not None: 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]] q = [q[index]]
answer = "\n".join([x.answer for x in q]) answer = "\n".join([x.answer for x in q])
for x in q: for x in q:
@@ -199,14 +252,43 @@ class WordBank(db.Model):
if _path.exists(): if _path.exists():
_path.unlink() _path.unlink()
await cls.delete.where( await cls.delete.where(
(cls.problem == problem) (cls.update_time == x.update_time)
& (cls.problem == problem)
& (cls.answer == x.answer) & (cls.answer == x.answer)
& (cls.group_id == group_id) & (cls.group_id == group_id)
).gino.status() ).gino.status()
return answer 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": elif type_ == "get":
q = await q.gino.all() q = await q.gino.all()
if q: if q:
return [(x.answer, x.format.split("<format>")[:-1]) for x in q] return [(x.answer, x.format.split("<format>")[:-1]) for x in q]
return False return False
+184 -73
View File
@@ -4,9 +4,9 @@ from services.log import logger
from configs.path_config import DATA_PATH from configs.path_config import DATA_PATH
from utils.http_utils import AsyncHttpx from utils.http_utils import AsyncHttpx
from ._data_source import WordBankBuilder from ._data_source import WordBankBuilder
from configs.config import Config
from utils.message_builder import image from utils.message_builder import image
from utils.image_utils import text2image from utils.image_utils import text2image
from .message_handle import get_one_answer
from .model import WordBank from .model import WordBank
from nonebot.adapters.onebot.v11 import ( from nonebot.adapters.onebot.v11 import (
Bot, Bot,
@@ -18,6 +18,7 @@ from nonebot import on_command
import random import random
import os import os
import re import re
from configs.config import NICKNAME, Config
__zx_plugin_name__ = "词库问答 [Admin]" __zx_plugin_name__ = "词库问答 [Admin]"
__plugin_usage__ = """ __plugin_usage__ = """
@@ -25,45 +26,51 @@ usage:
对指定问题的随机回答,对相同问题可以设置多个不同回答 对指定问题的随机回答,对相同问题可以设置多个不同回答
删除词条后每个词条的id可能会变化,请查看后再删除 删除词条后每个词条的id可能会变化,请查看后再删除
指令: 指令:
添加词条问...答...:添加问答词条,可重复添加相同问题的不同回答 添加词条 ?[模糊/关键字]...答...:添加问答词条,可重复添加相同问题的不同回答
删除词条 [问题/下标] ?[下标]:删除指定词条指定或全部回答 删除词条 [问题/下标] ?[下标]:删除指定词条指定或全部回答
修改词条 [问题/下标] ?[下标/新回答] [新回答]:修改指定词条指定回答默认修改为第一条
查看词条 ?[问题/下标]:查看全部词条或对应词条回答 查看词条 ?[问题/下标]:查看全部词条或对应词条回答
示例:添加词条问谁是萝莉答是我 示例:添加词条问谁是萝莉答是我
示例:删除词条 谁是萝莉 示例:删除词条 谁是萝莉
示例:删除词条 谁是萝莉 0 示例:删除词条 谁是萝莉 0
示例:删除词条 id:0 示例:删除词条 id:0
示例:修改词条 谁是萝莉 是你
示例:修改词条 谁是萝莉 0 是你
示例:修改词条 id:0 是你
示例:查看词条 示例:查看词条
示例:查看词条 谁是萝莉 示例:查看词条 谁是萝莉
示例:查看词条 id:0 示例:查看词条 id:0
""".strip() """.strip()
__plugin_des__ = "自定义词条内容随机回复" __plugin_des__ = "自定义词条内容随机回复"
__plugin_cmd__ = [ __plugin_cmd__ = [
"添加词条问...答..", "添加词条 ?[模糊/关键字]问...答..",
"删除词条 [问题/下标] ?[下标]", "删除词条 [问题/下标] ?[下标]",
"修改词条 [问题/下标] ?[下标/新回答] [新回答]",
"查看词条 ?[问题/下标]", "查看词条 ?[问题/下标]",
] ]
__plugin_version__ = 0.1 __plugin_version__ = 0.2
__plugin_author__ = "HibiKier" __plugin_author__ = "HibiKier"
__plugin_settings__ = { __plugin_settings__ = {
"admin_level": Config.get_config("word_bank", "WORD_BANK_LEVEL"), "admin_level": Config.get_config("word_bank", "WORD_BANK_LEVEL [LEVEL]"),
"cmd": ["词库问答", "添加词条", "删除词条", "查看词条"], "cmd": ["词库问答", "添加词条", "删除词条", "修改词条", "查看词条"],
} }
data_dir = DATA_PATH / "word_bank" data_dir = DATA_PATH / "word_bank"
data_dir.mkdir(parents=True, exist_ok=True) data_dir.mkdir(parents=True, exist_ok=True)
add_word = on_command("添加词条", priority=5, block=True) add_word = on_command("添加词条", priority=5, block=True)
delete_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) show_word = on_command("显示词条", aliases={"查看词条"}, priority=5, block=True)
@add_word.handle() @add_word.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State, arg: Message = CommandArg()): async def _(bot: Bot, event: GroupMessageEvent, state: T_State, arg: Message = CommandArg()):
msg = str(arg) msg = str(arg)
r = re.search(r"^问(.+)\s?答([\s\S]*)", msg) r = re.search(r"问(.+)\s?答([\s\S]*)", msg)
if not r: if not r:
await add_word.finish("未检测到词条问题...") await add_word.finish("未检测到词条问题...")
problem = r.group(1).strip() problem = r.group(1).strip()
@@ -75,10 +82,177 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State, arg: Message = C
idx = 0 idx = 0
for n in bot.config.nickname: for n in bot.config.nickname:
if n and problem.startswith(n): if n and problem.startswith(n):
_problem = f"[_to_me|{n}]" + problem[len(n) :] _problem = f"[_to_me|{n}]" + problem[len(n):]
break break
else: else:
_problem = problem _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) (data_dir / f"{event.group_id}").mkdir(exist_ok=True, parents=True)
_builder = WordBankBuilder(event.user_id, event.group_id, _problem) _builder = WordBankBuilder(event.user_id, event.group_id, _problem)
for at_ in get_message_at(event.json()): 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") _builder.set_placeholder(idx, f"__placeholder_{rand}_{idx}.jpg")
idx += 1 idx += 1
_builder.set_answer(answer) _builder.set_answer(answer)
await _builder.save() return _builder
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))
Generated
+150 -24
View File
@@ -377,6 +377,26 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple" url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "tsinghua" 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]] [[package]]
name = "fastapi" name = "fastapi"
version = "0.73.0" version = "0.73.0"
@@ -520,14 +540,14 @@ reference = "tsinghua"
[[package]] [[package]]
name = "httptools" name = "httptools"
version = "0.2.0" version = "0.4.0"
description = "A collection of framework independent HTTP protocol utils." description = "A collection of framework independent HTTP protocol utils."
category = "main" category = "main"
optional = false optional = false
python-versions = "*" python-versions = ">=3.5.0"
[package.extras] [package.extras]
test = ["Cython (==0.29.22)"] test = ["Cython (>=0.29.24,<0.30.0)"]
[package.source] [package.source]
type = "legacy" type = "legacy"
@@ -915,6 +935,19 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple" url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "tsinghua" 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]] [[package]]
name = "pydantic" name = "pydantic"
version = "1.9.0" version = "1.9.0"
@@ -1023,6 +1056,45 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple" url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "tsinghua" 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]] [[package]]
name = "pytz" name = "pytz"
version = "2021.3" version = "2021.3"
@@ -1130,6 +1202,22 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple" url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "tsinghua" 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]] [[package]]
name = "ruamel.yaml" name = "ruamel.yaml"
version = "0.17.21" version = "0.17.21"
@@ -1403,7 +1491,7 @@ reference = "tsinghua"
[[package]] [[package]]
name = "uvicorn" name = "uvicorn"
version = "0.17.5" version = "0.17.6"
description = "The lightning-fast ASGI server." description = "The lightning-fast ASGI server."
category = "main" category = "main"
optional = false optional = false
@@ -1414,7 +1502,7 @@ asgiref = ">=3.4.0"
click = ">=7.0" click = ">=7.0"
colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""}
h11 = ">=0.8" 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\""} python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""}
PyYAML = {version = ">=5.1", 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\""} 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\""} websockets = {version = ">=10.0", optional = true, markers = "extra == \"standard\""}
[package.extras] [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] [package.source]
type = "legacy" type = "legacy"
@@ -1522,7 +1610,7 @@ reference = "tsinghua"
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.8" python-versions = "^3.8"
content-hash = "2521c3cd7346f1f1b3b22276fa71ca9680447f85fc13fd23ede0e33d6f942423" content-hash = "a467c2f5d871d65cd76363a2839023a38bde9b4aacd37f3019dca6fe0f199723"
[metadata.files] [metadata.files]
aiofiles = [ aiofiles = [
@@ -1764,6 +1852,10 @@ dateparser = [
{file = "dateparser-1.1.0-py2.py3-none-any.whl", hash = "sha256:fec344db1f73d005182e214c0ff27313c748bbe0c1638ce9d48a809ddfdab2a0"}, {file = "dateparser-1.1.0-py2.py3-none-any.whl", hash = "sha256:fec344db1f73d005182e214c0ff27313c748bbe0c1638ce9d48a809ddfdab2a0"},
{file = "dateparser-1.1.0.tar.gz", hash = "sha256:faa2b97f51f3b5ff1ba2f17be90de2b733fb6191f89b4058787473e8202f3044"}, {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 = [ fastapi = [
{file = "fastapi-0.73.0-py3-none-any.whl", hash = "sha256:f0a618aff5f6942862f2d3f20f39b1c037e33314d1b8207fd1c3a2cca76dfd8c"}, {file = "fastapi-0.73.0-py3-none-any.whl", hash = "sha256:f0a618aff5f6942862f2d3f20f39b1c037e33314d1b8207fd1c3a2cca76dfd8c"},
{file = "fastapi-0.73.0.tar.gz", hash = "sha256:dcfee92a7f9a72b5d4b7ca364bd2b009f8fc10d95ed5769be20e94f39f7e5a15"}, {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"}, {file = "httpcore-0.14.7.tar.gz", hash = "sha256:7503ec1c0f559066e7e39bc4003fd2ce023d01cf51793e3c173b864eb456ead1"},
] ]
httptools = [ httptools = [
{file = "httptools-0.2.0-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:79dbc21f3612a78b28384e989b21872e2e3cf3968532601544696e4ed0007ce5"}, {file = "httptools-0.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcddfe70553be717d9745990dfdb194e22ee0f60eb8f48c0794e7bfeda30d2d5"},
{file = "httptools-0.2.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:78d03dd39b09c99ec917d50189e6743adbfd18c15d5944392d2eabda688bf149"}, {file = "httptools-0.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1ee0b459257e222b878a6c09ccf233957d3a4dcb883b0847640af98d2d9aac23"},
{file = "httptools-0.2.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:a23166e5ae2775709cf4f7ad4c2048755ebfb272767d244e1a96d55ac775cca7"}, {file = "httptools-0.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceafd5e960b39c7e0d160a1936b68eb87c5e79b3979d66e774f0c77d4d8faaed"},
{file = "httptools-0.2.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3ab1f390d8867f74b3b5ee2a7ecc9b8d7f53750bd45714bf1cb72a953d7dfa77"}, {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.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a7594f9a010cdf1e16a58b3bf26c9da39bbf663e3b8d46d39176999d71816658"}, {file = "httptools-0.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:abe829275cdd4174b4c4e65ad718715d449e308d59793bf3a931ee1bf7e7b86c"},
{file = "httptools-0.2.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:01b392a166adcc8bc2f526a939a8aabf89fe079243e1543fd0e7dc1b58d737cb"}, {file = "httptools-0.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7af6bdbd21a2a25d6784f6d67f44f5df33ef39b6159543b9f9064d365c01f919"},
{file = "httptools-0.2.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:80ffa04fe8c8dfacf6e4cef8277347d35b0442c581f5814f3b0cf41b65c43c6e"}, {file = "httptools-0.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5d1fe6b6661022fd6cac541f54a4237496b246e6f1c0a6b41998ee08a1135afe"},
{file = "httptools-0.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d5682eeb10cca0606c4a8286a3391d4c3c5a36f0c448e71b8bd05be4e1694bfb"}, {file = "httptools-0.4.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:48e48530d9b995a84d1d89ae6b3ec4e59ea7d494b150ac3bbc5e2ac4acce92cd"},
{file = "httptools-0.2.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:a289c27ccae399a70eacf32df9a44059ca2ba4ac444604b00a19a6c1f0809943"}, {file = "httptools-0.4.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a113789e53ac1fa26edf99856a61e4c493868e125ae0dd6354cf518948fbbd5c"},
{file = "httptools-0.2.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:813871f961edea6cb2fe312f2d9b27d12a51ba92545380126f80d0de1917ea15"}, {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.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:cc9be041e428c10f8b6ab358c6b393648f9457094e1dcc11b4906026d43cd380"}, {file = "httptools-0.4.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:7ee9f226acab9085037582c059d66769862706e8e8cd2340470ceb8b3850873d"},
{file = "httptools-0.2.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b08d00d889a118f68f37f3c43e359aab24ee29eb2e3fe96d64c6a2ba8b9d6557"}, {file = "httptools-0.4.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:701e66b59dd21a32a274771238025d58db7e2b6ecebbab64ceff51b8e31527ae"},
{file = "httptools-0.2.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:fd3b8905e21431ad306eeaf56644a68fdd621bf8f3097eff54d0f6bdf7262065"}, {file = "httptools-0.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6a1a7dfc1f9c78a833e2c4904757a0f47ce25d08634dd2a52af394eefe5f9777"},
{file = "httptools-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:200fc1cdf733a9ff554c0bb97a4047785cfaad9875307d6087001db3eb2b417f"}, {file = "httptools-0.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:903f739c9fb78dab8970b0f3ea51f21955b24b45afa77b22ff0e172fc11ef111"},
{file = "httptools-0.2.0.tar.gz", hash = "sha256:94505026be56652d7a530ab03d89474dc6021019d6b8682281977163b3471ea0"}, {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 = [ httpx = [
{file = "httpx-0.22.0-py3-none-any.whl", hash = "sha256:e35e83d1d2b9b2a609ef367cc4c1e66fd80b750348b20cc9e19d1952fc2ca3f6"}, {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-cp39-cp39-win_amd64.whl", hash = "sha256:7d190ee2eaef7831163f254dc58f6d2e2a22e27382b936aab51c835fc080c3d3"},
{file = "psutil-5.9.0.tar.gz", hash = "sha256:869842dbd66bb80c3217158e629d6fceaecc3a3166d3d1faee515b05dd26ca25"}, {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 = [ 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_10_9_x86_64.whl", hash = "sha256:cb23bcc093697cdea2708baae4f9ba0e972960a835af22560f6ae4e7e47d33f5"},
{file = "pydantic-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1d5278bd9f0eee04a44c712982343103bba63507480bfd2fc2790fa70cd64cf4"}, {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.tar.gz", hash = "sha256:a5de49a31e953b45ff2d2fd434bbc2670e8db5273606c1e737cc6b93eff3655f"},
{file = "python_dotenv-0.19.2-py2.py3-none-any.whl", hash = "sha256:32b2bdc1873fd3a3c346da1c6db83d0053c3c62f28f1f38516070c4c8971b1d3"}, {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 = [ pytz = [
{file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"}, {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"},
{file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"}, {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-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
{file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, {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" = [ "ruamel.yaml" = [
{file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"}, {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"},
{file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"}, {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"}, {file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"},
] ]
uvicorn = [ uvicorn = [
{file = "uvicorn-0.17.5-py3-none-any.whl", hash = "sha256:8adddf629b79857b48b999ae1b14d6c92c95d4d7840bd86461f09bee75f1653e"}, {file = "uvicorn-0.17.6-py3-none-any.whl", hash = "sha256:19e2a0e96c9ac5581c01eb1a79a7d2f72bb479691acd2b8921fce48ed5b961a6"},
{file = "uvicorn-0.17.5.tar.gz", hash = "sha256:c04a9c069111489c324f427501b3840d306c6b91a77b00affc136a840a3f45f1"}, {file = "uvicorn-0.17.6.tar.gz", hash = "sha256:5180f9d059611747d841a4a4c4ab675edf54c8489e97f96d0583ee90ac3bfc23"},
] ]
uvloop = [ uvloop = [
{file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d"}, {file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d"},
+2
View File
@@ -38,6 +38,8 @@ black = "^22.1.0"
pypinyin = "^0.46.0" pypinyin = "^0.46.0"
dateparser = "^1.1.0" dateparser = "^1.1.0"
cn2an = "^0.5.16" cn2an = "^0.5.16"
python-jose = "^3.3.0"
python-multipart = "^0.0.5"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
+2 -1
View File
@@ -8,7 +8,8 @@
"configs/path_config.py", "configs/path_config.py",
"configs/utils", "configs/utils",
"poetry.lock", "poetry.lock",
"pyproject.toml" "pyproject.toml",
"resources/font"
], ],
"add_file": [], "add_file": [],
"delete_file": [] "delete_file": []
+10 -6
View File
@@ -295,7 +295,7 @@ class AsyncPlaywright:
path: Union[Path, str], path: Union[Path, str],
element: Union[str, List[str]], element: Union[str, List[str]],
*, *,
sleep: Optional[int] = None, wait_time: Optional[int] = None,
viewport_size: Dict[str, int] = None, viewport_size: Dict[str, int] = None,
wait_until: Optional[ wait_until: Optional[
Literal["domcontentloaded", "load", "networkidle"] Literal["domcontentloaded", "load", "networkidle"]
@@ -311,7 +311,7 @@ class AsyncPlaywright:
:param url: 网址 :param url: 网址
:param path: 存储路径 :param path: 存储路径
:param element: 元素选择 :param element: 元素选择
:param sleep: 延迟截取 :param wait_time: 等待截取超时时间
:param viewport_size: 窗口大小 :param viewport_size: 窗口大小
:param wait_until: 等待类型 :param wait_until: 等待类型
:param timeout: 超时限制 :param timeout: 超时限制
@@ -325,14 +325,18 @@ class AsyncPlaywright:
try: try:
page = await cls.goto(url, wait_until=wait_until, **kwargs) page = await cls.goto(url, wait_until=wait_until, **kwargs)
await page.set_viewport_size(viewport_size) await page.set_viewport_size(viewport_size)
if sleep:
await asyncio.sleep(sleep)
if isinstance(element, str): 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: else:
card = page card = page
for e in element: 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_) await card.screenshot(path=path, timeout=timeout, type=type_)
return image(path) return image(path)
except Exception as e: except Exception as e:
+2 -1
View File
@@ -2,6 +2,7 @@ from typing import Union, Optional
from pathlib import Path from pathlib import Path
from ruamel.yaml import YAML from ruamel.yaml import YAML
import ujson as json import ujson as json
import copy
yaml = YAML(typ="safe") yaml = YAML(typ="safe")
@@ -47,7 +48,7 @@ class StaticData:
del self._data[key] del self._data[key]
def get_data(self) -> dict: def get_data(self) -> dict:
return self._data return copy.deepcopy(self._data)
def save(self, path: Union[str, Path] = None): def save(self, path: Union[str, Path] = None):
path = path if path else self.file path = path if path else self.file
+32 -13
View File
@@ -2,7 +2,7 @@ from utils.manager.data_class import StaticData
from nonebot.adapters.onebot.v11 import Bot from nonebot.adapters.onebot.v11 import Bot
from nonebot.adapters.onebot.v11.exception import ActionFailed from nonebot.adapters.onebot.v11.exception import ActionFailed
from services.log import logger from services.log import logger
from typing import Optional from typing import Optional, Literal
from utils.image_utils import BuildImage from utils.image_utils import BuildImage
from utils.utils import get_user_avatar from utils.utils import get_user_avatar
from pathlib import Path from pathlib import Path
@@ -80,9 +80,12 @@ class RequestManager(StaticData):
通过id获取群号 通过id获取群号
:param id_: id :param id_: id
""" """
return self._data["group"].get(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) -> Optional[int]: async def approve(self, bot: Bot, id_: int, type_: str) -> int:
""" """
同意请求 同意请求
:param bot: Bot :param bot: Bot
@@ -100,13 +103,28 @@ class RequestManager(StaticData):
""" """
return await self._set_add_request(bot, id_, type_, False) 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() 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): def set_group_name(self, group_name: str, group_id: int):
""" """
设置群聊名称 设置群聊名称
@@ -214,7 +232,7 @@ class RequestManager(StaticData):
async def _set_add_request( async def _set_add_request(
self, bot: Bot, id_: int, type_: str, approve: bool self, bot: Bot, id_: int, type_: str, approve: bool
) -> Optional[int]: ) -> int:
""" """
处理请求 处理请求
:param bot: Bot :param bot: Bot
@@ -223,7 +241,7 @@ class RequestManager(StaticData):
:param approve: 是否同意 :param approve: 是否同意
""" """
id_ = str(id_) id_ = str(id_)
if id_ in self._data[type_]: if id_ in self._data[type_].keys():
try: try:
if type_ == "private": if type_ == "private":
await bot.set_friend_add_request( await bot.set_friend_add_request(
@@ -242,12 +260,13 @@ class RequestManager(StaticData):
f"同意{self._data[type_][id_]['nickname']}({self._data[type_][id_]['id']})" f"同意{self._data[type_][id_]['nickname']}({self._data[type_][id_]['id']})"
f"的{'好友' if type_ == 'private' else '入群'}请求失败了..." f"的{'好友' if type_ == 'private' else '入群'}请求失败了..."
) )
return None return 1 # flag失效
logger.info( else:
f"同意{self._data[type_][id_]['nickname']}({self._data[type_][id_]['id']})" logger.info(
f"的{'好友' if type_ == 'private' else '入群'}请求..." f"{'同意' if approve else '拒绝'}{self._data[type_][id_]['nickname']}({self._data[type_][id_]['id']})"
) f"的{'好友' if type_ == 'private' else '入群'}请求..."
)
del self._data[type_][id_] del self._data[type_][id_]
self.save() self.save()
return rid return rid
return None return 2 # 未找到id