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

@@ -8,8 +8,9 @@ from typing import Union, Literal, Optional
8
8
  from nonebot.log import logger
9
9
  from expiringdict import ExpiringDict
10
10
  from nonebot import get_plugin_config
11
- from httpx import Response, AsyncClient
11
+ from httpx import Response
12
12
 
13
+ from .network.manager import network_manager
13
14
  from .utils import FGM
14
15
  from .config import Config
15
16
  from .mods import get_mods
@@ -25,7 +26,6 @@ api = "https://osu.ppy.sh/api/v2"
25
26
  sayoapi = "https://api.sayobot.cn"
26
27
  cache = ExpiringDict(max_len=1, max_age_seconds=86400)
27
28
  plugin_config = get_plugin_config(Config)
28
- proxy = plugin_config.osu_proxy
29
29
 
30
30
  key = plugin_config.osu_key
31
31
  client_id = plugin_config.osu_client
@@ -34,15 +34,15 @@ bg_url = plugin_config.info_bg
34
34
 
35
35
  @auto_retry
36
36
  async def safe_async_get(url, headers: Optional[dict] = None, params: Optional[dict] = None) -> Response:
37
- async with AsyncClient(proxy=proxy, follow_redirects=True) as client:
38
- req = await client.get(url, headers=headers, params=params)
37
+ client = await network_manager.get_client()
38
+ req = await client.get(url, headers=headers, params=params)
39
39
  return req
40
40
 
41
41
 
42
42
  @auto_retry
43
43
  async def safe_async_post(url, headers=None, data=None, json=None) -> Response:
44
- async with AsyncClient(proxy=proxy) as client:
45
- req = await client.post(url, headers=headers, data=data, json=json)
44
+ client = await network_manager.get_client()
45
+ req = await client.post(url, headers=headers, data=data, json=json)
46
46
  return req
47
47
 
48
48
 
