nonebot-plugin-osubot 6.19.0__py3-none-any.whl → 6.20.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.

@@ -1,3 +1,4 @@
1
+ import asyncio
1
2
  import random
2
3
  from io import BytesIO
3
4
  from urllib.parse import urlencode
@@ -73,6 +74,58 @@ async def get_headers() -> dict[str, str]:
73
74
  return {"Authorization": f"Bearer {token}", "x-api-version": "20220705"}
74
75
 
75
76
 
77
+ async def fetch_score_batch(
78
+ uid: Union[int, str],
79
+ mode: str,
80
+ scope: str,
81
+ batch_size: int,
82
+ offset: int,
83
+ legacy_only: bool,
84
+ include_failed: bool,
85
+ ) -> list[UnifiedScore]:
86
+ """并发获取单次批次数据"""
87
+ url = (
88
+ f"{api}/users/{uid}/scores/{scope}?mode={mode}&limit={batch_size}"
89
+ f"&offset={offset}&legacy_only={int(legacy_only)}"
90
+ f"&include_fails={int(include_failed)}"
91
+ )
92
+ data = await make_request(url, await get_headers(), "未找到该玩家BP")
93
+ if not data:
94
+ return []
95
+ scores = [NewScore(**i) for i in data]
96
+ return [
97
+ UnifiedScore(
98
+ mods=i.mods,
99
+ ruleset_id=i.ruleset_id,
100
+ rank=i.rank,
101
+ accuracy=i.accuracy * 100,
102
+ total_score=i.total_score,
103
+ ended_at=datetime.strptime(i.ended_at.replace("Z", ""), "%Y-%m-%dT%H:%M:%S") + timedelta(hours=8),
104
+ max_combo=i.max_combo,
105
+ statistics=i.statistics,
106
+ legacy_total_score=i.legacy_total_score,
107
+ passed=i.passed,
108
+ beatmap=UnifiedBeatmap(
109
+ id=i.beatmap_id,
110
+ set_id=i.beatmapset.id,
111
+ artist=i.beatmapset.artist,
112
+ title=i.beatmapset.title,
113
+ version=i.beatmap.version,
114
+ creator=i.beatmapset.creator,
115
+ total_length=i.beatmap.total_length,
116
+ mode=i.beatmap.mode_int,
117
+ bpm=i.beatmap.bpm,
118
+ cs=i.beatmap.cs,
119
+ ar=i.beatmap.ar,
120
+ hp=i.beatmap.drain,
121
+ od=i.beatmap.accuracy,
122
+ stars=i.beatmap.difficulty_rating,
123
+ ),
124
+ )
125
+ for i in scores
126
+ ]
127
+
128
+
76
129
  async def get_user_scores(
77
130
  uid: Union[int, str],
78
131
  mode: str,
@@ -81,47 +134,40 @@ async def get_user_scores(
81
134
  legacy_only: bool = 0,
82
135
  include_failed: bool = True,
83
136
  offset: int = 0,
84
- limit: int = 100,
137
+ limit: int = 200,
85
138
  ) -> list[UnifiedScore]:
86
139
  if source == "osu":
87
- url = (
88
- f"{api}/users/{uid}/scores/{scope}?mode={mode}&limit={limit}&legacy_only={int(legacy_only)}"
89
- f"&offset={offset}&include_fails={int(include_failed)}"
90
- )
91
- data = await make_request(url, await get_headers(), "未找到该玩家BP")
92
- scores = [NewScore(**i) for i in data]
93
- unified_scores = [
94
- UnifiedScore(
95
- mods=i.mods,
96
- ruleset_id=i.ruleset_id,
97
- rank=i.rank,
98
- accuracy=i.accuracy * 100,
99
- total_score=i.total_score,
100
- ended_at=datetime.strptime(i.ended_at.replace("Z", ""), "%Y-%m-%dT%H:%M:%S") + timedelta(hours=8),
101
- max_combo=i.max_combo,
102
- statistics=i.statistics,
103
- legacy_total_score=i.legacy_total_score,
104
- passed=i.passed,
105
- beatmap=UnifiedBeatmap(
106
- id=i.beatmap_id,
107
- set_id=i.beatmapset.id,
108
- artist=i.beatmapset.artist,
109
- title=i.beatmapset.title,
110
- version=i.beatmap.version,
111
- creator=i.beatmapset.creator,
112
- total_length=i.beatmap.total_length,
113
- mode=i.beatmap.mode_int,
114
- bpm=i.beatmap.bpm,
115
- cs=i.beatmap.cs,
116
- ar=i.beatmap.ar,
117
- hp=i.beatmap.drain,
118
- od=i.beatmap.accuracy,
119
- stars=i.beatmap.difficulty_rating,
120
- ),
121
- )
122
- for i in scores
123
- ]
124
- return unified_scores
140
+ if limit <= 0:
141
+ return []
142
+
143
+ # 计算需要多少次请求
144
+ # 计算需要多少批次
145
+ batch_size = 100
146
+ total_batches = (limit + batch_size - 1) // batch_size # ceiling(limit/batch_size)
147
+ all_scores = []
148
+ # 分批并发请求
149
+ for batch_idx in range(0, total_batches, 2):
150
+ current_batches = range(batch_idx, min(batch_idx + 2, total_batches))
151
+
152
+ # 生成 tasks(并发执行)
153
+ tasks = []
154
+ for batch_n in current_batches:
155
+ offset = batch_n * batch_size
156
+ actual_batch_size = min(batch_size, limit - len(all_scores) - offset)
157
+
158
+ if actual_batch_size <= 0:
159
+ continue # 已获取足够数据
160
+
161
+ task = fetch_score_batch(uid, mode, scope, actual_batch_size, offset, legacy_only, include_failed)
162
+ tasks.append(task)
163
+ # 并发请求当前批次
164
+ batch_results = await asyncio.gather(*tasks)
165
+
166
+ for batch_scores in batch_results:
167
+ all_scores.extend(batch_scores)
168
+ if len(all_scores) >= limit:
169
+ return all_scores[:limit] # 提前终止
170
+ return all_scores[:limit]
125
171
 
126
172
  elif source == "ppysb":
127
173
  url = f"https://api.ppy.sb/v1/get_player_scores?scope={scope}&id={uid}&mode={FGM[mode]}&limit={limit}&include_failed={int(include_failed)}"
@@ -414,13 +414,15 @@ def filter_scores_with_regex(scores_with_index, conditions):
414
414
 
415
415
  def trim_text_with_ellipsis(text, max_width, font):
416
416
  # 初始检查:空字符串或无需处理
417
- if not text or font.getbbox(text) <= max_width:
417
+ bbox = font.getbbox(text)
418
+ text_width = bbox[2] - bbox[0]
419
+ if not text or text_width <= max_width:
418
420
  return text
419
421
  # 逐字符检查
420
422
  ellipsis_symbol = "…"
421
- ellipsis_width = font.getbbox("…")
423
+ ellipsis_width = font.getbbox("…")[2] - font.getbbox("…")[0]
422
424
  # 确保最大宽度能至少容纳一个字符+省略号
423
- if max_width < font.getbbox("A") + ellipsis_width:
425
+ if max_width < font.getbbox("A")[2] - font.getbbox("A")[0] + ellipsis_width:
424
426
  return ellipsis_symbol
425
427
 
426
428
  truncated_text = ""
@@ -428,7 +430,7 @@ def trim_text_with_ellipsis(text, max_width, font):
428
430
 
429
431
  for char in text:
430
432
  # 检查当前字符宽度 + 省略号宽度是否超标
431
- char_width = font.getbbox(char)
433
+ char_width = font.getbbox(char)[2] - font.getbbox(char)[0]
432
434
  if current_width + char_width + ellipsis_width > max_width:
433
435
  break
434
436
  truncated_text += char
@@ -25,8 +25,8 @@ async def _bp(state: T_State):
25
25
  if not best.isdigit():
26
26
  await UniMessage.text("只能接受纯数字的bp参数").finish(reply_to=True)
27
27
  best = int(best)
28
- if best <= 0 or best > 100:
29
- await UniMessage.text("只允许查询bp 1-100 的成绩").finish(reply_to=True)
28
+ if best <= 0 or best > 200:
29
+ await UniMessage.text("只允许查询bp 1-200 的成绩").finish(reply_to=True)
30
30
  try:
31
31
  data = await draw_score(
32
32
  "bp",
@@ -52,11 +52,11 @@ async def _pfm(state: T_State):
52
52
  if "error" in state:
53
53
  await UniMessage.text(state["error"]).finish(reply_to=True)
54
54
  if not state["range"]:
55
- state["range"] = "1-100"
55
+ state["range"] = "1-200"
56
56
  ls = state["range"].split("-")
57
57
  low, high = int(ls[0]), int(ls[1])
58
- if not 0 < low < high <= 100:
59
- await UniMessage.text("仅支持查询bp1-100").finish(reply_to=True)
58
+ if not 0 < low < high <= 200:
59
+ await UniMessage.text("仅支持查询bp1-200").finish(reply_to=True)
60
60
  try:
61
61
  data = await draw_bp(
62
62
  "bp",
@@ -84,11 +84,11 @@ async def _tbp(state: T_State):
84
84
  if "error" in state:
85
85
  await UniMessage.text(state["error"]).finish(reply_to=True)
86
86
  if not state["range"]:
87
- state["range"] = "1-100"
87
+ state["range"] = "1-200"
88
88
  ls = state["range"].split("-")
89
89
  low, high = int(ls[0]), int(ls[1])
90
- if not 0 < low < high <= 100:
91
- await UniMessage.text("仅支持查询bp1-100").finish(reply_to=True)
90
+ if not 0 < low < high <= 200:
91
+ await UniMessage.text("仅支持查询bp1-200").finish(reply_to=True)
92
92
  try:
93
93
  data = await draw_bp(
94
94
  "tbp",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nonebot-plugin-osubot
3
- Version: 6.19.0
3
+ Version: 6.20.0
4
4
  Summary: OSUbot in NoneBot2
5
5
  License: AGPL-3.0
6
6
  Author-email: yaowan233 <572473053@qq.com>
@@ -1,5 +1,5 @@
1
1
  nonebot_plugin_osubot/__init__.py,sha256=Q-mTTnOIdKiKG6JrVm-kqpPrAhOP9lWyiKHNRqA7gpc,1478
2
- nonebot_plugin_osubot/api.py,sha256=lzp86uRbnt_Vd9qg2kIHy26EB7ODFGKkbNA6H2iJNh0,14933
2
+ nonebot_plugin_osubot/api.py,sha256=hBG_Ugmkwwdw99XNO--1Q8bzfibxbcH3aZHBbfOkEvc,16304
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/database/__init__.py,sha256=7CDo9xU_DGLQ6uTj_mU_Px92phg_DMU5mP6WvgOxFLY,101
@@ -49,7 +49,7 @@ nonebot_plugin_osubot/draw/templates/bpa_chart.html,sha256=cnpM0qRvvyCMTRP-mIOAB
49
49
  nonebot_plugin_osubot/draw/templates/echarts.min.js,sha256=IF32ooP8NPIzQg_fs7lVHpwG92JcCPE1TZAEyFSgGZU,1022939
50
50
  nonebot_plugin_osubot/draw/templates/mod_chart.html,sha256=Iz71KM5v9z_Rt2vqJ5WIZumRIHgDfcGIUmWGvVGZ-nQ,1508
51
51
  nonebot_plugin_osubot/draw/templates/pp_rank_line_chart.html,sha256=Gyf-GR8ZBlWQTks0TlB3M8EWUBMVwiUaesFAmDISxLo,1417
52
- nonebot_plugin_osubot/draw/utils.py,sha256=fvFs7h8G0UavHAe4vXWugKJZ2ZQmZb0bGl-BSPG95hc,15138
52
+ nonebot_plugin_osubot/draw/utils.py,sha256=LZVDmrQizVwVFX0kDJISYYO68jmZbn5Xz40gV5bj8bo,15276
53
53
  nonebot_plugin_osubot/exceptions.py,sha256=N_FsEk-9Eu2QnuznhdfWn6OoyA1HH73Q7bUaY89gVi0,40
54
54
  nonebot_plugin_osubot/file.py,sha256=GhG54EdVsxFf8_8GZOPh4YGvw9iw5bAX9JFz4Mg4kMg,4379
55
55
  nonebot_plugin_osubot/info/__init__.py,sha256=I7YlMQiuHExEeJWqyzZb2I-Vl2uql3Py2LdhSH2Z9N0,136
@@ -58,7 +58,7 @@ nonebot_plugin_osubot/info/bind.py,sha256=b2ua625hbYym7rpb-kLBB-VDP5YWFdmT5RweM5
58
58
  nonebot_plugin_osubot/mania/__init__.py,sha256=XRPn563jDJiPohFekcoFFCqCJYznb-6uORneioZv4xI,5534
59
59
  nonebot_plugin_osubot/matcher/__init__.py,sha256=yID7QcdQF6_Mouwbej3JwYUBbKSU3VQdrjq6B1Fz9P8,1331
60
60
  nonebot_plugin_osubot/matcher/bind.py,sha256=QQJc2S7XFo5tu4CPloIET6fKaeiQixgb8M7QvULV6E0,2834
61
- nonebot_plugin_osubot/matcher/bp.py,sha256=ULNVgrlRCe9w6N7yC7Yw3Y-OSJQKR8AZfBkB5jToOPQ,3983
61
+ nonebot_plugin_osubot/matcher/bp.py,sha256=GidJfuZ9lJ7LwMq126DDOwMksNUOz4Bkab83OlKg8t8,3983
62
62
  nonebot_plugin_osubot/matcher/bp_analyze.py,sha256=S1dhYiGqtPQapJt4PB5mNKfpPbOpVNAqDIToNEJVpSY,3949
63
63
  nonebot_plugin_osubot/matcher/getbg.py,sha256=Ar2yIST556MYRxzuXCLSDAMAmESRENN1fCty-kdH7PI,699
64
64
  nonebot_plugin_osubot/matcher/guess.py,sha256=iE5oRZ3mDTC_wcjLJTyyAWlmnDzkAgzGN5rvoTIn0uM,24093
@@ -403,6 +403,6 @@ nonebot_plugin_osubot/schema/sayo_beatmap.py,sha256=lS1PQZ-HvHl0VhkzlI0-pNLeJrLY
403
403
  nonebot_plugin_osubot/schema/score.py,sha256=zHU-w2e7RzMDL8vdPkX5vggcqalBo83JTvu96abcflo,3124
404
404
  nonebot_plugin_osubot/schema/user.py,sha256=sxNmqymG_kIVuGuzfchSv9UML6NPG70cqo2_h5xDIpM,2250
405
405
  nonebot_plugin_osubot/utils/__init__.py,sha256=pyv8XxBcCOeQVDj1E4dgvktzcefgQXfKBlarsYGx1sg,815
406
- nonebot_plugin_osubot-6.19.0.dist-info/WHEEL,sha256=B19PGBCYhWaz2p_UjAoRVh767nYQfk14Sn4TpIZ-nfU,87
407
- nonebot_plugin_osubot-6.19.0.dist-info/METADATA,sha256=lHB8m5ljuFCJHaM6dNi-AwN82VMLvKMQFNiw67c0CkI,4476
408
- nonebot_plugin_osubot-6.19.0.dist-info/RECORD,,
406
+ nonebot_plugin_osubot-6.20.0.dist-info/WHEEL,sha256=B19PGBCYhWaz2p_UjAoRVh767nYQfk14Sn4TpIZ-nfU,87
407
+ nonebot_plugin_osubot-6.20.0.dist-info/METADATA,sha256=h46-68Jkbr_-bhXPbyK_BS-f7kVOO585n8TGDpPYijc,4476
408
+ nonebot_plugin_osubot-6.20.0.dist-info/RECORD,,