diff --git a/.env.dev b/.env.dev
index 283bcdfa..d73006a1 100644
--- a/.env.dev
+++ b/.env.dev
@@ -13,6 +13,7 @@ SESSION_EXPIRE_TIMEOUT=60
# B站相关插件Cookie配置(可选)
# 强烈建议您正确配置此项, 否则B站动态及直播间监控很可能被B站风控限制!
+BILI_UID=
BILI_SESSDATA=
BILI_CSRF=
diff --git a/README.md b/README.md
index be3fca19..4c6f3c34 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
## 当前适配nonebot2版本
-[Nonebot2 PreRelease v2.0.0a9](https://github.com/nonebot/nonebot2/releases/tag/v2.0.0a9)
+[Nonebot2 PreRelease v2.0.0a9.post1](https://github.com/nonebot/nonebot2/releases/tag/v2.0.0a9.post1)
## 配套使用的api
diff --git a/omega_miya/plugins/Omega_help/__init__.py b/omega_miya/plugins/Omega_help/__init__.py
index c30394ab..0a0eb239 100644
--- a/omega_miya/plugins/Omega_help/__init__.py
+++ b/omega_miya/plugins/Omega_help/__init__.py
@@ -36,7 +36,7 @@ async def handle_first_receive(bot: Bot, event: GroupMessageEvent, state: T_Stat
else:
# 如果用户没有发送参数, 则发送功能列表并结束此命令
plugins_list = '\n'.join(p.export.custom_name for p in plugins)
- await bot_help.finish(f'我现在支持的插件有: \n\n{plugins_list}\n\n输入"/help [插件]"即可查看对应帮助')
+ await bot_help.finish(f'我现在支持的插件有: \n\n{plugins_list}\n\n注意: 群组权限等级未达到要求的命令不会被响应\n输入"/help [插件]"即可查看插件详情及帮助')
@bot_help.got('plugin_name', prompt='你想查询哪个插件的用法呢?')
diff --git a/omega_miya/plugins/bilibili_dynamic_monitor/monitor.py b/omega_miya/plugins/bilibili_dynamic_monitor/monitor.py
index 109f1c2d..2ac3c29d 100644
--- a/omega_miya/plugins/bilibili_dynamic_monitor/monitor.py
+++ b/omega_miya/plugins/bilibili_dynamic_monitor/monitor.py
@@ -2,7 +2,7 @@
from nonebot import logger, require, get_bots
from nonebot.adapters.cqhttp import MessageSegment
from omega_miya.utils.Omega_Base import DBSubscription, DBDynamic, DBTable
-from .utils import get_dynamic_info, get_user_info, get_user_dynamic, pic_2_base64
+from .utils import get_user_dynamic_history, get_user_info, get_user_dynamic, get_dynamic_info, pic_2_base64
# 启用检查动态状态的定时任务
@@ -86,7 +86,7 @@ async def bilibili_dynamic_monitor():
async def check_dynamic(dy_uid):
# 获取动态并返回动态类型及内容
try:
- _res = await get_dynamic_info(dy_uid=dy_uid)
+ _res = await get_user_dynamic_history(dy_uid=dy_uid)
if not _res.success():
logger.error(f'bilibili_dynamic_monitor: 获取动态失败, uid: {dy_uid}, error: {_res.info}')
return
@@ -117,27 +117,38 @@ async def check_dynamic(dy_uid):
logger.info(f"用户: {dy_uid}/{dynamic_info[num]['name']} 新动态: {dynamic_info[num]['id']}")
# 转发的动态
if dynamic_info[num]['type'] == 1:
- # 原动态type=2, 带图片
- if dynamic_info[num]['origin']['type'] == 2:
- # 处理图片序列
- pic_segs = ''
- for pic_url in dynamic_info[num]['origin']['origin_pics']:
- _res = await pic_2_base64(pic_url)
- pic_b64 = _res.result
- pic_segs += f'{MessageSegment.image(pic_b64)}\n'
- msg = '{}转发了{}的动态!\n\n“{}”\n{}\n{}\n@{}: {}\n{}'.format(
- dynamic_info[num]['name'], dynamic_info[num]['origin']['name'],
- dynamic_info[num]['content'], dynamic_info[num]['url'], '=' * 16,
- dynamic_info[num]['origin']['name'], dynamic_info[num]['origin']['content'],
- pic_segs
- )
- # 原动态为其他类型, 无图
- else:
+ # 获取原动态信息
+ origin_dynamic_id = dynamic_info[num]['origin']
+ _dy_res = await get_dynamic_info(dynamic_id=origin_dynamic_id)
+ if not _dy_res.success():
msg = '{}转发了{}的动态!\n\n“{}”\n{}\n{}\n@{}: {}'.format(
- dynamic_info[num]['name'], dynamic_info[num]['origin']['name'],
+ dynamic_info[num]['name'], 'Unknown',
dynamic_info[num]['content'], dynamic_info[num]['url'], '=' * 16,
- dynamic_info[num]['origin']['name'], dynamic_info[num]['origin']['content']
+ 'Unknown', '获取原动态失败'
)
+ else:
+ origin_dynamic_info = _dy_res.result
+ # 原动态type=2, 带图片
+ if origin_dynamic_info['type'] == 2:
+ # 处理图片序列
+ pic_segs = ''
+ for pic_url in origin_dynamic_info['origin_pics']:
+ _res = await pic_2_base64(pic_url)
+ pic_b64 = _res.result
+ pic_segs += f'{MessageSegment.image(pic_b64)}\n'
+ msg = '{}转发了{}的动态!\n\n“{}”\n{}\n{}\n@{}: {}\n{}'.format(
+ dynamic_info[num]['name'], origin_dynamic_info['name'],
+ dynamic_info[num]['content'], dynamic_info[num]['url'], '=' * 16,
+ origin_dynamic_info['name'], origin_dynamic_info['content'],
+ pic_segs
+ )
+ # 原动态为其他类型, 无图
+ else:
+ msg = '{}转发了{}的动态!\n\n“{}”\n{}\n{}\n@{}: {}'.format(
+ dynamic_info[num]['name'], origin_dynamic_info['name'],
+ dynamic_info[num]['content'], dynamic_info[num]['url'], '=' * 16,
+ origin_dynamic_info['name'], origin_dynamic_info['content']
+ )
for group_id in notice_group:
for _bot in bots:
try:
diff --git a/omega_miya/plugins/bilibili_dynamic_monitor/utils.py b/omega_miya/plugins/bilibili_dynamic_monitor/utils.py
index 8a43f183..b2b4b886 100644
--- a/omega_miya/plugins/bilibili_dynamic_monitor/utils.py
+++ b/omega_miya/plugins/bilibili_dynamic_monitor/utils.py
@@ -13,6 +13,7 @@
global_config = nonebot.get_driver().config
BILI_SESSDATA = global_config.bili_sessdata
BILI_CSRF = global_config.bili_csrf
+BILI_UID = global_config.bili_uid
def check_bili_cookies() -> Result:
@@ -41,8 +42,8 @@ async def fetch_json(url: str, paras: dict = None) -> Result:
'accept-language:': 'zh-CN,zh;q=0.9',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36',
- 'origin': 'https://space.bilibili.com',
- 'referer': 'https://space.bilibili.com/'}
+ 'origin': 'https://t.bilibili.com',
+ 'referer': 'https://t.bilibili.com/'}
async with session.get(url=url, params=paras, headers=headers, cookies=cookies, timeout=timeout) as rp:
_json = await rp.json()
result = Result(error=False, info='Success', result=_json)
@@ -68,7 +69,8 @@ async def get_image(pic_url: str):
async with aiohttp.ClientSession(timeout=timeout) as session:
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36',
- 'referer': 'https://www.bilibili.com/'}
+ 'origin': 'https://t.bilibili.com',
+ 'referer': 'https://t.bilibili.com/'}
async with session.get(url=pic_url, headers=headers, timeout=timeout) as resp:
_res = await resp.read()
return _res
@@ -128,10 +130,14 @@ def get_user_dynamic(user_id: int) -> Result:
# 查询动态并返回动态类型及内容
-async def get_dynamic_info(dy_uid) -> Result:
+async def get_user_dynamic_history(dy_uid) -> Result:
_DYNAMIC_INFO = {} # 这个字典用来放最后的输出结果
url = DYNAMIC_API_URL
- payload = {'host_uid': dy_uid}
+ if BILI_UID:
+ payload = {'visitor_uid': BILI_UID, 'host_uid': dy_uid,
+ 'offset_dynamic_id': 0, 'need_top': 0, 'platform': 'web'}
+ else:
+ payload = {'host_uid': dy_uid, 'offset_dynamic_id': 0, 'need_top': 0, 'platform': 'web'}
result = await fetch_json(url=url, paras=payload)
if not result.success():
@@ -168,55 +174,10 @@ async def get_dynamic_info(dy_uid) -> Result:
name = cards['desc']['user_profile']['info']['uname']
# 这是转发动态时评论的内容
content = card['item']['content']
- # 这是被转发的原动态信息
- try:
- origin_dy_uid = cards['desc']['origin']['dynamic_id']
- __payload = {'dynamic_id': origin_dy_uid}
-
- result = await fetch_json(url=GET_DYNAMIC_DETAIL_API_URL, paras=__payload)
- origin_dynamic = dict(result.result)
- origin_card = origin_dynamic['data']['card']
- origin_name = origin_card['desc']['user_profile']['info']['uname']
- origin_pics_list = []
- if origin_card['desc']['type'] == 1:
- origin_description = json.loads(origin_card['card'])['item']['content']
- elif origin_card['desc']['type'] == 2:
- origin_description = json.loads(origin_card['card'])['item']['description']
- origin_pics = json.loads(origin_card['card'])['item']['pictures']
- for item in origin_pics:
- try:
- origin_pics_list.append(item['img_src'])
- except (KeyError, TypeError):
- continue
- elif origin_card['desc']['type'] == 4:
- origin_description = json.loads(origin_card['card'])['item']['content']
- elif origin_card['desc']['type'] == 8:
- origin_description = json.loads(origin_card['card'])['dynamic']
- if not origin_description:
- origin_description = json.loads(origin_card['card'])['title']
- elif origin_card['desc']['type'] == 16:
- origin_description = json.loads(origin_card['card'])['item']['description']
- elif origin_card['desc']['type'] == 32:
- origin_description = json.loads(origin_card['card'])['title']
- elif origin_card['desc']['type'] == 64:
- origin_description = json.loads(origin_card['card'])['summary']
- elif origin_card['desc']['type'] == 256:
- origin_description = json.loads(origin_card['card'])['intro']
- elif origin_card['desc']['type'] == 512:
- origin_description = json.loads(origin_card['card'])['apiSeasonInfo']['title']
- elif origin_card['desc']['type'] == 2048:
- origin_description = json.loads(origin_card['card'])['vest']['content']
- else:
- origin_description = ''
- origin = dict({'id': origin_dy_uid, 'type': origin_card['desc']['type'], 'url': '',
- 'name': origin_name, 'content': origin_description, 'origin': '',
- 'origin_pics': origin_pics_list})
- except Exception as e:
- # 原动态被删除
- origin = dict({'id': -1, 'type': -1, 'url': '',
- 'name': 'Unknow', 'content': '原动态被删除', 'origin': repr(e)})
+ # 这是被转发的原动态id
+ origin_dynamic_id = cards['desc']['origin']['dynamic_id']
card_dic = dict({'id': dy_id, 'type': 1, 'url': url,
- 'name': name, 'content': content, 'origin': origin})
+ 'name': name, 'content': content, 'origin': origin_dynamic_id})
_DYNAMIC_INFO[card_num] = card_dic
# type=2, 这是一条原创的动态(有图片)
elif cards['desc']['type'] == 2:
@@ -352,17 +313,71 @@ async def get_dynamic_info(dy_uid) -> Result:
dy_id = cards['desc']['dynamic_id']
# 这是动态的链接
url = DYNAMIC_URL + str(cards['desc']['dynamic_id'])
- name = 'Unknow'
+ name = 'Unknown'
card_dic = dict({'id': dy_id, 'type': -1, 'url': url,
'name': name, 'content': '', 'origin': ''})
_DYNAMIC_INFO[card_num] = card_dic
return Result(error=False, info='Success', result=_DYNAMIC_INFO)
+async def get_dynamic_info(dynamic_id) -> Result:
+ __payload = {'dynamic_id': dynamic_id}
+ _res = await fetch_json(url=GET_DYNAMIC_DETAIL_API_URL, paras=__payload)
+ if not _res.success():
+ return _res
+ else:
+ try:
+ origin_dynamic = dict(_res.result)
+ origin_card = origin_dynamic['data']['card']
+ origin_name = origin_card['desc']['user_profile']['info']['uname']
+ origin_pics_list = []
+ if origin_card['desc']['type'] == 1:
+ origin_description = json.loads(origin_card['card'])['item']['content']
+ elif origin_card['desc']['type'] == 2:
+ origin_description = json.loads(origin_card['card'])['item']['description']
+ origin_pics = json.loads(origin_card['card'])['item']['pictures']
+ for item in origin_pics:
+ try:
+ origin_pics_list.append(item['img_src'])
+ except (KeyError, TypeError):
+ continue
+ elif origin_card['desc']['type'] == 4:
+ origin_description = json.loads(origin_card['card'])['item']['content']
+ elif origin_card['desc']['type'] == 8:
+ origin_description = json.loads(origin_card['card'])['dynamic']
+ if not origin_description:
+ origin_description = json.loads(origin_card['card'])['title']
+ elif origin_card['desc']['type'] == 16:
+ origin_description = json.loads(origin_card['card'])['item']['description']
+ elif origin_card['desc']['type'] == 32:
+ origin_description = json.loads(origin_card['card'])['title']
+ elif origin_card['desc']['type'] == 64:
+ origin_description = json.loads(origin_card['card'])['summary']
+ elif origin_card['desc']['type'] == 256:
+ origin_description = json.loads(origin_card['card'])['intro']
+ elif origin_card['desc']['type'] == 512:
+ origin_description = json.loads(origin_card['card'])['apiSeasonInfo']['title']
+ elif origin_card['desc']['type'] == 2048:
+ origin_description = json.loads(origin_card['card'])['vest']['content']
+ else:
+ origin_description = ''
+ origin = dict({'id': dynamic_id, 'type': origin_card['desc']['type'], 'url': '',
+ 'name': origin_name, 'content': origin_description, 'origin': '',
+ 'origin_pics': origin_pics_list})
+ result = Result(error=False, info='Success', result=origin)
+ except Exception as e:
+ # 原动态被删除
+ origin = dict({'id': dynamic_id, 'type': -1, 'url': '',
+ 'name': 'Unknown', 'content': '原动态被删除', 'origin': repr(e)})
+ result = Result(error=True, info='Dynamic not found', result=origin)
+ return result
+
+
__all__ = [
'pic_2_base64',
'get_user_info',
'get_user_dynamic',
+ 'get_user_dynamic_history',
'get_dynamic_info'
]
@@ -370,5 +385,5 @@ async def get_dynamic_info(dy_uid) -> Result:
import asyncio
loop = asyncio.get_event_loop()
- res = loop.run_until_complete(get_dynamic_info(dy_uid=846180))
+ res = loop.run_until_complete(get_user_dynamic_history(dy_uid=846180))
print(res)
diff --git a/omega_miya/plugins/bilibili_live_monitor/monitor.py b/omega_miya/plugins/bilibili_live_monitor/monitor.py
index 26c0d405..6269259c 100644
--- a/omega_miya/plugins/bilibili_live_monitor/monitor.py
+++ b/omega_miya/plugins/bilibili_live_monitor/monitor.py
@@ -1,7 +1,8 @@
import asyncio
+import time
from nonebot import logger, require, get_driver, get_bots
from nonebot.adapters.cqhttp import MessageSegment
-from omega_miya.utils.Omega_Base import DBSubscription, DBTable
+from omega_miya.utils.Omega_Base import DBSubscription, DBHistory, DBTable
from .utils import get_live_info, get_user_info, pic_2_base64, verify_cookies
@@ -20,8 +21,9 @@ async def init_live_info():
if _res.success():
logger.opt(colors=True).info(f'Bilibili 已登录! 当前用户: {_res.result}')
else:
- logger.opt(colors=True).warning(f'Bilibili 未登录! B站动态及直播间监控很可能被B站风控限制! 请在配置中设置cookies以保证插件正常运行!')
+ logger.opt(colors=True).warning(f'Bilibili 登录状态异常: {_res.info}! 建议在配置中正确设置cookies!')
+ logger.opt(colors=True).info('init_live_info: 初始化B站直播间监控列表...')
t = DBTable(table_name='Subscription')
for item in t.list_col_with_condition('sub_id', 'sub_type', 1).result:
sub_id = int(item[0])
@@ -50,7 +52,7 @@ async def init_live_info():
except Exception as e:
logger.error(f'init_live_info: 获取直播间信息错误, room_id: {sub_id}, error: {repr(e)}')
continue
- logger.info('init_live_info: 初始化完成')
+ logger.opt(colors=True).info('init_live_info: B站直播间监控列表初始化完成.')
# 初始化任务加入启动序列
@@ -194,6 +196,11 @@ async def check_live(room_id: int):
try:
# 现在状态为未开播
if live_info['status'] == 0:
+ live_start_info = f"LiveEnd! Room: {room_id}/{up_name}"
+ new_event = DBHistory(time=int(time.time()), self_id=-1, post_type='bilibili', detail_type='live')
+ new_event.add(sub_type='live_end', user_id=room_id, user_name=up_name,
+ raw_data=repr(live_info), msg_data=live_start_info)
+
msg = f'{up_name}下播了'
# 通知有通知权限且订阅了该直播间的群
for group_id in notice_group:
@@ -209,9 +216,12 @@ async def check_live(room_id: int):
logger.info(f"直播间: {room_id}/{up_name} 下播了")
# 现在状态为直播中
elif live_info['status'] == 1:
- # 打一条log记录准确开播信息
- logger.info(f"开播记录: LiveStart! Room: {room_id}/{up_name}, Title: {live_info['title']}, "
- f"TrueTime: {live_info['time']}")
+ # 记录准确开播信息
+ live_start_info = f"LiveStart! Room: {room_id}/{up_name}, Title: {live_info['title']}, " \
+ f"TrueTime: {live_info['time']}"
+ new_event = DBHistory(time=int(time.time()), self_id=-1, post_type='bilibili', detail_type='live')
+ new_event.add(sub_type='live_start', user_id=room_id, user_name=up_name,
+ raw_data=repr(live_info), msg_data=live_start_info)
cover_pic = await pic_2_base64(url=live_info.get('cover_img'))
if cover_pic.success():
@@ -232,6 +242,11 @@ async def check_live(room_id: int):
logger.info(f"直播间: {room_id}/{up_name} 开播了")
# 现在状态为未开播(轮播中)
elif live_info['status'] == 2:
+ live_start_info = f"LiveEnd! Room: {room_id}/{up_name}"
+ new_event = DBHistory(time=int(time.time()), self_id=-1, post_type='bilibili', detail_type='live')
+ new_event.add(sub_type='live_end_with_playlist', user_id=room_id, user_name=up_name,
+ raw_data=repr(live_info), msg_data=live_start_info)
+
msg = f'{up_name}下播了(轮播中)'
for group_id in notice_group:
for _bot in bots:
diff --git a/omega_miya/plugins/bilibili_live_monitor/utils.py b/omega_miya/plugins/bilibili_live_monitor/utils.py
index a6d199fe..db3a8011 100644
--- a/omega_miya/plugins/bilibili_live_monitor/utils.py
+++ b/omega_miya/plugins/bilibili_live_monitor/utils.py
@@ -11,6 +11,7 @@
global_config = nonebot.get_driver().config
BILI_SESSDATA = global_config.bili_sessdata
BILI_CSRF = global_config.bili_csrf
+BILI_UID = global_config.bili_uid
def check_bili_cookies() -> Result:
@@ -66,6 +67,7 @@ async def get_image(pic_url: str):
async with aiohttp.ClientSession(timeout=timeout) as session:
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36',
+ 'origin': 'https://www.bilibili.com',
'referer': 'https://www.bilibili.com/'}
async with session.get(url=pic_url, headers=headers, timeout=timeout) as resp:
_res = await resp.read()
@@ -144,7 +146,11 @@ async def verify_cookies() -> Result:
data = dict(_res.result.get('data'))
if code == 0 and data.get('isLogin'):
uname = data.get('uname')
- result = Result(error=False, info='Success login', result=uname)
+ mid = data.get('mid')
+ if mid == BILI_UID:
+ result = Result(error=False, info='Success login', result=uname)
+ else:
+ result = Result(error=True, info='Logged user UID does not match', result=uname)
else:
result = Result(error=True, info='Not login', result='')
return result
diff --git a/omega_miya/plugins/miya_button/__init__.py b/omega_miya/plugins/miya_button/__init__.py
new file mode 100644
index 00000000..8b78e7a0
--- /dev/null
+++ b/omega_miya/plugins/miya_button/__init__.py
@@ -0,0 +1,35 @@
+import re
+import os
+from nonebot import MatcherGroup, logger
+from nonebot.typing import T_State
+from nonebot.rule import to_me
+from nonebot.adapters.cqhttp.bot import Bot
+from nonebot.adapters.cqhttp.message import MessageSegment
+from nonebot.adapters.cqhttp.event import GroupMessageEvent
+from nonebot.adapters.cqhttp.permission import GROUP
+from omega_miya.utils.Omega_plugin_utils import has_command_permission, permission_level
+from .resources import MiyaVoice
+
+"""
+miya按钮bot实现版本
+测试中
+"""
+
+
+button = MatcherGroup(type='message', rule=to_me() & has_command_permission() & permission_level(level=10),
+ permission=GROUP, priority=100, block=False)
+
+
+miya_button = button.on_endswith(msg='喵一个')
+
+
+@miya_button.handle()
+async def miya_button(bot: Bot, event: GroupMessageEvent, state: T_State):
+ arg = str(event.get_plaintext()).strip().lower()
+ voice = re.sub('喵一个', '', arg)
+ voice_file = MiyaVoice().get_voice_filepath(voice=voice)
+ if not os.path.exists(voice_file):
+ await bot.send(event=event, message='喵?')
+ else:
+ msg = MessageSegment.record(file=f'file:///{voice_file}')
+ await bot.send(event=event, message=msg)
diff --git a/omega_miya/plugins/miya_button/resources/__init__.py b/omega_miya/plugins/miya_button/resources/__init__.py
new file mode 100644
index 00000000..0038a0ed
--- /dev/null
+++ b/omega_miya/plugins/miya_button/resources/__init__.py
@@ -0,0 +1,56 @@
+import os
+import random
+
+
+class Voice(object):
+ def __init__(self):
+ self.VoicesFiles = None
+
+ def get_voice_filepath(self, voice: str) -> str:
+ plugin_path = os.path.dirname(os.path.abspath(__file__))
+ if voice in self.VoicesFiles.keys():
+ return os.path.join(plugin_path, 'voices', self.VoicesFiles[voice]['file'])
+ else:
+ voice_list = []
+ for name, content in self.VoicesFiles.items():
+ if voice == content['tag']:
+ voice_list.append(content['file'])
+ if not voice_list:
+ return ''
+ else:
+ return os.path.join(plugin_path, 'voices', random.choice(voice_list))
+
+
+class MiyaVoice(Voice):
+ def __init__(self):
+ """
+ 硬编码音频素材文件
+ 先暂时这样以后有空再改
+ """
+ super().__init__()
+ self.VoicesFiles = {
+ '表演绝活': {
+ 'file': '0.mp3',
+ 'tag': ''
+ },
+ '你才千岁幼猫': {
+ 'file': '2.mp3',
+ 'tag': ''
+ },
+ '坏蛋': {
+ 'file': '3.mp3',
+ 'tag': ''
+ },
+ '我信了你的鬼话': {
+ 'file': '4.mp3',
+ 'tag': ''
+ },
+ '来打我': {
+ 'file': '5.mp3',
+ 'tag': ''
+ },
+ '说别人憨的人': {
+ 'file': '6.mp3',
+ 'tag': ''
+ }
+ }
diff --git a/requirements.txt b/requirements.txt
index 61a1c34d..206aab2b 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,15 +1,15 @@
-nonebot2==2.0.0a9
+nonebot2==2.0.0a9.post1
sqlalchemy~=1.3.23
mysqlclient~=2.0.3
-aiocqhttp
+aiocqhttp~=1.3.0
bs4~=0.0.1
-lxml
+lxml~=4.6.2
Pillow~=8.1.0
beautifulsoup4~=4.9.3
Jinja2~=2.11.3
aiohttp~=3.7.3
-xlwt
+xlwt~=1.3.0
ujson~=4.0.2
-msgpack
+msgpack~=1.0.2
pydantic~=1.7.2
APScheduler~=3.6.3
\ No newline at end of file