nonebot-plugin-R6States 1.1.0__tar.gz → 1.1.1__tar.gz

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.
Files changed (15) hide show
  1. {nonebot_plugin_r6states-1.1.0 → nonebot_plugin_r6states-1.1.1}/PKG-INFO +18 -29
  2. {nonebot_plugin_r6states-1.1.0 → nonebot_plugin_r6states-1.1.1}/README.md +17 -28
  3. {nonebot_plugin_r6states-1.1.0 → nonebot_plugin_r6states-1.1.1}/pyproject.toml +1 -1
  4. {nonebot_plugin_r6states-1.1.0 → nonebot_plugin_r6states-1.1.1}/src/nonebot_plugin_R6States/__init__.py +5 -1
  5. {nonebot_plugin_r6states-1.1.0 → nonebot_plugin_r6states-1.1.1}/src/nonebot_plugin_R6States/config.py +4 -0
  6. {nonebot_plugin_r6states-1.1.0 → nonebot_plugin_r6states-1.1.1}/src/nonebot_plugin_R6States/formatter.py +17 -1
  7. {nonebot_plugin_r6states-1.1.0 → nonebot_plugin_r6states-1.1.1}/src/nonebot_plugin_R6States/r6data.py +1 -25
  8. {nonebot_plugin_r6states-1.1.0 → nonebot_plugin_r6states-1.1.1}/src/nonebot_plugin_R6States/renderer.py +8 -1
  9. {nonebot_plugin_r6states-1.1.0 → nonebot_plugin_r6states-1.1.1}/src/nonebot_plugin_R6States/service.py +7 -4
  10. {nonebot_plugin_r6states-1.1.0 → nonebot_plugin_r6states-1.1.1}/LICENSE +0 -0
  11. {nonebot_plugin_r6states-1.1.0 → nonebot_plugin_r6states-1.1.1}/src/nonebot_plugin_R6States/assets/MonaSans.ttf +0 -0
  12. {nonebot_plugin_r6states-1.1.0 → nonebot_plugin_r6states-1.1.1}/src/nonebot_plugin_R6States/assets/NotoSansSC.ttf +0 -0
  13. {nonebot_plugin_r6states-1.1.0 → nonebot_plugin_r6states-1.1.1}/src/nonebot_plugin_R6States/cache.py +0 -0
  14. {nonebot_plugin_r6states-1.1.0 → nonebot_plugin_r6states-1.1.1}/src/nonebot_plugin_R6States/config_mannger.py +0 -0
  15. {nonebot_plugin_r6states-1.1.0 → nonebot_plugin_r6states-1.1.1}/src/nonebot_plugin_R6States/storage.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nonebot-plugin-R6States
3
- Version: 1.1.0
3
+ Version: 1.1.1
4
4
  Summary: 查询指定玩家的各项数据
5
5
  Author-Email: Siornya <wxh200607@outlook.com>
6
6
  License: MIT
@@ -15,53 +15,42 @@ Description-Content-Type: text/markdown
15
15
 
16
16
  一个基于 **NoneBot2** 的《彩虹六号:围攻》战绩查询插件
17
17
 
18
- ---
19
-
20
18
  ## 功能特性
21
19
 
22
20
  * ✅ 通过 QQ 指令查询 R6 玩家战绩
23
21
  * ✅ 支持 **单人查询 / 多人查询**
