mirror of
https://github.com/A-Minos/nonebot-plugin-tetris-stats.git
synced 2026-03-05 05:36:54 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2cfcba9b07 | |||
| 86adf2621a | |||
| 1219aeda8f | |||
| 0d78007262 | |||
| 5dbd01b15c |
@@ -16,7 +16,7 @@ from .request import Request
|
|||||||
class Processor:
|
class Processor:
|
||||||
@classmethod
|
@classmethod
|
||||||
async def handle_bind(cls, message: str, qq_number: int | None) -> str:
|
async def handle_bind(cls, message: str, qq_number: int | None) -> str:
|
||||||
'''处理绑定消息'''
|
"""处理绑定消息"""
|
||||||
decoded_message = await handle_bind_message(message=message, game_type='IO')
|
decoded_message = await handle_bind_message(message=message, game_type='IO')
|
||||||
if decoded_message[0] is None:
|
if decoded_message[0] is None:
|
||||||
return decoded_message[1][0]
|
return decoded_message[1][0]
|
||||||
@@ -25,6 +25,12 @@ class Processor:
|
|||||||
if user_id_stats[0] is False:
|
if user_id_stats[0] is False:
|
||||||
return user_id_stats[1]
|
return user_id_stats[1]
|
||||||
user_id = decoded_message[1][1]
|
user_id = decoded_message[1][1]
|
||||||
|
if qq_number is None: # 理论上是不会有None出现的, ide快乐行属于是(
|
||||||
|
logger.error('获取QQ号失败')
|
||||||
|
return '获取QQ号失败'
|
||||||
|
return await DataBase.write_bind_info(
|
||||||
|
qq_number=qq_number, user=user_id, game_type='IO'
|
||||||
|
)
|
||||||
elif decoded_message[0] == 'Name':
|
elif decoded_message[0] == 'Name':
|
||||||
user_data = await cls.get_user_data(user_name=decoded_message[1][1])
|
user_data = await cls.get_user_data(user_name=decoded_message[1][1])
|
||||||
if user_data[0] is False:
|
if user_data[0] is False:
|
||||||
@@ -32,18 +38,14 @@ class Processor:
|
|||||||
if user_data[1] is False:
|
if user_data[1] is False:
|
||||||
return f'用户信息请求错误:\n{user_data[2]["error"]}'
|
return f'用户信息请求错误:\n{user_data[2]["error"]}'
|
||||||
user_id = await cls.get_user_id(user_data[2])
|
user_id = await cls.get_user_id(user_data[2])
|
||||||
if qq_number is None: # 理论上是不会有None出现的, ide快乐行属于是(
|
if qq_number is None: # 理论上是不会有None出现的, ide快乐行属于是(
|
||||||
logger.error('获取QQ号失败')
|
logger.error('获取QQ号失败')
|
||||||
return '获取QQ号失败'
|
return '获取QQ号失败'
|
||||||
return (
|
return await DataBase.write_bind_info(
|
||||||
await DataBase.write_bind_info(
|
qq_number=qq_number, user=user_id, game_type='IO'
|
||||||
qq_number=qq_number,
|
|
||||||
user=user_id,
|
|
||||||
game_type='IO'
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
logger.error('预期外行为, 请上报GitHub')
|
logger.error('预期外行为, 请上报GitHub')
|
||||||
return '出现预期外行为,请查看后台信息'
|
return '出现预期外行为, 请查看后台信息'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def handle_rank(cls, message: str):
|
async def handle_rank(cls, message: str):
|
||||||
@@ -74,11 +76,17 @@ class Processor:
|
|||||||
if query_rank.lower() not in (i for i in ranks_percentiles.keys()):
|
if query_rank.lower() not in (i for i in ranks_percentiles.keys()):
|
||||||
return '未知段位'
|
return '未知段位'
|
||||||
|
|
||||||
result = await Request.request('https://ch.tetr.io/api/users/lists/league/all')
|
result = await Request.request(
|
||||||
|
'https://ch.tetr.io/api/users/lists/league/all'
|
||||||
|
)
|
||||||
users: list = result[2]['data']['users']
|
users: list = result[2]['data']['users']
|
||||||
|
|
||||||
def avg(rank_users: list, column: str, playercount: int | None = None) -> float:
|
def avg(
|
||||||
return sum(i['league'][column] for i in rank_users) / (playercount or len(rank_users))
|
rank_users: list, column: str, playercount: int | None = None
|
||||||
|
) -> float:
|
||||||
|
return sum(i['league'][column] for i in rank_users) / (
|
||||||
|
playercount or len(rank_users)
|
||||||
|
)
|
||||||
|
|
||||||
for rank, percentile in ranks_percentiles.items():
|
for rank, percentile in ranks_percentiles.items():
|
||||||
offset = math.floor((percentile / 100) * len(users)) - 1
|
offset = math.floor((percentile / 100) * len(users)) - 1
|
||||||
@@ -97,8 +105,8 @@ class Processor:
|
|||||||
playercount=playercount,
|
playercount=playercount,
|
||||||
avgapm=avg_apm,
|
avgapm=avg_apm,
|
||||||
avgpps=avg_pps,
|
avgpps=avg_pps,
|
||||||
avgvs=avg_vs
|
avgvs=avg_vs,
|
||||||
)
|
)
|
||||||
|
|
||||||
return await Processor.handle_rank(message=message)
|
return await Processor.handle_rank(message=message)
|
||||||
else:
|
else:
|
||||||
@@ -123,12 +131,16 @@ class Processor:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def handle_query(cls, message: str, qq_number: int | None):
|
async def handle_query(cls, message: str, qq_number: int | None):
|
||||||
'''处理查询消息'''
|
"""处理查询消息"""
|
||||||
decoded_message = await handle_stats_query_message(message=message, game_type='IO')
|
decoded_message = await handle_stats_query_message(
|
||||||
|
message=message, game_type='IO'
|
||||||
|
)
|
||||||
if decoded_message[0] is None:
|
if decoded_message[0] is None:
|
||||||
return decoded_message[1][0]
|
return decoded_message[1][0]
|
||||||
if decoded_message[0] == 'AT': # 在入口处判断是否@bot本身
|
if decoded_message[0] == 'AT': # 在入口处判断是否@bot本身
|
||||||
bind_info = await DataBase.query_bind_info(qq_number=decoded_message[1][1], game_type='IO')
|
bind_info = await DataBase.query_bind_info(
|
||||||
|
qq_number=decoded_message[1][1], game_type='IO'
|
||||||
|
)
|
||||||
if bind_info is None:
|
if bind_info is None:
|
||||||
return '未查询到绑定信息'
|
return '未查询到绑定信息'
|
||||||
return f'* 由于无法验证绑定信息, 不能保证查询到的用户为本人\n{await Processor.generate_message(user_id=bind_info)}'
|
return f'* 由于无法验证绑定信息, 不能保证查询到的用户为本人\n{await Processor.generate_message(user_id=bind_info)}'
|
||||||
@@ -136,7 +148,9 @@ class Processor:
|
|||||||
if qq_number is None:
|
if qq_number is None:
|
||||||
logger.error('获取QQ号失败')
|
logger.error('获取QQ号失败')
|
||||||
return '获取QQ号失败, 请联系bot主人'
|
return '获取QQ号失败, 请联系bot主人'
|
||||||
bind_info = await DataBase.query_bind_info(qq_number=qq_number, game_type='IO')
|
bind_info = await DataBase.query_bind_info(
|
||||||
|
qq_number=qq_number, game_type='IO'
|
||||||
|
)
|
||||||
if bind_info is None:
|
if bind_info is None:
|
||||||
return '未查询到绑定信息'
|
return '未查询到绑定信息'
|
||||||
return f'* 由于无法验证绑定信息, 不能保证查询到的用户为本人\n{await Processor.generate_message(user_id=bind_info)}'
|
return f'* 由于无法验证绑定信息, 不能保证查询到的用户为本人\n{await Processor.generate_message(user_id=bind_info)}'
|
||||||
@@ -147,11 +161,9 @@ class Processor:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_user_data(
|
async def get_user_data(
|
||||||
cls,
|
cls, user_name: str | None = None, user_id: str | None = None
|
||||||
user_name: str | None = None,
|
|
||||||
user_id: str | None = None
|
|
||||||
) -> tuple[bool, bool, dict[str, Any]]:
|
) -> tuple[bool, bool, dict[str, Any]]:
|
||||||
'''获取用户数据'''
|
"""获取用户数据"""
|
||||||
if user_name is not None and user_id is None:
|
if user_name is not None and user_id is None:
|
||||||
user_data_url = f'https://ch.tetr.io/api/users/{user_name}'
|
user_data_url = f'https://ch.tetr.io/api/users/{user_name}'
|
||||||
elif user_name is None and user_id is not None:
|
elif user_name is None and user_id is not None:
|
||||||
@@ -162,11 +174,9 @@ class Processor:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_solo_data(
|
async def get_solo_data(
|
||||||
cls,
|
cls, user_name: str | None = None, user_id: str | None = None
|
||||||
user_name: str | None = None,
|
|
||||||
user_id: str | None = None
|
|
||||||
) -> tuple[bool, bool, dict[str, Any]]:
|
) -> tuple[bool, bool, dict[str, Any]]:
|
||||||
'''获取Solo数据'''
|
"""获取Solo数据"""
|
||||||
if user_name is not None and user_id is None:
|
if user_name is not None and user_id is None:
|
||||||
user_solo_url = f'https://ch.tetr.io/api/users/{user_name}/records'
|
user_solo_url = f'https://ch.tetr.io/api/users/{user_name}/records'
|
||||||
elif user_name is None and user_id is not None:
|
elif user_name is None and user_id is not None:
|
||||||
@@ -177,12 +187,12 @@ class Processor:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_user_id(cls, user_data: dict) -> str:
|
async def get_user_id(cls, user_data: dict) -> str:
|
||||||
'''获取用户ID'''
|
"""获取用户ID"""
|
||||||
return user_data['data']['user']['_id']
|
return user_data['data']['user']['_id']
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def check_user_id(cls, user_id: str) -> tuple[bool, str]:
|
async def check_user_id(cls, user_id: str) -> tuple[bool, str]:
|
||||||
'''检查用户ID是否有效 返回值为tuple[bool, message]'''
|
"""检查用户ID是否有效 返回值为tuple[bool, message]"""
|
||||||
user_data = await cls.get_user_data(user_id=user_id)
|
user_data = await cls.get_user_data(user_id=user_id)
|
||||||
if user_data[0] is False:
|
if user_data[0] is False:
|
||||||
return False, '用户信息请求失败'
|
return False, '用户信息请求失败'
|
||||||
@@ -190,18 +200,19 @@ class Processor:
|
|||||||
return False, f'用户信息请求错误:\n{user_data[2]["error"]}'
|
return False, f'用户信息请求错误:\n{user_data[2]["error"]}'
|
||||||
if user_id == user_data[2]['data']['user']['_id']:
|
if user_id == user_data[2]['data']['user']['_id']:
|
||||||
return True, ''
|
return True, ''
|
||||||
raise ValueError('服务器返回的userID和用户提供的不一致, 这种情况理论上不应该发生, 以防万一还是写一下(x')
|
raise ValueError('服务器返回的userID和用户提供的不一致, 这种情况理论上不应该发生, 以防万一还是写一下(x')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_league_stats(cls, user_data: dict) -> dict[str, Any]:
|
async def get_league_stats(cls, user_data: dict) -> dict[str, Any]:
|
||||||
'''获取排位统计数据'''
|
"""获取排位统计数据"""
|
||||||
league = user_data['data']['user']['league']
|
league = user_data['data']['user']['league']
|
||||||
league_stats: dict[str, Any] = {}
|
league_stats: dict[str, Any] = {}
|
||||||
if league['gamesplayed'] != 0:
|
if league['gamesplayed'] != 0:
|
||||||
league_stats['PPS'] = league['pps']
|
league_stats['PPS'] = league['pps']
|
||||||
league_stats['APM'] = league['apm']
|
league_stats['APM'] = league['apm']
|
||||||
league_stats['VS'] = 0 if league['vs'] is None else league['vs']
|
league_stats['VS'] = 0 if league['vs'] is None else league['vs']
|
||||||
league_stats['Rank'] = 'Z' if league['rank'] == 'z' else league['rank'].upper(
|
league_stats['Rank'] = (
|
||||||
|
'Z' if league['rank'] == 'z' else league['rank'].upper()
|
||||||
)
|
)
|
||||||
if league['rating'] == -1:
|
if league['rating'] == -1:
|
||||||
league_stats['Rank'] = None
|
league_stats['Rank'] = None
|
||||||
@@ -211,28 +222,29 @@ class Processor:
|
|||||||
league_stats['RD'] = round(league['rd'], 2)
|
league_stats['RD'] = round(league['rd'], 2)
|
||||||
league_stats['Standing'] = league['standing']
|
league_stats['Standing'] = league['standing']
|
||||||
league_stats['LPM'] = round((league['pps'] * 24), 2)
|
league_stats['LPM'] = round((league['pps'] * 24), 2)
|
||||||
league_stats['APL'] = round(
|
league_stats['APL'] = round((league_stats['APM'] / league_stats['LPM']), 2)
|
||||||
(league_stats['APM'] / league_stats['LPM']), 2)
|
|
||||||
league_stats['ADPM'] = round((league_stats['VS'] * 0.6), 2)
|
league_stats['ADPM'] = round((league_stats['VS'] * 0.6), 2)
|
||||||
league_stats['ADPL'] = round(
|
league_stats['ADPL'] = round(
|
||||||
(league_stats['ADPM'] / league_stats['LPM']), 2)
|
(league_stats['ADPM'] / league_stats['LPM']), 2
|
||||||
|
)
|
||||||
return league_stats
|
return league_stats
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_sprint_stats(cls, solo_data: dict) -> dict[str, Any]:
|
async def get_sprint_stats(cls, solo_data: dict) -> dict[str, Any]:
|
||||||
'''获取40L统计数据'''
|
"""获取40L统计数据"""
|
||||||
sprint_stats = {}
|
sprint_stats = {}
|
||||||
solo = solo_data['data']['records']['40l']
|
solo = solo_data['data']['records']['40l']
|
||||||
if solo['record'] is not None:
|
if solo['record'] is not None:
|
||||||
sprint_stats['Time'] = round(
|
sprint_stats['Time'] = round(
|
||||||
solo['record']['endcontext']['finalTime'] / 1000, 2)
|
solo['record']['endcontext']['finalTime'] / 1000, 2
|
||||||
|
)
|
||||||
if solo['rank'] is not None:
|
if solo['rank'] is not None:
|
||||||
sprint_stats['Rank'] = solo['rank']
|
sprint_stats['Rank'] = solo['rank']
|
||||||
return sprint_stats
|
return sprint_stats
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_blitz_stats(cls, solo_data: dict) -> dict[str, Any]:
|
async def get_blitz_stats(cls, solo_data: dict) -> dict[str, Any]:
|
||||||
'''获取Blitz统计数据'''
|
"""获取Blitz统计数据"""
|
||||||
blitz_stats = {}
|
blitz_stats = {}
|
||||||
blitz = solo_data['data']['records']['blitz']
|
blitz = solo_data['data']['records']['blitz']
|
||||||
if blitz['record'] is not None:
|
if blitz['record'] is not None:
|
||||||
@@ -243,14 +255,12 @@ class Processor:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def generate_message(
|
async def generate_message(
|
||||||
cls,
|
cls, user_name: str | None = None, user_id: str | None = None
|
||||||
user_name: str | None = None,
|
|
||||||
user_id: str | None = None
|
|
||||||
) -> str:
|
) -> str:
|
||||||
'''生成消息'''
|
"""生成消息"""
|
||||||
user_data, solo_data = await gather(
|
user_data, solo_data = await gather(
|
||||||
cls.get_user_data(user_name=user_name, user_id=user_id),
|
cls.get_user_data(user_name=user_name, user_id=user_id),
|
||||||
cls.get_solo_data(user_name=user_name, user_id=user_id)
|
cls.get_solo_data(user_name=user_name, user_id=user_id),
|
||||||
)
|
)
|
||||||
if user_data[0] is False:
|
if user_data[0] is False:
|
||||||
return '用户信息请求失败'
|
return '用户信息请求失败'
|
||||||
@@ -269,18 +279,19 @@ class Processor:
|
|||||||
message += f'用户 {user_name} 暂无段位, {league_stats["Rating"]} TR'
|
message += f'用户 {user_name} 暂无段位, {league_stats["Rating"]} TR'
|
||||||
else:
|
else:
|
||||||
message += f'{league_stats["Rank"]} 段用户 {user_name} {league_stats["Rating"]} TR (#{league_stats["Standing"]})'
|
message += f'{league_stats["Rank"]} 段用户 {user_name} {league_stats["Rating"]} TR (#{league_stats["Standing"]})'
|
||||||
message += f', 段位分 {league_stats["Glicko"]}±{league_stats["RD"]}, 最近十场的数据:'
|
message += (
|
||||||
|
f', 段位分 {league_stats["Glicko"]}±{league_stats["RD"]}, 最近十场的数据:'
|
||||||
|
)
|
||||||
message += f'\nL\'PM: {league_stats["LPM"]} ( {league_stats["PPS"]} pps )'
|
message += f'\nL\'PM: {league_stats["LPM"]} ( {league_stats["PPS"]} pps )'
|
||||||
message += f'\nAPM: {league_stats["APM"]} ( x{league_stats["APL"]} )'
|
message += f'\nAPM: {league_stats["APM"]} ( x{league_stats["APL"]} )'
|
||||||
if league_stats["VS"] != 0:
|
if league_stats['VS'] != 0:
|
||||||
message += f'\nADPM: {league_stats["ADPM"]} ( x{league_stats["ADPL"]} ) ( {league_stats["VS"]}vs )'
|
message += f'\nADPM: {league_stats["ADPM"]} ( x{league_stats["ADPL"]} ) ( {league_stats["VS"]}vs )'
|
||||||
if solo_data[0] is False:
|
if solo_data[0] is False:
|
||||||
return f'{message}\nSolo统计数据请求失败'
|
return f'{message}\nSolo统计数据请求失败'
|
||||||
if solo_data[1] is False:
|
if solo_data[1] is False:
|
||||||
return f'{message}\nSolo统计数据请求错误:\n{solo_data[2]["error"]}'
|
return f'{message}\nSolo统计数据请求错误:\n{solo_data[2]["error"]}'
|
||||||
sprint_stats, blitz_stats = await gather(
|
sprint_stats, blitz_stats = await gather(
|
||||||
cls.get_sprint_stats(solo_data[2]),
|
cls.get_sprint_stats(solo_data[2]), cls.get_blitz_stats(solo_data[2])
|
||||||
cls.get_blitz_stats(solo_data[2])
|
|
||||||
)
|
)
|
||||||
message += f'\n40L: {sprint_stats["Time"]}s' if 'Time' in sprint_stats else ''
|
message += f'\n40L: {sprint_stats["Time"]}s' if 'Time' in sprint_stats else ''
|
||||||
message += f' ( #{sprint_stats["Rank"]} )' if 'Rank' in sprint_stats else ''
|
message += f' ( #{sprint_stats["Rank"]} )' if 'Rank' in sprint_stats else ''
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import aiohttp
|
from httpx import AsyncClient, HTTPError
|
||||||
from nonebot import get_driver
|
from nonebot import get_driver
|
||||||
from nonebot.log import logger
|
from nonebot.log import logger
|
||||||
from playwright.async_api import Browser, Response, async_playwright
|
from playwright.async_api import Browser, Response, async_playwright
|
||||||
@@ -27,26 +27,27 @@ async def _():
|
|||||||
|
|
||||||
|
|
||||||
class Request:
|
class Request:
|
||||||
'''网络请求相关类'''
|
"""网络请求相关类"""
|
||||||
|
|
||||||
_browser: Browser | None = None
|
_browser: Browser | None = None
|
||||||
_headers: dict | None = None
|
_headers: dict | None = None
|
||||||
_cookies: dict | None = None
|
_cookies: dict | None = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def _init_playwright(cls) -> Browser:
|
async def _init_playwright(cls) -> Browser:
|
||||||
'''初始化playwright'''
|
"""初始化playwright"""
|
||||||
playwright = await async_playwright().start()
|
playwright = await async_playwright().start()
|
||||||
cls._browser = await playwright.firefox.launch()
|
cls._browser = await playwright.firefox.launch()
|
||||||
return cls._browser
|
return cls._browser
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def _get_browser(cls) -> Browser:
|
async def _get_browser(cls) -> Browser:
|
||||||
'''获取浏览器对象'''
|
"""获取浏览器对象"""
|
||||||
return cls._browser or await cls._init_playwright()
|
return cls._browser or await cls._init_playwright()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def _anti_cloudflare(cls, url: str) -> tuple[bool, bool, dict[str, Any]]:
|
async def _anti_cloudflare(cls, url: str) -> tuple[bool, bool, dict[str, Any]]:
|
||||||
'''用firefox硬穿五秒盾'''
|
"""用firefox硬穿五秒盾"""
|
||||||
browser = await cls._get_browser()
|
browser = await cls._get_browser()
|
||||||
context = await browser.new_context()
|
context = await browser.new_context()
|
||||||
page = await context.new_page()
|
page = await context.new_page()
|
||||||
@@ -58,7 +59,7 @@ class Request:
|
|||||||
if text is None:
|
if text is None:
|
||||||
await page.wait_for_timeout(1000)
|
await page.wait_for_timeout(1000)
|
||||||
continue
|
continue
|
||||||
if await page.title() == 'Please Wait... | Cloudflare':
|
if await page.title() == "Please Wait... | Cloudflare":
|
||||||
# TODO 有无人来做一个过验证码(
|
# TODO 有无人来做一个过验证码(
|
||||||
break
|
break
|
||||||
try:
|
try:
|
||||||
@@ -69,40 +70,35 @@ class Request:
|
|||||||
assert isinstance(response, Response)
|
assert isinstance(response, Response)
|
||||||
cls._headers = await response.request.all_headers()
|
cls._headers = await response.request.all_headers()
|
||||||
try:
|
try:
|
||||||
cls._cookies = {i['name']: i['value'] for i in await context.cookies()}
|
cls._cookies = {
|
||||||
|
i["name"]: i["value"] for i in await context.cookies()
|
||||||
|
}
|
||||||
except KeyError:
|
except KeyError:
|
||||||
cls._cookies = None
|
cls._cookies = None
|
||||||
await page.close()
|
await page.close()
|
||||||
await context.close()
|
await context.close()
|
||||||
return True, data['success'], data
|
return True, data["success"], data
|
||||||
await page.close()
|
await page.close()
|
||||||
await context.close()
|
await context.close()
|
||||||
return True, False, {'error': '绕过五秒盾失败'}
|
return True, False, {"error": "绕过五秒盾失败"}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def init_cache(cls) -> None:
|
async def init_cache(cls) -> None:
|
||||||
'''初始化缓存文件'''
|
"""初始化缓存文件"""
|
||||||
if not os.path.exists(os.path.dirname(config.cache_path)):
|
if not os.path.exists(os.path.dirname(config.cache_path)):
|
||||||
os.makedirs(os.path.dirname(config.cache_path))
|
os.makedirs(os.path.dirname(config.cache_path))
|
||||||
if not os.path.exists(config.cache_path):
|
if not os.path.exists(config.cache_path):
|
||||||
with open(file=config.cache_path, mode='w', encoding='UTF-8') as file:
|
with open(file=config.cache_path, mode="w", encoding="UTF-8") as file:
|
||||||
file.write(
|
file.write(dumps({"headers": cls._headers, "cookies": cls._cookies}))
|
||||||
dumps(
|
|
||||||
{
|
|
||||||
'headers': cls._headers,
|
|
||||||
'cookies': cls._cookies
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def read_cache(cls) -> None:
|
async def read_cache(cls) -> None:
|
||||||
'''读取缓存文件'''
|
"""读取缓存文件"""
|
||||||
try:
|
try:
|
||||||
with open(file=config.cache_path, mode='r', encoding='UTF-8') as file:
|
with open(file=config.cache_path, mode="r", encoding="UTF-8") as file:
|
||||||
json = loads(file.read())
|
json = loads(file.read())
|
||||||
cls._headers = json['headers']
|
cls._headers = json["headers"]
|
||||||
cls._cookies = json['cookies']
|
cls._cookies = json["cookies"]
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
await cls.init_cache()
|
await cls.init_cache()
|
||||||
except PermissionError:
|
except PermissionError:
|
||||||
@@ -114,17 +110,10 @@ class Request:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def write_cache(cls) -> None:
|
async def write_cache(cls) -> None:
|
||||||
'''写入缓存文件'''
|
"""写入缓存文件"""
|
||||||
try:
|
try:
|
||||||
with open(file=config.cache_path, mode='r+', encoding='UTF-8') as file:
|
with open(file=config.cache_path, mode="r+", encoding="UTF-8") as file:
|
||||||
file.write(
|
file.write(dumps({"headers": cls._headers, "cookies": cls._cookies}))
|
||||||
dumps(
|
|
||||||
{
|
|
||||||
'headers': cls._headers,
|
|
||||||
'cookies': cls._cookies
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
await cls.init_cache()
|
await cls.init_cache()
|
||||||
except PermissionError:
|
except PermissionError:
|
||||||
@@ -136,20 +125,20 @@ class Request:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def request(cls, url: str) -> tuple[bool, bool, dict[str, Any]]:
|
async def request(cls, url: str) -> tuple[bool, bool, dict[str, Any]]:
|
||||||
'''请求api'''
|
"""请求api"""
|
||||||
try:
|
try:
|
||||||
async with aiohttp.ClientSession(cookies=cls._cookies) as session:
|
async with AsyncClient(cookies=cls._cookies) as session:
|
||||||
async with session.get(url, headers=cls._headers) as resp:
|
response = await session.get(url, headers=cls._headers)
|
||||||
data = await resp.json()
|
data = loads(response.content)
|
||||||
return True, data['success'], data
|
return True, data["success"], data
|
||||||
except aiohttp.client_exceptions.ClientConnectorError as error: # type: ignore
|
except HTTPError as error:
|
||||||
logger.error(f'请求错误\n{error}')
|
logger.error(f"请求错误\n{error}")
|
||||||
return False, False, {}
|
return False, False, {}
|
||||||
except aiohttp.client_exceptions.ContentTypeError: # type: ignore
|
except JSONDecodeError:
|
||||||
return await cls._anti_cloudflare(url)
|
return await cls._anti_cloudflare(url)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def close_browser(cls) -> None:
|
async def close_browser(cls) -> None:
|
||||||
'''关闭浏览器对象'''
|
"""关闭浏览器对象"""
|
||||||
if isinstance(cls._browser, Browser):
|
if isinstance(cls._browser, Browser):
|
||||||
await cls._browser.close()
|
await cls._browser.close()
|
||||||
|
|||||||
61
poetry.lock
generated
61
poetry.lock
generated
@@ -1,4 +1,4 @@
|
|||||||
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiohttp"
|
name = "aiohttp"
|
||||||
@@ -308,6 +308,17 @@ files = [
|
|||||||
{file = "Brotli-1.0.9.zip", hash = "sha256:4d1b810aa0ed773f81dceda2cc7b403d01057458730e309856356d4ef4188438"},
|
{file = "Brotli-1.0.9.zip", hash = "sha256:4d1b810aa0ed773f81dceda2cc7b403d01057458730e309856356d4ef4188438"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "certifi"
|
||||||
|
version = "2023.7.22"
|
||||||
|
description = "Python package for providing Mozilla's CA Bundle."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"},
|
||||||
|
{file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "charset-normalizer"
|
name = "charset-normalizer"
|
||||||
version = "2.1.1"
|
version = "2.1.1"
|
||||||
@@ -549,6 +560,27 @@ files = [
|
|||||||
{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
|
{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "httpcore"
|
||||||
|
version = "0.18.0"
|
||||||
|
description = "A minimal low-level HTTP client."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "httpcore-0.18.0-py3-none-any.whl", hash = "sha256:adc5398ee0a476567bf87467063ee63584a8bce86078bf748e48754f60202ced"},
|
||||||
|
{file = "httpcore-0.18.0.tar.gz", hash = "sha256:13b5e5cd1dca1a6636a6aaea212b19f4f85cd88c366a2b82304181b769aab3c9"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
anyio = ">=3.0,<5.0"
|
||||||
|
certifi = "*"
|
||||||
|
h11 = ">=0.13,<0.15"
|
||||||
|
sniffio = "==1.*"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
http2 = ["h2 (>=3,<5)"]
|
||||||
|
socks = ["socksio (==1.*)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httptools"
|
name = "httptools"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@@ -602,6 +634,29 @@ files = [
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
test = ["Cython (>=0.29.24,<0.30.0)"]
|
test = ["Cython (>=0.29.24,<0.30.0)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "httpx"
|
||||||
|
version = "0.25.0"
|
||||||
|
description = "The next generation HTTP client."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "httpx-0.25.0-py3-none-any.whl", hash = "sha256:181ea7f8ba3a82578be86ef4171554dd45fec26a02556a744db029a0a27b7100"},
|
||||||
|
{file = "httpx-0.25.0.tar.gz", hash = "sha256:47ecda285389cb32bb2691cc6e069e3ab0205956f681c5b2ad2325719751d875"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
certifi = "*"
|
||||||
|
httpcore = ">=0.18.0,<0.19.0"
|
||||||
|
idna = "*"
|
||||||
|
sniffio = "*"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
brotli = ["brotli", "brotlicffi"]
|
||||||
|
cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
|
||||||
|
http2 = ["h2 (>=3,<5)"]
|
||||||
|
socks = ["socksio (==1.*)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "3.4"
|
version = "3.4"
|
||||||
@@ -1116,7 +1171,7 @@ files = [
|
|||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
numpy = [
|
numpy = [
|
||||||
{version = ">=1.21.0", markers = "python_version >= \"3.10\""},
|
{version = ">=1.21.0", markers = "python_version >= \"3.10\" and python_version < \"3.11\""},
|
||||||
{version = ">=1.23.2", markers = "python_version >= \"3.11\""},
|
{version = ">=1.23.2", markers = "python_version >= \"3.11\""},
|
||||||
]
|
]
|
||||||
python-dateutil = ">=2.8.1"
|
python-dateutil = ">=2.8.1"
|
||||||
@@ -1901,4 +1956,4 @@ multidict = ">=4.0"
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.10,<3.12"
|
python-versions = "^3.10,<3.12"
|
||||||
content-hash = "7c10b6db4d8cf72d4453cf8eaa0d622eef3958483cc0fc239c08bd4f7dc9151b"
|
content-hash = "afbec27d3b608d5bceef47355368e1643dc211e194cb5d0f6b021b3a9fed60ef"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "nonebot-plugin-tetris-stats"
|
name = "nonebot-plugin-tetris-stats"
|
||||||
version = "0.4.2"
|
version = "0.4.4"
|
||||||
description = "一个基于nonebot2的用于查询TETRIS相关游戏玩家数据的插件"
|
description = "一个基于nonebot2的用于查询TETRIS相关游戏玩家数据的插件"
|
||||||
authors = ["scdhh <wallfjjd@gmail.com>"]
|
authors = ["scdhh <wallfjjd@gmail.com>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -19,6 +19,7 @@ pandas = "^1.4.3"
|
|||||||
playwright = "^1.24.1"
|
playwright = "^1.24.1"
|
||||||
ujson = "^5.4.0"
|
ujson = "^5.4.0"
|
||||||
Brotli = "^1.0.9"
|
Brotli = "^1.0.9"
|
||||||
|
httpx = "^0.25.0"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
mypy = "^0.991"
|
mypy = "^0.991"
|
||||||
|
|||||||
Reference in New Issue
Block a user