nonebot-plugin-osubot 6.24.2__py3-none-any.whl → 6.25.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of nonebot-plugin-osubot might be problematic. Click here for more details.

@@ -428,7 +428,7 @@ async def get_map_bg(mapid, sid, bg_name) -> BytesIO:
428
428
  f"https://dl.sayobot.cn/beatmaps/files/{sid}/{bg_name}",
429
429
  ]
430
430
  )
431
- return BytesIO(res)
431
+ return BytesIO(res.content)
432
432
 
433
433
 
434
434
  async def get_seasonal_bg() -> Optional[dict]:
@@ -1,10 +1,15 @@
1
+ import base64
2
+ import hashlib
1
3
  import re
4
+ import urllib
2
5
  import random
3
6
  import asyncio
7
+ import uuid
4
8
  from pathlib import Path
5
9
  from typing import Union, Optional
6
10
  from io import BytesIO, TextIOWrapper
7
11
 
12
+ from nonebot.adapters.onebot.v11 import Bot
8
13
  from nonebot.log import logger
9
14
 
10
15
  from .schema import Badge
@@ -18,7 +23,6 @@ user_cache_path = Path() / "data" / "osu" / "user"
18
23
  badge_cache_path = Path() / "data" / "osu" / "badge"
19
24
  team_cache_path = Path() / "data" / "osu" / "team"
