@sjtdev/koishi-plugin-dota2tracker 2.2.2 → 2.3.0
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.
- package/changelog.md +32 -0
- package/dist/index.js +1 -1
- package/lib/index.js +128 -94
- package/{queries → lib/queries}/MatchInfo.graphql +13 -0
- package/lib/templates/common/components/building_icons.ejs +20 -0
- package/lib/templates/common/styles/normalize.min.css +1 -0
- package/lib/templates/hero/hero_1.ejs +69 -0
- package/lib/templates/images/7.38_simple_minimap.png +0 -0
- package/lib/templates/item/item/recipe.ejs +9 -0
- package/lib/templates/item/item/style.css +1 -0
- package/lib/templates/item/item.ejs +52 -0
- package/lib/templates/item/itemlist.ejs +11 -0
- package/lib/templates/match/match_1/base.css +1 -0
- package/lib/templates/match/match_1/item.ejs +1 -0
- package/lib/templates/match/match_1/main.ejs +8 -0
- package/lib/templates/match/match_1/player.ejs +1 -0
- package/lib/templates/match/match_1/style.css +1 -0
- package/lib/templates/match/match_1.ejs +18 -0
- package/lib/templates/match/match_2/original.css +1 -0
- package/lib/templates/match/match_2/original.ejs +10 -0
- package/lib/templates/match/match_2+/charts.ejs +1 -0
- package/lib/templates/match/match_2+/extra.css +1 -0
- package/lib/templates/match/match_2+/lane_outcome.ejs +56 -0
- package/lib/templates/match/match_2+/map.ejs +160 -0
- package/lib/templates/match/match_2+.ejs +1 -0
- package/lib/templates/match/match_2.ejs +1 -0
- package/lib/templates/player/player_1/base.css +1 -0
- package/lib/templates/player/player_1/private.ejs +1 -0
- package/lib/templates/player/player_1.ejs +78 -0
- package/lib/templates/rank/rank_fun.ejs +1 -0
- package/lib/templates/report/daily/base.css +1 -0
- package/lib/templates/report/daily.ejs +29 -0
- package/package.json +2 -2
- package/template/hero/hero_1.ejs +0 -900
- package/template/item/item/recipe.ejs +0 -51
- package/template/item/item/style.css +0 -244
- package/template/item/item.ejs +0 -140
- package/template/item/itemlist.ejs +0 -99
- package/template/match/match_1/item.ejs +0 -11
- package/template/match/match_1/main.ejs +0 -37
- package/template/match/match_1/player.ejs +0 -154
- package/template/match/match_1/style.css +0 -764
- package/template/match/match_1.ejs +0 -56
- package/template/match/match_2/original.css +0 -463
- package/template/match/match_2/original.ejs +0 -192
- package/template/match/match_2+/charts.ejs +0 -261
- package/template/match/match_2+/extra.css +0 -143
- package/template/match/match_2+/lane_outcome.ejs +0 -157
- package/template/match/match_2+.ejs +0 -27
- package/template/match/match_2.ejs +0 -18
- package/template/player/player_1/private.ejs +0 -5
- package/template/player/player_1.ejs +0 -654
- package/template/rank/rank_fun.ejs +0 -131
- package/template/report/daily.ejs +0 -191
- /package/{queries → lib/queries}/Constants.graphql +0 -0
- /package/{queries → lib/queries}/GetWeeklyMetaByPosition.graphql +0 -0
- /package/{queries → lib/queries}/PlayerExtraInfo.graphql +0 -0
- /package/{queries → lib/queries}/PlayerInfoWith25Matches.graphql +0 -0
- /package/{queries → lib/queries}/PlayerPerformanceForHeroRecommendation.graphql +0 -0
- /package/{queries → lib/queries}/PlayersInfoWith10MatchesForGuild.graphql +0 -0
- /package/{queries → lib/queries}/PlayersLastmatchRankinfo.graphql +0 -0
- /package/{queries → lib/queries}/PlayersMatchesForDaily.graphql +0 -0
- /package/{queries → lib/queries}/RequestMatchDataAnalysis.graphql +0 -0
- /package/{queries → lib/queries}/VerifyingPlayer.graphql +0 -0
- /package/{template → lib/templates}/images/bei.jpg +0 -0
- /package/{template → lib/templates}/images/disconnected.png +0 -0
- /package/{template → lib/templates}/images/flag_dire.png +0 -0
- /package/{template → lib/templates}/images/flag_radiant.png +0 -0
- /package/{template → lib/templates}/images/hero_badge_1.png +0 -0
- /package/{template → lib/templates}/images/hero_badge_2.png +0 -0
- /package/{template → lib/templates}/images/hero_badge_3.png +0 -0
- /package/{template → lib/templates}/images/hero_badge_4.png +0 -0
- /package/{template → lib/templates}/images/hero_badge_5.png +0 -0
- /package/{template → lib/templates}/images/hero_badge_6.png +0 -0
- /package/{template → lib/templates}/images/lane_fail.svg +0 -0
- /package/{template → lib/templates}/images/lane_jungle.svg +0 -0
- /package/{template → lib/templates}/images/lane_stomp.svg +0 -0
- /package/{template → lib/templates}/images/lane_stomped.svg +0 -0
- /package/{template → lib/templates}/images/lane_tie.svg +0 -0
- /package/{template → lib/templates}/images/lane_victory.svg +0 -0
- /package/{template → lib/templates}/images/logo_dire.png +0 -0
- /package/{template → lib/templates}/images/logo_radiant.png +0 -0
- /package/{template → lib/templates}/images/medal_0.png +0 -0
- /package/{template → lib/templates}/images/medal_1.png +0 -0
- /package/{template → lib/templates}/images/medal_2.png +0 -0
- /package/{template → lib/templates}/images/medal_3.png +0 -0
- /package/{template → lib/templates}/images/medal_4.png +0 -0
- /package/{template → lib/templates}/images/medal_5.png +0 -0
- /package/{template → lib/templates}/images/medal_6.png +0 -0
- /package/{template → lib/templates}/images/medal_7.png +0 -0
- /package/{template → lib/templates}/images/medal_8.png +0 -0
- /package/{template → lib/templates}/images/medal_8b.png +0 -0
- /package/{template → lib/templates}/images/medal_8c.png +0 -0
- /package/{template → lib/templates}/images/scepter.png +0 -0
- /package/{template → lib/templates}/images/scepter_0.png +0 -0
- /package/{template → lib/templates}/images/scepter_1.png +0 -0
- /package/{template → lib/templates}/images/shard.png +0 -0
- /package/{template → lib/templates}/images/shard_0.png +0 -0
- /package/{template → lib/templates}/images/shard_1.png +0 -0
- /package/{template → lib/templates}/images/star_0.png +0 -0
- /package/{template → lib/templates}/images/star_1.png +0 -0
- /package/{template → lib/templates}/images/star_2.png +0 -0
- /package/{template → lib/templates}/images/star_3.png +0 -0
- /package/{template → lib/templates}/images/star_4.png +0 -0
- /package/{template → lib/templates}/images/star_5.png +0 -0
- /package/{template → lib/templates}/images/xi.jpg +0 -0
package/changelog.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# 更新日志
|
|
2
2
|
|
|
3
|
+
## [2.3.0](https://github.com/sjtdev/koishi-plugin-dota2tracker/compare/v2.2.3...v2.3.0) (2025-12-17)
|
|
4
|
+
|
|
5
|
+
### ✨ 新增功能
|
|
6
|
+
|
|
7
|
+
* 适配`7.40`新英雄 **朗戈**,更新`dotaconstants`依赖。 ([8308ed9](https://github.com/sjtdev/koishi-plugin-dota2tracker/commit/8308ed96c641519ec52a39329e2c3312b0fa2dc8))
|
|
8
|
+
* **templates/match_2+:** 添加小地图,显示防御塔、兵营、基地的存活状态,数据可用时显示建筑被拆毁时间。(高地仅显示各路近战兵营被破时间、4塔被全部拆除时间、基地时间) ([923b40f](https://github.com/sjtdev/koishi-plugin-dota2tracker/commit/923b40fc8f24e05ce4b28dfa9a20ad3166c8e2f5))
|
|
9
|
+
|
|
10
|
+
### 🚀 功能优化
|
|
11
|
+
|
|
12
|
+
* **api:** 将超时时间从10秒统一上调到15秒,解决某些数据量大的耗时任务失败概率高的问题。 ([b3f51de](https://github.com/sjtdev/koishi-plugin-dota2tracker/commit/b3f51defd49ac7c9c176f894aa9d94d9b392b3aa))
|
|
13
|
+
* **logger:** 优化部分查询情况下的报错提示日志。 ([79ba28b](https://github.com/sjtdev/koishi-plugin-dota2tracker/commit/79ba28b4822df8858e4f4c8d71303b339d407569))
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug 修复
|
|
16
|
+
|
|
17
|
+
* **command/query-hero:** 修复了指令`查询英雄`无参数但携带`-r/--random`选项无响应的问题。(即无法通过`查询英雄 -r`直接随机查询英雄的问题) ([c56a22c](https://github.com/sjtdev/koishi-plugin-dota2tracker/commit/c56a22caacf65eef573e6434b1032b06f589c2b8))
|
|
18
|
+
* **command/query-match:** 修复`2.2.2`更新导致指令`查询比赛`不可用的问题。 ([be5061c](https://github.com/sjtdev/koishi-plugin-dota2tracker/commit/be5061c9b67593cceb8067d480b965cb47c53d4d))
|
|
19
|
+
|
|
20
|
+
### ⚡ 性能提升
|
|
21
|
+
|
|
22
|
+
* **command/query-match:** 清理`2.0.0`大型重构时遗留耗时代码,提升执行速度。 ([4c0bf34](https://github.com/sjtdev/koishi-plugin-dota2tracker/commit/4c0bf34c358af3d508bf1be6be88fd1e86fefc17))
|
|
23
|
+
* **templates:** 重构大部分模板,使其构建打包后体积更小,略微提升生成速度。 ([fdf1d33](https://github.com/sjtdev/koishi-plugin-dota2tracker/commit/fdf1d3367e12f8e46124df31d6f432e7fd0a7187))
|
|
24
|
+
|
|
25
|
+
### [2.2.3](https://github.com/sjtdev/koishi-plugin-dota2tracker/compare/v2.2.2...v2.2.3) (2025-11-20)
|
|
26
|
+
|
|
27
|
+
### 🎨 样式
|
|
28
|
+
|
|
29
|
+
* **console:** 为`OpenDotaAPI访问次数`添加“今日”使其描述更准确。 ([a77ced6](https://github.com/sjtdev/koishi-plugin-dota2tracker/commit/a77ced6619ea07612e2afbf762f0a4fa1e7a0e9a))
|
|
30
|
+
|
|
31
|
+
### 🐛 Bug 修复
|
|
32
|
+
|
|
33
|
+
* **opendota:** 修复`cache`调用配置项失败导致`opendota`访问缓存失败的问题。 ([30fdb90](https://github.com/sjtdev/koishi-plugin-dota2tracker/commit/30fdb90a69d231590d665235f92c7f8287995278))
|
|
34
|
+
|
|
3
35
|
### [2.2.2](https://github.com/sjtdev/koishi-plugin-dota2tracker/compare/v2.2.1...v2.2.2) (2025-11-19)
|
|
4
36
|
|
|
5
37
|
### ✨ 新增功能
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{store as u,icons as _}from"@koishijs/client";import{resolveComponent as a,createBlock as m,openBlock as p,withCtx as n,createVNode as l,createTextVNode as s,toDisplayString as k,unref as f,createElementVNode as i,createElementBlock as h,createStaticVNode as g}from"vue";const w={__name:"page",setup(t){return(e,o)=>{const r=a("k-card"),c=a("k-comment"),d=a("k-layout");return p(),m(d,null,{default:n(()=>[l(r,null,{default:n(()=>[s("OpenDotaAPI
|
|
1
|
+
import{store as u,icons as _}from"@koishijs/client";import{resolveComponent as a,createBlock as m,openBlock as p,withCtx as n,createVNode as l,createTextVNode as s,toDisplayString as k,unref as f,createElementVNode as i,createElementBlock as h,createStaticVNode as g}from"vue";const w={__name:"page",setup(t){return(e,o)=>{const r=a("k-card"),c=a("k-comment"),d=a("k-layout");return p(),m(d,null,{default:n(()=>[l(r,null,{default:n(()=>[s("OpenDotaAPI 今日已访问 "+k(f(u).apiCount.opendota)+" 次。",1)]),_:1}),l(r,null,{default:n(()=>[l(c,{type:"warning"},{default:n(()=>[...o[0]||(o[0]=[i("p",null,[s("该页面正在开发中,关于这个页面有任何想法或建议欢迎投稿至 "),i("a",{target:"_blank",href:"https://github.com/sjtdev/koishi-plugin-dota2tracker/issues"},"项目issues"),s("。")],-1)])]),_:1})]),_:1})]),_:1})}}},x=(t,e)=>{const o=t.__vccOpts||t;for(const[r,c]of e)o[r]=c;return o},C={},v={xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 600 600",fill:"currentColor"};function y(t,e){return p(),h("svg",v,[...e[0]||(e[0]=[g('<g><rect height="544" width="540" y="28.5" x="32" stroke="currentColor" fill="none" stroke-width="35" rx="50" ry="58"></rect><g transform="translate(300,300) scale(0.75) translate(-300,-300)" stroke-linejoin="round" stroke-linecap="round"><path d="m95,145.5l54,-30.5l361,306l-36,86l-113,-26l-266,-335.5z" stroke="currentColor" fill="none" stroke-width="35"></path><path transform="rotate(3.87406 449.593 151.279)" d="m458.26078,90.18786c0,0 61.75777,47.47474 61.75777,46.77658c0,0.69816 -8.66776,76.09922 -8.66776,75.40106c0,0.69816 -132.18329,-101.23291 -132.18329,-101.93106c0,0.69816 79.09328,-20.24658 79.09328,-20.24658z" stroke="currentColor" fill="none" stroke-width="35"></path><path d="m84,466.55926c0,0 16.71429,-92.78604 16.71429,-93.55926c0,0.77322 127.28571,110.57003 127.28571,109.79682c0,0.77322 -96.42857,23.96973 -96.42857,23.19651c0,0.77322 -47.57143,-39.43407 -47.57143,-39.43407z" stroke="currentColor" fill="none" stroke-width="35"></path></g></g>',1)])])}const B=x(C,[["render",y]]),V=t=>{_.register("dota2",B),t.page({name:"DOTA2Tracker(实验性)",path:"/dota2tracker",component:w,fields:["apiCount"],icon:"dota2"})};export{V as default};
|
package/lib/index.js
CHANGED
|
@@ -259,7 +259,8 @@ var require_en_US_constants = __commonJS({
|
|
|
259
259
|
"136": "Marci",
|
|
260
260
|
"137": "Primal Beast",
|
|
261
261
|
"138": "Muerta",
|
|
262
|
-
"145": "Kez"
|
|
262
|
+
"145": "Kez",
|
|
263
|
+
"155": "Largo"
|
|
263
264
|
},
|
|
264
265
|
behavior: {
|
|
265
266
|
"Unit Target": "Unit Target",
|
|
@@ -571,7 +572,8 @@ var require_zh_CN_constants = __commonJS({
|
|
|
571
572
|
"136": "玛西",
|
|
572
573
|
"137": "獸",
|
|
573
574
|
"138": "琼英碧灵",
|
|
574
|
-
"145": "凯"
|
|
575
|
+
"145": "凯",
|
|
576
|
+
"155": "朗戈"
|
|
575
577
|
},
|
|
576
578
|
behavior: {
|
|
577
579
|
"Unit Target": "单位目标",
|
|
@@ -1520,9 +1522,9 @@ __name(handleError, "handleError");
|
|
|
1520
1522
|
|
|
1521
1523
|
// src/app/core/match.service.ts
|
|
1522
1524
|
var MatchService = class _MatchService extends import_koishi4.Service {
|
|
1523
|
-
constructor(ctx,
|
|
1525
|
+
constructor(ctx, pluginVersion) {
|
|
1524
1526
|
super(ctx, "dota2tracker.match", true);
|
|
1525
|
-
this.pluginVersion =
|
|
1527
|
+
this.pluginVersion = pluginVersion;
|
|
1526
1528
|
}
|
|
1527
1529
|
static {
|
|
1528
1530
|
__name(this, "MatchService");
|
|
@@ -2027,12 +2029,12 @@ var PlayerService = class _PlayerService extends import_koishi5.Service {
|
|
|
2027
2029
|
const lastMatchQuery = await this.ctx.dota2tracker.stratzAPI.queryPlayersLastMatchRankInfo({
|
|
2028
2030
|
steamAccountIds: [steamId]
|
|
2029
2031
|
});
|
|
2030
|
-
if (lastMatchQuery.players[0].steamAccount.isAnonymous) return {
|
|
2032
|
+
if (lastMatchQuery.players[0].steamAccount.isAnonymous) return { id: 0, isAnonymous: true };
|
|
2031
2033
|
lastMatchId = lastMatchQuery.players[0].matches[0]?.id;
|
|
2032
2034
|
} catch (error) {
|
|
2033
2035
|
this.logger.error(error);
|
|
2034
2036
|
}
|
|
2035
|
-
return {
|
|
2037
|
+
return { id: lastMatchId };
|
|
2036
2038
|
}
|
|
2037
2039
|
async getFormattedPlayerData(steamId, heroId, languageTag) {
|
|
2038
2040
|
const playerQuery = await this.ctx.dota2tracker.stratzAPI.queryPlayerInfoWith25Matches({
|
|
@@ -2221,6 +2223,7 @@ var CacheService = class extends import_koishi6.Service {
|
|
|
2221
2223
|
}
|
|
2222
2224
|
constructor(ctx) {
|
|
2223
2225
|
super(ctx, "dota2tracker.cache", true);
|
|
2226
|
+
this.config = ctx.config;
|
|
2224
2227
|
}
|
|
2225
2228
|
get msUntilUTCEndOfDay() {
|
|
2226
2229
|
const now = import_luxon4.DateTime.utc();
|
|
@@ -2257,8 +2260,8 @@ var CacheService = class extends import_koishi6.Service {
|
|
|
2257
2260
|
async getMatchCache(matchId) {
|
|
2258
2261
|
return this.ctx.cache.get("dt_previous_query_results", String(matchId));
|
|
2259
2262
|
}
|
|
2260
|
-
setMatchCache(matchId, matchQuery,
|
|
2261
|
-
this.ctx.cache.set("dt_previous_query_results", String(matchQuery.match.id), { data: matchQuery, pluginVersion
|
|
2263
|
+
setMatchCache(matchId, matchQuery, pluginVersion) {
|
|
2264
|
+
this.ctx.cache.set("dt_previous_query_results", String(matchQuery.match.id), { data: matchQuery, pluginVersion }, DAYS_30);
|
|
2262
2265
|
}
|
|
2263
2266
|
markMatchAsSended(matchId) {
|
|
2264
2267
|
this.ctx.cache.set("dt_sended_match_id", String(matchId), void 0, DAYS_30);
|
|
@@ -2387,14 +2390,6 @@ var import_path = __toESM(require("path"));
|
|
|
2387
2390
|
var import_axios2 = __toESM(require("axios"));
|
|
2388
2391
|
var import_https_proxy_agent = require("https-proxy-agent");
|
|
2389
2392
|
var StratzAPI = class extends import_koishi8.Service {
|
|
2390
|
-
constructor(ctx, pluginDir3) {
|
|
2391
|
-
super(ctx, "dota2tracker.stratz-api", true);
|
|
2392
|
-
this.pluginDir = pluginDir3;
|
|
2393
|
-
this.config = ctx.config;
|
|
2394
|
-
this.queue = new MiniQueue(ctx, { interval: 200 });
|
|
2395
|
-
this.http = import_axios2.default.create({ timeout: 1e4, signal: this.abortController.signal });
|
|
2396
|
-
ctx.on("dispose", () => this.dispose());
|
|
2397
|
-
}
|
|
2398
2393
|
static {
|
|
2399
2394
|
__name(this, "StratzAPI");
|
|
2400
2395
|
}
|
|
@@ -2402,12 +2397,21 @@ var StratzAPI = class extends import_koishi8.Service {
|
|
|
2402
2397
|
queue;
|
|
2403
2398
|
http;
|
|
2404
2399
|
abortController = new AbortController();
|
|
2400
|
+
graphqlQueriesDir;
|
|
2401
|
+
constructor(ctx, currentDir) {
|
|
2402
|
+
super(ctx, "dota2tracker.stratz-api", true);
|
|
2403
|
+
this.config = ctx.config;
|
|
2404
|
+
this.graphqlQueriesDir = import_path.default.join(currentDir, "queries");
|
|
2405
|
+
this.queue = new MiniQueue(ctx, { interval: 200 });
|
|
2406
|
+
this.http = import_axios2.default.create({ timeout: 15e3, signal: this.abortController.signal });
|
|
2407
|
+
ctx.on("dispose", () => this.dispose());
|
|
2408
|
+
}
|
|
2405
2409
|
dispose() {
|
|
2406
2410
|
this.queue.dispose();
|
|
2407
2411
|
this.abortController.abort();
|
|
2408
2412
|
}
|
|
2409
2413
|
async queryGetWeeklyMetaByPosition({ bracketIds }) {
|
|
2410
|
-
return this.query("GetWeeklyMetaByPosition", { bracketIds }, (data) => !!data
|
|
2414
|
+
return this.query("GetWeeklyMetaByPosition", { bracketIds }, (data) => !!data?.heroStats);
|
|
2411
2415
|
}
|
|
2412
2416
|
async queryPlayerPerformanceForHeroRecommendation({ steamAccountId, recentDateTime }) {
|
|
2413
2417
|
return this.query(
|
|
@@ -2416,7 +2420,7 @@ var StratzAPI = class extends import_koishi8.Service {
|
|
|
2416
2420
|
steamAccountId,
|
|
2417
2421
|
recentDateTime
|
|
2418
2422
|
},
|
|
2419
|
-
(data) => !!data
|
|
2423
|
+
(data) => !!data?.player
|
|
2420
2424
|
);
|
|
2421
2425
|
}
|
|
2422
2426
|
async queryPlayersMatchesForDaily(steamAccountIds, seconds) {
|
|
@@ -2426,11 +2430,11 @@ var StratzAPI = class extends import_koishi8.Service {
|
|
|
2426
2430
|
steamAccountIds,
|
|
2427
2431
|
seconds
|
|
2428
2432
|
},
|
|
2429
|
-
(data) => !!data
|
|
2433
|
+
(data) => !!data?.players
|
|
2430
2434
|
);
|
|
2431
2435
|
}
|
|
2432
2436
|
async queryVerifyingPlayer(steamAccountId) {
|
|
2433
|
-
return this.query("VerifyingPlayer", { steamAccountId }, (data) => !!data
|
|
2437
|
+
return this.query("VerifyingPlayer", { steamAccountId }, (data) => !!data?.player);
|
|
2434
2438
|
}
|
|
2435
2439
|
async queryPlayerExtraInfo({ steamAccountId, matchCount, heroIds }) {
|
|
2436
2440
|
return this.query(
|
|
@@ -2440,11 +2444,11 @@ var StratzAPI = class extends import_koishi8.Service {
|
|
|
2440
2444
|
matchCount,
|
|
2441
2445
|
heroIds
|
|
2442
2446
|
},
|
|
2443
|
-
(data) => !!data
|
|
2447
|
+
(data) => !!data?.player
|
|
2444
2448
|
);
|
|
2445
2449
|
}
|
|
2446
2450
|
async queryPlayersInfoWith10MatchesForGuild({ steamAccountIds }) {
|
|
2447
|
-
return this.query("PlayersInfoWith10MatchesForGuild", { steamAccountIds }, (data) => !!data
|
|
2451
|
+
return this.query("PlayersInfoWith10MatchesForGuild", { steamAccountIds }, (data) => !!data?.players);
|
|
2448
2452
|
}
|
|
2449
2453
|
async queryPlayerInfoWith25Matches({ steamAccountId, heroIds }) {
|
|
2450
2454
|
return this.query(
|
|
@@ -2453,17 +2457,17 @@ var StratzAPI = class extends import_koishi8.Service {
|
|
|
2453
2457
|
steamAccountId,
|
|
2454
2458
|
heroIds
|
|
2455
2459
|
},
|
|
2456
|
-
(data) => !!data
|
|
2460
|
+
(data) => !!data?.player
|
|
2457
2461
|
);
|
|
2458
2462
|
}
|
|
2459
2463
|
async queryPlayersLastMatchRankInfo({ steamAccountIds }) {
|
|
2460
|
-
return this.query("PlayersLastmatchRankinfo", { steamAccountIds }, (data) => !!data
|
|
2464
|
+
return this.query("PlayersLastmatchRankinfo", { steamAccountIds }, (data) => !!data?.players);
|
|
2461
2465
|
}
|
|
2462
2466
|
async queryConstants(languageTag) {
|
|
2463
|
-
return this.query("Constants", { language: this.ctx.dota2tracker.i18n.getGraphqlLanguageTag(languageTag) }, (data) => !!data
|
|
2467
|
+
return this.query("Constants", { language: this.ctx.dota2tracker.i18n.getGraphqlLanguageTag(languageTag) }, (data) => !!data?.constants);
|
|
2464
2468
|
}
|
|
2465
2469
|
async queryMatchInfo(matchId) {
|
|
2466
|
-
return this.query("MatchInfo", { matchId }, (data) => !!data
|
|
2470
|
+
return this.query("MatchInfo", { matchId }, (data) => !!data?.match);
|
|
2467
2471
|
}
|
|
2468
2472
|
async requestParseMatch(matchId) {
|
|
2469
2473
|
const response = await this.query("RequestMatchDataAnalysis", {
|
|
@@ -2524,7 +2528,7 @@ var StratzAPI = class extends import_koishi8.Service {
|
|
|
2524
2528
|
});
|
|
2525
2529
|
}
|
|
2526
2530
|
loadGraphqlFile(queryName) {
|
|
2527
|
-
return import_fs.default.readFileSync(import_path.default.join(this.
|
|
2531
|
+
return import_fs.default.readFileSync(import_path.default.join(this.graphqlQueriesDir, `${queryName}.graphql`), { encoding: "utf-8" }).replace(/[\r\n]+/g, " ");
|
|
2528
2532
|
}
|
|
2529
2533
|
};
|
|
2530
2534
|
var MiniQueue = class {
|
|
@@ -2591,7 +2595,7 @@ var ValveAPI = class extends import_koishi9.Service {
|
|
|
2591
2595
|
constructor(ctx) {
|
|
2592
2596
|
super(ctx, "dota2tracker.valve-api", true);
|
|
2593
2597
|
this.config = ctx.config;
|
|
2594
|
-
this.http = import_axios3.default.create({ timeout:
|
|
2598
|
+
this.http = import_axios3.default.create({ timeout: 15e3, signal: this.abortController.signal, baseURL: this.baseURL });
|
|
2595
2599
|
ctx.on("dispose", () => this.dispose());
|
|
2596
2600
|
}
|
|
2597
2601
|
dispose() {
|
|
@@ -2665,14 +2669,15 @@ var ImageFormat = /* @__PURE__ */ ((ImageFormat2) => {
|
|
|
2665
2669
|
// src/app/presentation/image.renderer.ts
|
|
2666
2670
|
var import_luxon5 = require("luxon");
|
|
2667
2671
|
var ImageRenderer = class extends import_koishi10.Service {
|
|
2668
|
-
constructor(ctx, pluginDir3) {
|
|
2669
|
-
super(ctx, "dota2tracker.image", true);
|
|
2670
|
-
this.pluginDir = pluginDir3;
|
|
2671
|
-
this.config = ctx.config;
|
|
2672
|
-
}
|
|
2673
2672
|
static {
|
|
2674
2673
|
__name(this, "ImageRenderer");
|
|
2675
2674
|
}
|
|
2675
|
+
templateDir;
|
|
2676
|
+
constructor(ctx, currentDir) {
|
|
2677
|
+
super(ctx, "dota2tracker.image", true);
|
|
2678
|
+
this.config = ctx.config;
|
|
2679
|
+
this.templateDir = import_path2.default.join(currentDir, "templates");
|
|
2680
|
+
}
|
|
2676
2681
|
async renderToImageByFile(data, templateName, type, languageTag) {
|
|
2677
2682
|
const html = await this.generateHTML(data, { source: "FILE", templateName, type }, languageTag);
|
|
2678
2683
|
return this.ctx.puppeteer.render(html);
|
|
@@ -2700,7 +2705,7 @@ var ImageRenderer = class extends import_koishi10.Service {
|
|
|
2700
2705
|
try {
|
|
2701
2706
|
let html;
|
|
2702
2707
|
if (template.source === "FILE") {
|
|
2703
|
-
const templatePath = import_path2.default.join(this.
|
|
2708
|
+
const templatePath = import_path2.default.join(this.templateDir, template.type, `${template.templateName}.ejs`);
|
|
2704
2709
|
html = await import_ejs.default.renderFile(templatePath, templateData, {
|
|
2705
2710
|
strict: false
|
|
2706
2711
|
});
|
|
@@ -2710,7 +2715,7 @@ var ImageRenderer = class extends import_koishi10.Service {
|
|
|
2710
2715
|
async: true
|
|
2711
2716
|
});
|
|
2712
2717
|
}
|
|
2713
|
-
if (process.env.NODE_ENV === "development") import_fs2.default.writeFileSync(import_path2.default.
|
|
2718
|
+
if (process.env.NODE_ENV === "development") import_fs2.default.writeFileSync(import_path2.default.resolve(process.cwd(), "temp.html"), html);
|
|
2714
2719
|
return html;
|
|
2715
2720
|
} catch (error) {
|
|
2716
2721
|
this.logger.error(error);
|
|
@@ -2720,8 +2725,8 @@ var ImageRenderer = class extends import_koishi10.Service {
|
|
|
2720
2725
|
getImageUrl(image, type = "local" /* Local */, format = "png" /* png */) {
|
|
2721
2726
|
if (type === "local" /* Local */) {
|
|
2722
2727
|
try {
|
|
2723
|
-
if (format === "svg" /* svg */) return import_fs2.default.readFileSync(import_path2.default.join(this.
|
|
2724
|
-
const imageData = import_fs2.default.readFileSync(import_path2.default.join(this.
|
|
2728
|
+
if (format === "svg" /* svg */) return import_fs2.default.readFileSync(import_path2.default.join(this.templateDir, "images", `${image}.svg`));
|
|
2729
|
+
const imageData = import_fs2.default.readFileSync(import_path2.default.join(this.templateDir, "images", `${image}.${format}`));
|
|
2725
2730
|
const base64Data = imageData.toString("base64");
|
|
2726
2731
|
return `data:image/png;base64,${base64Data}`;
|
|
2727
2732
|
} catch (error) {
|
|
@@ -3455,20 +3460,25 @@ __name(resolvePlayerAndHandleErrors, "resolvePlayerAndHandleErrors");
|
|
|
3455
3460
|
// src/app/commands/hero-of-the-day.command.ts
|
|
3456
3461
|
var import_luxon10 = require("luxon");
|
|
3457
3462
|
function registerHeroOfTheDayCommand(ctx) {
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3463
|
+
const name2 = "hero-of-the-day";
|
|
3464
|
+
ctx.command(`dota2tracker.${name2} <input_data>`).alias("今日英雄").option("days", "-d <value:number>").action(async ({ session, options }, input_data) => {
|
|
3465
|
+
try {
|
|
3466
|
+
const steamId = await resolvePlayerAndHandleErrors(ctx, session, input_data);
|
|
3467
|
+
if (steamId === null) return;
|
|
3468
|
+
const days = clamp(options.days, 1, 180, 30);
|
|
3469
|
+
const result = await ctx.dota2tracker.stratzAPI.queryPlayerPerformanceForHeroRecommendation({
|
|
3470
|
+
steamAccountId: steamId,
|
|
3471
|
+
recentDateTime: import_luxon10.DateTime.now().minus({ days }).toUnixInteger()
|
|
3472
|
+
});
|
|
3473
|
+
const recommendationPromise = ctx.dota2tracker.player.getHeroRecommendation(steamId, result.player);
|
|
3474
|
+
const metaPromise = ctx.dota2tracker.hero.getWeeklyHeroMeta(PlayerService.estimateWeightedRank(result.player));
|
|
3475
|
+
const [recommendation, weeklyHeroMeta] = await Promise.all([recommendationPromise, metaPromise]);
|
|
3476
|
+
const languageTag = await ctx.dota2tracker.i18n.getLanguageTag({ session });
|
|
3477
|
+
const message = ctx.dota2tracker.messageBuilder.buildHeroOfTheDayMessage(languageTag, recommendation, weeklyHeroMeta);
|
|
3478
|
+
return message;
|
|
3479
|
+
} catch (error) {
|
|
3480
|
+
handleError(error, ctx.logger(name2), ctx.dota2tracker.i18n, ctx.config);
|
|
3481
|
+
}
|
|
3472
3482
|
});
|
|
3473
3483
|
}
|
|
3474
3484
|
__name(registerHeroOfTheDayCommand, "registerHeroOfTheDayCommand");
|
|
@@ -3476,14 +3486,19 @@ __name(registerHeroOfTheDayCommand, "registerHeroOfTheDayCommand");
|
|
|
3476
3486
|
// src/app/commands/query-hero.command.ts
|
|
3477
3487
|
function registerQueryHeroCommand(ctx) {
|
|
3478
3488
|
ctx.command("dota2tracker.query-hero <input_data>").option("random", "-r").alias("查询英雄").action(async ({ session, options }, input_data) => {
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3489
|
+
const name2 = "query-hero";
|
|
3490
|
+
try {
|
|
3491
|
+
if (input_data || options.random) {
|
|
3492
|
+
await session.send(session.text(".querying_hero"));
|
|
3493
|
+
const languageTag = await ctx.dota2tracker.i18n.getLanguageTag({ session });
|
|
3494
|
+
const heroData = await ctx.dota2tracker.hero.getHeroDetails(input_data, languageTag, options.random);
|
|
3495
|
+
if (!heroData) return session.text(".not_found");
|
|
3496
|
+
const image = await ctx.dota2tracker.image.renderToImageByFile(heroData, ctx.config.template_hero, "hero" /* Hero */, languageTag);
|
|
3497
|
+
const message = ctx.dota2tracker.messageBuilder.buildHeroMessage(heroData);
|
|
3498
|
+
await session.send(message + image);
|
|
3499
|
+
}
|
|
3500
|
+
} catch (error) {
|
|
3501
|
+
handleError(error, ctx.logger(name2), ctx.dota2tracker.i18n, ctx.config);
|
|
3487
3502
|
}
|
|
3488
3503
|
});
|
|
3489
3504
|
}
|
|
@@ -3539,25 +3554,40 @@ __name(registerQueryItemCommand, "registerQueryItemCommand");
|
|
|
3539
3554
|
// src/app/commands/query-match.command.ts
|
|
3540
3555
|
function registerQueryMatchCommand(ctx) {
|
|
3541
3556
|
ctx.command("dota2tracker.query-match <match_id>").alias("查询比赛").option("parse", "-p").option("template", "-t <value:string>").action(async ({ session, options }, match_id) => {
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3557
|
+
const name2 = "query-match";
|
|
3558
|
+
try {
|
|
3559
|
+
if (!match_id) return session.text(".empty_input");
|
|
3560
|
+
if (!/^\d{1,11}$/.test(match_id)) return session.text(".match_id_invalid");
|
|
3561
|
+
await session.send(session.text(".querying_match"));
|
|
3562
|
+
return await handleQueryMatchCommand(ctx, ctx.config, session, options, match_id);
|
|
3563
|
+
} catch (error) {
|
|
3564
|
+
handleError(error, ctx.logger(name2), ctx.dota2tracker.i18n, ctx.config);
|
|
3565
|
+
}
|
|
3546
3566
|
});
|
|
3547
3567
|
ctx.command("dota2tracker.query-recent-match [input_data]").alias("查询最近比赛").option("parse", "-p").option("template", "-t <value:string>").action(async ({ session, options }, input_data) => {
|
|
3548
|
-
const
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3568
|
+
const name2 = "query-recent-match";
|
|
3569
|
+
try {
|
|
3570
|
+
const steamId = await resolvePlayerAndHandleErrors(ctx, session, input_data);
|
|
3571
|
+
if (steamId === null) return;
|
|
3572
|
+
session.send(session.text(".querying_match"));
|
|
3573
|
+
let lastMatch;
|
|
3574
|
+
try {
|
|
3575
|
+
lastMatch = await ctx.dota2tracker.player.getLastMatchId(Number(steamId));
|
|
3576
|
+
} catch (error) {
|
|
3577
|
+
handleError(error, ctx.logger(name2), ctx.dota2tracker.i18n, ctx.config);
|
|
3578
|
+
}
|
|
3579
|
+
if (!lastMatch?.id) return session.text(".query_failed");
|
|
3580
|
+
if (lastMatch.isAnonymous) return session.text(".is_anonymous");
|
|
3581
|
+
return await handleQueryMatchCommand(ctx, ctx.config, session, options, lastMatch.id);
|
|
3582
|
+
} catch (error) {
|
|
3583
|
+
handleError(error, ctx.logger(name2), ctx.dota2tracker.i18n, ctx.config);
|
|
3584
|
+
}
|
|
3555
3585
|
});
|
|
3556
3586
|
}
|
|
3557
3587
|
__name(registerQueryMatchCommand, "registerQueryMatchCommand");
|
|
3558
3588
|
async function handleQueryMatchCommand(ctx, config, session, options, matchId) {
|
|
3559
3589
|
const languageTag = await ctx.dota2tracker.i18n.getLanguageTag({ session });
|
|
3560
|
-
const result = await ctx.dota2tracker.match.getMatchResult({ matchId: Number(matchId),
|
|
3590
|
+
const result = await ctx.dota2tracker.match.getMatchResult({ matchId: Number(matchId), waitForParse: options.parse, allowFallback: config.enableOpenDotaFallback });
|
|
3561
3591
|
if (result.status === "PENDING") {
|
|
3562
3592
|
const subscriber = ctx.dota2tracker.parsePolling.createSubscriberByCommand(session, languageTag, { templateName: options?.template });
|
|
3563
3593
|
ctx.dota2tracker.parsePolling.add(result.matchId, [subscriber]);
|
|
@@ -3568,7 +3598,6 @@ async function handleQueryMatchCommand(ctx, config, session, options, matchId) {
|
|
|
3568
3598
|
const formattedMatchData = await ctx.dota2tracker.match.generateMatchData(result.matchData, languageTag);
|
|
3569
3599
|
const message = ctx.dota2tracker.messageBuilder.buildMatchMessage(languageTag, formattedMatchData, []);
|
|
3570
3600
|
const image = await ctx.dota2tracker.image.renderToImageByFile(formattedMatchData, options.template || config.template_match, "match" /* Match */, languageTag);
|
|
3571
|
-
await ctx.dota2tracker.image.renderToImageByFile(formattedMatchData, options.template || config.template_match, "match" /* Match */, languageTag);
|
|
3572
3601
|
return message + image;
|
|
3573
3602
|
}
|
|
3574
3603
|
}
|
|
@@ -3589,18 +3618,23 @@ __name(registerQueryMembersCommand, "registerQueryMembersCommand");
|
|
|
3589
3618
|
// src/app/commands/query-player.command.ts
|
|
3590
3619
|
function registerQueryPlayerCommand(ctx) {
|
|
3591
3620
|
ctx.command("dota2tracker.query-player <input_data>").option("hero", "-o <value:string>").alias("查询玩家").action(async ({ session, options }, input_data) => {
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
if (
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3621
|
+
const name2 = "query-player";
|
|
3622
|
+
try {
|
|
3623
|
+
if (session.guild || !session.guild && input_data) {
|
|
3624
|
+
const steamId = await resolvePlayerAndHandleErrors(ctx, session, input_data);
|
|
3625
|
+
if (steamId === null) return;
|
|
3626
|
+
session.send(session.text(".querying_player"));
|
|
3627
|
+
const heroId = ctx.dota2tracker.i18n.findHeroIdInLocale(options.hero);
|
|
3628
|
+
const languageTag = await ctx.dota2tracker.i18n.getLanguageTag({ session });
|
|
3629
|
+
const formattedPlayerData = await ctx.dota2tracker.player.getFormattedPlayerData(steamId, heroId, languageTag);
|
|
3630
|
+
const image = await ctx.dota2tracker.image.renderToImageByFile(formattedPlayerData, ctx.config.template_player, "player" /* Player */, languageTag);
|
|
3631
|
+
const message = ctx.dota2tracker.messageBuilder.buildPlayerMessage(steamId);
|
|
3632
|
+
return message + image;
|
|
3633
|
+
} else {
|
|
3634
|
+
return session.text("commands.dota2tracker.common.messages.user_not_in_group");
|
|
3635
|
+
}
|
|
3636
|
+
} catch (error) {
|
|
3637
|
+
handleError(error, ctx.logger(name2), ctx.dota2tracker.i18n, ctx.config);
|
|
3604
3638
|
}
|
|
3605
3639
|
});
|
|
3606
3640
|
}
|
|
@@ -3685,7 +3719,7 @@ var OpenDotaAPI = class extends import_koishi15.Service {
|
|
|
3685
3719
|
constructor(ctx) {
|
|
3686
3720
|
super(ctx, "dota2tracker.opendota-api", true);
|
|
3687
3721
|
this.config = ctx.config;
|
|
3688
|
-
this.http = import_axios4.default.create({ timeout:
|
|
3722
|
+
this.http = import_axios4.default.create({ timeout: 15e3, signal: this.abortController.signal, baseURL: this.BASE_URL });
|
|
3689
3723
|
ctx.on("dispose", () => this.dispose());
|
|
3690
3724
|
}
|
|
3691
3725
|
dispose() {
|
|
@@ -4177,7 +4211,7 @@ var globRequire_locales_schema_yml = __glob({
|
|
|
4177
4211
|
});
|
|
4178
4212
|
|
|
4179
4213
|
// src/config.ts
|
|
4180
|
-
var
|
|
4214
|
+
var templateDir = import_path4.default.join(__dirname, "templates");
|
|
4181
4215
|
var allI18nConfigs = Object.fromEntries(Object.keys(LanguageTags).map((lang) => [lang, globRequire_locales_schema_yml(`./locales/${lang}.schema.yml`)._config]));
|
|
4182
4216
|
var Config = import_koishi17.Schema.intersect([
|
|
4183
4217
|
import_koishi17.Schema.intersect([
|
|
@@ -4248,9 +4282,9 @@ var Config = import_koishi17.Schema.intersect([
|
|
|
4248
4282
|
]).i18n(getI18n("report"))
|
|
4249
4283
|
]),
|
|
4250
4284
|
import_koishi17.Schema.object({
|
|
4251
|
-
template_match: import_koishi17.Schema.union([...readDirectoryFilesSync(import_path4.default.join(
|
|
4252
|
-
template_player: import_koishi17.Schema.union([...readDirectoryFilesSync(import_path4.default.join(
|
|
4253
|
-
template_hero: import_koishi17.Schema.union([...readDirectoryFilesSync(import_path4.default.join(
|
|
4285
|
+
template_match: import_koishi17.Schema.union([...readDirectoryFilesSync(import_path4.default.join(templateDir, "match"))]).default("match_1"),
|
|
4286
|
+
template_player: import_koishi17.Schema.union([...readDirectoryFilesSync(import_path4.default.join(templateDir, "player"))]).default("player_1"),
|
|
4287
|
+
template_hero: import_koishi17.Schema.union([...readDirectoryFilesSync(import_path4.default.join(templateDir, "hero"))]).default("hero_1"),
|
|
4254
4288
|
playerRankEstimate: import_koishi17.Schema.boolean().default(true),
|
|
4255
4289
|
templateFonts: import_koishi17.Schema.array(String).default([]).role("table")
|
|
4256
4290
|
}).i18n(getI18n("template"))
|
|
@@ -4284,13 +4318,13 @@ var inject = {
|
|
|
4284
4318
|
required: ["database", "puppeteer", "cache"],
|
|
4285
4319
|
optional: ["cron", "console"]
|
|
4286
4320
|
};
|
|
4287
|
-
var pluginDir2 = import_path5.default.resolve(__dirname, "..");
|
|
4288
|
-
var pluginVersion = require(import_path5.default.join(pluginDir2, "package.json")).version;
|
|
4289
4321
|
async function apply(ctx, config) {
|
|
4290
4322
|
const logger = ctx.logger("dota2tracker");
|
|
4323
|
+
const currentDir = import_path5.default.resolve(__dirname);
|
|
4324
|
+
const pluginVersion = require(import_path5.default.join(currentDir, "..", "package.json")).version;
|
|
4291
4325
|
ctx.dota2tracker = {};
|
|
4292
4326
|
ctx.dota2tracker.i18n = new I18NService(ctx);
|
|
4293
|
-
ctx.dota2tracker.image = new ImageRenderer(ctx,
|
|
4327
|
+
ctx.dota2tracker.image = new ImageRenderer(ctx, currentDir);
|
|
4294
4328
|
ctx.dota2tracker.messageBuilder = new MessageBuilder(ctx);
|
|
4295
4329
|
ctx.dota2tracker.match = new MatchService(ctx, pluginVersion);
|
|
4296
4330
|
ctx.dota2tracker.player = new PlayerService(ctx);
|
|
@@ -4310,7 +4344,7 @@ async function apply(ctx, config) {
|
|
|
4310
4344
|
ctx.dota2tracker.cache = new CacheService(ctx);
|
|
4311
4345
|
ctx.dota2tracker.database = new DatabaseService(ctx);
|
|
4312
4346
|
ctx.dota2tracker.valveAPI = new ValveAPI(ctx);
|
|
4313
|
-
ctx.dota2tracker.stratzAPI = new StratzAPI(ctx,
|
|
4347
|
+
ctx.dota2tracker.stratzAPI = new StratzAPI(ctx, currentDir);
|
|
4314
4348
|
if (config.enableOpenDotaFallback) {
|
|
4315
4349
|
ctx.dota2tracker.opendotaAPI = new OpenDotaAPI(ctx);
|
|
4316
4350
|
ctx.dota2tracker.opendotaAdapter = new OpenDotaAdapter(ctx);
|
|
@@ -4326,7 +4360,7 @@ async function apply(ctx, config) {
|
|
|
4326
4360
|
registerQueryHeroCommand(ctx);
|
|
4327
4361
|
registerQueryItemCommand(ctx);
|
|
4328
4362
|
registerHeroOfTheDayCommand(ctx);
|
|
4329
|
-
if (config.enableConsole) registerConsolePage(ctx);
|
|
4363
|
+
if (ctx.console && config.enableConsole) registerConsolePage(ctx);
|
|
4330
4364
|
}
|
|
4331
4365
|
__name(apply, "apply");
|
|
4332
4366
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -20,6 +20,19 @@ query MatchInfo($matchId: Long!) {
|
|
|
20
20
|
radiantNetworthLeads
|
|
21
21
|
radiantExperienceLeads
|
|
22
22
|
winRates
|
|
23
|
+
|
|
24
|
+
towerStatusRadiant
|
|
25
|
+
towerStatusDire
|
|
26
|
+
barracksStatusRadiant
|
|
27
|
+
barracksStatusDire
|
|
28
|
+
|
|
29
|
+
playbackData {
|
|
30
|
+
buildingEvents {
|
|
31
|
+
time
|
|
32
|
+
npcId
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
23
36
|
players {
|
|
24
37
|
steamAccountId
|
|
25
38
|
steamAccount {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<svg style="display:none"><defs><symbol id="tower" viewBox="0 0 64 64"><rect x="8" y="8" width="48" height="48" fill="#000"/><rect x="14" y="14" width="36" height="36" fill="currentColor"/><rect x="14" y="36" width="36" height="14" fill="#000" fill-opacity="0.2"/></symbol><symbol id="tower_angle" viewBox="0 0 64 64"><path d="M32,4 L8,22 L8,40 L32,58 L56,40 L56,22 Z" stroke="#000" stroke-width="10" stroke-linejoin="round"/><path d="M32,4 L8,22 L8,40 L32,58 L56,40 L56,22 Z" fill="currentColor"/><path d="M8,22 L32,40 L32,58 L8,40 Z" fill="#000" fill-opacity="0.2"/><path d="M56,22 L32,40 L32,58 L56,40 Z" fill="#000" fill-opacity="0.4"/></symbol><symbol id="rax" viewBox="0 0 64 64"><rect x="8" y="8" width="48" height="48" fill="#000"/><rect x="14" y="14" width="36" height="36" fill="currentColor"/><path d="M 14 14 L 14 39 L 19 34 L 19 16 Z" fill="#000" fill-opacity="0.05"/><path d="M 14 14 L 50 14 L 45 16 L 19 16 Z" fill="#000" fill-opacity="0.5"/><path d="M 45 16 L 50 14 L 50 39 L 45 34 Z" fill="#000" fill-opacity="0.25"/><path d="M 14 39 L 19 34 L 45 34 L 50 39 Z" fill="#000" fill-opacity="0.15"/><rect x="14" y="39" width="36" height="11" fill="#000" fill-opacity="0.25"/><rect x="19" y="8" width="26" height="26" fill="#000"/><rect x="20" y="9" width="24" height="24" fill="currentColor"/><path d="M 20 9 L 32 13 L 20 25 Z" fill="#000" fill-opacity="0.05"/><path d="M 20 9 L 32 13 L 44 9 Z" fill="#000" fill-opacity="0.5"/><path d="M 44 9 L 32 13 L 44 25 Z" fill="#000" fill-opacity="0.25"/><path d="M 20 25 L 32 13 L 44 25 Z" fill="#000" fill-opacity="0.15"/><rect x="20" y="25" width="24" height="8" fill="#000" fill-opacity="0.25"/></symbol><symbol id="rax_angle" viewBox="0 0 48 48"><path d="M14 12 L24 4 L34 12 L34 18 L24 26 L14 18 Z" fill="#000" stroke="#000" stroke-width="8" stroke-linejoin="round"/><path d="M6 22 L24 6 L42 22 L42 30 L24 44 L6 30 Z" fill="#000" stroke="#000" stroke-width="8" stroke-linejoin="round"/><path d="M6 22 L24 6 L42 22 L42 30 L24 44 L6 30 Z" fill="currentColor"/><path d="M6 22 L24 36 L24 44 L6 30 Z" fill="#000" fill-opacity="0.25"/><path d="M42 22 L24 36 L24 44 L42 30 Z" fill="#000" fill-opacity="0.35"/><path d="M6 22 L24 13 L24 6 Z" fill="#000" fill-opacity="0.22"/><path d="M42 22 L24 13 L24 6 Z" fill="#000" fill-opacity="0.38"/><path d="M42 22 L24 13 L24 36 Z" fill="#000" fill-opacity="0.32"/><path d="M14 12 L24 4 L34 12 L34 18 L24 26 L14 18 Z" fill="#000" stroke="#000" stroke-width="2"/><path d="M14 12 L24 4 L34 12 L34 18 L24 26 L14 18 Z" fill="currentColor"/><path d="M14 12 L24 20 L24 26 L14 18 Z" fill="#000" fill-opacity="0.25"/><path d="M34 12 L24 20 L24 26 L34 18 Z" fill="#000" fill-opacity="0.35"/><path d="M14 12 L24 4 L24 7Z" fill="#000" fill-opacity="0.22"/><path d="M34 12 L24 4 L24 7Z" fill="#000" fill-opacity="0.38"/><path d="M24 20 L24 7L34 12 Z" fill="#000" fill-opacity="0.32"/></symbol><symbol id="fort" viewBox="0 0 64 64"><path fill="#000" stroke="#000" stroke-width="10" stroke-linejoin="round" d="M 50.5 23.7
|
|
2
|
+
L 48.4 20.2
|
|
3
|
+
L 45.4 15.1 L 41.0 18.0 L 39.1 15.8 L 38.2 9.9 L 37.7 6.9 L 35.3 6.9
|
|
4
|
+
L 32.5 8.3 L 27.2 14.5 L 24.1 19.5 L 22.9 20.2 L 21.7 20.4 L 19.3 19.7
|
|
5
|
+
L 15.4 23.5 L 15.4 24.9 L 19.7 29.6 L 19.5 32.8 L 17.9 34.5 L 15.4 34.9 L 15.3 37.2
|
|
6
|
+
L 23.7 49.2 L 37.1 57.5 L 52.4 37.8 L 52.5 27.7 L 47.1 30.0 L 50.5 23.7 Z"/><path fill="currentColor" d="M 50.5 23.7
|
|
7
|
+
L 48.4 20.2
|
|
8
|
+
L 45.4 15.1 L 41.0 18.0 L 39.1 15.8 L 38.2 9.9 L 37.7 6.9 L 35.3 6.9
|
|
9
|
+
L 32.5 8.3 L 27.2 14.5 L 24.1 19.5 L 22.9 20.2 L 21.7 20.4 L 19.3 19.7
|
|
10
|
+
L 15.4 23.5 L 15.4 24.9 L 19.7 29.6 L 19.5 32.8 L 17.9 34.5 L 15.4 34.9 L 15.3 37.2
|
|
11
|
+
L 23.7 49.2 L 37.1 57.5 L 52.4 37.8 L 52.5 27.7 L 47.1 30.0 L 50.5 23.7 Z"/><path fill="#000" fill-opacity="0.25" d="M 39.1 15.8 L 29.3 28.5 L 31.1 34.2 L 26.5 40.5 L 28.2 49.5 L 37.1 57.5
|
|
12
|
+
L 23.7 49.2 L 15.3 37.2 L 22.1 39.9 L 22.3 34.9 L 21.1 33.3 L 19.5 32.8
|
|
13
|
+
L 19.7 29.6 L 15.4 24.9 C 15.4 25.0 15.4 23.5 15.4 23.5 L 16.8 22.3
|
|
14
|
+
L 23.2 30.1 L 26.1 27.2 L 25.9 23.2 L 27.4 20.6 L 29.1 17.0 L 38.2 9.9 L 39.1 15.8 Z
|
|
15
|
+
M 36.3 52.7 L 41.4 39.0 L 45.8 34.8 L 52.5 31.5 L 52.5 27.7 L 47.1 30.0
|
|
16
|
+
L 44.3 33.0 L 39.9 30.7 L 33.8 36.0 L 38.6 39.0 L 29.7 46.5 L 36.3 52.7 Z"/><path fill="#000" fill-opacity="0.4" d="M 33.8 36.0 L 39.9 30.7 L 44.3 33.0 L 47.1 30.0
|
|
17
|
+
L 50.5 23.7 L 48.4 20.2 L 37.0 27.9 L 41.0 18.0 L 39.1 15.8
|
|
18
|
+
L 29.3 28.5 L 31.1 34.2 L 33.8 36.0 Z
|
|
19
|
+
M 26.5 40.5 L 29.7 46.5 L 36.3 52.7 L 41.4 39.0 L 45.8 34.8 L 52.5 31.5
|
|
20
|
+
L 52.5 37.8 L 37.1 57.5 L 28.2 49.5 L 26.5 40.5 Z"/></symbol></defs></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}
|