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.
Files changed (30) hide show
  1. tenko_plugin_bf1-0.1.0/.gitignore +8 -0
  2. tenko_plugin_bf1-0.1.0/PKG-INFO +38 -0
  3. tenko_plugin_bf1-0.1.0/README.md +19 -0
  4. tenko_plugin_bf1-0.1.0/pyproject.toml +34 -0
  5. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/__init__.py +300 -0
  6. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/OFL-Oswald.txt +93 -0
  7. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/Oswald.ttf +0 -0
  8. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/backgrounds/0.jpg +0 -0
  9. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/backgrounds/1.jpg +0 -0
  10. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/backgrounds/2.jpg +0 -0
  11. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/backgrounds/3.jpg +0 -0
  12. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/backgrounds/4.jpg +0 -0
  13. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/backgrounds/5.jpg +0 -0
  14. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/bfban.webp +0 -0
  15. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/bfeac.webp +0 -0
  16. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/classes/assault.webp +0 -0
  17. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/classes/cavalry.webp +0 -0
  18. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/classes/medic.webp +0 -0
  19. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/classes/pilot.webp +0 -0
  20. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/classes/scout.webp +0 -0
  21. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/classes/support.webp +0 -0
  22. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/assets/classes/tanker.webp +0 -0
  23. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/pages.py +380 -0
  24. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/templates/_base.html +53 -0
  25. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/templates/_macros.html +27 -0
  26. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/templates/items.html +58 -0
  27. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/templates/servers.html +53 -0
  28. tenko_plugin_bf1-0.1.0/src/tenko_plugin_bf1/templates/stats.html +201 -0
  29. tenko_plugin_bf1-0.1.0/tests/conftest.py +6 -0
  30. tenko_plugin_bf1-0.1.0/tests/test_bf1.py +211 -0
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ dist/
5
+ build/
6
+ .pytest_cache/
7
+ .ruff_cache/
8
+ .tenko/
@@ -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.