@@ -105,8 +105,10 @@ async def fetch_score_batch(
105
105
  statistics=i.statistics,
106
106
  legacy_total_score=i.legacy_total_score,
107
107
  passed=i.passed,
108
+ pp=i.pp,
108
109
  beatmap=UnifiedBeatmap(
109
110
  id=i.beatmap_id,
111
+ user_id=i.beatmap.user_id,
110
112
  set_id=i.beatmapset.id,
111
113
  artist=i.beatmapset.artist,
112
114
  title=i.beatmapset.title,
@@ -198,6 +200,7 @@ async def get_user_scores(
198
200
  ),
199
201
  beatmap=UnifiedBeatmap(
200
202
  id=i.beatmap.id,
203
+ user_id=i.beatmap.user_id,
201
204
  set_id=i.beatmap.set_id,
202
205
  artist=i.beatmap.artist,
203
206
  title=i.beatmap.title,
@@ -673,9 +673,10 @@ Catch.prototype.draw2 = function (SCALE, SPEED = 1, params = {}) {
673
673
  // 按总物件数/时间控制密度
674
674
  let totalTime = this.fullCatchObjects[this.fullCatchObjects.length - 1].time - this.fullCatchObjects[0].time;
675
675
  if (this.fullCatchObjects.length * 1000 / totalTime > 2) comboSplit = Math.ceil(this.fullCatchObjects.length * 1000 / totalTime) * 10;
676
- // 按0.5*位数修约
677
- let roundBy = Math.pow(10, comboSplit.toString().length) * 0.5;
676
+ // 按0.5*(10^(位数-1&&最小为2))修约 60=>50 270=>250 820=>800 1434=>1500 1834=>2000
677
+ let roundBy = Math.pow(10, Math.max(comboSplit.toString().length - 1, 2)) * 0.5;
678
678
  comboSplit = Math.round(comboSplit / roundBy) * roundBy;
679
+ if (comboSplit <= 0) comboSplit = 20;
679
680
  for (let i = 0; i < this.fullCatchObjects.length; i++) {
680
681
  let showCombo = null;
681
682
  if (objs[i].type === "Fruit" || objs[i].type === "Droplet") {
@@ -799,4 +800,4 @@ Catch.prototype.processProgressBar = function (ctx, totalTime) {
799
800
  ctx.fillRect(ctxwidth * time / totalTime, 0, ctxwidth * (endtime - time) / totalTime, ctxheight);
800
801
  }
801
802
  }
802
- };
803
+ };
@@ -41,7 +41,7 @@
41
41
 
42
42
  const createImg = function () {
43
43
  const self = this;
44
- const osufile = `{{ osu_file }}`;
44
+ const osufile = {{ osu_file|tojson }};
45
45
  let SPEED = 1;
46
46
  let mods = {
47
47
  HR: {{ is_hr }},
@@ -85,4 +85,4 @@
85
85
  </script>
86
86
  </body>
87
87
 
88
- </html>
88
+ </html>
@@ -28,19 +28,25 @@ from .utils import (
28
28
  open_user_icon,
29
29
  filter_scores_with_regex,
30
30
  trim_text_with_ellipsis,
31
+ draw_text_with_outline,
31
32
  )
32
33
  from .static import (
33
34
  Image,
34
35
  IconLs,
35
- Venera_75,
36
+ Venera_60,
37
+ Torus_Regular_15,
36
38
  Torus_Regular_20,
37
- Torus_Regular_30,
38
- Torus_Regular_75,
39
+ Torus_Regular_25,
40
+ Torus_Regular_50,
41
+ Torus_Regular_60,
42
+ Torus_SemiBold_15,
39
43
  Torus_SemiBold_20,
40
44
  Torus_SemiBold_25,
41
45
  Torus_SemiBold_30,
42
46
  osufile,
43
47
  extra_30,
48
+ Stardiff,
49
+ Stars,
44
50
  )
45
51
 
46
52
 
@@ -197,15 +203,23 @@ async def draw_score_pic(score_info: UnifiedScore, info: UnifiedUser, map_json,
197
203
  original_ss_pp_info = get_ss_pp(str(osu.absolute()), 0, is_lazer)
198
204
  if_pp, ss_pp = get_if_pp_ss_pp(score_info, str(osu.absolute()), is_lazer)
199
205
  # 新建图片
200
- im = Image.new("RGBA", (1500, 720))
206
+ im = Image.new("RGBA", (1280, 720))
201
207
  draw = ImageDraw.Draw(im)
202
208
  # 获取cover并裁剪,高斯,降低亮度
203
209
  try:
204
210
  bg = await get_bg(mapinfo.id, mapinfo.beatmapset_id)
205
- cover_crop = await crop_bg((1500, 720), bg)
206
- cover_gb = cover_crop.filter(ImageFilter.GaussianBlur(3))
207
- cover_img = ImageEnhance.Brightness(cover_gb).enhance(2 / 4.0)
208
- im.alpha_composite(cover_img, (0, 0))
211
+ cover_crop_left = await crop_bg((1280, 720), bg)
212
+ cover_gb_left = cover_crop_left.filter(ImageFilter.GaussianBlur(10))
213
+ cover_gb_left = ImageEnhance.Brightness(cover_gb_left).enhance(1)
214
+ cover_img_left = ImageEnhance.Brightness(cover_gb_left).enhance(2 / 4.0)
215
+ im.alpha_composite(cover_img_left, (0, 0))
216
+ # 新小图 BG
217
+ cover_crop_right = await crop_bg((650, 270), bg)
218
+ cover_gb_right = cover_crop_right.filter(ImageFilter.GaussianBlur(1))
219
+ cover_gb_right = ImageEnhance.Brightness(cover_gb_right).enhance(0.9)
220
+ cover_img_right = ImageEnhance.Brightness(cover_gb_right).enhance(1)
221
+ cover_img_right = draw_fillet(cover_img_right, 15)
222
+ im.alpha_composite(cover_img_right, (0, 50))
209
223
  except NetworkError:
210
224
  ...
211
225
  # 获取成绩背景做底图
@@ -213,58 +227,70 @@ async def draw_score_pic(score_info: UnifiedScore, info: UnifiedUser, map_json,
213
227
  recent_bg = Image.open(bg).convert("RGBA")
214
228
  im.alpha_composite(recent_bg)
215
229
  # 模式
216
- draw.text((75, 75), IconLs[score_info.ruleset_id], font=extra_30, anchor="lt")
230
+ draw_text_with_outline(
231
+ draw,
232
+ (30, 80),
233
+ IconLs[score_info.ruleset_id],
234
+ extra_30,
235
+ anchor="lm",
236
+ fill=(255, 255, 255, 255),
237
+ )
217
238
  # 难度星星
218
- stars_bg = stars_diff(pp_info.difficulty.stars)
239
+ stars_bg = stars_diff(pp_info.difficulty.stars, Stars)
219
240
  stars_img = stars_bg.resize((85, 37))
220
- im.alpha_composite(stars_img, (122, 72))
241
+ im.alpha_composite(stars_img, (552, 67))
242
+ # 难度竖条
243
+ star_bg = stars_diff(pp_info.difficulty.stars, Stardiff)
244
+ star_img = star_bg.resize((20, 271))
245
+ im.alpha_composite(star_img, (0, 50))
246
+ # 星级
221
247
  if pp_info.difficulty.stars < 6.5:
222
248
  color = (0, 0, 0, 255)
223
249
  else:
224
250
  color = (255, 217, 102, 255)
225
- # 星级
251
+
226
252
  draw.text(
227
- (128, 90),
253
+ (556, 85),
228
254
  f"★{pp_info.difficulty.stars:.2f}",
229
255
  font=Torus_SemiBold_20,
230
256
  anchor="lm",
231
257
  fill=color,
232
258
  )
233
259
  # mods
234
- if any(i in score_info.mods for i in (Mod(acronym="HD"), Mod(acronym="FL"), Mod(acronym="FI"))):
235
- ranking = ["XH", "SH", "A", "B", "C", "D", "F"]
236
- else:
237
- ranking = ["X", "S", "A", "B", "C", "D", "F"]
260
+ # if any(i in score_info.mods for i in (Mod(acronym="HD"), Mod(acronym="FL"), Mod(acronym="FI"))):
261
+ # ranking = ["XH", "SH", "A", "B", "C", "D", "F"]
262
+ # else:
263
+ # ranking = ["X", "S", "A", "B", "C", "D", "F"]
238
264
  if score_info.mods:
239
265
  for mods_num, s_mods in enumerate(score_info.mods):
240
266
  mods_bg = osufile / "mods" / f"{s_mods.acronym}.png"
241
267
  try:
242
268
  mods_img = Image.open(mods_bg).convert("RGBA")
243
- im.alpha_composite(mods_img, (500 + 50 * mods_num, 160))
269
+ im.alpha_composite(mods_img, (880 + 50 * mods_num, 100))
244
270
  except FileNotFoundError:
245
271
  pass
246
272
  # 成绩S-F
247
- rank_ok = False
248
- for rank_num, i in enumerate(ranking):
249
- rank_img = osufile / "ranking" / f"ranking-{i}.png"
250
- if rank_ok:
251
- rank_b = Image.open(rank_img).convert("RGBA").resize((48, 24))
252
- rank_new = Image.new("RGBA", rank_b.size, (0, 0, 0, 0))
253
- rank_bg = Image.blend(rank_new, rank_b, 0.5)
254
- elif i != score_info.rank:
255
- rank_b = Image.open(rank_img).convert("RGBA").resize((48, 24))
256
- rank_new = Image.new("RGBA", rank_b.size, (0, 0, 0, 0))
257
- rank_bg = Image.blend(rank_new, rank_b, 0.2)
258
- else:
259
- rank_bg = Image.open(rank_img).convert("RGBA").resize((48, 24))
260
- rank_ok = True
261
- im.alpha_composite(rank_bg, (75, 163 + 39 * rank_num))
273
+ # rank_ok = False
274
+ # for rank_num, i in enumerate(ranking):
275
+ # rank_img = osufile / "ranking" / f"ranking-{i}.png"
276
+ # if rank_ok:
277
+ # rank_b = Image.open(rank_img).convert("RGBA").resize((48, 24))
278
+ # rank_new = Image.new("RGBA", rank_b.size, (0, 0, 0, 0))
279
+ # rank_bg = Image.blend(rank_new, rank_b, 0.5)
280
+ # elif i != score_info.rank:
281
+ # rank_b = Image.open(rank_img).convert("RGBA").resize((48, 24))
282
+ # rank_new = Image.new("RGBA", rank_b.size, (0, 0, 0, 0))
283
+ # rank_bg = Image.blend(rank_new, rank_b, 0.2)
284
+ # else:
285
+ # rank_bg = Image.open(rank_img).convert("RGBA").resize((48, 24))
286
+ # rank_ok = True
287
+ # im.alpha_composite(rank_bg, (75, 163 + 39 * rank_num))
262
288
  # 成绩+acc
263
289
  im = draw_acc(im, score_info.accuracy, score_info.ruleset_id)
264
290
  # 地区
265
291
  country = osufile / "flags" / f"{info.country_code}.png"
266
292
  country_bg = Image.open(country).convert("RGBA").resize((66, 45))
267
- im.alpha_composite(country_bg, (250, 577))
293
+ im.alpha_composite(country_bg, (208, 597))
268
294
  if info.team and info.team.flag_url:
269
295
  team_path = team_cache_path / f"{info.team.id}.png"
270
296
  if not team_path.exists():
@@ -273,11 +299,11 @@ async def draw_score_pic(score_info: UnifiedScore, info: UnifiedUser, map_json,
273
299
  team_img.save(team_path)
274
300
  try:
275
301
  team_img = Image.open(team_path).convert("RGBA").resize((80, 40))
276
- im.alpha_composite(team_img, (250, 640))
302
+ im.alpha_composite(team_img, (208, 660))
277
303
  except UnidentifiedImageError:
278
304
  team_path.unlink()
279
305
  raise NetworkError("team 图片下载错误,请重试!")
280
- draw.text((340, 660), info.team.name, font=Torus_Regular_20, anchor="lt")
306
+ draw.text((297, 675), info.team.name, font=Torus_Regular_20, anchor="lt")
281
307
  # supporter
282
308
  # if info.is_supporter:
283
309
  # im.alpha_composite(SupporterBg.resize((40, 40)), (250, 640))
@@ -309,38 +335,38 @@ async def draw_score_pic(score_info: UnifiedScore, info: UnifiedUser, map_json,
309
335
  ]
310
336
 
311
337
  for num, (orig, new) in enumerate(zip(original_mapdiff, mapdiff)):
312
- orig_difflen = int(250 * max(0, orig) / 10) if orig <= 10 else 250
313
- new_difflen = int(250 * max(0, new) / 10) if new <= 10 else 250
338
+ orig_difflen = int(400 * max(0, orig) / 10) if orig <= 10 else 400
339
+ new_difflen = int(400 * max(0, new) / 10) if new <= 10 else 400
314
340
  if is_close(new, orig):
315
341
  color = (255, 255, 255, 255)
316
342
  diff_len = Image.new("RGBA", (orig_difflen, 8), color)
317
- im.alpha_composite(diff_len, (1190, 306 + 35 * num))
343
+ im.alpha_composite(diff_len, (165, 352 + 35 * num))
318
344
  elif new > orig and not (score_info.ruleset_id == 3 and mapinfo.mode == "osu"):
319
345
  color = (198, 92, 102, 255)
320
346
  orig_color = (246, 136, 144, 255)
321
347
  new_diff_len = Image.new("RGBA", (new_difflen, 8), color)
322
- im.alpha_composite(new_diff_len, (1190, 306 + 35 * num))
348
+ im.alpha_composite(new_diff_len, (165, 352 + 35 * num))
323
349
  orig_diff_len = Image.new("RGBA", (orig_difflen, 8), orig_color)
324
- im.alpha_composite(orig_diff_len, (1190, 306 + 35 * num))
350
+ im.alpha_composite(orig_diff_len, (165, 352 + 35 * num))
325
351
  elif new < orig and not (score_info.ruleset_id == 3 and mapinfo.mode == "osu"):
326
352
  color = (161, 212, 238, 255)
327
353
  orig_color = (255, 255, 255, 255)
328
354
  orig_diff_len = Image.new("RGBA", (orig_difflen, 8), orig_color)
329
- im.alpha_composite(orig_diff_len, (1190, 306 + 35 * num))
355
+ im.alpha_composite(orig_diff_len, (165, 352 + 35 * num))
330
356
  new_diff_len = Image.new("RGBA", (new_difflen, 8), color)
331
- im.alpha_composite(new_diff_len, (1190, 306 + 35 * num))
357
+ im.alpha_composite(new_diff_len, (165, 352 + 35 * num))
332
358
  else:
333
359
  raise Exception("没有这种情况")
334
360
  if new == round(new):
335
361
  draw.text(
336
- (1470, 310 + 35 * num),
362
+ (610, 355 + 35 * num),
337
363
  f"{new:.0f}",
338
364
  font=Torus_SemiBold_20,
339
365
  anchor="mm",
340
366
  )
341
367
  else:
342
368
  draw.text(
343
- (1470, 310 + 35 * num),
369
+ (610, 355 + 35 * num),
344
370
  f"{new:.2f}" if new != round(new, 1) else f"{new:.1f}",
345
371
  font=Torus_SemiBold_20,
346
372
  anchor="mm",
@@ -351,253 +377,291 @@ async def draw_score_pic(score_info: UnifiedScore, info: UnifiedUser, map_json,
351
377
  if stars > original_stars:
352
378
  color = (198, 92, 102, 255)
353
379
  orig_color = (246, 111, 34, 255)
354
- new_difflen = int(250 * max(0.0, stars) / 10) if stars <= 10 else 250
380
+ new_difflen = int(400 * max(0.0, stars) / 10) if stars <= 10 else 400
355
381
  new_diff_len = Image.new("RGBA", (new_difflen, 8), color)
356
- im.alpha_composite(new_diff_len, (1190, 446))
357
- orig_difflen = int(250 * max(0.0, original_stars) / 10) if original_stars <= 10 else 250
382
+ im.alpha_composite(new_diff_len, (165, 490))
383
+ orig_difflen = int(400 * max(0.0, original_stars) / 10) if original_stars <= 10 else 400
358
384
  orig_diff_len = Image.new("RGBA", (orig_difflen, 8), orig_color)
359
- im.alpha_composite(orig_diff_len, (1190, 446))
385
+ im.alpha_composite(orig_diff_len, (165, 490))
360
386
  elif stars < original_stars:
361
387
  color = (161, 187, 127, 255)
362
388
  orig_color = (255, 204, 34, 255)
363
- orig_difflen = int(250 * max(0.0, original_stars) / 10) if original_stars <= 10 else 250
389
+ orig_difflen = int(400 * max(0.0, original_stars) / 10) if original_stars <= 10 else 400
364
390
  orig_diff_len = Image.new("RGBA", (orig_difflen, 8), orig_color)
365
- im.alpha_composite(orig_diff_len, (1190, 446))
366
- new_difflen = int(250 * max(0.0, stars) / 10) if stars <= 10 else 250
391
+ im.alpha_composite(orig_diff_len, (165, 490))
392
+ new_difflen = int(400 * max(0.0, stars) / 10) if stars <= 10 else 400
367
393
  new_diff_len = Image.new("RGBA", (new_difflen, 8), color)
368
- im.alpha_composite(new_diff_len, (1190, 446))
394
+ im.alpha_composite(new_diff_len, (165, 490))
369
395
  else:
370
396
  color = (255, 204, 34, 255)
371
- difflen = int(250 * stars / 10) if stars <= 10 else 250
397
+ difflen = int(400 * stars / 10) if stars <= 10 else 400
372
398
  diff_len = Image.new("RGBA", (difflen, 8), color)
373
- im.alpha_composite(diff_len, (1190, 446))
374
- draw.text((1470, 450), f"{stars:.2f}", font=Torus_SemiBold_20, anchor="mm")
399
+ im.alpha_composite(diff_len, (165, 490))
400
+ draw.text((610, 493), f"{stars:.2f}", font=Torus_SemiBold_20, anchor="mm")
375
401
  # 时长 - 滑条
376
- diff_info = (
402
+ diff_info = [
377
403
  calc_songlen(mapinfo.total_length),
378
404
  f"{mapinfo.bpm:.1f}",
379
405
  mapinfo.count_circles,
380
406
  mapinfo.count_sliders,
381
- )
407
+ ] + ([mapinfo.count_spinners] if score_info.ruleset_id != 3 else [])
382
408
  for num, i in enumerate(diff_info):
383
- draw.text(
384
- (1070 + 120 * num, 245),
409
+ draw_text_with_outline(
410
+ draw,
411
+ (70 + 120 * num, 300),
385
412
  f"{i}",
386
413
  font=Torus_Regular_20,
387
414
  anchor="lm",
388
415
  fill=(255, 204, 34, 255),
389
416
  )
390
417
  # 状态
391
- draw.text((1400, 184), mapinfo.status.capitalize(), font=Torus_SemiBold_20, anchor="mm")
418
+ draw_text_with_outline(
419
+ draw,
420
+ (595, 115),
421
+ mapinfo.status.capitalize(),
422
+ Torus_SemiBold_15,
423
+ anchor="mm",
424
+ fill=(255, 255, 255, 255),
425
+ )
426
+ # setid
427
+ draw.text((32, 25), f"Setid:{mapinfo.beatmapset_id}", font=Torus_SemiBold_20, anchor="lm")
392
428
  # mapid
393
- draw.text((1485, 90), f"Mapid: {mapinfo.id}", font=Torus_SemiBold_20, anchor="rm")
429
+ draw.text((650, 25), f"Mapid: {mapinfo.id}", font=Torus_SemiBold_20, anchor="rm")
394
430
  # 曲名
395
- draw.text(
396
- (75, 38),
397
- f"{mapinfo.beatmapset.title} | by {mapinfo.beatmapset.artist_unicode}",
398
- font=Torus_SemiBold_30,
431
+ title = trim_text_with_ellipsis(mapinfo.beatmapset.title, 600, Torus_SemiBold_30)
432
+ draw_text_with_outline(
433
+ draw,
434
+ (30, 200),
435
+ title,
436
+ Torus_SemiBold_30,
437
+ anchor="lm",
438
+ fill=(255, 255, 255, 255),
439
+ )
440
+ # 艺术家
441
+ artist = trim_text_with_ellipsis(mapinfo.beatmapset.artist, 600, Torus_SemiBold_20)
442
+ draw_text_with_outline(
443
+ draw,
444
+ (30, 230),
445
+ artist,
446
+ Torus_SemiBold_20,
399
447
  anchor="lm",
448
+ fill=(255, 255, 255, 255),
400
449
  )
401
- # 谱面版本,mapper
450
+ # mapper
402
451
  if mapinfo.owners:
403
452
  owner_names = [owner.username for owner in mapinfo.owners]
404
453
  owners_str = ", ".join(owner_names)
405
- mapper = f"{mapinfo.version} | 谱师: {owners_str}"
454
+ mapper = f"谱师: {owners_str}"
406
455
 
407
456
  else:
408
- mapper = f"{mapinfo.version} | 谱师: {mapinfo.beatmapset.creator}"
409
- mapper = trim_text_with_ellipsis(mapper, 1000, Torus_SemiBold_20)
410
-
411
- draw.text(
412
- (225, 90),
457
+ mapper = f"谱师: {mapinfo.beatmapset.creator}"
458
+ mapper = trim_text_with_ellipsis(mapper, 600, Torus_SemiBold_15)
459
+ draw_text_with_outline(
460
+ draw,
461
+ (30, 265),
413
462
  mapper,
414
- font=Torus_SemiBold_20,
463
+ Torus_SemiBold_15,
464
+ anchor="lm",
465
+ fill=(255, 255, 255, 255),
466
+ )
467
+ # 谱面版本
468
+ version = trim_text_with_ellipsis(mapinfo.version, 450, Torus_SemiBold_15)
469
+ draw_text_with_outline(
470
+ draw,
471
+ (65, 80),
472
+ version,
473
+ Torus_SemiBold_15,
415
474
  anchor="lm",
475
+ fill=(255, 255, 255, 255),
416
476
  )
417
477
  # 评价
418
- draw.text((316, 307), score_info.rank, font=Venera_75, anchor="mm")
478
+ draw.text((772, 185), score_info.rank, font=Venera_60, anchor="mm")
419
479
  # 分数
420
480
  draw.text(
421
- (498, 251),
481
+ (880, 165),
422
482
  f"{score_info.legacy_total_score or score_info.total_score:,}",
423
- font=Torus_Regular_75,
483
+ font=Torus_Regular_60,
424
484
  anchor="lm",
425
485
  )
426
486
  # 时间
427
- draw.text((498, 341), "达成时间:", font=Torus_SemiBold_20, anchor="lm")
428
- draw.text((630, 341), score_info.ended_at.strftime("%Y-%m-%d %H:%M:%S"), font=Torus_SemiBold_20, anchor="lm")
487
+ draw.text((883, 230), "达成时间:", font=Torus_SemiBold_20, anchor="lm")
488
+ draw.text((985, 230), score_info.ended_at.strftime("%Y-%m-%d %H:%M:%S"), font=Torus_SemiBold_20, anchor="lm")
429
489
  # 全球排名
430
- draw.text((583, 410), f"#{grank}" if grank else "", font=Torus_SemiBold_25, anchor="mm")
490
+ draw.text((883, 260), "全球排行:" if grank else "", font=Torus_SemiBold_20, anchor="lm")
491
+ draw.text((985, 260), f"#{grank}" if grank else "", font=Torus_SemiBold_25, anchor="lm")
431
492
  # 左下玩家名
432
- draw.text((250, 530), info.username, font=Torus_SemiBold_30, anchor="lm")
493
+ draw.text((208, 550), info.username, font=Torus_SemiBold_30, anchor="lm")
433
494
  # 国内排名
434
495
  draw.text(
435
- (325, 610),
496
+ (283, 630),
436
497
  f"#{info.statistics.country_rank}",
437
498
  font=Torus_SemiBold_25,
438
499
  anchor="lm",
439
500
  )
440
501
  if score_info.ruleset_id in {0, 4, 8}:
441
- draw.text((720, 550), ss_pp, font=Torus_Regular_30, anchor="mm")
442
- draw.text((840, 550), if_pp, font=Torus_Regular_30, anchor="mm")
443
- draw.text((960, 550), f"{pp_info.pp:.0f}", font=Torus_Regular_30, anchor="mm")
444
- draw.text((720, 645), f"{pp_info.pp_aim:.0f}", font=Torus_Regular_30, anchor="mm")
445
- draw.text((840, 645), f"{pp_info.pp_speed:.0f}", font=Torus_Regular_30, anchor="mm")
446
- draw.text((960, 645), f"{pp_info.pp_accuracy:.0f}", font=Torus_Regular_30, anchor="mm")
502
+ draw.text((1066, 393), ss_pp, font=Torus_Regular_25, anchor="mm")
503
+ draw.text((933, 393), if_pp, font=Torus_Regular_25, anchor="mm")
504
+ draw.text((768, 438), f"{pp_info.pp:.0f}", font=Torus_Regular_50, anchor="mm")
505
+ draw.text((933, 482), f"{pp_info.pp_aim:.0f}", font=Torus_Regular_25, anchor="mm")
506
+ draw.text((1066, 482), f"{pp_info.pp_speed:.0f}", font=Torus_Regular_25, anchor="mm")
507
+ draw.text((1200, 482), f"{pp_info.pp_accuracy:.0f}", font=Torus_Regular_25, anchor="mm")
447
508
  draw.text(
448
- (1157, 550),
509
+ (768, 577),
449
510
  f"{score_info.accuracy:.2f}%",
450
- font=Torus_Regular_30,
511
+ font=Torus_Regular_25,
451
512
  anchor="mm",
452
513
  )
453
514
  draw.text(
454
- (1385, 550),
515
+ (768, 666),
455
516
  f"{score_info.max_combo:,}/{pp_info.difficulty.max_combo}",
456
- font=Torus_Regular_30,
517
+ font=Torus_Regular_25,
457
518
  anchor="mm",
458
519
  )
459
520
  draw.text(
460
- (1100, 645),
521
+ (933, 577),
461
522
  f"{score_info.statistics.great or 0}",
462
- font=Torus_Regular_30,
523
+ font=Torus_Regular_25,
463
524
  anchor="mm",
464
525
  )
465
526
  draw.text(
466
- (1214, 645),
527
+ (1066, 577),
467
528
  f"{score_info.statistics.ok or 0}",
468
- font=Torus_Regular_30,
529
+ font=Torus_Regular_25,
469
530
  anchor="mm",
470
531
  )
471
532
  draw.text(
472
- (1328, 645),
533
+ (933, 666),
473
534
  f"{score_info.statistics.meh or 0}",
474
- font=Torus_Regular_30,
535
+ font=Torus_Regular_25,
475
536
  anchor="mm",
476
537
  )
477
538
  draw.text(
478
- (1442, 645),
539
+ (1066, 666),
479
540
  f"{score_info.statistics.miss or 0}",
480
- font=Torus_Regular_30,
541
+ font=Torus_Regular_25,
481
542
  anchor="mm",
482
543
  )
483
544
  elif score_info.ruleset_id in {1, 5}:
484
545
  draw.text(
485
- (1118, 550),
546
+ (768, 577),
486
547
  f"{score_info.accuracy:.2f}%",
487
- font=Torus_Regular_30,
548
+ font=Torus_Regular_25,
488
549
  anchor="mm",
489
550
  )
490
- draw.text((1270, 550), f"{score_info.max_combo:,}", font=Torus_Regular_30, anchor="mm")
491
- draw.text((1420, 550), f"{pp_info.pp:.0f}/{ss_pp}", font=Torus_Regular_30, anchor="mm")
551
+ draw.text((768, 666), f"{score_info.max_combo:,}", font=Torus_Regular_25, anchor="mm")
552
+ draw.text((768, 438), f"{pp_info.pp:.0f}", font=Torus_Regular_50, anchor="mm")
553
+ draw.text((933, 393), f"{ss_pp}", font=Torus_Regular_25, anchor="mm")
492
554
  draw.text(
493
- (1118, 645),
555
+ (933, 577),
494
556
  f"{score_info.statistics.great or 0}",
495
- font=Torus_Regular_30,
557
+ font=Torus_Regular_25,
496
558
  anchor="mm",
497
559
  )
498
560
  draw.text(
499
- (1270, 645),
561
+ (1066, 577),
500
562
  f"{score_info.statistics.ok or 0}",
501
- font=Torus_Regular_30,
563
+ font=Torus_Regular_25,
502
564
  anchor="mm",
503
565
  )
504
566
  draw.text(
505
- (1420, 645),
567
+ (933, 666),
506
568
  f"{score_info.statistics.miss or 0}",
507
- font=Torus_Regular_30,
569
+ font=Torus_Regular_25,
508
570
  anchor="mm",
509
571
  )
510
572
  elif score_info.ruleset_id in {2, 6}:
511
573
  draw.text(
512
- (1083, 550),
574
+ (768, 577),
513
575
  f"{score_info.accuracy:.2f}%",
514
- font=Torus_Regular_30,
576
+ font=Torus_Regular_25,
515
577
  anchor="mm",
516
578
  )
517
579
  draw.text(
518
- (1247, 550),
580
+ (768, 666),
519
581
  f"{score_info.max_combo}/{pp_info.difficulty.max_combo}",
520
- font=Torus_Regular_30,
582
+ font=Torus_Regular_25,
521
583
  anchor="mm",
522
584
  )
523
- draw.text((1411, 550), f"{pp_info.pp:.0f}/{ss_pp}", font=Torus_Regular_30, anchor="mm")
585
+ draw.text((768, 438), f"{pp_info.pp:.0f}", font=Torus_Regular_50, anchor="mm")
586
+ draw.text((933, 393), f"{ss_pp}", font=Torus_Regular_25, anchor="mm")
524
587
  draw.text(
525
- (1062, 645),
588
+ (933, 577),
526
589
  f"{score_info.statistics.great or 0}",
527
- font=Torus_Regular_30,
590
+ font=Torus_Regular_25,
528
591
  anchor="mm",
529
592
  )
530
593
  draw.text(
531
- (1185, 645),
594
+ (1066, 577),
532
595
  f"{score_info.statistics.large_tick_hit or 0}",
533
- font=Torus_Regular_30,
596
+ font=Torus_Regular_25,
534
597
  anchor="mm",
535
598
  )
536
599
  draw.text(
537
- (1309, 645),
600
+ (933, 666),
538
601
  f"{score_info.statistics.small_tick_miss or 0}",
539
- font=Torus_Regular_30,
602
+ font=Torus_Regular_25,
540
603
  anchor="mm",
541
604
  )
542
605
  draw.text(
543
- (1432, 645),
606
+ (1066, 666),
544
607
  f"{score_info.statistics.miss or 0}",
545
- font=Torus_Regular_30,
608
+ font=Torus_Regular_25,
546
609
  anchor="mm",
547
610
  )
548
611
  else:
549
612
  draw.text(
550
- (1002, 580),
613
+ (933, 600),
551
614
  (
552
615
  f"{score_info.statistics.perfect / score_info.statistics.great:.1f}:1"
553
616
  if (score_info.statistics.great or 0) != 0
554
617
  else "∞:1"
555
618
  ),
556
- font=Torus_Regular_20,
619
+ font=Torus_Regular_15,
557
620
  anchor="mm",
558
621
  )
559
622
  draw.text(
560
- (1002, 550),
623
+ (768, 577),
561
624
  f"{score_info.accuracy:.2f}%",
562
- font=Torus_Regular_30,
625
+ font=Torus_Regular_25,
563
626
  anchor="mm",
564
627
  )
565
- draw.text((1197, 550), f"{score_info.max_combo}", font=Torus_Regular_30, anchor="mm")
566
- draw.text((1395, 550), f"{pp_info.pp:.0f}/{ss_pp}", font=Torus_Regular_30, anchor="mm")
628
+ draw.text((768, 666), f"{score_info.max_combo}", font=Torus_Regular_25, anchor="mm")
629
+ draw.text((768, 438), f"{pp_info.pp:.0f}", font=Torus_Regular_50, anchor="mm")
630
+ draw.text((933, 393), f"{ss_pp}", font=Torus_Regular_25, anchor="mm")
567
631
  draw.text(
568
- (953, 645),
632
+ (933, 577),
569
633
  f"{score_info.statistics.perfect or 0}",
570
- font=Torus_Regular_30,
634
+ font=Torus_Regular_25,
571
635
  anchor="mm",
572
636
  )
573
637
  draw.text(
574
- (1051, 645),
638
+ (1066, 577),
575
639
  f"{score_info.statistics.great or 0}",
576
- font=Torus_Regular_30,
640
+ font=Torus_Regular_25,
577
641
  anchor="mm",
578
642
  )
579
643
  draw.text(
580
- (1150, 645),
644
+ (1200, 577),
581
645
  f"{score_info.statistics.good or 0}",
582
- font=Torus_Regular_30,
646
+ font=Torus_Regular_25,
583
647
  anchor="mm",
584
648
  )
585
649
  draw.text(
586
- (1249, 645),
650
+ (933, 666),
587
651
  f"{score_info.statistics.ok or 0}",
588
- font=Torus_Regular_30,
652
+ font=Torus_Regular_25,
589
653
  anchor="mm",
590
654
  )
591
655
  draw.text(
592
- (1347, 645),
656
+ (1066, 666),
593
657
  f"{score_info.statistics.meh or 0}",
594
- font=Torus_Regular_30,
658
+ font=Torus_Regular_25,
595
659
  anchor="mm",
596
660
  )
597
661
  draw.text(
598
- (1445, 645),
662
+ (1200, 666),
599
663
  f"{score_info.statistics.miss or 0}",
600
- font=Torus_Regular_30,
664
+ font=Torus_Regular_25,
601
665
  anchor="mm",
602
666
  )
603
667
  user_icon = await open_user_icon(info, source)
@@ -607,7 +671,7 @@ async def draw_score_pic(score_info: UnifiedScore, info: UnifiedUser, map_json,
607
671
  if not getattr(user_icon, "is_animated", False):
608
672
  icon_bg = user_icon.convert("RGBA").resize((170, 170))
609
673
  icon_img = draw_fillet(icon_bg, 15)
610
- im.alpha_composite(icon_img, (60, 510))
674
+ im.alpha_composite(icon_img, (27, 532))
611
675
  byt = BytesIO()
612
676
  im.convert("RGB").save(byt, "jpeg")
613
677
  im.close()
@@ -620,7 +684,7 @@ async def draw_score_pic(score_info: UnifiedScore, info: UnifiedUser, map_json,
620
684
  # 创建一个新的 RGBA 图片,将 PNG 图片作为背景,将当前帧添加到背景上
621
685
  rgba_frame = Image.new("RGBA", im.size, (0, 0, 0, 0))
622
686
  rgba_frame.paste(im, (0, 0), im)
623
- rgba_frame.paste(gif_frame, (60, 510), gif_frame)
687
+ rgba_frame.paste(gif_frame, (27, 532), gif_frame)
624
688
  # 将 RGBA 图片转换为 RGB 模式,并添加到 GIF 图片中
625
689
  gif_frames.append(rgba_frame)
626
690
  gif_bytes = BytesIO()
@@ -15,14 +15,15 @@ Torus_Regular_35 = ImageFont.truetype(str(osufile / "fonts" / "Torus Regular.otf
15
15
  Torus_Regular_40 = ImageFont.truetype(str(osufile / "fonts" / "Torus Regular.otf"), 40)
16
16
  Torus_Regular_45 = ImageFont.truetype(str(osufile / "fonts" / "Torus Regular.otf"), 45)
17
17
  Torus_Regular_50 = ImageFont.truetype(str(osufile / "fonts" / "Torus Regular.otf"), 50)
18
- Torus_Regular_75 = ImageFont.truetype(str(osufile / "fonts" / "Torus Regular.otf"), 75)
18
+ Torus_Regular_60 = ImageFont.truetype(str(osufile / "fonts" / "Torus Regular.otf"), 60)
19
+ Torus_SemiBold_15 = ImageFont.truetype(str(osufile / "fonts" / "Torus SemiBold.otf"), 15)
19
20
  Torus_SemiBold_20 = ImageFont.truetype(str(osufile / "fonts" / "Torus SemiBold.otf"), 20)
20
21
  Torus_SemiBold_25 = ImageFont.truetype(str(osufile / "fonts" / "Torus SemiBold.otf"), 25)
21
22
  Torus_SemiBold_30 = ImageFont.truetype(str(osufile / "fonts" / "Torus SemiBold.otf"), 30)
22
23
  Torus_SemiBold_40 = ImageFont.truetype(str(osufile / "fonts" / "Torus SemiBold.otf"), 40)
23
24
  Torus_SemiBold_45 = ImageFont.truetype(str(osufile / "fonts" / "Torus SemiBold.otf"), 45)
24
25
  Torus_SemiBold_50 = ImageFont.truetype(str(osufile / "fonts" / "Torus SemiBold.otf"), 50)
25
- Venera_75 = ImageFont.truetype(str(osufile / "fonts" / "Venera.otf"), 75)
26
+ Venera_60 = ImageFont.truetype(str(osufile / "fonts" / "Venera.otf"), 60)
26
27
  extra_30 = ImageFont.truetype(str(osufile / "fonts" / "Extra.otf"), 30)
27
28
 
28
29
  InfoImg = Image.open(osufile / "info.png").convert("RGBA")
@@ -36,6 +37,7 @@ MapBg = Image.open(osufile / "beatmapinfo.png").convert("RGBA")
36
37
  MapBg1 = Image.open(osufile / "maniabeatmapinfo.png").convert("RGBA")
37
38
  BarImg = Image.open(osufile / "work" / "bmap.png").convert("RGBA")
38
39
  Stars = Image.open(osufile / "work" / "stars.png").convert("RGBA")
40
+ Stardiff = Image.open(osufile / "work" / "stardiff.png").convert("RGBA")
39
41
  TeamBlue = Image.open(osufile / "match" / "team_blue.png").convert("RGBA")
40
42
  TeamRed = Image.open(osufile / "match" / "team_red.png").convert("RGBA")
41
43
  MpLink = Image.open(osufile / "match" / "mplink.png").convert("RGBA")
@@ -43,7 +43,7 @@
43
43
  xAxis: [
44
44
  {
45
45
  type: 'category',
46
- data: Array.from({ length: 100 }, (_, i) => i),
46
+ data: Array.from({ length: 200 }, (_, i) => i),
47
47
  axisLabel: {
48
48
  interval: 9
49
49
  }
@@ -6,13 +6,14 @@ from io import BytesIO
6
6
  from typing import Union, Optional
7
7
  from difflib import SequenceMatcher
8
8
 
9
+ from PIL.ImageFile import ImageFile
9
10
  from matplotlib.figure import Figure
10
11
  from PIL import ImageDraw, ImageFilter, ImageEnhance, UnidentifiedImageError
11
12
 
12
13
  from ..schema.user import UnifiedUser
13
14
  from ..schema import SeasonalBackgrounds
14
15
  from ..api import safe_async_get, get_seasonal_bg
15
- from .static import Path, Image, Stars, ColorArr, np, osufile
16
+ from .static import Path, Image, ColorArr, np, osufile
16
17
  from ..file import map_path, download_osu, get_projectimg, user_cache_path, team_cache_path
17
18
 
18
19
 
@@ -120,8 +121,8 @@ def draw_acc(img: Image, acc: float, mode: int):
120
121
  ax.clear()
121
122
  fig.clf()
122
123
  fig.clear()
123
- score_acc_img = Image.open(acc_img).convert("RGBA").resize((576, 432))
124
- img.alpha_composite(score_acc_img, (25, 83))
124
+ score_acc_img = Image.open(acc_img).convert("RGBA").resize((384, 288))
125
+ img.alpha_composite(score_acc_img, (580, 35))
125
126
  return img
126
127
 
127
128
 
@@ -183,7 +184,7 @@ async def crop_bg(size: tuple[int, int], path: Union[str, Path, BytesIO, Image.I
183
184
  return sf
184
185
 
185
186
 
186
- def stars_diff(stars: float):
187
+ def stars_diff(stars: float, stars_bg: ImageFile):
187
188
  if stars < 0.1:
188
189
  r, g, b = 170, 170, 170
189
190
  elif stars >= 9:
@@ -192,10 +193,10 @@ def stars_diff(stars: float):
192
193
  # 颜色取色参考 https://github.com/ppy/osu-web/blob/97997d9c7b7f9c49f9b3cdd776c71afb9872c34b/resources/js/utils/beatmap-helper.ts#L20
193
194
  r, g, b, _a = ColorArr[int(stars * 100)]
194
195
  # 打开底图
195
- xx, yy = Stars.size
196
+ xx, yy = stars_bg.size
196
197
  # 填充背景
197
- img = Image.new("RGBA", Stars.size, (r, g, b))
198
- img.paste(Stars, (0, 0, xx, yy), Stars)
198
+ img = Image.new("RGBA", stars_bg.size, (r, g, b))
199
+ img.paste(stars_bg, (0, 0, xx, yy), stars_bg)
199
200
  # 把白色变透明
200
201
  arr = np.array(img)
201
202
  # 创建mask,将白色替换为True,其他颜色替换为False
@@ -438,3 +439,18 @@ def trim_text_with_ellipsis(text, max_width, font):
438
439
 
439
440
  # 返回截断后的字符串 + 省略号
440
441
  return truncated_text + ellipsis_symbol if truncated_text else ellipsis_symbol
442
+
443
+
444
+ # 字体描边函数
445
+ def draw_text_with_outline(draw, position, text, font, anchor, fill):
446
+ for dx in [-1, 0, 1]:
447
+ for dy in [-1, 0, 1]:
448
+ if dx != 0 or dy != 0:
449
+ draw.text(
450
+ (position[0] + dx, position[1] + dy),
451
+ text,
452
+ font=font,
453
+ anchor=anchor,
454
+ fill=(0, 0, 0, 255),
455
+ )
456
+ draw.text(position, text, font=font, anchor=anchor, fill=fill)
@@ -7,9 +7,8 @@ from nonebot_plugin_alconna import UniMessage
7
7
 
8
8
  from ..utils import NGM
9
9
  from .utils import split_msg
10
- from ..schema import NewScore
11
10
  from ..database import UserData
12
- from ..api import osu_api, get_users
11
+ from ..api import get_user_scores, get_users
13
12
  from ..exceptions import NetworkError
14
13
  from ..draw.score import cal_score_info
15
14
  from ..draw.echarts import draw_bpa_plot
@@ -35,17 +34,11 @@ async def _(event: Event, state: T_State):
35
34
  uid = state["user"]
36
35
  lazer_mode = "lazer模式下" if state["is_lazer"] else "stable模式下"
37
36
  try:
38
- bp_info = await osu_api(
39
- "bp",
40
- uid,
41
- NGM[state["mode"]],
42
- legacy_only=int(not user.lazer_mode),
43
- )
37
+ score_ls = await get_user_scores(uid, NGM[state["mode"]], "best", legacy_only=not state["is_lazer"])
44
38
  except NetworkError as e:
45
39
  await UniMessage.text(
46
40
  f"在查找用户:{state['username']} {NGM[state['mode']]}模式 {lazer_mode}时 {str(e)}"
47
41
  ).finish(reply_to=True)
48
- score_ls = [NewScore(**i) for i in bp_info]
49
42
  for score in score_ls:
50
43
  for mod in score.mods:
51
44
  if mod.acronym == "DT" or mod.acronym == "NC":
@@ -106,6 +99,6 @@ async def _(event: Event, state: T_State):
106
99
  mapper_pp_data.append({"name": user_dic.get(mapper, ""), "value": round(pp, 2)})
107
100
  if len(mapper_pp_data) > 20:
108
101
  mapper_pp_data = mapper_pp_data[:20]
109
- name = f"{score_ls[0].user.username} {NGM[state['mode']]} 模式 "
102
+ name = f"{state['username']} {NGM[state['mode']]} 模式 "
110
103
  byt = await draw_bpa_plot(name, pp_ls, length_ls, pp_data, mapper_pp_data)
111
104
  await UniMessage.image(raw=byt).finish(reply_to=True)
@@ -16,6 +16,8 @@ from nonebot.internal.rule import Rule, Event
16
16
  from nonebot_plugin_alconna import At, UniMsg, UniMessage
17
17
  from nonebot_plugin_session import SessionId, SessionIdType
18
18
 
19
+ from ..draw.taiko_preview import parse_map, map_to_image
20
+ from ..schema.score import UnifiedScore
19
21
  from ..utils import NGM
20
22
  from ..info import get_bg
21
23
  from .utils import split_msg
@@ -23,7 +25,7 @@ from ..schema import NewScore
23
25
  from ..exceptions import NetworkError
24
26
  from ..database.models import UserData
25
27
  from ..mania import generate_preview_pic
26
- from ..api import osu_api, safe_async_get
28
+ from ..api import safe_async_get, get_user_scores
27
29
  from ..file import map_path, download_tmp_osu
28
30
  from ..draw.catch_preview import draw_cath_preview
29
31
 
@@ -46,23 +48,27 @@ data_path = Path() / "data" / "osu"
46
48
  pcm_path = data_path / "out.pcm"
47
49
 
48
50
 
49
- async def get_random_beatmap_set(binded_id, group_id, ttl=10) -> (NewScore, str):
50
- if ttl == 0:
51
- return
52
- selected_user = random.choice(binded_id)
53
- if not selected_user:
54
- return
55
- user = await UserData.filter(user_id=selected_user).first()
56
- try:
57
- bp_info = await osu_api("bp", user.osu_id, NGM[str(user.osu_mode)])
58
- except NetworkError:
59
- return await get_random_beatmap_set(binded_id, group_id, ttl - 1)
60
- selected_score = random.choice([NewScore(**i) for i in bp_info])
61
- if selected_score.beatmapset.id not in guess_song_cache[group_id]:
62
- guess_song_cache[group_id].add(selected_score.beatmapset.id)
63
- else:
64
- return await get_random_beatmap_set(binded_id, group_id, ttl - 1)
65
- return selected_score, user.osu_name
51
+ async def get_random_beatmap_set(binded_id, group_id) -> (UnifiedScore, str):
52
+ # 获取已猜过的歌曲集合
53
+ guessed_songs = guess_song_cache[group_id]
54
+ available_scores = []
55
+ for user_id in binded_id:
56
+ try:
57
+ user = await UserData.filter(user_id=user_id).first()
58
+ if not user:
59
+ continue
60
+ bp_info = await get_user_scores(user.osu_id, NGM[str(user.osu_mode)], "best")
61
+ # 过滤掉已猜过的歌曲
62
+ unguessed_scores = [(score, user.osu_name) for score in bp_info if score.beatmapset.id not in guessed_songs]
63
+ available_scores.extend(unguessed_scores)
64
+ except NetworkError:
65
+ continue # 跳过网络错误的用户,继续尝试其他用户
66
+ if not available_scores:
67
+ return None, None # 所有歌曲都被猜过了
68
+ # 随机选择一个未猜过的成绩
69
+ selected_score, osu_name = random.choice(available_scores)
70
+ guess_song_cache[group_id].add(selected_score.beatmapset.id)
71
+ return selected_score, osu_name
66
72
 
67
73
 
68
74
  @guess_audio.handle(parameterless=[split_msg()])
@@ -89,12 +95,11 @@ async def _(
89
95
  if not user_data:
90
96
  await UniMessage.text("该用户未绑定osu账号").finish(reply_to=True)
91
97
  try:
92
- bp_info = await osu_api("bp", user_data.osu_id, NGM[state["mode"]])
98
+ bp_ls = await get_user_scores(user_data.osu_id, NGM[state["mode"]], "best")
93
99
  except NetworkError as e:
94
100
  await UniMessage.text(f"在查找用户:{state['username']} {NGM[state['mode']]}模式bp时 {str(e)}").finish(
95
101
  reply_to=True
96
102
  )
97
- bp_ls = [NewScore(**i) for i in bp_info]
98
103
  filtered_bp_ls = [i for i in bp_ls if i.beatmapset.id not in guess_song_cache[group_id]]
99
104
  if not filtered_bp_ls:
100
105
  await UniMessage.text(state["username"] + "的bp已经被你们猜过一遍了 -_-").finish(reply_to=True)
@@ -103,12 +108,11 @@ async def _(
103
108
  selected_user = user_data.osu_name
104
109
  elif state["user"]:
105
110
  try:
106
- bp_info = await osu_api("bp", state["user"], NGM[state["mode"]])
111
+ bp_ls = await get_user_scores(state["user"], NGM[state["mode"]], "best")
107
112
  except NetworkError as e:
108
113
  await UniMessage.text(f"在查找用户:{state['username']} {NGM[state['mode']]}模式bp时 {str(e)}").finish(
109
114
  reply_to=True
110
115
  )
111
- bp_ls = [NewScore(**i) for i in bp_info]
112
116
  filtered_bp_ls = [i for i in bp_ls if i.beatmapset.id not in guess_song_cache[group_id]]
113
117
  if not filtered_bp_ls:
114
118
  await UniMessage.text(state["username"] + "的bp已经被你们猜过一遍了 -_-").finish(reply_to=True)
@@ -354,12 +358,11 @@ async def _(
354
358
  if not user_data:
355
359
  await UniMessage.text("该用户未绑定osu账号").finish(reply_to=True)
356
360
  try:
357
- bp_info = await osu_api("bp", user_data.osu_id, NGM[state["mode"]])
361
+ bp_ls = await get_user_scores(user_data.osu_id, NGM[state["mode"]], "best")
358
362
  except NetworkError as e:
359
363
  await UniMessage.text(f"在查找用户:{state['username']} {NGM[state['mode']]}模式bp时 {str(e)}").finish(
360
364
  reply_to=True
361
365
  )
362
- bp_ls = [NewScore(**i) for i in bp_info]
363
366
  filtered_bp_ls = [i for i in bp_ls if i.beatmapset.id not in guess_song_cache[session_id]]
364
367
  if not filtered_bp_ls:
365
368
  await UniMessage.text(state["username"] + "的bp已经被你们猜过一遍了 -_-").finish(reply_to=True)
@@ -368,12 +371,11 @@ async def _(
368
371
  selected_user = user_data.osu_name
369
372
  elif state["user"]:
370
373
  try:
371
- bp_info = await osu_api("bp", state["user"], NGM[state["mode"]])
374
+ bp_ls = await get_user_scores(state["user"], NGM[state["mode"]], "best")
372
375
  except NetworkError as e:
373
376
  await UniMessage.text(f"在查找用户:{state['username']} {NGM[state['mode']]}模式bp时 {str(e)}").finish(
374
377
  reply_to=True
375
378
  )
376
- bp_ls = [NewScore(**i) for i in bp_info]
377
379
  filtered_bp_ls = [i for i in bp_ls if i.beatmapset.id not in guess_song_cache[session_id]]
378
380
  if not filtered_bp_ls:
379
381
  await UniMessage.text(state["username"] + "的bp已经被你们猜过一遍了 -_-").finish(reply_to=True)
@@ -422,7 +424,7 @@ async def _(
422
424
  await UniMessage.text("由于未绑定OSU账号,本次随机选择模式进行猜歌\n" + state["error"]).send(reply_to=True)
423
425
  else:
424
426
  mode = state["mode"]
425
- if mode == "0" or mode == "1":
427
+ if mode == "0":
426
428
  await UniMessage.text("该模式暂不支持猜歌").finish(reply_to=True)
427
429
  binded_id = await UserData.filter(osu_mode=mode).values_list("user_id", flat=True)
428
430
  if not binded_id:
@@ -435,12 +437,11 @@ async def _(
435
437
  if not user_data:
436
438
  await UniMessage.text("该用户未绑定osu账号").finish(reply_to=True)
437
439
  try:
438
- bp_info = await osu_api("bp", user_data.osu_id, NGM[state["mode"]])
440
+ bp_ls = await get_user_scores(user_data.osu_id, NGM[state["mode"]], "best")
439
441
  except NetworkError as e:
440
442
  await UniMessage.text(f"在查找用户:{state['username']} {NGM[state['mode']]}模式bp时 {str(e)}").finish(
441
443
  reply_to=True
442
444
  )
443
- bp_ls = [NewScore(**i) for i in bp_info]
444
445
  filtered_bp_ls = [i for i in bp_ls if i.beatmapset.id not in guess_song_cache[session_id]]
445
446
  if not filtered_bp_ls:
446
447
  await UniMessage.text(state["username"] + "的bp已经被你们猜过一遍了 -_-").finish(reply_to=True)
@@ -449,12 +450,11 @@ async def _(
449
450
  guess_song_cache[session_id].add(selected_score.beatmapset.id)
450
451
  elif state["user"]:
451
452
  try:
452
- bp_info = await osu_api("bp", state["user"], NGM[state["mode"]])
453
+ bp_ls = await get_user_scores(state["user"], NGM[state["mode"]], "best")
453
454
  except NetworkError as e:
454
455
  await UniMessage.text(f"在查找用户:{state['username']} {NGM[state['mode']]}模式bp时 {str(e)}").finish(
455
456
  reply_to=True
456
457
  )
457
- bp_ls = [NewScore(**i) for i in bp_info]
458
458
  filtered_bp_ls = [i for i in bp_ls if i.beatmapset.id not in guess_song_cache[session_id]]
459
459
  if not filtered_bp_ls:
460
460
  await UniMessage.text(state["username"] + "的bp已经被你们猜过一遍了 -_-").finish(reply_to=True)
@@ -471,18 +471,18 @@ async def _(
471
471
  chart_set_timeout(matcher, session_id)
472
472
  if mode == "3":
473
473
  osu = await download_tmp_osu(selected_score.beatmap.id)
474
- byt = await generate_preview_pic(osu)
475
- await (
476
- UniMessage.text(f"开始谱面猜歌游戏,猜猜下面谱面的曲名吧,该曲抽选自 {selected_user} 的bp")
477
- + UniMessage.image(raw=byt)
478
- ).finish()
474
+ pic = await generate_preview_pic(osu)
475
+ elif mode == "1":
476
+ osu = await download_tmp_osu(selected_score.beatmap.id)
477
+ beatmap = parse_map(osu)
478
+ pic = map_to_image(beatmap)
479
479
  else:
480
480
  mods = [i.acronym for i in selected_score.mods]
481
481
  pic = await draw_cath_preview(selected_score.beatmap.id, selected_score.beatmapset.id, mods)
482
- await (
483
- UniMessage.text(f"开始谱面猜歌游戏,猜猜下面谱面的曲名吧,该曲抽选自 {selected_user} 的bp")
484
- + UniMessage.image(raw=pic)
485
- ).finish()
482
+ await (
483
+ UniMessage.text(f"开始谱面猜歌游戏,猜猜下面谱面的曲名吧,该曲抽选自 {selected_user} 的bp")
484
+ + UniMessage.image(raw=pic)
485
+ ).finish()
486
486
 
487
487
 
488
488
  @chart_word_matcher.handle()
@@ -33,7 +33,9 @@ def split_msg():
33
33
  state["query"] = []
34
34
  state["target"] = None
35
35
  state["is_lazer"] = True if not user_data else user_data.lazer_mode
36
- arg = arg.extract_plain_text().strip()
36
+ arg = (
37
+ arg.extract_plain_text().strip().replace("=", "=").replace(":", ":").replace("&", "&").replace("#", "#")
38
+ )
37
39
  matches = re.findall(pattern, arg)
38
40
  for match in matches:
39
41
  if match[0]:
@@ -85,9 +87,9 @@ def split_msg():
85
87
  if state["source"] == "ppysb" and not arg.strip():
86
88
  sb_user_data = await SbUserData.get_or_none(user_id=qq)
87
89
  if sb_user_data:
88
- state["user"] = sb_user_data.osu_id if user_data else 0
89
- state["username"] = sb_user_data.osu_name if user_data else ""
90
+ state["user"] = sb_user_data.osu_id
91
+ state["username"] = sb_user_data.osu_name
90
92
  else:
91
- state["error"] = "该账号尚未绑定sb 服务器,请输入 /sbbind 用户名 绑定账号"
93
+ state["error"] = "该账号尚未绑定 sb 服务器,请输入 /sbbind 用户名 绑定账号"
92
94
 
93
95
  return Depends(dependency)
@@ -0,0 +1,30 @@
1
+ import httpx
2
+ import asyncio
3
+ from ..config import Config
4
+ from httpx import AsyncClient
5
+ from nonebot import get_plugin_config
6
+
7
+
8
+ plugin_config = get_plugin_config(Config)
9
+ proxy = plugin_config.osu_proxy
10
+
11
+
12
+ class NetworkManager:
13
+ def __init__(self):
14
+ self._client = None
15
+ self._lock = asyncio.Lock()
16
+
17
+ async def get_client(self) -> AsyncClient:
18
+ if self._client is None:
19
+ async with self._lock:
20
+ if self._client is None:
21
+ self._client = AsyncClient(
22
+ proxy=proxy,
23
+ follow_redirects=True,
24
+ limits=httpx.Limits(max_keepalive_connections=20, max_connections=100, keepalive_expiry=30),
25
+ timeout=httpx.Timeout(30.0),
26
+ )
27
+ return self._client
28
+
29
+
30
+ network_manager = NetworkManager()
Binary file
Binary file
Binary file
Binary file
@@ -113,6 +113,7 @@ class UnifiedBeatmap(Base):
113
113
  ar: float
114
114
  hp: float
115
115
  stars: float
116
+ user_id: int
116
117
 
117
118
 
118
119
  class UnifiedScore(Base):
@@ -127,3 +128,4 @@ class UnifiedScore(Base):
127
128
  statistics: NewStatistics
128
129
  beatmap: Optional[UnifiedBeatmap] = None
129
130
  passed: bool
131
+ pp: Optional[float] = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nonebot-plugin-osubot
3
- Version: 6.20.1
3
+ Version: 6.22.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=8GXbumU7_1D0gyYuGtjHgNT82rSXKZUV-WG5mpUubYE,16336
2
+ nonebot_plugin_osubot/api.py,sha256=-KTefbeRJJwXD0I2hywJfD3ogpLyrrphJoZGoru_3U4,16410
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
@@ -17,7 +17,7 @@ nonebot_plugin_osubot/draw/catch_preview_templates/js/beatmap/timingpoint.js,sha
17
17
  nonebot_plugin_osubot/draw/catch_preview_templates/js/catch/LegacyRandom.js,sha256=HDiks3FCVQBtVK5VzN7YV9Ekkk_WHYi0fPk3-XCQaj8,2155
18
18
  nonebot_plugin_osubot/draw/catch_preview_templates/js/catch/PalpableCatchHitObject.js,sha256=CnUBiIkAqFUaCrc5TXRtL_fuK20NeK1yMti1Sp4dJAE,5702
19
19
  nonebot_plugin_osubot/draw/catch_preview_templates/js/catch/bananashower.js,sha256=sPISMifUWVXK9nXuOD7JUPeXBNPBKquiDNKpYzbSJyg,988
20
- nonebot_plugin_osubot/draw/catch_preview_templates/js/catch/catch.js,sha256=MnYorETgLMjXJ8X0uPNyapD3m0KHHzHdoPnkUcBU374,34430
20
+ nonebot_plugin_osubot/draw/catch_preview_templates/js/catch/catch.js,sha256=uLbMrZBZfvj1YxJVIeirFWVVJ2apLPfB1VY_KHq-hSE,34561
21
21
  nonebot_plugin_osubot/draw/catch_preview_templates/js/catch/fruit.js,sha256=ABvFMbmXP0t5wACUhSVbu8sVJ845mWJ9sWiQJtMV5co,437
22
22
  nonebot_plugin_osubot/draw/catch_preview_templates/js/catch/juicestream.js,sha256=kWjmPtT_UgnUlg5G-_8q-1AyNTv6GLRp0bOOfiMcojc,7347
23
23
  nonebot_plugin_osubot/draw/catch_preview_templates/js/fakeaudio.js,sha256=gJrdVwNrAVjWx80z_XkzybL9ldfV97nKl7k1v7NulBg,1211
@@ -36,20 +36,20 @@ nonebot_plugin_osubot/draw/catch_preview_templates/js/standard/spinner.js,sha256
36
36
  nonebot_plugin_osubot/draw/catch_preview_templates/js/util.js,sha256=XveUlX-d0vrUnXaGbR8y428s6Nw2zhDR235pFko_MxM,1504
37
37
  nonebot_plugin_osubot/draw/catch_preview_templates/js/viewbox.js,sha256=RPsUyoKNAQub2n7oUGwWyFppZu0iVuy6DD5gggQZS3E,1641
38
38
  nonebot_plugin_osubot/draw/catch_preview_templates/js/zip.min.js,sha256=pQmrVxuGCMeLS-XS3QsVhzjwap28fr9Ya083uPlg1sM,86405
39
- nonebot_plugin_osubot/draw/catch_preview_templates/pic.html,sha256=kmwzKgqWzWRPAlg0eqVibSZpC9NVo-U9gGrxRl3D6m0,3086
39
+ nonebot_plugin_osubot/draw/catch_preview_templates/pic.html,sha256=jwPxxyYFLGpbD29h8u_j3LBY33ya7BS2R_t95EldAmk,3092
40
40
  nonebot_plugin_osubot/draw/echarts.py,sha256=OzL_397yWrDAIXsEWf72hAiqdIUBz1D182cli6ORD6s,1037
41
41
  nonebot_plugin_osubot/draw/info.py,sha256=i2YcJmSdTpwhZl_nDe7GJv4jQTB8_9oBfpq2Zw2hwo0,11162
42
42
  nonebot_plugin_osubot/draw/map.py,sha256=4M8xRd0dIbC5g1s8I4eTZ3vRglM6r_TSznFOZ62K2wk,8654
43
43
  nonebot_plugin_osubot/draw/match_history.py,sha256=GBJl6lAA27U7NSMC2isEzD_YsoIPAeG6ijDu7Oflcl0,997
44
44
  nonebot_plugin_osubot/draw/rating.py,sha256=pA7mGLI4IujmYB6kQf_tSkR7mZGpUAVLRLyaAsZhqTM,20397
45
- nonebot_plugin_osubot/draw/score.py,sha256=Y2-BUuKYgYoXk-Uq0PFNg3WaU_SUfSB6Ebo446xslUg,27553
46
- nonebot_plugin_osubot/draw/static.py,sha256=2rznsXZTcKggQ5JSpsJg4y6uWP4e-Y40U7v_QAwLT4Q,3969
45
+ nonebot_plugin_osubot/draw/score.py,sha256=z7HNBOFVHaarbeo2FK8vIpoGVhu47E2BgCqG_axTwSo,29610
46
+ nonebot_plugin_osubot/draw/static.py,sha256=wdlzNO3xyiauKiMLr_h-B9uAsFU7fX_Y-fOusYKZP3k,4132
47
47
  nonebot_plugin_osubot/draw/taiko_preview.py,sha256=tqhuHSdxUJEuXqKHMJDeSLdusuJhSnMMiaG5FbUnaJw,11441
48
- nonebot_plugin_osubot/draw/templates/bpa_chart.html,sha256=cnpM0qRvvyCMTRP-mIOABQlaaqxQwG5kLUxlo4h7F7w,7012
48
+ nonebot_plugin_osubot/draw/templates/bpa_chart.html,sha256=R-fO46HtmC4y3brMFGz6MQkna8ke7WGHVIgUnvcY248,7012
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=LZVDmrQizVwVFX0kDJISYYO68jmZbn5Xz40gV5bj8bo,15276
52
+ nonebot_plugin_osubot/draw/utils.py,sha256=6QDbByPQZCxI0k_i5MsExyWZ-sHgJUw6nEWLv85IgLY,15826
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
@@ -59,9 +59,9 @@ nonebot_plugin_osubot/mania/__init__.py,sha256=XRPn563jDJiPohFekcoFFCqCJYznb-6uO
59
59
  nonebot_plugin_osubot/matcher/__init__.py,sha256=yID7QcdQF6_Mouwbej3JwYUBbKSU3VQdrjq6B1Fz9P8,1331
60
60
  nonebot_plugin_osubot/matcher/bind.py,sha256=QQJc2S7XFo5tu4CPloIET6fKaeiQixgb8M7QvULV6E0,2834
61
61
  nonebot_plugin_osubot/matcher/bp.py,sha256=GidJfuZ9lJ7LwMq126DDOwMksNUOz4Bkab83OlKg8t8,3983
62
- nonebot_plugin_osubot/matcher/bp_analyze.py,sha256=S1dhYiGqtPQapJt4PB5mNKfpPbOpVNAqDIToNEJVpSY,3949
62
+ nonebot_plugin_osubot/matcher/bp_analyze.py,sha256=L2ccAi7TQvpD036DyA27D8wuiqBBkcfLT0ldk_Apv8w,3820
63
63
  nonebot_plugin_osubot/matcher/getbg.py,sha256=Ar2yIST556MYRxzuXCLSDAMAmESRENN1fCty-kdH7PI,699
64
- nonebot_plugin_osubot/matcher/guess.py,sha256=iE5oRZ3mDTC_wcjLJTyyAWlmnDzkAgzGN5rvoTIn0uM,24093
64
+ nonebot_plugin_osubot/matcher/guess.py,sha256=QmD-a88pnPfSSBAvSpD2tlHDGMRpYWqtxfNuLsHuBYM,24144
65
65
  nonebot_plugin_osubot/matcher/history.py,sha256=ZYkVJHdXuVJmhovRhwxFdqNp0o2uJJOACAZhhutyS3w,1577
66
66
  nonebot_plugin_osubot/matcher/info.py,sha256=8CJHTOMTx_nzJ4ZwXo6ZfBwCuO3DtLprRX7jnMtPilk,858
67
67
  nonebot_plugin_osubot/matcher/map.py,sha256=sFpOoFv63y-NOkCJhE6aW0DRYDl_8SoQOPsdq50QxT0,1404
@@ -80,11 +80,12 @@ nonebot_plugin_osubot/matcher/score.py,sha256=Nk6dpDlszKJKdboTSQRBf-wMGioHIPqKSV
80
80
  nonebot_plugin_osubot/matcher/update.py,sha256=MHpxoJmU0hKW82XuM9YpyCxUUjjiNKwejnRgYwueR4Q,3168
81
81
  nonebot_plugin_osubot/matcher/update_mode.py,sha256=0Wy6Y1-rN7XcqBZyo37mYFdixq-4HxCwZftUaiYhZqE,1602
82
82
  nonebot_plugin_osubot/matcher/url_match.py,sha256=opx4DYSQ83tk0qlMOBirl4TC49WXHgMZ04lMr9NCHNs,746
83
- nonebot_plugin_osubot/matcher/utils.py,sha256=BUAorxXrWioiMzR0nZ8NUeaV48ZGUDe4FXLWxKT72g4,4240
83
+ nonebot_plugin_osubot/matcher/utils.py,sha256=gWmNa31wUxcY_PNSNLy348x5_7sTY9ttMKH-5V5jkuE,4304
84
84
  nonebot_plugin_osubot/mods.py,sha256=vxIWYX0HwTxetPAHWZK5ojEMfqV9HFlWT0YC4Yncgb8,1402
85
85
  nonebot_plugin_osubot/network/__init__.py,sha256=WOijcd81yhnpGKYeiDIOxbBDVI12GHPRGoOFfxrUuQk,61
86
86
  nonebot_plugin_osubot/network/auto_retry.py,sha256=vDfYGbEVPF6WZLYXmRVkNvaxf6_6RyIqEAcA7TRwV_k,565
87
87
  nonebot_plugin_osubot/network/first_response.py,sha256=zETRc6g0AG8ExLyHZTLUl7uzUCdUVIL0IfxvdEtCPt0,932
88
+ nonebot_plugin_osubot/network/manager.py,sha256=x0GI1cFv3m3ZIS4oNJed197PaRo8_Vut_2J7m9ySV30,858
88
89
  nonebot_plugin_osubot/osufile/Best Performance.png,sha256=qBNeZcym5vIqyE23K62ohjVBEPCjlNP9wQgXaT20XyY,704
89
90
  nonebot_plugin_osubot/osufile/History Score.jpg,sha256=yv3-GaJ7sBAbAPMFlUeoyg1PzMhv31Ip5bC4H0qJfSA,836
90
91
  nonebot_plugin_osubot/osufile/beatmapinfo.png,sha256=QQjMIGgAdvH6VKgHrrCZWasawsBb95erij4a_B0gjwA,30694
@@ -372,10 +373,10 @@ nonebot_plugin_osubot/osufile/mods/SD.png,sha256=JptCZkXv8kOpIksYimWdC7hRJXShL7N
372
373
  nonebot_plugin_osubot/osufile/mods/SO.png,sha256=qKPoP-LXNU3nSVn8e7z2d6LddFOG-v4gbBEUOb6BtXU,1493
373
374
  nonebot_plugin_osubot/osufile/mods/TD.png,sha256=ZAprUMXNcqs_tXPF0HU34qa_NUZo6U-NemMjVxBOk10,1309
374
375
  nonebot_plugin_osubot/osufile/mods/V2.png,sha256=Y3ju7KMECnGiECRM1MnZUuuwQqbflkdz6I7XjCCTw2E,1354
375
- nonebot_plugin_osubot/osufile/pfm_ctb.png,sha256=UYiDqal_wQHJxqOGYrXNs587FZg6qGALAA6OaQaqPws,44488
376
- nonebot_plugin_osubot/osufile/pfm_mania.png,sha256=YnLEs9SUivR8BfTZQDyGZXQJMYLIB_v8KuA9qbuysMQ,45512
377
- nonebot_plugin_osubot/osufile/pfm_std.png,sha256=_H7i_QKzwkDH70avZLvn7dqcldRpKBHH_Y8AkflmNTI,47809
378
- nonebot_plugin_osubot/osufile/pfm_taiko.png,sha256=ds3C71ZKGiXd6LgxuLfrKxDrfPbzrbMddmh9IwLW3tk,44271
376
+ nonebot_plugin_osubot/osufile/pfm_ctb.png,sha256=9JJu8J1gR_nQhb1EWOuJUOjNitYK_gcH6MRGiYtvsGI,53451
377
+ nonebot_plugin_osubot/osufile/pfm_mania.png,sha256=PKlAezcKALo0UMnrYvTCL3pd-oJNEU76qLVPhfDLFdk,52208
378
+ nonebot_plugin_osubot/osufile/pfm_std.png,sha256=ts7e7IUeoalLuw0Pww0X3trXUpZwnWRsOqdQ1RzcdFM,57032
379
+ nonebot_plugin_osubot/osufile/pfm_taiko.png,sha256=7OAhXVfL_j7pZ4MZAXexgSMNmUyVNsvvq1Ij2XzFEgc,55756
379
380
  nonebot_plugin_osubot/osufile/ranking/ranking-A.png,sha256=nA4kI67IbnC-gfu1pCnPupHC45Uoqa6LYmJKawzV_L0,4364
380
381
  nonebot_plugin_osubot/osufile/ranking/ranking-B.png,sha256=KJCEGr7OkQ12rqng6_JL9r6qLTAclPO_PtHHmLFQooY,4214
381
382
  nonebot_plugin_osubot/osufile/ranking/ranking-C.png,sha256=SsIWOTOymgMze521v4G_rdqL6QqpE64R1dhxpmhzBAc,3862
@@ -389,6 +390,7 @@ nonebot_plugin_osubot/osufile/work/bmap.png,sha256=Uc2znhA_SQ5Zc5MxdtIlnK2YlkhQS
389
390
  nonebot_plugin_osubot/osufile/work/center.png,sha256=AJkhRnPCv1aYPg5nLV2ZCtDGo3lPzP3orT62DvS3c_4,83
390
391
  nonebot_plugin_osubot/osufile/work/left.png,sha256=wJtK_kGmtRumI3F-vK0cl2Q2N1v7DB5bc9qofe4LyqI,128
391
392
  nonebot_plugin_osubot/osufile/work/right.png,sha256=WKkaefFr5C_pdm9mFMyYMUS_BNzMPATmqlvebVU36OY,136
393
+ nonebot_plugin_osubot/osufile/work/stardiff.png,sha256=mBPOspsqNr1FkDolzTpnyIz-8NIKfrYn_dXi_eMS34s,156
392
394
  nonebot_plugin_osubot/osufile/work/stars.png,sha256=zifMmxxjOGzSZAm1tr-3ky_yy18p1bbjgkl3zYRMcp4,305
393
395
  nonebot_plugin_osubot/osufile/work/stars_expertplus.png,sha256=cmfpErs-flPxHEhVmS9H8vy8H7mrChvV_Q4A77oSVFY,517
394
396
  nonebot_plugin_osubot/osufile/work/suppoter.png,sha256=4V4eNhB8_8KIXsQ1jzFyHUkicKPb5LPehoIcri4h8E4,16061
@@ -400,9 +402,9 @@ nonebot_plugin_osubot/schema/beatmap.py,sha256=UnobfZEHq1V2HG-A4j3BECubO-dB1JzTM
400
402
  nonebot_plugin_osubot/schema/match.py,sha256=lR3pGNVR9K_5GZAdOLG6Ki-W3fwJvgMlNhYOzKNE3lg,494
401
403
  nonebot_plugin_osubot/schema/ppysb/__init__.py,sha256=JK2Z4n44gUJPVKdETMJYJ5uIw-4a8T50y6j5n-YrlYM,1375
402
404
  nonebot_plugin_osubot/schema/sayo_beatmap.py,sha256=lS1PQZ-HvHl0VhkzlI0-pNLeJrLYWVqmKAo6xZr5I2U,959
403
- nonebot_plugin_osubot/schema/score.py,sha256=zHU-w2e7RzMDL8vdPkX5vggcqalBo83JTvu96abcflo,3124
405
+ nonebot_plugin_osubot/schema/score.py,sha256=Qtvs5Hfvw5m2b00e8ZjrcrLGDyL4lqDspRwrkORY-xU,3172
404
406
  nonebot_plugin_osubot/schema/user.py,sha256=sxNmqymG_kIVuGuzfchSv9UML6NPG70cqo2_h5xDIpM,2250
405
407
  nonebot_plugin_osubot/utils/__init__.py,sha256=pyv8XxBcCOeQVDj1E4dgvktzcefgQXfKBlarsYGx1sg,815
406
- nonebot_plugin_osubot-6.20.1.dist-info/WHEEL,sha256=B19PGBCYhWaz2p_UjAoRVh767nYQfk14Sn4TpIZ-nfU,87
407
- nonebot_plugin_osubot-6.20.1.dist-info/METADATA,sha256=cosRoPpyiqrB686g_vBWg7JhlUs9jQ7CGFVvGZonDzU,4476
408
- nonebot_plugin_osubot-6.20.1.dist-info/RECORD,,
408
+ nonebot_plugin_osubot-6.22.0.dist-info/WHEEL,sha256=B19PGBCYhWaz2p_UjAoRVh767nYQfk14Sn4TpIZ-nfU,87
409
+ nonebot_plugin_osubot-6.22.0.dist-info/METADATA,sha256=VsdL1bH40h2QTlFxd3MxYe48g5QgvzFSClziBS9u5EA,4476
410
+ nonebot_plugin_osubot-6.22.0.dist-info/RECORD,,