Support wish import lazy refresh

This commit is contained in:
xtaodada 2024-12-01 18:03:22 +08:00
parent 682cfb1f0f
commit 4af2cab246
Signed by: xtaodada
GPG Key ID: 4CBB3F4FA8C85659
8 changed files with 164 additions and 88 deletions

View File

@ -9,6 +9,7 @@ from typing import Dict, List, Optional, Tuple, TYPE_CHECKING
import aiofiles
from simnet import StarRailClient, Region
from simnet.errors import AuthkeyTimeout, InvalidAuthkey
from simnet.models.base import add_timezone
from simnet.models.starrail.wish import StarRailBannerType
from simnet.utils.player import recognize_starrail_server
@ -111,7 +112,7 @@ class GachaLog(GachaLogOnlineView, GachaLogRanks):
return False
async def move_history_info(self, user_id: str, uid: str, new_user_id: str) -> bool:
"""移动历史抽卡记录数据
"""移动历史跃迁记录数据
:param user_id: 用户id
:param uid: 原神uid
:param new_user_id: 新用户id
@ -237,7 +238,7 @@ class GachaLog(GachaLogOnlineView, GachaLogRanks):
# 检查导入后的数据是否合法
await self.verify_data(i)
i.sort(key=lambda x: (x.time, x.id))
gacha_log.update_time = datetime.datetime.now()
gacha_log.update_time = add_timezone(datetime.datetime.now())
gacha_log.import_type = import_type.value
await self.save_gacha_log_info(str(user_id), uid, gacha_log)
await self.recount_one_from_uid(user_id, player_id)
@ -256,21 +257,38 @@ class GachaLog(GachaLogOnlineView, GachaLogRanks):
else:
return StarRailClient(player_id=player_id, region=Region.OVERSEAS, lang="zh-cn")
async def get_gacha_log_data(self, user_id: int, player_id: int, authkey: str) -> int:
async def get_gacha_log_data(self, user_id: int, player_id: int, authkey: str, is_lazy: bool) -> int:
"""使用authkey获取跃迁记录数据并合并旧数据
:param user_id: 用户id
:param player_id: 玩家id
:param authkey: authkey
:param is_lazy: 是否快速导入
:return: 更新结果
"""
new_num = 0
gacha_log, _ = await self.load_history_info(str(user_id), str(player_id))
# 将唯一 id 放入临时数据中,加快查找速度
temp_id_data = {pool_name: {i.id: i for i in pool_data} for pool_name, pool_data in gacha_log.item_list.items()}
temp_id_data = {pool_name: [i.id for i in pool_data] for pool_name, pool_data in gacha_log.item_list.items()}
client = self.get_game_client(player_id)
try:
for pool_id, pool_name in GACHA_TYPE_LIST.items():
wish_history = await client.wish_history(pool_id.value, authkey=authkey)
if pool_name not in temp_id_data:
temp_id_data[pool_name] = []
if pool_name not in gacha_log.item_list:
gacha_log.item_list[pool_name] = []
min_id = 0
if is_lazy and gacha_log.item_list[pool_name]:
with contextlib.suppress(ValueError):
min_id = int(gacha_log.item_list[pool_name][-1].id)
wish_history = await client.wish_history(pool_id.value, authkey=authkey, min_id=min_id)
if not is_lazy:
min_id = wish_history[0].id if wish_history else min_id
if min_id:
gacha_log.item_list[pool_name][:] = filter(
lambda i: int(i.id) < min_id, gacha_log.item_list[pool_name]
)
for data in wish_history:
item = GachaItem(
id=str(data.id),
@ -280,28 +298,15 @@ class GachaLog(GachaLogOnlineView, GachaLogRanks):
item_id=str(data.item_id),
item_type=data.type,
rank_type=str(data.rarity),
time=datetime.datetime(
data.time.year,
data.time.month,
data.time.day,
data.time.hour,
data.time.minute,
data.time.second,
),
time=data.time,
)
if pool_name not in temp_id_data:
temp_id_data[pool_name] = {}
if pool_name not in gacha_log.item_list:
gacha_log.item_list[pool_name] = []
if item.id not in temp_id_data[pool_name].keys():
if item.id not in temp_id_data[pool_name] or (not is_lazy and min_id):
gacha_log.item_list[pool_name].append(item)
temp_id_data[pool_name][item.id] = item
temp_id_data[pool_name].append(item.id)
new_num += 1
else:
old_item: GachaItem = temp_id_data[pool_name][item.id]
old_item.gacha_id = item.gacha_id
old_item.item_id = item.item_id
await asyncio.sleep(1)
except AuthkeyTimeout as exc:
raise GachaLogAuthkeyTimeout from exc
except InvalidAuthkey as exc:
@ -310,11 +315,15 @@ class GachaLog(GachaLogOnlineView, GachaLogRanks):
await client.shutdown()
for i in gacha_log.item_list.values():
i.sort(key=lambda x: (x.time, x.id))
gacha_log.update_time = datetime.datetime.now()
gacha_log.update_time = add_timezone(datetime.datetime.now())
gacha_log.import_type = ImportType.PaiGram.value
await self.save_gacha_log_info(str(user_id), str(player_id), gacha_log)
return new_num
@staticmethod
def format_time(time: str) -> datetime.datetime:
return add_timezone(datetime.datetime.strptime(time, "%Y-%m-%d %H:%M:%S"))
@staticmethod
def check_avatar_up(name: str, gacha_time: datetime.datetime) -> bool:
if name in {"姬子", "瓦尔特", "布洛妮娅", "杰帕德", "克拉拉", "彦卿", "白露"}:
@ -547,8 +556,8 @@ class GachaLog(GachaLogOnlineView, GachaLogRanks):
self, gacha_log: "GachaLogInfo", pool: StarRailBannerType, assets: Optional["AssetsService"]
):
"""
获取抽卡记录分析数据
:param gacha_log: 抽卡记录
获取跃迁记录分析数据
:param gacha_log: 跃迁记录
:param pool: 池子类型
:param assets: 资源服务
:return: 分析数据

View File

@ -13,7 +13,7 @@ class GachaLogMigrate(IMigrateData, GachaLog):
old_uid_list: List[int]
async def migrate_data_msg(self) -> str:
return f"{len(self.old_uid_list)} 个账号的抽卡记录数据"
return f"{len(self.old_uid_list)} 个账号的跃迁记录数据"
async def migrate_data(self) -> bool:
uid_list = []
@ -21,7 +21,7 @@ class GachaLogMigrate(IMigrateData, GachaLog):
if not await self.move_history_info(str(self.old_user_id), str(uid), str(self.new_user_id)):
uid_list.append(str(uid))
if uid_list:
raise MigrateDataException(f"抽卡记录数据迁移失败uid {','.join(uid_list)}")
raise MigrateDataException(f"跃迁记录数据迁移失败uid {','.join(uid_list)}")
return True
@classmethod

View File

@ -2,7 +2,9 @@ import datetime
from enum import Enum
from typing import Any, Dict, List, Union
from pydantic import field_validator, BaseModel
from pydantic import field_validator
from simnet.models.base import APIModel as BaseModel, DateTimeField, add_timezone
from metadata.shortname import not_real_roles, roleToId, lightConeToId
from modules.gacha_log.const import SRGF_VERSION
@ -21,7 +23,7 @@ class FiveStarItem(BaseModel):
type: str
isUp: bool
isBig: bool
time: datetime.datetime
time: DateTimeField
class FourStarItem(BaseModel):
@ -29,7 +31,7 @@ class FourStarItem(BaseModel):
icon: str
count: int
type: str
time: datetime.datetime
time: DateTimeField
class GachaItem(BaseModel):
@ -40,7 +42,7 @@ class GachaItem(BaseModel):
item_id: str = ""
item_type: str
rank_type: str
time: datetime.datetime
time: DateTimeField
@field_validator("name")
@classmethod
@ -75,7 +77,7 @@ class GachaItem(BaseModel):
class GachaLogInfo(BaseModel):
user_id: str
uid: str
update_time: datetime.datetime
update_time: DateTimeField
import_type: str = ""
item_list: Dict[str, List[GachaItem]] = {
"角色跃迁": [],
@ -100,8 +102,8 @@ class Pool:
self.four = four
self.from_ = kwargs.get("from")
self.to = to
self.from_time = datetime.datetime.strptime(self.from_, "%Y-%m-%d %H:%M:%S")
self.to_time = datetime.datetime.strptime(self.to, "%Y-%m-%d %H:%M:%S")
self.from_time = add_timezone(datetime.datetime.strptime(self.from_, "%Y-%m-%d %H:%M:%S"))
self.to_time = add_timezone(datetime.datetime.strptime(self.to, "%Y-%m-%d %H:%M:%S"))
self.start = self.from_time
self.start_init = False
self.end = self.to_time

View File

@ -12,7 +12,7 @@ from modules.gacha_log.error import GachaLogWebNotConfigError, GachaLogWebUpload
class GachaLogWebConfig(Settings):
"""抽卡记录在线查询配置"""
"""跃迁记录在线查询配置"""
url: Optional[str] = ""
token: Optional[str] = ""
@ -25,7 +25,7 @@ DEFAULT_POOL = "角色跃迁"
class GachaLogOnlineView:
"""抽卡记录在线查询"""
"""跃迁记录在线查询"""
gacha_log_path: Path

View File

@ -18,11 +18,11 @@ if TYPE_CHECKING:
class GachaLogError(Exception):
"""抽卡记录异常"""
"""跃迁记录异常"""
class GachaLogRanks:
"""抽卡记录排行榜"""
"""跃迁记录排行榜"""
gacha_log_path: Path
ITEM_LIST_MAP = {
@ -62,8 +62,8 @@ class GachaLogRanks:
self, gacha_log: "GachaLogInfo", pool: StarRailBannerType, assets: Optional["AssetsService"]
):
"""
获取抽卡记录分析数据
:param gacha_log: 抽卡记录
获取跃迁记录分析数据
:param gacha_log: 跃迁记录
:param pool: 池子类型
:param assets: 资源服务
:return: 分析数据
@ -87,7 +87,7 @@ class GachaLogRanks:
try:
gacha_log = GachaLogInfo.parse_obj(await self.load_json(file_path))
if gacha_log.get_import_type != ImportType.PaiGram:
raise GachaLogError("不支持的抽卡记录类型")
raise GachaLogError("不支持的跃迁记录类型")
except ValueError as e:
raise GachaLogError from e
player_id = int(gacha_log.uid)