20
25
  api_ls = [
21
- "https://api.chimu.moe/v1/download/",
22
26
  "https://osu.direct/api/d/",
23
27
  "https://txy1.sayobot.cn/beatmaps/download/novideo/",
24
28
  "https://catboy.best/d/",
@@ -35,16 +39,55 @@ if not team_cache_path.exists():
35
39
  team_cache_path.mkdir(parents=True, exist_ok=True)
36
40
 
37
41
 
42
+ def extract_filename_from_headers(headers: dict[str, str]) -> Optional[str]:
43
+ """
44
+ 从 Content-Disposition 响应头中提取文件名,并处理 URL 编码。
45
+
46
+ Args:
47
+ headers: 响应头字典。
48
+
49
+ Returns:
50
+ 提取到的文件名字符串,如果失败则返回 None。
51
+ """
52
+ disposition = headers.get("content-disposition", "")
53
+ if not disposition:
54
+ return None
55
+
56
+ match_utf8 = re.search(r"filename\*=(?:utf-8''|)(.+?)(?:;|$)", disposition, re.IGNORECASE)
57
+
58
+ if match_utf8:
59
+ # 提取匹配到的文件名部分
60
+ encoded_filename = match_utf8.group(1).strip('"').strip()
61
+
62
+ try:
63
+ return urllib.parse.unquote(encoded_filename)
64
+ except Exception as e:
65
+ # 如果解码失败,记录错误并尝试使用原始编码
66
+ print(f"警告: 解码 filename* 失败: {e}. 使用原始编码.")
67
+ return encoded_filename
68
+
69
+ match_normal = re.search(r"filename=\"?(.+?)\"?(\s|;|$)", disposition, re.IGNORECASE)
70
+ if match_normal:
71
+ # 普通 filename 字段也可能包含 URL 编码,进行解码
72
+ filename = match_normal.group(1).strip('"').strip()
73
+ try:
74
+ return urllib.parse.unquote(filename)
75
+ except Exception:
76
+ return filename
77
+
78
+ return None
79
+
80
+
38
81
  async def download_map(setid: int) -> Optional[Path]:
39
82
  urls = [i + str(setid) for i in api_ls]
40
83
  logger.info(f"开始下载地图: <{setid}>")
41
84
  req = await get_first_response(urls)
42
- filename = f"{setid}.osz"
85
+ filename = extract_filename_from_headers(req.headers)
43
86
  filepath = map_path.parent / filename
44
- with open(filepath, "wb") as f:
45
- f.write(req.read())
87
+ with open(filepath.absolute(), "wb") as f:
88
+ f.write(req.content)
46
89
  logger.info(f"地图: <{setid}> 下载完毕")
47
- return filepath
90
+ return filepath.absolute()
48
91
 
49
92
 
50
93
  @auto_retry
@@ -61,7 +104,7 @@ async def download_osu(set_id, map_id):
61
104
  filepath = map_path / str(set_id) / filename
62
105
  filepath.parent.mkdir(parents=True, exist_ok=True)
63
106
  with open(filepath, "wb") as f:
64
- f.write(req)
107
+ f.write(req.content)
65
108
  return filepath
66
109
  else:
67
110
  raise Exception("下载出错,请稍后再试")
@@ -125,3 +168,130 @@ async def save_info_pic(user: str, byt: bytes):
125
168
  path.mkdir()
126
169
  with open(path / "info.png", "wb") as f:
127
170
  f.write(BytesIO(byt).getvalue())
171
+
172
+
173
+ def calculate_file_chunks(file_path: str, chunk_size: int = 1024 * 64) -> tuple[list[bytes], str, int]:
174
+ """
175
+ 计算文件分片和 SHA256
176
+
177
+ Args:
178
+ file_path: 文件路径
179
+ chunk_size: 分片大小(默认64KB)
180
+
181
+ Returns:
182
+ (chunks, sha256_hash, total_size)
183
+ """
184
+ chunks = []
185
+ hasher = hashlib.sha256()
186
+ total_size = 0
187
+
188
+ with open(file_path, "rb") as f:
189
+ while True:
190
+ chunk = f.read(chunk_size)
191
+ if not chunk:
192
+ break
193
+ chunks.append(chunk)
194
+ hasher.update(chunk)
195
+ total_size += len(chunk)
196
+
197
+ sha256_hash = hasher.hexdigest()
198
+
199
+ return chunks, sha256_hash, total_size
200
+
201
+
202
+ MAX_CONCURRENT_UPLOADS = 20
203
+
204
+
205
+ async def _upload_chunk(
206
+ bot: "Bot",
207
+ stream_id: str,
208
+ chunk_data: bytes,
209
+ chunk_index: int,
210
+ total_chunks: int,
211
+ total_size: int,
212
+ sha256_hash: str,
213
+ filename: str,
214
+ semaphore: asyncio.Semaphore,
215
+ ) -> None:
216
+ """内部函数,用于异步上传单个文件分片"""
217
+ async with semaphore:
218
+ # 将分片数据编码为 base64
219
+ chunk_base64 = base64.b64encode(chunk_data).decode("utf-8")
220
+
221
+ # 构建参数
222
+ params = {
223
+ "stream_id": stream_id,
224
+ "chunk_data": chunk_base64,
225
+ "chunk_index": chunk_index,
226
+ "total_chunks": total_chunks,
227
+ "file_size": total_size,
228
+ "expected_sha256": sha256_hash,
229
+ "filename": filename,
230
+ "file_retention": 30 * 1000,
231
+ }
232
+
233
+ # 发送分片
234
+ response = await bot.call_api("upload_file_stream", **params)
235
+
236
+ logger.info(
237
+ f"分片 {chunk_index + 1}/{total_chunks} 上传成功 "
238
+ f"(接收: {response.get('received_chunks', 0)}/{response.get('total_chunks', 0)})"
239
+ )
240
+
241
+
242
+ async def upload_file_stream_batch(bot: Bot, file_path: Path, chunk_size: int = 1024 * 64) -> str:
243
+ """
244
+ 一次性批量上传文件流
245
+
246
+ Args:
247
+ bot: Bot 实例
248
+ file_path: 要上传的文件路径
249
+ chunk_size: 分片大小
250
+
251
+ Returns:
252
+ 上传完成后的文件路径
253
+ """
254
+ if not file_path.exists():
255
+ raise FileNotFoundError(f"文件不存在: {file_path}")
256
+
257
+ # 分析文件
258
+ chunks, sha256_hash, total_size = calculate_file_chunks(str(file_path), chunk_size)
259
+ stream_id = str(uuid.uuid4())
260
+
261
+ logger.info(f"\n开始上传文件: {file_path.name}")
262
+ logger.info(f"流ID: {stream_id}")
263
+
264
+ # 一次性发送所有分片
265
+ total_chunks = len(chunks)
266
+ # 创建信号量,限制最大并发数
267
+ semaphore = asyncio.Semaphore(MAX_CONCURRENT_UPLOADS)
268
+
269
+ # 创建所有分片上传任务
270
+ upload_tasks = []
271
+ for chunk_index, chunk_data in enumerate(chunks):
272
+ task = _upload_chunk(
273
+ bot, stream_id, chunk_data, chunk_index, total_chunks, total_size, sha256_hash, file_path.name, semaphore
274
+ )
275
+ upload_tasks.append(task)
276
+
277
+ try:
278
+ await asyncio.gather(*upload_tasks)
279
+ except Exception as e:
280
+ logger.error(f"\n文件分片上传过程中发生错误: {e}")
281
+ # 这里可以选择执行清理逻辑,如通知服务器取消上传
282
+ raise e
283
+
284
+ # 发送完成信号
285
+ logger.info("\n所有分片发送完成,请求文件合并...")
286
+ complete_params = {"stream_id": stream_id, "is_complete": True}
287
+
288
+ response = await bot.call_api("upload_file_stream", **complete_params)
289
+
290
+ if response.get("status") == "file_complete":
291
+ logger.info("✅ 文件上传成功!")
292
+ logger.info(f" - 文件路径: {response.get('file_path')}")
293
+ logger.info(f" - 文件大小: {response.get('file_size')} 字节")
294
+ logger.info(f" - SHA256: {response.get('sha256')}")
295
+ return response.get("file_path")
296
+ else:
297
+ raise Exception(f"文件状态异常: {response}")
@@ -5,6 +5,7 @@ from .getbg import getbg
5
5
  from .match import match
6
6
  from .medal import medal
7
7
  from .score import score
8
+ from .osudl import osudl
8
9
  from .pr import pr, recent
9
10
  from .rating import rating
10
11
  from .history import history
@@ -57,4 +58,5 @@ __all__ = [
57
58
  "match",
58
59
  "rating",
59
60
  "group_pp_rank",
61
+ "osudl",
60
62
  ]
@@ -1,5 +1,6 @@
1
1
  from pathlib import Path
2
2
 
3
+ from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent
3
4
  from nonebot.rule import ArgumentParser
4
5
  from nonebot.internal.adapter import Message
5
6
  from nonebot_plugin_alconna import UniMessage
@@ -10,6 +11,7 @@ from nonebot.params import CommandArg, ShellCommandArgv
10
11
  from ..api import get_beatmapsets_info, osu_api
11
12
  from ..mania import Options, convert_mania_map
12
13
  from ..schema import Beatmap
14
+ from ..file import upload_file_stream_batch
13
15
 
14
16
  parser = ArgumentParser("convert", description="变换mania谱面")
15
17
  parser.add_argument("--set", type=int, help="要转换的谱面的setid")
@@ -38,7 +40,7 @@ async def _(argv: list[str] = ShellCommandArgv()):
38
40
  return
39
41
  options = Options(**vars(args))
40
42
  if options.map:
41
- map_data = await osu_api("map", options.map)
43
+ map_data = await osu_api("map", map_id=options.map)
42
44
  mapinfo = Beatmap(**map_data)
43
45
  beatmapsets_info = await get_beatmapsets_info(mapinfo.beatmapset_id)
44
46
  options.set = mapinfo.beatmapset_id
@@ -67,7 +69,7 @@ change = on_command("倍速", priority=11, block=True)
67
69
 
68
70
 
69
71
  @change.handle()
70
- async def _(msg: Message = CommandArg()):
72
+ async def _(bot: Bot, event: GroupMessageEvent, msg: Message = CommandArg()):
71
73
  args = msg.extract_plain_text().strip().split()
72
74
  argv = ["--map"]
73
75
  if not args:
@@ -88,7 +90,7 @@ async def _(msg: Message = CommandArg()):
88
90
  args = parser.parse_args(argv)
89
91
  options = Options(**vars(args))
90
92
  if options.map:
91
- map_data = await osu_api("map", options.map)
93
+ map_data = await osu_api("map", map_id=options.map)
92
94
  mapinfo = Beatmap(**map_data)
93
95
  beatmapsets_info = await get_beatmapsets_info(mapinfo.beatmapset_id)
94
96
  options.set = mapinfo.beatmapset_id
@@ -97,9 +99,10 @@ async def _(msg: Message = CommandArg()):
97
99
  if not osz_path:
98
100
  await UniMessage.text("未找到该地图,请检查是否搞混了mapID与setID").finish(reply_to=True)
99
101
  file_path = osz_path.absolute()
102
+ server_osz_path = await upload_file_stream_batch(bot, file_path)
103
+
100
104
  try:
101
- with open(file_path, "rb") as f:
102
- await UniMessage.file(raw=f.read()).send()
105
+ await bot.call_api("upload_group_file", group_id=event.group_id, file=server_osz_path, name=osz_path.name)
103
106
  except ActionFailed:
104
107
  await UniMessage.text("上传文件失败,可能是群空间满或没有权限导致的").send(reply_to=True)
105
108
  finally:
@@ -113,7 +116,7 @@ generate_full_ln = on_command("反键", priority=11, block=True)
113
116
 
114
117
 
115
118
  @generate_full_ln.handle()
116
- async def _(msg: Message = CommandArg()):
119
+ async def _(bot: Bot, event: GroupMessageEvent, msg: Message = CommandArg()):
117
120
  args = msg.extract_plain_text().strip().split()
118
121
  if not args:
119
122
  await UniMessage.text("请输入需要转ln的地图setID").finish(reply_to=True)
@@ -133,9 +136,9 @@ async def _(msg: Message = CommandArg()):
133
136
  if not osz_path:
134
137
  await UniMessage.text("未找到该地图,请检查是否搞混了mapID与setID").finish(reply_to=True)
135
138
  file_path = osz_path.absolute()
139
+ server_osz_path = await upload_file_stream_batch(bot, file_path)
136
140
  try:
137
- with open(file_path, "rb") as f:
138
- await UniMessage.file(raw=f.read()).send()
141
+ await bot.call_api("upload_group_file", group_id=event.group_id, file=server_osz_path, name=osz_path.name)
139
142
  except ActionFailed:
140
143
  await UniMessage.text("上传文件失败,可能是群空间满或没有权限导致的").send(reply_to=True)
141
144
  finally:
@@ -1,22 +1,23 @@
1
1
  from nonebot import on_command
2
+ from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent
2
3
  from nonebot.params import CommandArg
3
4
  from nonebot.internal.adapter import Message
4
5
  from nonebot_plugin_alconna import UniMessage
5
6
 
6
- from ..file import download_map
7
+ from ..file import download_map, upload_file_stream_batch
7
8
 
8
9
  osudl = on_command("osudl", priority=11, block=True)
9
10
 
10
11
 
11
12
  @osudl.handle()
12
- async def _osudl(setid: Message = CommandArg()):
13
+ async def _osudl(bot: Bot, event: GroupMessageEvent, setid: Message = CommandArg()):
13
14
  setid = setid.extract_plain_text().strip()
14
15
  if not setid or not setid.isdigit():
15
16
  await UniMessage.text("请输入正确的地图ID").send(reply_to=True)
16
17
  osz_path = await download_map(int(setid))
17
- file_path = osz_path.absolute()
18
+ server_osz_path = await upload_file_stream_batch(bot, osz_path)
18
19
  try:
19
- await UniMessage.file(path=file_path).send()
20
+ await bot.call_api("upload_group_file", group_id=event.group_id, file=server_osz_path, name=osz_path.name)
20
21
  except Exception:
21
22
  await UniMessage.text("上传文件失败,可能是群空间满或没有权限导致的").send(reply_to=True)
22
23
  finally:
@@ -43,7 +43,7 @@ async def handle_recommend(state: T_State, matcher: type[Matcher]):
43
43
  await matcher.finish("今天已经没有可以推荐的图啦,明天再来吧")
44
44
  return None
45
45
  bid = int(re.findall("https://osu.ppy.sh/beatmaps/(.*)", recommend_map.mapLink)[0])
46
- map_data = await osu_api("map", bid)
46
+ map_data = await osu_api("map", map_id=bid)
47
47
  map_info = Beatmap(**map_data)
48
48
  sid = map_info.beatmapset_id
49
49
  s = (
@@ -25,6 +25,6 @@ async def get_first_response(urls: list[str]):
25
25
  for task in done:
26
26
  response = task.result()
27
27
  if response is not None and response.status_code == 200:
28
- return response.content
28
+ return response
29
29
  tasks = [task for task in tasks if not task.done()]
30
30
  return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nonebot-plugin-osubot
3
- Version: 6.24.2
3
+ Version: 6.25.0
4
4
  Summary: OSUbot in NoneBot2
5
5
  License: AGPL-3.0
6
6
  Author-email: yaowan233 <572473053@qq.com>
@@ -8,6 +8,7 @@ Requires-Python: >=3.9.8,<3.13
8
8
  Requires-Dist: expiringdict>=1.2.2
9
9
  Requires-Dist: httpx>=0.23.3
10
10
  Requires-Dist: matplotlib>=3.7.1
11
+ Requires-Dist: nonebot-adapter-onebot>=2.4.6
11
12
  Requires-Dist: nonebot-plugin-alconna>=0.46.4
12
13
  Requires-Dist: nonebot-plugin-apscheduler>=0.4.0
13
14
  Requires-Dist: nonebot-plugin-htmlrender>=0.3.1
@@ -1,5 +1,5 @@
1
1
  nonebot_plugin_osubot/__init__.py,sha256=Q-mTTnOIdKiKG6JrVm-kqpPrAhOP9lWyiKHNRqA7gpc,1478
2
- nonebot_plugin_osubot/api.py,sha256=3jJbgJdL1sJ_FcbTqJQxRPLlJueaUXhH0REfy8Ff-ps,16521
2
+ nonebot_plugin_osubot/api.py,sha256=ZPbtDWz_zBUaL-3RnpXJuDhZ-RAVPCzvHp4MHShpGiE,16529
3
3
  nonebot_plugin_osubot/beatmap_stats_moder.py,sha256=mNNTufc-gvO4NdYa3TnealSZI4-LBoiTlb599SeLBck,2915
4
4
  nonebot_plugin_osubot/config.py,sha256=Ub2s5Ny09-d1ZwT6x8cirB6zWy0brtO-oZV3W0qEM5Q,311
5
5
  nonebot_plugin_osubot/data/osu/1849145.osz,sha256=enbHOvDu6ZkvQBM7gtvgZBY-r0a7z87pG62Xm9hXUSI,6933013
@@ -93,12 +93,12 @@ nonebot_plugin_osubot/draw/templates/mod_chart.html,sha256=Iz71KM5v9z_Rt2vqJ5WIZ
93
93
  nonebot_plugin_osubot/draw/templates/pp_rank_line_chart.html,sha256=Gyf-GR8ZBlWQTks0TlB3M8EWUBMVwiUaesFAmDISxLo,1417
94
94
  nonebot_plugin_osubot/draw/utils.py,sha256=6QDbByPQZCxI0k_i5MsExyWZ-sHgJUw6nEWLv85IgLY,15826
95
95
  nonebot_plugin_osubot/exceptions.py,sha256=N_FsEk-9Eu2QnuznhdfWn6OoyA1HH73Q7bUaY89gVi0,40
96
- nonebot_plugin_osubot/file.py,sha256=kfnaf68q0Au54YMUqhD2nYnusEsHqjxJKy2LqPA8urI,4104
96
+ nonebot_plugin_osubot/file.py,sha256=p8E9oeopvMLT-T_b0PARZifOX_m1HXsxJGGtbZFMVfY,9387
97
97
  nonebot_plugin_osubot/info/__init__.py,sha256=I7YlMQiuHExEeJWqyzZb2I-Vl2uql3Py2LdhSH2Z9N0,136
98
98
  nonebot_plugin_osubot/info/bg.py,sha256=Icua4bS38k0c-WbLUjhfS4IXOF83bgyu_oa2HwX4ZEQ,1541
99
99
  nonebot_plugin_osubot/info/bind.py,sha256=b2ua625hbYym7rpb-kLBB-VDP5YWFdmT5RweM58ggWw,4934
100
100
  nonebot_plugin_osubot/mania/__init__.py,sha256=t5-24nd2FiZTKvMFvNg8ZV9Lp_OFSHjhj_gWUV3s1es,5560
101
- nonebot_plugin_osubot/matcher/__init__.py,sha256=yID7QcdQF6_Mouwbej3JwYUBbKSU3VQdrjq6B1Fz9P8,1331
101
+ nonebot_plugin_osubot/matcher/__init__.py,sha256=0f2_aeiBos3evT9eZRRh73Z0gpdwRcH8XpzUP6jbq-0,1369
102
102
  nonebot_plugin_osubot/matcher/bind.py,sha256=QQJc2S7XFo5tu4CPloIET6fKaeiQixgb8M7QvULV6E0,2834
103
103
  nonebot_plugin_osubot/matcher/bp.py,sha256=GidJfuZ9lJ7LwMq126DDOwMksNUOz4Bkab83OlKg8t8,3983
104
104
  nonebot_plugin_osubot/matcher/bp_analyze.py,sha256=xi40HVOcTvmHWR4WNLm706126CulfpV5UP0500FNiD8,4159
@@ -107,17 +107,17 @@ nonebot_plugin_osubot/matcher/guess.py,sha256=Bv4Rt11eB65hdsPu6KhCjmEP1AacFUwA0u
107
107
  nonebot_plugin_osubot/matcher/history.py,sha256=ZYkVJHdXuVJmhovRhwxFdqNp0o2uJJOACAZhhutyS3w,1577
108
108
  nonebot_plugin_osubot/matcher/info.py,sha256=8CJHTOMTx_nzJ4ZwXo6ZfBwCuO3DtLprRX7jnMtPilk,858
109
109
  nonebot_plugin_osubot/matcher/map.py,sha256=sFpOoFv63y-NOkCJhE6aW0DRYDl_8SoQOPsdq50QxT0,1404
110
- nonebot_plugin_osubot/matcher/map_convert.py,sha256=Q3oNK8NvOUE56mOXQ0PvRQkZZ0nLbtFrAp9wxqw-7Ak,5973
110
+ nonebot_plugin_osubot/matcher/map_convert.py,sha256=oklwbbcrEuous-mtHgGN3bN3PkDqKb95XXjIMSEp5Yk,6343
111
111
  nonebot_plugin_osubot/matcher/match.py,sha256=uyrm8I_WgHK2ya1q46AUxNk_cQiKKh7GKlUzrrG1o7w,472
112
112
  nonebot_plugin_osubot/matcher/medal.py,sha256=LZf8hlXGHy8mdK2l97SsYCChfYYovEDBGNbUPO3AOsw,2967
113
113
  nonebot_plugin_osubot/matcher/mu.py,sha256=l3Ebz47o46EvN2nvo9-zzQI4CTaLMcd5XW0qljqSGIM,445
114
114
  nonebot_plugin_osubot/matcher/osu_help.py,sha256=64rOkYEOETvU8AF__0xLZjVRs3cTac0D1XEultPK_kM,728
115
- nonebot_plugin_osubot/matcher/osudl.py,sha256=yLEblYnLprTf2T00FiRWJ8CuCd0IHyUY9Ka68yAKOXo,872
115
+ nonebot_plugin_osubot/matcher/osudl.py,sha256=aItoFVYgozZHINpBuWv38syixtTOtaTtyBpWKZo94uI,1091
116
116
  nonebot_plugin_osubot/matcher/pr.py,sha256=xGjQvEJHmIZkq9luu8TtbjBB8FykGI4Wzi_-eXghOjQ,4951
117
117
  nonebot_plugin_osubot/matcher/preview.py,sha256=22zNjRdpwxbmIsyZQlUE-qXQBCQCfP_2WobGP7nZZh4,2314
118
118
  nonebot_plugin_osubot/matcher/rank.py,sha256=sFEim3cR_vswzLmbagjqy-ypolcprAxQpawiSEcFqEI,3619
119
119
  nonebot_plugin_osubot/matcher/rating.py,sha256=JY1Q1ELU3Y1FhQ7kVWVkgVsYEVxkAcxjsoMcwC_u234,450
120
- nonebot_plugin_osubot/matcher/recommend.py,sha256=DChL83UNSi_XDHUm1ksPwUgQE12PI_EHaxXkJQoZ5Oc,2521
120
+ nonebot_plugin_osubot/matcher/recommend.py,sha256=4R8rzxi-tC7aCb__64KzAKZo_-ginSb_U0HWK6xaRmI,2528
121
121
  nonebot_plugin_osubot/matcher/score.py,sha256=Nk6dpDlszKJKdboTSQRBf-wMGioHIPqKSVWrnT0Xbns,1212
122
122
  nonebot_plugin_osubot/matcher/update.py,sha256=MHpxoJmU0hKW82XuM9YpyCxUUjjiNKwejnRgYwueR4Q,3168
123
123
  nonebot_plugin_osubot/matcher/update_mode.py,sha256=0Wy6Y1-rN7XcqBZyo37mYFdixq-4HxCwZftUaiYhZqE,1602
@@ -126,7 +126,7 @@ nonebot_plugin_osubot/matcher/utils.py,sha256=gWmNa31wUxcY_PNSNLy348x5_7sTY9ttMK
126
126
  nonebot_plugin_osubot/mods.py,sha256=vxIWYX0HwTxetPAHWZK5ojEMfqV9HFlWT0YC4Yncgb8,1402
127
127
  nonebot_plugin_osubot/network/__init__.py,sha256=WOijcd81yhnpGKYeiDIOxbBDVI12GHPRGoOFfxrUuQk,61
128
128
  nonebot_plugin_osubot/network/auto_retry.py,sha256=vDfYGbEVPF6WZLYXmRVkNvaxf6_6RyIqEAcA7TRwV_k,565
129
- nonebot_plugin_osubot/network/first_response.py,sha256=zETRc6g0AG8ExLyHZTLUl7uzUCdUVIL0IfxvdEtCPt0,932
129
+ nonebot_plugin_osubot/network/first_response.py,sha256=jIYIF476aIUgpIcN08Wo8tXiwu0paNebCcaTuRPmlS4,924
130
130
  nonebot_plugin_osubot/network/manager.py,sha256=x0GI1cFv3m3ZIS4oNJed197PaRo8_Vut_2J7m9ySV30,858
131
131
  nonebot_plugin_osubot/osufile/Best Performance.png,sha256=qBNeZcym5vIqyE23K62ohjVBEPCjlNP9wQgXaT20XyY,704
132
132
  nonebot_plugin_osubot/osufile/History Score.jpg,sha256=yv3-GaJ7sBAbAPMFlUeoyg1PzMhv31Ip5bC4H0qJfSA,836
@@ -447,6 +447,6 @@ nonebot_plugin_osubot/schema/ppysb/__init__.py,sha256=JK2Z4n44gUJPVKdETMJYJ5uIw-
447
447
  nonebot_plugin_osubot/schema/score.py,sha256=o32jKDESzFwOFPZnzjKqxNIj0MPUL9mFvBqgaZARHac,3269
448
448
  nonebot_plugin_osubot/schema/user.py,sha256=sxNmqymG_kIVuGuzfchSv9UML6NPG70cqo2_h5xDIpM,2250
449
449
  nonebot_plugin_osubot/utils/__init__.py,sha256=pyv8XxBcCOeQVDj1E4dgvktzcefgQXfKBlarsYGx1sg,815
450
- nonebot_plugin_osubot-6.24.2.dist-info/WHEEL,sha256=B19PGBCYhWaz2p_UjAoRVh767nYQfk14Sn4TpIZ-nfU,87
451
- nonebot_plugin_osubot-6.24.2.dist-info/METADATA,sha256=E6_OnpJTXY_ZSRh3e1GqCiUS3xPWFrvb3PuKT32YD6M,4476
452
- nonebot_plugin_osubot-6.24.2.dist-info/RECORD,,
450
+ nonebot_plugin_osubot-6.25.0.dist-info/WHEEL,sha256=B19PGBCYhWaz2p_UjAoRVh767nYQfk14Sn4TpIZ-nfU,87
451
+ nonebot_plugin_osubot-6.25.0.dist-info/METADATA,sha256=Sy7rPJeo1uzQ5ek4gPoImi2JtSt7Nvoe8y-eMIoQ6eQ,4521
452
+ nonebot_plugin_osubot-6.25.0.dist-info/RECORD,,