24
22
  * ✅ 数据源自 [R6Data API](https://r6data.com/)
25
- * ✅ 玩家数据缓存半小时,降低 API 用量
23
+ * ✅ 玩家数据缓存,降低 API 用量
26
24
  * ✅ 数据分析功能
27
25
  * ⚙️ 地图筛选 `-m / --map`
28
26
 
29
- ---
30
-
31
- ## 参考运行环境
32
-
33
- * **Python 3.12**
34
- * **NoneBot2**
35
- * **OneBot v11**
36
- * **NapCat(反向 WebSocket)**
37
-
38
- ---
39
-
40
27
  ## Usage 使用说明
41
28
 
42
29
  ### 安装
43
30
 
44
- 可以直接将 `nonebot_plugin_R6States` 文件夹放入插件目录中。
31
+ - (推荐)使用nb安装`nb plugin install nonebot-plugin-R6States`
32
+ - 使用pip安装`pip install nonebot-plugin-R6States`
33
+ - 下载release放到`plugins`文件夹中
45
34
 
46
- ### 数据查询
35
+ ### 指令
47
36
 
48
- ```text
49
- /R6 <player_ids...>
50
- ```
37
+ 数据查询:`/R6 <player_ids...>`
38
+ 其他指令与帮助信息:`/R6help`
51
39
 
52
- ### 帮助信息
40
+ ## 环境配置
53
41
 
54
- ```text
55
- /R6help
56
- ```
42
+ CURRENT_SEASON = "Y11S2"
57
43
 
58
- ### 额外获取地图信息
44
+ R6_OUTPUT_IMAGE = True
59
45
 
60
- ```text
61
- -m / --map <map_name>
62
- ```
46
+ R6_CACHE_MINUTES = 45
47
+
48
+ ## 参考运行环境
63
49
 
64
- ---
50
+ * **Python 3.12**
51
+ * **NoneBot2**
52
+ * **OneBot v11**
53
+ * **NapCat(反向 WebSocket)**
65
54
 
66
55
  ## 特别提醒
67
56
 
@@ -2,53 +2,42 @@
2
2
 
3
3
  一个基于 **NoneBot2** 的《彩虹六号:围攻》战绩查询插件
4
4
 
5
- ---
6
-
7
5
  ## 功能特性
8
6
 
9
7
  * ✅ 通过 QQ 指令查询 R6 玩家战绩
10
8
  * ✅ 支持 **单人查询 / 多人查询**
11
9
  * ✅ 数据源自 [R6Data API](https://r6data.com/)
12
- * ✅ 玩家数据缓存半小时,降低 API 用量
10
+ * ✅ 玩家数据缓存,降低 API 用量
13
11
  * ✅ 数据分析功能
14
12
  * ⚙️ 地图筛选 `-m / --map`
15
13
 
16
- ---
17
-
18
- ## 参考运行环境
19
-
20
- * **Python 3.12**
21
- * **NoneBot2**
22
- * **OneBot v11**
23
- * **NapCat(反向 WebSocket)**
24
-
25
- ---
26
-
27
14
  ## Usage 使用说明
28
15
 
29
16
  ### 安装
30
17
 
31
- 可以直接将 `nonebot_plugin_R6States` 文件夹放入插件目录中。
18
+ - (推荐)使用nb安装`nb plugin install nonebot-plugin-R6States`
19
+ - 使用pip安装`pip install nonebot-plugin-R6States`
20
+ - 下载release放到`plugins`文件夹中
32
21
 
33
- ### 数据查询
22
+ ### 指令
34
23
 
35
- ```text
36
- /R6 <player_ids...>
37
- ```
24
+ 数据查询:`/R6 <player_ids...>`
25
+ 其他指令与帮助信息:`/R6help`
38
26
 
39
- ### 帮助信息
27
+ ## 环境配置
40
28
 
41
- ```text
42
- /R6help
43
- ```
29
+ CURRENT_SEASON = "Y11S2"
44
30
 
45
- ### 额外获取地图信息
31
+ R6_OUTPUT_IMAGE = True
46
32
 
47
- ```text
48
- -m / --map <map_name>
49
- ```
33
+ R6_CACHE_MINUTES = 45
34
+
35
+ ## 参考运行环境
50
36
 
51
- ---
37
+ * **Python 3.12**
38
+ * **NoneBot2**
39
+ * **OneBot v11**
40
+ * **NapCat(反向 WebSocket)**
52
41
 
53
42
  ## 特别提醒
54
43
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nonebot-plugin-R6States"
3
- version = "1.1.0"
3
+ version = "1.1.1"
4
4
  description = "查询指定玩家的各项数据"
5
5
  authors = [
6
6
  { name = "Siornya", email = "wxh200607@outlook.com" },
@@ -104,9 +104,13 @@ async def _(event: MessageEvent, args: Message = CommandArg()):
104
104
  await r6.send(f"⚠️ 当前 API Key 已设置 {age:.0f} 天,可能已过期,如查询失败请 /r6key 重设")
105
105
 
106
106
  # 并发取数(单个失败不连累其余),再按原顺序逐个发送
107
+ ttl = plugin_config.r6_cache_minutes * 60
107
108
  results = await asyncio.gather(
108
109
  *(
109
- get_full_stats(pid, scopes, platform, season_year=plugin_config.current_season)
110
+ get_full_stats(
111
+ pid, scopes, platform,
112
+ season_year=plugin_config.current_season, ttl=ttl,
113
+ )
110
114
  for pid in tokens
111
115
  ),
112
116
  return_exceptions=True,
@@ -11,3 +11,7 @@ class Config(BaseModel):
11
11
  # 查询结果渲染成图片,失败时自动回退文本
12
12
  # 在 .env 用 R6_OUTPUT_IMAGE 覆盖
13
13
  r6_output_image: bool = True
14
+
15
+ # 玩家数据本地缓存时长(分钟)。越大越省 API 用量,但数据越旧。
16
+ # 在 .env 用 R6_CACHE_MINUTES 覆盖
17
+ r6_cache_minutes: int = 45
@@ -7,11 +7,23 @@ fullStats 顶层有三块:
7
7
  """
8
8
  from __future__ import annotations
9
9
 
10
- from typing import Any
10
+ from typing import Any, Optional
11
+ from datetime import timezone, datetime, timedelta
11
12
 
12
13
  #: 默认展示出场最多的前 N 个干员
13
14
  TOP_N = 8
14
15
 
16
+ #: 展示时间用东八区(CN bot)
17
+ _CST = timezone(timedelta(hours=8))
18
+
19
+
20
+ def fetched_label(data: dict[str, Any]) -> Optional[str]:
21
+ """数据实际取回时刻(随缓存保存),格式 '更新 06-14 17:30';无则返回 None。"""
22
+ ts = data.get("_fetched_at")
23
+ if not ts:
24
+ return None
25
+ return "更新 " + datetime.fromtimestamp(ts, _CST).strftime("%m-%d %H:%M")
26
+
15
27
  _SIDE_CN = {"Attacker": "攻", "Defender": "防"}
16
28
  _BOARD_CN = {
17
29
  "ranked": "排位", "casual": "休闲", "standard": "标准",
@@ -103,4 +115,8 @@ def format_full_stats(player_id: str, data: dict[str, Any], top_n: int = TOP_N)
103
115
 
104
116
  if not board_lines and not operators:
105
117
  return f"🎯 {handle}:没有查询到数据"
118
+
119
+ label = fetched_label(data)
120
+ if label:
121
+ lines.append(label)
106
122
  return "\n".join(lines)
@@ -1,11 +1,6 @@
1
1
  """
2
- r6data.py Python 移植版 r6-data.js (Players + Game)
2
+ 对 https://api.r6data.com/api 的封装,从官方 npm `r6-data.js` 移植修改而来。
3
3
 
4
- 对 https://api.r6data.com/api 的薄封装,异步 (httpx)。从官方 npm 包
5
- `r6-data.js` 移植而来;源码层逻辑长期稳定,只有下面 ``易变常量`` 区域会
6
- 随赛季/平台调整,跟新版时基本只动那几行。
7
-
8
- 依赖: pip install httpx
9
4
  用法:
10
5
  from .r6data import R6Client
11
6
 
@@ -13,31 +8,12 @@ r6data.py — Python 移植版 r6-data.js (Players + Game)
13
8
  info = await r6.players.get_account_info("PlayerName", "uplay")
14
9
  ops = await r6.game.get_operators(side="attacker")
15
10
  await r6.aclose()
16
-
17
- NoneBot 启动时检查版本(贴进插件 __init__.py):
18
- from nonebot import get_driver, logger
19
- from .r6data import check_latest_version, BASED_ON_VERSION
20
-
21
- @get_driver().on_startup
22
- async def _r6_version_check():
23
- latest = await check_latest_version()
24
- if latest and latest != BASED_ON_VERSION:
25
- logger.warning(
26
- f"r6data 移植基于 r6-data.js@{BASED_ON_VERSION}, "
27
- f"上游最新为 {latest},建议核对是否有接口变化。"
28
- )
29
11
  """
30
12
 
31
13
  from __future__ import annotations
32
-
33
14
  from typing import Any, Optional, Sequence, Mapping
34
-
35
15
  import httpx
36
16
 
37
- # ──────────────────────────────────────────────────────────────────────────
38
- # 易变常量 —— 跟随上游版本时,基本只需要改这一段
39
- # ──────────────────────────────────────────────────────────────────────────
40
-
41
17
  #: 本移植对标的 r6-data.js 版本。启动时与 npm 最新版比对。
42
18
  BASED_ON_VERSION = "3.1.7"
43
19
 
@@ -6,7 +6,7 @@ from pathlib import Path
6
6
 
7
7
  from PIL import Image, ImageDraw, ImageFont
8
8
 
9
- from .formatter import _format_boards
9
+ from .formatter import _format_boards, fetched_label
10
10
 
11
11
  _ASSETS = Path(__file__).parent / "assets"
12
12
  _MONA = str(_ASSETS / "MonaSans.ttf")
@@ -188,6 +188,13 @@ def render_full_stats(player_id: str, data: dict[str, Any]) -> bytes:
188
188
  _draw(draw, p, y, "没有查询到数据", 22, _GRAY)
189
189
  y += _font(_NOTO, 22).getmetrics()[1]
190
190
 
191
+ # 底部:数据实际取回时间(右下角,小字)
192
+ label = fetched_label(data)
193
+ if label:
194
+ y += 14 * _SCALE + _ascent(14)
195
+ _draw_right(draw, rcol, y, label, 14, _DIM)
196
+ y += _font(_NOTO, 14).getmetrics()[1]
197
+
191
198
  img = img.crop((0, 0, _W, y + _PAD))
192
199
  buf = BytesIO()
193
200
  img.save(buf, format="PNG")
@@ -6,6 +6,7 @@
6
6
  """
7
7
  from __future__ import annotations
8
8
 
9
+ import time
9
10
  from typing import Any, Optional
10
11
 
11
12
  import httpx
@@ -15,9 +16,6 @@ from .storage import PLAYER_CACHE_FILE
15
16
  from .r6data import R6Client, R6APIError
16
17
  from .config_mannger import resolve_apikey
17
18
 
18
- #: 玩家快照属于会变的数据,缓存 45 分钟即可显著降低 API 用量。
19
- FULL_STATS_TTL = 45 * 60
20
-
21
19
  VALID_PLATFORMS = ("uplay", "psn", "xbl")
22
20
 
23
21
  #: 免费申请 api-key 的入口,附在缺/失效提示里
@@ -57,6 +55,8 @@ async def get_full_stats(
57
55
  platform: str = "uplay",
58
56
  season_year: str | None = None,
59
57
  modes: str | None = None,
58
+ *,
59
+ ttl: float,
60
60
  ) -> dict[str, Any]:
61
61
  if platform not in VALID_PLATFORMS:
62
62
  raise ServiceError(f"平台无效,可选:{', '.join(VALID_PLATFORMS)}")
@@ -85,5 +85,8 @@ async def get_full_stats(
85
85
  except Exception as e: # noqa: BLE001 - 网络等异常统一兜底
86
86
  raise ServiceError(f"请求失败:{type(e).__name__}") from e
87
87
 
88
- await _cache.set(cache_key, data, FULL_STATS_TTL)
88
+ # 打上实际取数时间,随数据一起缓存(缓存命中时展示的就是这个原始取数时刻)
89
+ if isinstance(data, dict):
90
+ data["_fetched_at"] = time.time()
91
+ await _cache.set(cache_key, data, ttl)
89
92
  return data