View File

@ -38,7 +38,7 @@ class SetCommandPlugin(Plugin):
group_command = [
BotCommand("help", "帮助"),
BotCommand("warp_log", "查看跃迁记录"),
BotCommand("warp_log_online_view", "抽卡记录在线浏览"),
BotCommand("warp_log_online_view", "跃迁记录在线浏览"),
BotCommand("warp_log_rank", "抽卡排行榜"),
BotCommand("warp_waiting_list", "未复刻列表"),
BotCommand("action_log", "查询登录记录"),

View File

@ -3,7 +3,13 @@ from io import BytesIO
from typing import Optional, TYPE_CHECKING, List, Union, Tuple, Dict
from simnet.models.starrail.wish import StarRailBannerType
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram import (
InlineKeyboardButton,
InlineKeyboardMarkup,
ReplyKeyboardMarkup,
ReplyKeyboardRemove,
TelegramObject,
)
from telegram.constants import ChatAction
from telegram.ext import ConversationHandler, filters
from telegram.helpers import create_deep_linked_url
@ -49,7 +55,7 @@ if TYPE_CHECKING:
from gram_core.services.players.models import Player
from gram_core.services.template.models import RenderResult
INPUT_URL, INPUT_FILE, CONFIRM_DELETE = range(10100, 10103)
INPUT_URL, INPUT_LAZY, CONFIRM_DELETE = range(10100, 10103)
WAITING = f"{config.notice.bot_name}正在从服务器获取数据,请稍后"
WISHLOG_NOT_FOUND = f"{config.notice.bot_name}没有找到你的跃迁记录,快来私聊{config.notice.bot_name}导入吧~"
WISHLOG_WEB = """<b>跃迁记录详细信息查询</b>
@ -59,9 +65,26 @@ WISHLOG_WEB = """<b>跃迁记录详细信息查询</b>
有效期为 1 小时过期需重新申请如怀疑泄漏请立即重新申请"""
class WishLogPluginData(TelegramObject):
player_id: int = 0
authkey: str = ""
def reset_data(self):
self.player_id = 0
self.authkey = ""
class WishLogPlugin(Plugin.Conversation):
"""跃迁记录导入/导出/分析"""
IMPORT_HINT = (
"<b>开始导入跃迁历史记录:请通过 https://starrailstation.com/cn/warp#import 获取跃迁记录链接后发送给我"
"(非 starrailstation.com 导出的文件数据)</b>\n\n"
f"> 你还可以向彦卿发送从其他工具导出的 SRGF {SRGF_VERSION} 标准的记录文件\n"
# "> 在绑定 Cookie 时添加 stoken 可能有特殊效果哦(仅限国服)\n"
"<b>注意:导入的数据将会与旧数据进行合并。</b>"
)
def __init__(
self,
template_service: TemplateService,
@ -90,7 +113,13 @@ class WishLogPlugin(Plugin.Conversation):
return player.player_id
async def _refresh_user_data(
self, user: "User", player_id: int, data: dict = None, authkey: str = None, verify_uid: bool = True
self,
user: "User",
player_id: int,
data: dict = None,
authkey: str = None,
verify_uid: bool = True,
is_lazy: bool = True,
) -> str:
"""刷新用户数据
:param user: 用户
@ -101,7 +130,7 @@ class WishLogPlugin(Plugin.Conversation):
try:
logger.debug("尝试获取已绑定的星穹铁道账号")
if authkey:
new_num = await self.gacha_log.get_gacha_log_data(user.id, player_id, authkey)
new_num = await self.gacha_log.get_gacha_log_data(user.id, player_id, authkey, is_lazy)
return "更新完成,本次没有新增数据" if new_num == 0 else f"更新完成,本次共新增{new_num}条跃迁记录"
if data:
new_num = await self.gacha_log.import_gacha_log_data(user.id, player_id, data, verify_uid)
@ -165,6 +194,12 @@ class WishLogPlugin(Plugin.Conversation):
text = "文件解析失败,请检查文件是否符合 SRGF 标准"
await reply.edit_text(text)
async def can_gen_authkey(self, user_id: int, player_id: int) -> bool:
return False
async def gen_authkey(self, uid: int, player_id: int) -> Optional[str]:
return None
@conversation.entry_point
@handler.command(command="warp_log_import", filters=filters.ChatType.PRIVATE, block=False)
@handler.message(filters=filters.Regex("^导入跃迁记录(.*)") & filters.ChatType.PRIVATE, block=False)
@ -174,48 +209,71 @@ class WishLogPlugin(Plugin.Conversation):
message = update.effective_message
user = update.effective_user
player_id = await self.get_player_id(user.id, uid, offset)
context.chat_data["uid"] = player_id
args = self.get_args(context)
wish_log_plugin_data: WishLogPluginData = context.chat_data.get("wish_log_plugin_data")
if wish_log_plugin_data is None:
wish_log_plugin_data = WishLogPluginData()
context.chat_data["wish_log_plugin_data"] = wish_log_plugin_data
else:
wish_log_plugin_data.reset_data()
wish_log_plugin_data.player_id = player_id
logger.info("用户 %s[%s] 导入跃迁记录命令请求", user.full_name, user.id)
authkey = from_url_get_authkey(args[0] if args else "")
if authkey == "warp_log_import":
authkey = ""
if not authkey:
await message.reply_text(
"<b>开始导入跃迁历史记录:请通过 https://starrailstation.com/cn/warp#import 获取跃迁记录链接后发送给我"
"(非 starrailstation.com 导出的文件数据)</b>\n\n"
f"> 你还可以向彦卿发送从其他工具导出的 SRGF {SRGF_VERSION} 标准的记录文件\n"
# "> 在绑定 Cookie 时添加 stoken 可能有特殊效果哦(仅限国服)\n"
"<b>注意:导入的数据将会与旧数据进行合并。</b>",
parse_mode="html",
)
return INPUT_URL
text = WAITING
if not args:
text += "\n\n> 由于你绑定的 Cookie 中存在 stoken ,本次通过 stoken 自动刷新数据"
reply = await message.reply_text(text)
await message.reply_chat_action(ChatAction.TYPING)
data = await self._refresh_user_data(user, player_id, authkey=authkey)
await reply.edit_text(data)
return ConversationHandler.END
keyboard = None
if await self.can_gen_authkey(user.id, player_id):
keyboard = ReplyKeyboardMarkup([["自动导入"], ["退出"]], one_time_keyboard=True)
await message.reply_text(self.IMPORT_HINT, parse_mode="html", reply_markup=keyboard)
return INPUT_URL
@conversation.state(state=INPUT_URL)
@handler.message(filters=~filters.COMMAND, block=False)
async def import_data_from_message(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> int:
message = update.effective_message
user = update.effective_user
player_id = context.chat_data["uid"]
wish_log_plugin_data: WishLogPluginData = context.chat_data.get("wish_log_plugin_data")
player_id = wish_log_plugin_data.player_id
if message.document:
logger.info("用户 %s[%s] 从文件导入跃迁记录", user.full_name, user.id)
await self.import_from_file(user, player_id, message)
return ConversationHandler.END
if not message.text:
await message.reply_text("请发送文件或链接")
return INPUT_URL
authkey = from_url_get_authkey(message.text)
reply = await message.reply_text(WAITING)
if message.text == "自动导入":
authkey = await self.gen_authkey(user.id, player_id)
if not authkey:
await message.reply_text(
"自动生成 authkey 失败,请尝试通过其他方式导入。", reply_markup=ReplyKeyboardRemove()
)
return ConversationHandler.END
elif message.text == "退出":
await message.reply_text("取消导入跃迁记录", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
else:
authkey = from_url_get_authkey(message.text)
wish_log_plugin_data.authkey = authkey
keyboard = ReplyKeyboardMarkup([["快速导入(推荐)"], ["全量刷新"], ["退出"]], one_time_keyboard=True)
await message.reply_text("请选择导入方式", parse_mode="html", reply_markup=keyboard)
return INPUT_LAZY
@conversation.state(state=INPUT_LAZY)
@handler.message(filters=~filters.COMMAND, block=False)
async def get_lazy_from_message(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> int:
message = update.effective_message
user = update.effective_user
wish_log_plugin_data: WishLogPluginData = context.chat_data.get("wish_log_plugin_data")
player_id = wish_log_plugin_data.player_id
authkey = wish_log_plugin_data.authkey
is_lazy = True
if message.text == "全量刷新":
is_lazy = False
elif message.text == "退出":
await message.reply_text("取消导入跃迁记录", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
logger.info("用户 %s[%s] 从 authkey 导入跃迁记录 is_lazy[%s]", user.full_name, user.id, is_lazy)
reply = await message.reply_text(WAITING, reply_markup=ReplyKeyboardRemove())
await message.reply_chat_action(ChatAction.TYPING)
text = await self._refresh_user_data(user, player_id, authkey=authkey)
await reply.edit_text(text)
text = await self._refresh_user_data(user, player_id, authkey=authkey, is_lazy=is_lazy)
self.add_delete_message_job(reply, delay=1)
await message.reply_text(text, reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
@conversation.entry_point
@ -225,10 +283,16 @@ class WishLogPlugin(Plugin.Conversation):
uid, offset = self.get_real_uid_or_offset(update)
message = update.effective_message
user = update.effective_user
wish_log_plugin_data: WishLogPluginData = context.chat_data.get("wish_log_plugin_data")
if wish_log_plugin_data is None:
wish_log_plugin_data = WishLogPluginData()
context.chat_data["wish_log_plugin_data"] = wish_log_plugin_data
else:
wish_log_plugin_data.reset_data()
logger.info("用户 %s[%s] 删除跃迁记录命令请求", user.full_name, user.id)
try:
player_id = await self.get_player_id(user.id, uid, offset)
context.chat_data["uid"] = player_id
wish_log_plugin_data.player_id = player_id
except PlayerNotFoundError:
logger.info("未查询到用户 %s[%s] 所绑定的账号信息", user.full_name, user.id)
await message.reply_text(config.notice.user_not_found)
@ -247,8 +311,9 @@ class WishLogPlugin(Plugin.Conversation):
async def command_confirm_delete(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> int:
message = update.effective_message
user = update.effective_user
wish_log_plugin_data: WishLogPluginData = context.chat_data.get("wish_log_plugin_data")
if message.text == "确定":
status = await self.gacha_log.remove_history_info(str(user.id), str(context.chat_data["uid"]))
status = await self.gacha_log.remove_history_info(str(user.id), str(wish_log_plugin_data.player_id))
await message.reply_text("跃迁记录已删除" if status else "跃迁记录删除失败")
return ConversationHandler.END
await message.reply_text("已取消")
@ -468,7 +533,7 @@ class WishLogPlugin(Plugin.Conversation):
try:
png_data = await self.rander_wish_log_analysis(user_id, uid, pool_type)
except GachaLogNotFound:
png_data = "未找到抽卡记录"
png_data = "未找到跃迁记录"
if isinstance(png_data, str):
await callback_query.answer(png_data, show_alert=True)
self.add_delete_message_job(message, delay=1)
@ -503,7 +568,7 @@ class WishLogPlugin(Plugin.Conversation):
else:
png_data = await self.gacha_log.get_pool_analysis(user_id, uid, pool_type, self.assets_service, group)
except GachaLogNotFound:
png_data = "未找到抽卡记录"
png_data = "未找到跃迁记录"
if isinstance(png_data, str):
await callback_query.answer(png_data, show_alert=True)
self.add_delete_message_job(message, delay=1)
@ -568,7 +633,7 @@ class WishLogPlugin(Plugin.Conversation):
user = update.effective_user
logger.info("用户 %s[%s] wish_log_rank_recount 命令请求", user.full_name, user.id)
message = update.effective_message
reply = await message.reply_text("正在重新统计抽卡记录排行榜")
reply = await message.reply_text("正在重新统计跃迁记录排行榜")
await self.gacha_log.recount_all_data(reply)
await reply.edit_text("重新统计完成")

16
uv.lock
View File

@ -39,11 +39,11 @@ wheels = [
[[package]]
name = "aiohappyeyeballs"
version = "2.4.3"
version = "2.4.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/bc/69/2f6d5a019bd02e920a3417689a89887b39ad1e350b562f9955693d900c40/aiohappyeyeballs-2.4.3.tar.gz", hash = "sha256:75cf88a15106a5002a8eb1dab212525c00d1f4c0fa96e551c9fbe6f09a621586", size = 21809 }
sdist = { url = "https://files.pythonhosted.org/packages/7f/55/e4373e888fdacb15563ef6fa9fa8c8252476ea071e96fb46defac9f18bf2/aiohappyeyeballs-2.4.4.tar.gz", hash = "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745", size = 21977 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f7/d8/120cd0fe3e8530df0539e71ba9683eade12cae103dd7543e50d15f737917/aiohappyeyeballs-2.4.3-py3-none-any.whl", hash = "sha256:8a7a83727b2756f394ab2895ea0765a0a8c475e3c71e98d43d76f22b4b435572", size = 14742 },
{ url = "https://files.pythonhosted.org/packages/b9/74/fbb6559de3607b3300b9be3cc64e97548d55678e44623db17820dbd20002/aiohappyeyeballs-2.4.4-py3-none-any.whl", hash = "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8", size = 14756 },
]
[[package]]
@ -141,11 +141,11 @@ wheels = [
[[package]]
name = "aiolimiter"
version = "1.1.0"
version = "1.1.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/df/62/6de944a6839a68f7d69e552e26d12234d9c556472e4c277a3a563013640a/aiolimiter-1.1.0.tar.gz", hash = "sha256:461cf02f82a29347340d031626c92853645c099cb5ff85577b831a7bd21132b5", size = 6229 }
sdist = { url = "https://files.pythonhosted.org/packages/ec/93/fcb0673940fd8843e73082265e5b5e0e078367b6525797487d3f50263ab8/aiolimiter-1.1.1.tar.gz", hash = "sha256:4b5740c96ecf022d978379130514a26c18001e7450ba38adf19515cd0970f68f", size = 6097 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/60/69/4b7dea755fafa10b248928da836a2cc8b5cff0762f363234e24218040f8e/aiolimiter-1.1.0-py3-none-any.whl", hash = "sha256:0b4997961fc58b8df40279e739f9cf0d3e255e63e9a44f64df567a8c17241e24", size = 7212 },
{ url = "https://files.pythonhosted.org/packages/d2/cc/8b6f2ef4c821928a22368bc14935087ae2687085059604448887920dec3d/aiolimiter-1.1.1-py3-none-any.whl", hash = "sha256:bf23dafbd1370e0816792fbcfb8fb95d5138c26e05f839fe058f5440bea006f5", size = 5771 },
]
[[package]]
@ -2069,7 +2069,7 @@ wheels = [
[[package]]
name = "simnet"
version = "0.2.0"
source = { git = "https://github.com/PaiGramTeam/SIMNet#d7756addb558356adc65e7e14dc86e0a3cb5d8bd" }
source = { git = "https://github.com/PaiGramTeam/SIMNet#97053ad235a354b15f6c1fd577455c2d53590ebd" }
dependencies = [
{ name = "httpx" },
{ name = "pydantic" },
@ -2215,7 +2215,7 @@ wheels = [
[[package]]
name = "starrailrelicscore"
version = "0.2.0"
source = { git = "https://github.com/PaiGramTeam/StarRailRelicScore#e26509eac9b55875cf3c48a53ecef051403b8867" }
source = { git = "https://github.com/PaiGramTeam/StarRailRelicScore#13a734372c4bcce5da11ad2f90cc511a156461e8" }
dependencies = [
{ name = "pydantic" },
]