tenko-plugin-bf1 0.1.0__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.
- tenko_plugin_bf1-0.1.0/.gitignore +8 -0
- tenko_plugin_bf1-0.1.0/PKG-INFO +38 -0
- tenko_plugin_bf1-0.1.0/README.md +19 -0
- tenko_plugin_bf1-0.1.0/pyproject.toml +34 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/__init__.py +300 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/OFL-Oswald.txt +93 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/Oswald.ttf +0 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/backgrounds/0.jpg +0 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/backgrounds/1.jpg +0 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/backgrounds/2.jpg +0 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/backgrounds/3.jpg +0 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/backgrounds/4.jpg +0 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/backgrounds/5.jpg +0 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/bfban.webp +0 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/bfeac.webp +0 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/classes/assault.webp +0 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/classes/cavalry.webp +0 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/classes/medic.webp +0 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/classes/pilot.webp +0 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/classes/scout.webp +0 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/classes/support.webp +0 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/classes/tanker.webp +0 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/pages.py +380 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/templates/_base.html +53 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/templates/_macros.html +27 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/templates/items.html +58 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/templates/servers.html +53 -0
- tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/templates/stats.html +201 -0
- tenko_plugin_bf1-0.1.0/tests/conftest.py +6 -0
- tenko_plugin_bf1-0.1.0/tests/test_bf1.py +211 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: tenko-plugin-bf1
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Tenko 战地一查询:经 bf-manager 查询玩家战绩、武器、载具和服务器
|
|
5
|
+
Project-URL: Homepage, https://github.com/g1331/tenko-plugins/tree/main/plugins/bf1
|
|
6
|
+
Project-URL: Repository, https://github.com/g1331/tenko-plugins
|
|
7
|
+
License-Expression: GPL-3.0-only
|
|
8
|
+
Keywords: battlefield,bf1,qq,tenko,tenko-plugin
|
|
9
|
+
Classifier: Framework :: AsyncIO
|
|
10
|
+
Classifier: Natural Language :: Chinese (Simplified)
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Topic :: Communications :: Chat
|
|
13
|
+
Requires-Python: <3.14,>=3.11
|
|
14
|
+
Requires-Dist: httpx>=0.27
|
|
15
|
+
Requires-Dist: jinja2>=3
|
|
16
|
+
Requires-Dist: tenko-api<0.5,>=0.4
|
|
17
|
+
Requires-Dist: zhconv>=1.4
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# 战地一查询
|
|
21
|
+
|
|
22
|
+
经 [bf-manager](https://github.com/g1331/bf-manager) 的公开查询接口查询战地一数据,回复文字。
|
|
23
|
+
|
|
24
|
+
| 命令 | 说明 |
|
|
25
|
+
| --- | --- |
|
|
26
|
+
| `/bf1s <EA 名字>` | 生涯战绩 |
|
|
27
|
+
| `/bf1w <EA 名字>` | 武器击杀前十 |
|
|
28
|
+
| `/bf1v <EA 名字>` | 载具击杀前十 |
|
|
29
|
+
| `/bf1ss <服务器名关键字>` | 搜索有人的私服,显示前十个 |
|
|
30
|
+
|
|
31
|
+
新群默认开启。
|
|
32
|
+
|
|
33
|
+
## 配置
|
|
34
|
+
|
|
35
|
+
```toml
|
|
36
|
+
[plugin_config.bf1]
|
|
37
|
+
base_url = "https://bfm.umrbotech.com" # bf-manager 地址,不带 /api/v1
|
|
38
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# 战地一查询
|
|
2
|
+
|
|
3
|
+
经 [bf-manager](https://github.com/g1331/bf-manager) 的公开查询接口查询战地一数据,回复文字。
|
|
4
|
+
|
|
5
|
+
| 命令 | 说明 |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| `/bf1s <EA 名字>` | 生涯战绩 |
|
|
8
|
+
| `/bf1w <EA 名字>` | 武器击杀前十 |
|
|
9
|
+
| `/bf1v <EA 名字>` | 载具击杀前十 |
|
|
10
|
+
| `/bf1ss <服务器名关键字>` | 搜索有人的私服,显示前十个 |
|
|
11
|
+
|
|
12
|
+
新群默认开启。
|
|
13
|
+
|
|
14
|
+
## 配置
|
|
15
|
+
|
|
16
|
+
```toml
|
|
17
|
+
[plugin_config.bf1]
|
|
18
|
+
base_url = "https://bfm.umrbotech.com" # bf-manager 地址,不带 /api/v1
|
|
19
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "tenko-plugin-bf1"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Tenko 战地一查询:经 bf-manager 查询玩家战绩、武器、载具和服务器"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "GPL-3.0-only"
|
|
7
|
+
requires-python = ">=3.11,<3.14"
|
|
8
|
+
keywords = ["tenko", "tenko-plugin", "qq", "battlefield", "bf1"]
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Framework :: AsyncIO",
|
|
11
|
+
"Natural Language :: Chinese (Simplified)",
|
|
12
|
+
"Programming Language :: Python :: 3",
|
|
13
|
+
"Topic :: Communications :: Chat",
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"tenko-api>=0.4,<0.5",
|
|
17
|
+
"httpx>=0.27",
|
|
18
|
+
"jinja2>=3",
|
|
19
|
+
"zhconv>=1.4",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://github.com/g1331/tenko-plugins/tree/main/plugins/bf1"
|
|
24
|
+
Repository = "https://github.com/g1331/tenko-plugins"
|
|
25
|
+
|
|
26
|
+
[project.entry-points."tenko.plugins"]
|
|
27
|
+
bf1 = "tenko_plugin_bf1"
|
|
28
|
+
|
|
29
|
+
[build-system]
|
|
30
|
+
requires = ["hatchling"]
|
|
31
|
+
build-backend = "hatchling.build"
|
|
32
|
+
|
|
33
|
+
[tool.hatch.build.targets.wheel]
|
|
34
|
+
packages = ["src/tenko_plugin_bf1"]
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
"""战地一查询:战绩、武器、载具和服务器。
|
|
2
|
+
|
|
3
|
+
数据全部来自 bf-manager 的公开查询接口;这里解析命令、取数据,图片排版在 :mod:`.pages`,
|
|
4
|
+
渲染不了时回文字。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
from typing import Any
|
|
11
|
+
from urllib.parse import urlparse
|
|
12
|
+
|
|
13
|
+
import httpx
|
|
14
|
+
from arclet.alconna import Alconna, AllParam, Args, CommandMeta
|
|
15
|
+
from arclet.entari import BasicConfModel, MessageChain, Session, command, plugin
|
|
16
|
+
from arclet.entari.command import Query
|
|
17
|
+
from loguru import logger
|
|
18
|
+
from satori import Image
|
|
19
|
+
from tenko_api.features import declare
|
|
20
|
+
from tenko_api.render import RenderService
|
|
21
|
+
|
|
22
|
+
from . import pages
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Bf1Config(BasicConfModel):
|
|
26
|
+
"""对应 config/tenko.toml 中的 ``[plugin_config.bf1]``。"""
|
|
27
|
+
|
|
28
|
+
base_url: str = "https://bfm.umrbotech.com"
|
|
29
|
+
"""bf-manager 地址,不带 /api/v1。"""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
plugin.metadata(
|
|
33
|
+
"战地一查询",
|
|
34
|
+
author=["13"],
|
|
35
|
+
version="0.1.0",
|
|
36
|
+
description="经 bf-manager 查询战地一玩家战绩、武器、载具和服务器。",
|
|
37
|
+
urls={
|
|
38
|
+
"homepage": "https://github.com/g1331/tenko-plugins/tree/main/plugins/bf1",
|
|
39
|
+
"repository": "https://github.com/g1331/tenko-plugins",
|
|
40
|
+
},
|
|
41
|
+
config=Bf1Config,
|
|
42
|
+
)
|
|
43
|
+
declare(scope="group", default_enabled=True)
|
|
44
|
+
|
|
45
|
+
config = plugin.get_config(Bf1Config)
|
|
46
|
+
|
|
47
|
+
TOP = 10
|
|
48
|
+
ITEM_CARDS = 12
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Bf1Error(Exception):
|
|
52
|
+
"""要原样回给群友的错误。"""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
async def _get(path: str, **params: Any) -> Any:
|
|
56
|
+
# EA 接口慢,战绩和武器常要好几秒。
|
|
57
|
+
url = f"{config.base_url.rstrip('/')}/api/v1/bf1{path}"
|
|
58
|
+
try:
|
|
59
|
+
async with httpx.AsyncClient(timeout=30) as client:
|
|
60
|
+
response = await client.get(url, params=params)
|
|
61
|
+
except httpx.HTTPError as error:
|
|
62
|
+
logger.warning("bf-manager 请求失败:{} {}", url, type(error).__name__)
|
|
63
|
+
raise Bf1Error("查询服务暂时连不上,稍后再试") from error
|
|
64
|
+
if response.status_code >= 400:
|
|
65
|
+
try:
|
|
66
|
+
message = response.json()["error"]["message"]
|
|
67
|
+
except (ValueError, KeyError, TypeError):
|
|
68
|
+
message = f"查询失败({response.status_code})"
|
|
69
|
+
raise Bf1Error(message)
|
|
70
|
+
return response.json()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
async def _persona(name: str) -> dict[str, Any]:
|
|
74
|
+
result = await _get("/players/search", name=name)
|
|
75
|
+
personas = result["personas"]
|
|
76
|
+
if not personas:
|
|
77
|
+
raise Bf1Error(f"没找到玩家 {name}")
|
|
78
|
+
# 重名时 bf-manager 会回填生涯时长,0 是没玩过 BF1 的空号。
|
|
79
|
+
return next((p for p in personas if p.get("time_played_hours")), personas[0])
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _hours(seconds: float | None) -> str:
|
|
83
|
+
return f"{(seconds or 0) / 3600:.1f} 小时"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _kpm(kills: int | None, seconds: float | None) -> str:
|
|
87
|
+
return f"{(kills or 0) / (seconds / 60):.2f}" if seconds else "-"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _n(value: Any) -> Any:
|
|
91
|
+
return "-" if value is None else value
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def format_stats(name: str, stats: dict[str, Any]) -> str:
|
|
95
|
+
s = stats["summary"]
|
|
96
|
+
wins, losses = s.get("wins") or 0, s.get("losses") or 0
|
|
97
|
+
win_rate = f"{wins / (wins + losses):.1%}" if wins + losses else "-"
|
|
98
|
+
kd = f"{s['kd']:.2f}" if s.get("kd") is not None else "-"
|
|
99
|
+
kpm = f"{s['kpm']:.2f}" if s.get("kpm") is not None else "-"
|
|
100
|
+
best = pages.CLASSES.get(s.get("best_class") or "", s.get("best_class") or "-")
|
|
101
|
+
longest = s.get("longest_headshot_meters")
|
|
102
|
+
return "\n".join(
|
|
103
|
+
[
|
|
104
|
+
f"{name} 的战地一战绩",
|
|
105
|
+
f"等级 {_n(s.get('rank'))} | 技巧 {_n(s.get('skill'))} | "
|
|
106
|
+
f"时长 {_hours(s.get('time_played_seconds'))}",
|
|
107
|
+
f"击杀 {_n(s.get('kills'))} | 死亡 {_n(s.get('deaths'))} | KD {kd} | KPM {kpm}",
|
|
108
|
+
f"胜 {wins} | 负 {losses} | 胜率 {win_rate}",
|
|
109
|
+
f"步兵击杀 {_n(s.get('infantry_kills'))} | 载具击杀 {_n(s.get('vehicle_kills'))}",
|
|
110
|
+
f"协助 {_n(s.get('assists'))} | 复活 {_n(s.get('revives'))} | "
|
|
111
|
+
f"治疗 {_n(s.get('heals'))} | 修理 {_n(s.get('repairs'))}",
|
|
112
|
+
f"狗牌 {_n(s.get('dogtags'))} | 最高连杀 {_n(s.get('max_killstreak'))} | "
|
|
113
|
+
f"最远爆头 {f'{longest:.1f} 米' if longest is not None else '-'}",
|
|
114
|
+
f"最佳兵种 {best}",
|
|
115
|
+
]
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def format_weapons(name: str, weapons: list[dict[str, Any]]) -> str:
|
|
120
|
+
top = pages.top_kills(weapons, TOP)
|
|
121
|
+
if not top:
|
|
122
|
+
return f"{name} 还没有武器击杀记录"
|
|
123
|
+
lines = [f"{name} 的武器击杀前 {len(top)}"]
|
|
124
|
+
for index, w in enumerate(top, 1):
|
|
125
|
+
accuracy = f"{w['accuracy']:.1f}%" if w.get("accuracy") is not None else "-"
|
|
126
|
+
headshot = f"{(w.get('headshots') or 0) / w['kills']:.1%}"
|
|
127
|
+
lines.append(
|
|
128
|
+
f"{index}. {pages.cn(w.get('name'))} · {pages.cn(w.get('category')) or '-'}\n"
|
|
129
|
+
f" 击杀 {w['kills']} | KPM {_kpm(w['kills'], w.get('time_seconds'))} | "
|
|
130
|
+
f"命中 {accuracy} | 爆头 {headshot} | {_hours(w.get('time_seconds'))}"
|
|
131
|
+
)
|
|
132
|
+
return "\n".join(lines)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def format_vehicles(name: str, vehicles: list[dict[str, Any]]) -> str:
|
|
136
|
+
top = pages.top_kills(vehicles, TOP)
|
|
137
|
+
if not top:
|
|
138
|
+
return f"{name} 还没有载具击杀记录"
|
|
139
|
+
lines = [f"{name} 的载具击杀前 {len(top)}"]
|
|
140
|
+
for index, v in enumerate(top, 1):
|
|
141
|
+
lines.append(
|
|
142
|
+
f"{index}. {pages.cn(v.get('name'))} · {pages.cn(v.get('category')) or '-'}\n"
|
|
143
|
+
f" 击杀 {v['kills']} | KPM {_kpm(v['kills'], v.get('time_seconds'))} | "
|
|
144
|
+
f"摧毁 {v.get('destroyed') or 0} | {_hours(v.get('time_seconds'))}"
|
|
145
|
+
)
|
|
146
|
+
return "\n".join(lines)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def format_servers(keyword: str, servers: list[dict[str, Any]], total: int) -> str:
|
|
150
|
+
if not servers:
|
|
151
|
+
return f"没找到名字含「{keyword}」的服务器(空服不在列表里)"
|
|
152
|
+
more = f",人最多的 {len(servers)} 个" if total > len(servers) else ""
|
|
153
|
+
lines = [f"「{keyword}」找到 {total} 个服务器{more}:"]
|
|
154
|
+
for index, s in enumerate(servers, 1):
|
|
155
|
+
queue = f"[{s['queue_count']}]" if s.get("queue_count") else ""
|
|
156
|
+
lines.append(
|
|
157
|
+
f"{index}. {s['name']}\n"
|
|
158
|
+
f" {s['player_count']}/{s['max_player_count']}{queue} | "
|
|
159
|
+
f"{s.get('mode_display_name') or '-'}·{s.get('map_display_name') or '-'} | "
|
|
160
|
+
f"{s.get('region_display_name') or '-'} | gameId {s.get('game_id')}"
|
|
161
|
+
)
|
|
162
|
+
return "\n".join(lines)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _source() -> str:
|
|
166
|
+
return urlparse(config.base_url).netloc or config.base_url
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
async def _optional(path: str) -> Any:
|
|
170
|
+
"""封禁、战队、在线状态这类附加信息拿不到就不显示,不影响主战绩。"""
|
|
171
|
+
|
|
172
|
+
try:
|
|
173
|
+
return await _get(path)
|
|
174
|
+
except Bf1Error:
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
async def _reply(service, template: str, context, fallback: str) -> MessageChain | str:
|
|
179
|
+
image = await pages.render(service, template, context)
|
|
180
|
+
return MessageChain(Image.of(raw=image, mime="image/jpeg")) if image else fallback
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
async def stats_reply(name: str, service) -> MessageChain | str:
|
|
184
|
+
persona = await _persona(name)
|
|
185
|
+
pid = persona["persona_id"]
|
|
186
|
+
stats, weapons, vehicles, ban, platoon, online = await asyncio.gather(
|
|
187
|
+
_get(f"/stats/{pid}"),
|
|
188
|
+
_get(f"/stats/{pid}/weapons"),
|
|
189
|
+
_get(f"/stats/{pid}/vehicles"),
|
|
190
|
+
_optional(f"/stats/{pid}/ban"),
|
|
191
|
+
_optional(f"/stats/{pid}/platoon"),
|
|
192
|
+
_optional(f"/stats/{pid}/online"),
|
|
193
|
+
)
|
|
194
|
+
context = pages.stats_page(
|
|
195
|
+
persona,
|
|
196
|
+
stats,
|
|
197
|
+
weapons["weapons"],
|
|
198
|
+
vehicles["vehicles"],
|
|
199
|
+
ban,
|
|
200
|
+
platoon,
|
|
201
|
+
online,
|
|
202
|
+
_source(),
|
|
203
|
+
)
|
|
204
|
+
return await _reply(
|
|
205
|
+
service, "stats.html", context, format_stats(persona["display_name"], stats)
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
async def items_reply(kind: str, name: str, service) -> MessageChain | str:
|
|
210
|
+
persona = await _persona(name)
|
|
211
|
+
pid = persona["persona_id"]
|
|
212
|
+
result, platoon = await asyncio.gather(
|
|
213
|
+
_get(f"/stats/{pid}/{kind}"), _optional(f"/stats/{pid}/platoon")
|
|
214
|
+
)
|
|
215
|
+
items = result[kind]
|
|
216
|
+
context = pages.items_page(kind, persona, platoon, items, _source(), ITEM_CARDS)
|
|
217
|
+
text = (format_weapons if kind == "weapons" else format_vehicles)(
|
|
218
|
+
persona["display_name"], items
|
|
219
|
+
)
|
|
220
|
+
return await _reply(service, "items.html", context, text)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
async def servers_reply(keyword: str, service) -> MessageChain | str:
|
|
224
|
+
result = await _get("/servers", name=keyword, limit=100)
|
|
225
|
+
# 接口不按人数排;多取一些自己排,total 是全部匹配数。
|
|
226
|
+
servers = sorted(result["items"], key=lambda s: s.get("player_count") or 0, reverse=True)
|
|
227
|
+
servers = servers[:TOP]
|
|
228
|
+
context = pages.servers_page(keyword, servers, result["total"], _source())
|
|
229
|
+
return await _reply(
|
|
230
|
+
service, "servers.html", context, format_servers(keyword, servers, result["total"])
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
async def _answer(query) -> MessageChain | str:
|
|
235
|
+
try:
|
|
236
|
+
return await query
|
|
237
|
+
except Bf1Error as error:
|
|
238
|
+
return str(error)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _player_command(name: str, what: str) -> Alconna:
|
|
242
|
+
return Alconna(
|
|
243
|
+
name,
|
|
244
|
+
Args["name", str],
|
|
245
|
+
meta=CommandMeta(
|
|
246
|
+
f"查询战地一玩家的{what}", usage=f"{name} <EA 名字>", example=f"/{name} SHlSAN13"
|
|
247
|
+
),
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@command.on(_player_command("bf1s", "生涯战绩"))
|
|
252
|
+
async def bf1s(
|
|
253
|
+
session: Session,
|
|
254
|
+
name: Query[str] = Query("name"), # noqa: B008
|
|
255
|
+
*,
|
|
256
|
+
render_service: RenderService,
|
|
257
|
+
):
|
|
258
|
+
return await _answer(stats_reply(name.result, render_service))
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
@command.on(_player_command("bf1w", "武器击杀排行"))
|
|
262
|
+
async def bf1w(
|
|
263
|
+
session: Session,
|
|
264
|
+
name: Query[str] = Query("name"), # noqa: B008
|
|
265
|
+
*,
|
|
266
|
+
render_service: RenderService,
|
|
267
|
+
):
|
|
268
|
+
return await _answer(items_reply("weapons", name.result, render_service))
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
@command.on(_player_command("bf1v", "载具击杀排行"))
|
|
272
|
+
async def bf1v(
|
|
273
|
+
session: Session,
|
|
274
|
+
name: Query[str] = Query("name"), # noqa: B008
|
|
275
|
+
*,
|
|
276
|
+
render_service: RenderService,
|
|
277
|
+
):
|
|
278
|
+
return await _answer(items_reply("vehicles", name.result, render_service))
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
server_command = Alconna(
|
|
282
|
+
"bf1ss",
|
|
283
|
+
Args["keyword", AllParam],
|
|
284
|
+
meta=CommandMeta(
|
|
285
|
+
"按名字搜索有人的战地一私服", usage="bf1ss <服务器名关键字>", example="/bf1ss sakula"
|
|
286
|
+
),
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@command.on(server_command)
|
|
291
|
+
async def bf1ss(
|
|
292
|
+
session: Session,
|
|
293
|
+
keyword: Query[list] = Query("keyword"), # noqa: B008
|
|
294
|
+
*,
|
|
295
|
+
render_service: RenderService,
|
|
296
|
+
):
|
|
297
|
+
text = " ".join(str(part) for part in keyword.result).strip()
|
|
298
|
+
if not text:
|
|
299
|
+
return "用法:/bf1ss <服务器名关键字>"
|
|
300
|
+
return await _answer(servers_reply(text, render_service))
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Copyright 2016 The Oswald Project Authors (https://github.com/googlefonts/OswaldFont)
|
|
2
|
+
|
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
4
|
+
This license is copied below, and is also available with a FAQ at:
|
|
5
|
+
https://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-----------------------------------------------------------
|
|
9
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
10
|
+
-----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
PREAMBLE
|
|
13
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
14
|
+
development of collaborative font projects, to support the font creation
|
|
15
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
16
|
+
open framework in which fonts may be shared and improved in partnership
|
|
17
|
+
with others.
|
|
18
|
+
|
|
19
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
20
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
21
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
22
|
+
redistributed and/or sold with any software provided that any reserved
|
|
23
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
24
|
+
however, cannot be released under any other type of license. The
|
|
25
|
+
requirement for fonts to remain under this license does not apply
|
|
26
|
+
to any document created using the fonts or their derivatives.
|
|
27
|
+
|
|
28
|
+
DEFINITIONS
|
|
29
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
30
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
31
|
+
include source files, build scripts and documentation.
|
|
32
|
+
|
|
33
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
34
|
+
copyright statement(s).
|
|
35
|
+
|
|
36
|
+
"Original Version" refers to the collection of Font Software components as
|
|
37
|
+
distributed by the Copyright Holder(s).
|
|
38
|
+
|
|
39
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
40
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
41
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
42
|
+
new environment.
|
|
43
|
+
|
|
44
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
45
|
+
writer or other person who contributed to the Font Software.
|
|
46
|
+
|
|
47
|
+
PERMISSION & CONDITIONS
|
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
49
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
50
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
51
|
+
Software, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
1) Neither the Font Software nor any of its individual components,
|
|
54
|
+
in Original or Modified Versions, may be sold by itself.
|
|
55
|
+
|
|
56
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
57
|
+
redistributed and/or sold with any software, provided that each copy
|
|
58
|
+
contains the above copyright notice and this license. These can be
|
|
59
|
+
included either as stand-alone text files, human-readable headers or
|
|
60
|
+
in the appropriate machine-readable metadata fields within text or
|
|
61
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
62
|
+
|
|
63
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
64
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
65
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
66
|
+
presented to the users.
|
|
67
|
+
|
|
68
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
69
|
+
Software shall not be used to promote, endorse or advertise any
|
|
70
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
71
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
72
|
+
permission.
|
|
73
|
+
|
|
74
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
75
|
+
must be distributed entirely under this license, and must not be
|
|
76
|
+
distributed under any other license. The requirement for fonts to
|
|
77
|
+
remain under this license does not apply to any document created
|
|
78
|
+
using the Font Software.
|
|
79
|
+
|
|
80
|
+
TERMINATION
|
|
81
|
+
This license becomes null and void if any of the above conditions are
|
|
82
|
+
not met.
|
|
83
|
+
|
|
84
|
+
DISCLAIMER
|
|
85
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
86
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
87
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
88
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
89
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
90
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
91
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
92
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
93
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|