@sjtdev/koishi-plugin-dota2tracker 1.0.3

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/lib/index.js ADDED
@@ -0,0 +1,1972 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
+ var __export = (target, all) => {
9
+ for (var name2 in all)
10
+ __defProp(target, name2, { get: all[name2], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ Config: () => Config,
34
+ apply: () => apply,
35
+ inject: () => inject,
36
+ name: () => name,
37
+ usage: () => usage
38
+ });
39
+ module.exports = __toCommonJS(src_exports);
40
+ var import_koishi = require("koishi");
41
+
42
+ // src/utils.ts
43
+ var import_axios = __toESM(require("axios"));
44
+ var import_fs = __toESM(require("fs"));
45
+ var dotaconstants = __toESM(require("dotaconstants"));
46
+ var CONFIGS = { STRATZ_API: { URL: "https://api.stratz.com/graphql", TOKEN: "" } };
47
+ async function query(query_str) {
48
+ return await import_axios.default.post(CONFIGS.STRATZ_API.URL, query_str, {
49
+ headers: {
50
+ "Content-Type": "application/graphql",
51
+ Authorization: `Bearer ${CONFIGS.STRATZ_API.TOKEN}`
52
+ }
53
+ });
54
+ }
55
+ __name(query, "query");
56
+ function getImageUrl(image, type = "local" /* Local */) {
57
+ if (type === "local" /* Local */) {
58
+ try {
59
+ const imageData = import_fs.default.readFileSync(`./node_modules/@sjtdev/koishi-plugin-dota2tracker/images/${image}.png`);
60
+ const base64Data = imageData.toString("base64");
61
+ return `data:image/png;base64,${base64Data}`;
62
+ } catch (error) {
63
+ console.error(error);
64
+ return "";
65
+ }
66
+ } else
67
+ return `https://cdn.cloudflare.steamstatic.com/apps/dota2/images/dota_react/${type}/${image}.png`;
68
+ }
69
+ __name(getImageUrl, "getImageUrl");
70
+ function getFormattedMatchData(match) {
71
+ match.radiantKillsCount = match.radiantKills ? match.radiantKills.reduce((acc, cva) => acc + cva, 0) : 0;
72
+ match.direKillsCount = match.direKills ? match.direKills.reduce((acc, cva) => acc + cva, 0) : 0;
73
+ match.party = {};
74
+ let party_index = 0;
75
+ const party_mark = ["I", "II", "III", "IV"];
76
+ let heroOrderList = {};
77
+ for (let hero of match.pickBans) {
78
+ if (hero.isPick)
79
+ heroOrderList[hero.heroId] = hero.order;
80
+ }
81
+ let processLaneOutcome = /* @__PURE__ */ __name(function(outcome) {
82
+ switch (outcome) {
83
+ case "RADIANT_VICTORY":
84
+ return { radiant: "victory", dire: "fail" };
85
+ case "RADIANT_STOMP":
86
+ return { radiant: "stomp", dire: "stomped" };
87
+ case "DIRE_VICTORY":
88
+ return { radiant: "fail", dire: "victory" };
89
+ case "DIRE_STOMP":
90
+ return { radiant: "stomped", dire: "stomp" };
91
+ default:
92
+ return { radiant: "tie", dire: "tie" };
93
+ }
94
+ }, "processLaneOutcome");
95
+ let laneResult = { top: {}, mid: {}, bottom: {} };
96
+ laneResult.top = processLaneOutcome(match.topLaneOutcome);
97
+ laneResult.mid = processLaneOutcome(match.midLaneOutcome);
98
+ laneResult.bottom = processLaneOutcome(match.bottomLaneOutcome);
99
+ match.players.forEach((player) => {
100
+ player.killContribution = (player.kills + player.assists) / (player.isRadiant ? match.radiantKillsCount : match.direKillsCount);
101
+ player.deathContribution = player.deaths / (player.isRadiant ? match.direKillsCount : match.radiantKillsCount);
102
+ player.titles = [];
103
+ player.mvpScore = // 计算MVP分数
104
+ player.kills * 5 + player.assists * 3 + player.stats.heroDamageReport.dealtTotal.stunDuration / 100 * 0.1 + (player.stats.heroDamageReport.dealtTotal.slowDuration + player.stats.heroDamageReport.dealtTotal.disableDuration) / 100 * 0.05 + player.heroDamage * 1e-3 + player.towerDamage * 0.01 + player.heroHealing * 2e-3 + player.imp * 0.25;
105
+ player.order = heroOrderList[player.hero.id];
106
+ if (player.partyId != null) {
107
+ if (!match.party[player.partyId])
108
+ match.party[player.partyId] = party_mark[party_index++];
109
+ }
110
+ const maxStackCountsByAbilityOrItem = player.stats.matchPlayerBuffEvent.reduce((acc, event) => {
111
+ const key = event.abilityId !== null ? `ability-${event.abilityId}` : `item-${event.itemId}`;
112
+ if (!acc[key] || event.stackCount > acc[key].stackCount) {
113
+ acc[key] = event;
114
+ }
115
+ return acc;
116
+ }, {});
117
+ player.stats.matchPlayerBuffEvent.splice(0, player.stats.matchPlayerBuffEvent.length, ...Object.values(maxStackCountsByAbilityOrItem));
118
+ switch (player.lane) {
119
+ case "SAFE_LANE":
120
+ player.laneResult = laneResult[player.isRadiant ? "bottom" : "top"][player.isRadiant ? "radiant" : "dire"];
121
+ break;
122
+ case "OFF_LANE":
123
+ player.laneResult = laneResult[!player.isRadiant ? "bottom" : "top"][player.isRadiant ? "radiant" : "dire"];
124
+ break;
125
+ default:
126
+ player.laneResult = laneResult.mid[player.isRadiant ? "radiant" : "dire"];
127
+ break;
128
+ }
129
+ let items_timelist = {};
130
+ player.supportItemsCount = { 30: 0, 40: 0, 42: 0, 43: 0, 188: 0 };
131
+ if (player.playbackData) {
132
+ for (let item of player.playbackData.purchaseEvents) {
133
+ items_timelist[item.itemId] = item.time;
134
+ if (item.itemId == 42 || item.itemId == 43)
135
+ items_timelist[218] = item.time;
136
+ switch (item.itemId) {
137
+ case 30:
138
+ case 40:
139
+ case 42:
140
+ case 43:
141
+ case 188:
142
+ player.supportItemsCount[item.itemId]++;
143
+ break;
144
+ }
145
+ }
146
+ }
147
+ player.items = [];
148
+ player.backpacks = [];
149
+ const prefix = "recipe_";
150
+ for (let i = 0; i <= 5; i++) {
151
+ const key = `item${i}Id`;
152
+ const itemId = player[key];
153
+ if (itemId === void 0 || itemId === null) {
154
+ player.items.push(null);
155
+ } else if (dotaconstants.item_ids[itemId]) {
156
+ const name2 = dotaconstants.item_ids[itemId];
157
+ const isRecipe = name2.startsWith(prefix);
158
+ const cleanName = isRecipe ? name2.substring(prefix.length) : name2;
159
+ player.items.push({
160
+ id: itemId,
161
+ name: cleanName,
162
+ time: items_timelist[itemId],
163
+ isRecipe
164
+ });
165
+ } else {
166
+ player.items.push(null);
167
+ }
168
+ }
169
+ for (let i = 0; i <= 2; i++) {
170
+ const key = `backpack${i}Id`;
171
+ const itemId = player[key];
172
+ if (itemId === void 0 || itemId === null) {
173
+ player.backpacks.push(null);
174
+ } else if (dotaconstants.item_ids[itemId]) {
175
+ const name2 = dotaconstants.item_ids[itemId];
176
+ const isRecipe = name2.startsWith(prefix);
177
+ const cleanName = isRecipe ? name2.substring(prefix.length) : name2;
178
+ player.backpacks.push({
179
+ id: itemId,
180
+ name: cleanName,
181
+ time: items_timelist[itemId],
182
+ isRecipe
183
+ });
184
+ } else {
185
+ player.backpacks.push(null);
186
+ }
187
+ }
188
+ if (player.additionalUnit) {
189
+ player.unitItems = [];
190
+ player.unitBackpacks = [];
191
+ const prefix2 = "recipe_";
192
+ for (let i = 0; i <= 5; i++) {
193
+ const key = `item${i}Id`;
194
+ const itemId = player.additionalUnit[key];
195
+ if (itemId === void 0 || itemId === null) {
196
+ player.unitItems.push(null);
197
+ } else if (dotaconstants.item_ids[itemId]) {
198
+ const name2 = dotaconstants.item_ids[itemId];
199
+ const isRecipe = name2.startsWith(prefix2);
200
+ const cleanName = isRecipe ? name2.substring(prefix2.length) : name2;
201
+ player.unitItems.push({
202
+ id: itemId,
203
+ name: cleanName,
204
+ time: items_timelist[itemId],
205
+ isRecipe
206
+ });
207
+ } else {
208
+ player.unitItems.push(null);
209
+ }
210
+ }
211
+ for (let i = 0; i <= 2; i++) {
212
+ const key = `backpack${i}Id`;
213
+ const itemId = player.additionalUnit[key];
214
+ if (itemId === void 0 || itemId === null) {
215
+ player.unitBackpacks.push(null);
216
+ } else if (dotaconstants.item_ids[itemId]) {
217
+ const name2 = dotaconstants.item_ids[itemId];
218
+ const isRecipe = name2.startsWith(prefix2);
219
+ const cleanName = isRecipe ? name2.substring(prefix2.length) : name2;
220
+ player.unitBackpacks.push({
221
+ id: itemId,
222
+ name: cleanName,
223
+ time: items_timelist[itemId],
224
+ isRecipe
225
+ });
226
+ } else {
227
+ player.unitBackpacks.push(null);
228
+ }
229
+ }
230
+ }
231
+ });
232
+ let ComparisonMode;
233
+ ((ComparisonMode2) => {
234
+ ComparisonMode2["Max"] = "max";
235
+ ComparisonMode2["Min"] = "min";
236
+ })(ComparisonMode || (ComparisonMode = {}));
237
+ function findMaxByProperty(primaryProperty, secondaryProperty = null, players = match.players, primaryMode = "max" /* Max */, secondaryMode = "max" /* Max */) {
238
+ return players.reduce((result, player) => {
239
+ const primaryComparison = primaryMode === "max" /* Max */ ? player[primaryProperty] > result[primaryProperty] : player[primaryProperty] < result[primaryProperty];
240
+ const secondaryComparison = secondaryMode === "max" /* Max */ ? player[secondaryProperty] > result[secondaryProperty] : player[secondaryProperty] < result[secondaryProperty];
241
+ if (primaryComparison) {
242
+ return player;
243
+ } else if (player[primaryProperty] === result[primaryProperty] && secondaryProperty && secondaryComparison) {
244
+ return player;
245
+ }
246
+ return result;
247
+ });
248
+ }
249
+ __name(findMaxByProperty, "findMaxByProperty");
250
+ findMaxByProperty(
251
+ "mvpScore",
252
+ void 0,
253
+ match.players.filter((player) => match.didRadiantWin == player.isRadiant)
254
+ ).titles.push({ name: "MVP", color: "#FFA500" });
255
+ findMaxByProperty(
256
+ "mvpScore",
257
+ void 0,
258
+ match.players.filter((player) => match.didRadiantWin != player.isRadiant)
259
+ ).titles.push({ name: "魂", color: "#6cf" });
260
+ findMaxByProperty("networth").titles.push({ name: "富", color: "#FFD700" });
261
+ findMaxByProperty("experiencePerMinute").titles.push({ name: "睿", color: "#8888FF" });
262
+ match.players.reduce(
263
+ (max, player) => player.stats.heroDamageReport.dealtTotal.stunDuration + player.stats.heroDamageReport.dealtTotal.disableDuration + player.stats.heroDamageReport.dealtTotal.slowDuration / 2 > max.stats.heroDamageReport.dealtTotal.stunDuration + max.stats.heroDamageReport.dealtTotal.disableDuration + max.stats.heroDamageReport.dealtTotal.slowDuration / 2 ? player : max
264
+ ).titles.push({ name: "控", color: "#FF00FF" });
265
+ findMaxByProperty("heroDamage").titles.push({ name: "爆", color: "#CC0088" });
266
+ findMaxByProperty("kills", "heroDamage").titles.push({ name: "破", color: "#DD0000" });
267
+ findMaxByProperty("deaths", "networth", void 0, void 0, "min" /* Min */).titles.push({ name: "鬼", color: "#CCCCCC" });
268
+ findMaxByProperty("assists", "heroDamage").titles.push({ name: "助", color: "#006400" });
269
+ findMaxByProperty("towerDamage", "heroDamage").titles.push({ name: "拆", color: "#FEDCBA" });
270
+ findMaxByProperty("heroHealing").titles.push({ name: "奶", color: "#00FF00" });
271
+ match.players.reduce(
272
+ (max, player) => player.stats.heroDamageReport.receivedTotal.physicalDamage + player.stats.heroDamageReport.receivedTotal.magicalDamage + player.stats.heroDamageReport.receivedTotal.pureDamage > max.stats.heroDamageReport.receivedTotal.physicalDamage + max.stats.heroDamageReport.receivedTotal.magicalDamage + max.stats.heroDamageReport.receivedTotal.pureDamage ? player : max
273
+ ).titles.push({ name: "耐", color: "#84A1C7" });
274
+ match.players.reduce((lowest, player) => {
275
+ const currentContribution = (player.kills + player.assists) / (player.isRadiant ? match.radiantKillsCount : match.direKillsCount);
276
+ const lowestContribution = (lowest.kills + lowest.assists) / (lowest.isRadiant ? match.radiantKillsCount : match.direKillsCount);
277
+ if (currentContribution < lowestContribution) {
278
+ return player;
279
+ } else if (currentContribution === lowestContribution) {
280
+ const currentPlayerScore = player.kills + player.assists;
281
+ const lowestPlayerScore = lowest.kills + lowest.assists;
282
+ if (currentPlayerScore < lowestPlayerScore) {
283
+ return player;
284
+ } else if (currentPlayerScore === lowestPlayerScore) {
285
+ return player.heroDamage < lowest.heroDamage ? player : lowest;
286
+ }
287
+ }
288
+ return lowest;
289
+ }).titles.push({ name: "摸", color: "#DDDDDD" });
290
+ return match;
291
+ }
292
+ __name(getFormattedMatchData, "getFormattedMatchData");
293
+
294
+ // src/queries.ts
295
+ var dotaconstants2 = __toESM(require("dotaconstants"));
296
+ function MATCH_INFO(matchId) {
297
+ return `
298
+ {
299
+ match(id: ${matchId}) {
300
+ id
301
+ didRadiantWin
302
+ lobbyType
303
+ gameMode
304
+ regionId
305
+ parsedDateTime
306
+ startDateTime
307
+ endDateTime
308
+ actualRank
309
+ rank
310
+ averageRank
311
+ durationSeconds
312
+ topLaneOutcome
313
+ midLaneOutcome
314
+ bottomLaneOutcome
315
+ radiantKills
316
+ direKills
317
+ players {
318
+ steamAccountId
319
+ steamAccount {
320
+ name
321
+ }
322
+ level
323
+ hero {
324
+ id
325
+ name
326
+ shortName
327
+ }
328
+ dotaPlus{
329
+ level
330
+ }
331
+ leaverStatus
332
+ partyId
333
+ position
334
+ lane
335
+ imp
336
+ kills
337
+ deaths
338
+ assists
339
+ isRadiant
340
+ networth
341
+ steamAccount {
342
+ seasonRank
343
+ seasonLeaderboardRank
344
+ }
345
+ item0Id
346
+ item1Id
347
+ item2Id
348
+ item3Id
349
+ item4Id
350
+ item5Id
351
+ backpack0Id
352
+ backpack1Id
353
+ backpack2Id
354
+ neutral0Id
355
+ stats {
356
+ matchPlayerBuffEvent {
357
+ abilityId
358
+ itemId
359
+ stackCount
360
+ }
361
+ }
362
+ playbackData {
363
+ purchaseEvents {
364
+ itemId
365
+ time
366
+ }
367
+ }
368
+ heroDamage
369
+ towerDamage
370
+ stats {
371
+ heroDamageReport {
372
+ receivedTotal {
373
+ physicalDamage
374
+ magicalDamage
375
+ pureDamage
376
+ }
377
+ }
378
+ }
379
+ numLastHits
380
+ numDenies
381
+ goldPerMinute
382
+ experiencePerMinute
383
+ heroHealing
384
+
385
+ stats {
386
+ campStack
387
+ heroDamageReport {
388
+ dealtTotal {
389
+ stunDuration
390
+ stunCount
391
+ slowDuration
392
+ slowCount
393
+ disableDuration
394
+ disableCount
395
+ }
396
+ }
397
+ }
398
+ additionalUnit{
399
+ item0Id
400
+ item1Id
401
+ item2Id
402
+ item3Id
403
+ item4Id
404
+ item5Id
405
+ backpack0Id
406
+ backpack1Id
407
+ backpack2Id
408
+ neutral0Id
409
+ }
410
+
411
+ isRandom
412
+ }
413
+ pickBans {
414
+ isPick
415
+ bannedHeroId
416
+ heroId
417
+ order
418
+ }
419
+ }
420
+ }
421
+
422
+ `;
423
+ }
424
+ __name(MATCH_INFO, "MATCH_INFO");
425
+ function VERIFYING_PLAYER(steamAccountId) {
426
+ return `
427
+ {
428
+ player(steamAccountId: ${steamAccountId}) {
429
+ matchCount
430
+ }
431
+ }
432
+
433
+ `;
434
+ }
435
+ __name(VERIFYING_PLAYER, "VERIFYING_PLAYER");
436
+ function PLAYERS_LASTMATCH(steamAccountIds) {
437
+ return `
438
+ {
439
+ players(steamAccountIds:${JSON.stringify(steamAccountIds)}) {
440
+ steamAccount{id}
441
+ matches(request:{take:1}){
442
+ id
443
+ parsedDateTime
444
+ startDateTime
445
+ }
446
+ }
447
+ }
448
+
449
+ `;
450
+ }
451
+ __name(PLAYERS_LASTMATCH, "PLAYERS_LASTMATCH");
452
+ function PLAYER_INFO_WITH_25_MATCHES(steamAccountId) {
453
+ return `
454
+ {
455
+ player(steamAccountId: ${steamAccountId}) {
456
+ steamAccount {
457
+ avatar
458
+ name
459
+ seasonRank
460
+ seasonLeaderboardRank
461
+ id
462
+ }
463
+ guildMember {
464
+ guild {
465
+ tag
466
+ }
467
+ }
468
+ matchCount
469
+ winCount
470
+ performance {
471
+ imp
472
+ }
473
+ heroesPerformance(take: 25, request: {matchGroupOrderBy: WIN_COUNT, take: 25}) {
474
+ hero {
475
+ id
476
+ shortName
477
+ }
478
+ imp
479
+ winCount
480
+ matchCount
481
+ }
482
+ matches(request: {take: 25}) {
483
+ id
484
+ rank
485
+ lobbyType
486
+ gameMode
487
+ endDateTime
488
+ durationSeconds
489
+ didRadiantWin
490
+ topLaneOutcome
491
+ midLaneOutcome
492
+ bottomLaneOutcome
493
+ radiantKills
494
+ direKills
495
+ players(steamAccountId: ${steamAccountId}) {
496
+ isRadiant
497
+ lane
498
+ kills
499
+ deaths
500
+ assists
501
+ award
502
+ imp
503
+ hero {
504
+ id
505
+ shortName
506
+ }
507
+ }
508
+ }
509
+ }
510
+
511
+ }
512
+
513
+ `;
514
+ }
515
+ __name(PLAYER_INFO_WITH_25_MATCHES, "PLAYER_INFO_WITH_25_MATCHES");
516
+ function PLAYER_EXTRA_INFO(steamAccountId, matchCount, totalHeroCount) {
517
+ return `{
518
+ player(steamAccountId: ${steamAccountId}) {
519
+ heroesPerformance(take: ${totalHeroCount}, request: {matchGroupOrderBy: MATCH_COUNT, take: ${matchCount}}) {
520
+ hero {
521
+ id
522
+ shortName
523
+ }
524
+ winCount
525
+ matchCount
526
+ imp
527
+ }
528
+ dotaPlus {
529
+ heroId
530
+ level
531
+ }
532
+ }
533
+ }
534
+ `;
535
+ }
536
+ __name(PLAYER_EXTRA_INFO, "PLAYER_EXTRA_INFO");
537
+ function CURRENT_GAMEVERSION() {
538
+ return `
539
+ {
540
+ constants {
541
+ gameVersions{name id}
542
+ }
543
+ }
544
+
545
+ `;
546
+ }
547
+ __name(CURRENT_GAMEVERSION, "CURRENT_GAMEVERSION");
548
+ function ALL_ABILITIES_CHINESE_NAME() {
549
+ return `
550
+ {
551
+ constants {
552
+ abilities(language:S_CHINESE){
553
+ id
554
+ language{displayName}
555
+ }
556
+ gameVersions{name id}
557
+ }
558
+ }
559
+
560
+ `;
561
+ }
562
+ __name(ALL_ABILITIES_CHINESE_NAME, "ALL_ABILITIES_CHINESE_NAME");
563
+ function HERO_INFO(heroId) {
564
+ return `
565
+ {
566
+ constants {
567
+ hero(id: ${heroId}, language: S_CHINESE) {
568
+ id
569
+ name
570
+ shortName
571
+ aliases
572
+ roles {
573
+ roleId
574
+ level
575
+ }
576
+ language {
577
+ displayName
578
+ lore
579
+ hype
580
+ }
581
+ abilities {
582
+ ability(language: S_CHINESE) {
583
+ name
584
+ language {
585
+ displayName
586
+ description
587
+ attributes
588
+ lore
589
+ aghanimDescription
590
+ shardDescription
591
+ notes
592
+ }
593
+ stat {
594
+ type
595
+ behavior
596
+ unitTargetType
597
+ unitTargetTeam
598
+ unitTargetFlags
599
+ unitDamageType
600
+ cooldown
601
+ manaCost
602
+ spellImmunity
603
+ isOnCastbar
604
+ isGrantedByShard
605
+ isGrantedByScepter
606
+ hasShardUpgrade
607
+ hasScepterUpgrade
608
+ }
609
+ }
610
+ }
611
+ talents {
612
+ abilityId
613
+ slot
614
+ }
615
+ }
616
+ }
617
+ }
618
+
619
+ `;
620
+ }
621
+ __name(HERO_INFO, "HERO_INFO");
622
+ function HERO_MATCHUP_WINRATE(heroId) {
623
+ return `
624
+ {
625
+ heroStats {
626
+ matchUp(heroId: ${heroId}, take: ${Object.keys(dotaconstants2.heroes).length - 1},bracketBasicIds:LEGEND_ANCIENT) {
627
+ heroId
628
+ matchCountWith
629
+ matchCountVs
630
+ with {
631
+ heroId1
632
+ winRateHeroId1
633
+ heroId2
634
+ winRateHeroId2
635
+ winCount
636
+ matchCount
637
+ }
638
+ vs {
639
+ heroId1
640
+ winRateHeroId1
641
+ heroId2
642
+ winRateHeroId2
643
+ winCount
644
+ matchCount
645
+ }
646
+ }
647
+ }
648
+ }
649
+
650
+ `;
651
+ }
652
+ __name(HERO_MATCHUP_WINRATE, "HERO_MATCHUP_WINRATE");
653
+
654
+ // src/index.ts
655
+ var import_fs2 = __toESM(require("fs"));
656
+ var cheerio = __toESM(require("cheerio"));
657
+ var import_moment = __toESM(require("moment"));
658
+ var dotaconstants3 = __toESM(require("dotaconstants"));
659
+
660
+ // src/dotaconstants_add.json
661
+ var gameMode = {
662
+ NONE: "",
663
+ ALL_PICK: "全英雄选择",
664
+ CAPTAINS_MODE: "队长模式",
665
+ RANDOM_DRAFT: "随机征召",
666
+ SINGLE_DRAFT: "单一征召",
667
+ ALL_RANDOM: "全英雄随机",
668
+ INTRO: "",
669
+ THE_DIRETIDE: "",
670
+ REVERSE_CAPTAINS_MODE: "",
671
+ THE_GREEVILING: "",
672
+ TUTORIAL: "教程模式",
673
+ MID_ONLY: "",
674
+ LEAST_PLAYED: "",
675
+ NEW_PLAYER_POOL: "",
676
+ COMPENDIUM_MATCHMAKING: "",
677
+ CUSTOM: "",
678
+ CAPTAINS_DRAFT: "",
679
+ BALANCED_DRAFT: "",
680
+ ABILITY_DRAFT: "",
681
+ EVENT: "",
682
+ ALL_RANDOM_DEATH_MATCH: "",
683
+ SOLO_MID: "",
684
+ ALL_PICK_RANKED: "全英雄选择",
685
+ TURBO: "加速模式",
686
+ MUTATION: "",
687
+ UNKNOWN: ""
688
+ };
689
+ var lobbyTypes = {
690
+ INVALID: "",
691
+ UNRANKED: "非天梯",
692
+ PRACTICE: "",
693
+ TOURNAMENT: "",
694
+ TUTORIAL: "",
695
+ COOP_VS_BOTS: "",
696
+ TEAM_MATCH: "",
697
+ SOLO_QUEUE: "",
698
+ RANKED: "天梯比赛",
699
+ SOLO_MID: "",
700
+ BATTLE_CUP: "",
701
+ EVENT: "",
702
+ NEW_PLAYER_POOL: ""
703
+ };
704
+ var region = {
705
+ "1": "US WEST",
706
+ "2": "US EAST",
707
+ "3": "EUROPE",
708
+ "5": "新加坡",
709
+ "6": "DUBAI",
710
+ "7": "AUSTRALIA",
711
+ "8": "STOCKHOLM",
712
+ "9": "AUSTRIA",
713
+ "10": "BRAZIL",
714
+ "11": "SOUTHAFRICA",
715
+ "12": "电信(上海)",
716
+ "13": "联通(一)",
717
+ "14": "CHILE",
718
+ "15": "PERU",
719
+ "16": "INDIA",
720
+ "17": "电信(广东)",
721
+ "18": "电信(浙江)",
722
+ "19": "日本",
723
+ "20": "电信(华中)",
724
+ "25": "联通(二)",
725
+ "37": "TAIWAN",
726
+ "38": "ARGENTINA"
727
+ };
728
+ var position = {
729
+ "1": "优势路",
730
+ "2": "中路",
731
+ "3": "烈士路",
732
+ "4": "采灵芝",
733
+ "5": "工具人"
734
+ };
735
+ var roles = { CARRY: "核心", ESCAPE: "逃生", NUKER: "爆发", INITIATOR: "先手", DURABLE: "耐久", DISABLER: "控制", JUNGLER: "打野", SUPPORT: "辅助", PUSHER: "推进" };
736
+ var primary_attrs = { all: "hero_universal", str: "hero_strength", agi: "hero_agility", int: "hero_intelligence" };
737
+ var behavior = {
738
+ "Unit Target": "单位目标",
739
+ Channeled: "持续施法",
740
+ Hidden: "默认隐藏",
741
+ Passive: "被动",
742
+ "No Target": "无目标",
743
+ Autocast: "自动施法",
744
+ "Instant Cast": "即时施法",
745
+ "Point Target": "点目标",
746
+ "Attack Modifier": "攻击特效",
747
+ AOE: "范围生效"
748
+ };
749
+ var target_team = { Enemy: "敌方单位", Both: "单位", Friendly: "友方单位" };
750
+ var HEROES_CHINESE = {
751
+ "1": ["敌法师", "敌法", "AM"],
752
+ "2": ["斧王"],
753
+ "3": ["祸乱之源", "祸乱", "水桶腰"],
754
+ "4": ["血魔"],
755
+ "5": ["水晶室女", "冰女", "CM"],
756
+ "6": ["卓尔游侠", "小黑"],
757
+ "7": ["撼地者", "小牛", "牛头"],
758
+ "8": ["主宰", "剑圣", "jugg", "奶棒人"],
759
+ "9": ["米拉娜", "白虎", "pom"],
760
+ "10": ["变体精灵", "水人"],
761
+ "11": ["影魔", "影魔王", "SF", "影儿魔儿"],
762
+ "12": ["幻影长矛手", "PL"],
763
+ "13": ["帕克"],
764
+ "14": ["帕吉", "屠夫", "扒鸡", "啪唧"],
765
+ "15": ["雷泽", "电魂", "电棍"],
766
+ "16": ["沙王", "SK"],
767
+ "17": ["风暴之灵", "蓝猫"],
768
+ "18": ["斯温", "流浪剑客", "流浪"],
769
+ "19": ["小小"],
770
+ "20": ["复仇之魂", "复仇", "VS"],
771
+ "21": ["风行者", "风行", "WR"],
772
+ "22": ["宙斯"],
773
+ "23": ["昆卡", "船长"],
774
+ "25": ["莉娜", "火女"],
775
+ "26": ["莱恩", "恶魔巫师", "Lion"],
776
+ "27": ["暗影萨满", "小Y", "小歪"],
777
+ "28": ["斯拉达", "大鱼", "大鱼人"],
778
+ "29": ["潮汐猎人", "潮汐", "西瓜皮"],
779
+ "30": ["巫医"],
780
+ "31": ["巫妖"],
781
+ "32": ["力丸", "隐形刺客", "隐刺"],
782
+ "33": ["谜团"],
783
+ "34": ["修补匠", "TK", "Tinker"],
784
+ "35": ["狙击手", "矮人火枪手", "火枪", "传说哥"],
785
+ "36": ["瘟疫法师", "死灵法", "NEC"],
786
+ "37": ["术士", "Warlock"],
787
+ "38": ["兽王"],
788
+ "39": ["痛苦女王", "女王", "QOP"],
789
+ "40": ["剧毒术士", "剧毒"],
790
+ "41": ["虚空假面", "虚空", "JB脸"],
791
+ "42": ["冥魂大帝", "骷髅王"],
792
+ "43": ["死亡先知", "DP"],
793
+ "44": ["幻影刺客", "幻刺", "PA"],
794
+ "45": ["帕格纳", "骨法", "湮灭法师"],
795
+ "46": ["圣堂刺客", "圣堂", "TA"],
796
+ "47": ["冥界亚龙", "毒龙", "Viper"],
797
+ "48": ["露娜", "月骑", "Luna"],
798
+ "49": ["龙骑士", "龙骑"],
799
+ "50": ["戴泽", "暗影牧师", "暗牧"],
800
+ "51": ["发条技师", "发条"],
801
+ "52": ["拉席克", "老鹿"],
802
+ "53": ["先知"],
803
+ "54": ["噬魂鬼", "小狗"],
804
+ "55": ["黑暗贤者", "黑贤"],
805
+ "56": ["克林克兹", "小骷髅"],
806
+ "57": ["全能骑士", "全能"],
807
+ "58": ["魅惑魔女", "小鹿"],
808
+ "59": ["哈斯卡", "神灵", "神灵武士"],
809
+ "60": ["暗夜魔王", "夜魔"],
810
+ "61": ["育母蜘蛛", "蜘蛛"],
811
+ "62": ["赏金猎人", "赏金"],
812
+ "63": ["编织者", "蚂蚁"],
813
+ "64": ["杰奇洛", "双头龙"],
814
+ "65": ["蝙蝠骑士", "蝙蝠"],
815
+ "66": ["陈", "老陈"],
816
+ "67": ["幽鬼", "SPE", "UG"],
817
+ "68": ["远古冰魄", "冰魂"],
818
+ "69": ["末日使者", "末日", "Doom"],
819
+ "70": ["熊战士", "拍拍", "拍拍熊"],
820
+ "71": ["裂魂人", "白牛"],
821
+ "72": ["矮人直升机", "飞机"],
822
+ "73": ["炼金术士", "炼金"],
823
+ "74": ["祈求者", "卡尔"],
824
+ "75": ["沉默术士", "沉默"],
825
+ "76": ["殁境神蚀者", "黑鸟"],
826
+ "77": ["狼人"],
827
+ "78": ["酒仙", "熊猫", "熊猫酒仙"],
828
+ "79": ["暗影恶魔", "毒狗"],
829
+ "80": ["德鲁伊", "熊德"],
830
+ "81": ["混沌骑士", "混沌", "CK"],
831
+ "82": ["米波"],
832
+ "83": ["树精卫士", "大树", "树精"],
833
+ "84": ["食人魔魔法师", "蓝胖"],
834
+ "85": ["不朽尸王", "尸王"],
835
+ "86": ["拉比克"],
836
+ "87": ["干扰者", "萨尔"],
837
+ "88": ["司夜刺客", "小强"],
838
+ "89": ["娜迦海妖", "小娜迦"],
839
+ "90": ["光之守卫", "光法"],
840
+ "91": ["艾欧", "小精灵"],
841
+ "92": ["维萨吉", "死灵龙", "死灵飞龙"],
842
+ "93": ["斯拉克", "小鱼", "小鱼人"],
843
+ "94": ["美杜莎", "一姐", "美杜莎"],
844
+ "95": ["巨魔战将", "巨魔", "巨馍蘸酱"],
845
+ "96": ["半人马战行者", "人马", "半人马"],
846
+ "97": ["马格纳斯", "猛犸"],
847
+ "98": ["伐木机", "花母鸡"],
848
+ "99": ["钢背兽", "钢背"],
849
+ "100": ["巨牙海民", "海民"],
850
+ "101": ["天怒法师", "天怒"],
851
+ "102": ["亚巴顿"],
852
+ "103": ["上古巨神", "大牛"],
853
+ "104": ["军团指挥官", "军团"],
854
+ "105": ["工程师", "炸弹", "炸弹人"],
855
+ "106": ["灰烬之灵", "火猫"],
856
+ "107": ["大地之灵", "土猫"],
857
+ "108": ["孽主", "大屁股"],
858
+ "109": ["恐怖利刃", "TB"],
859
+ "110": ["凤凰", "烧鸡"],
860
+ "111": ["神谕者", "神谕"],
861
+ "112": ["寒冬飞龙", "冰龙"],
862
+ "113": ["天穹守望者", "电狗"],
863
+ "114": ["齐天大圣", "大圣"],
864
+ "119": ["邪影芳灵", "小仙女", "花仙子"],
865
+ "120": ["石鳞剑士", "滚滚"],
866
+ "121": ["天涯墨客", "墨客"],
867
+ "123": ["森海飞霞", "松鼠", "小松鼠", "小松许"],
868
+ "126": ["虚无之灵", "紫猫"],
869
+ "128": ["电炎绝手", "老奶奶"],
870
+ "129": ["玛尔斯"],
871
+ "135": ["破晓辰星", "大锤"],
872
+ "136": ["玛西"],
873
+ "137": ["獸", "畜"],
874
+ "138": ["琼英碧灵", "奶绿", "绿奶奶"]
875
+ };
876
+ var WIN_NEGATIVE = ["侥幸赢得了比赛", "走狗屎运赢得了比赛", "躺赢了比赛", "打团都没来, 队友4V5赢得了比赛"];
877
+ var WIN_POSITIVE = ["带领团队走向了胜利", "暴打对面后赢得了胜利", " CARRY全场赢得了胜利", "把对面当猪宰了, 赢得了胜利", "又赢了, 这游戏就是这么枯燥, 且乏味", "直接进行一个比赛的赢"];
878
+ var LOSE_NEGATIVE = ["被人按在地上摩擦, 输掉了这场比赛", "悲惨地输掉了比赛", "头都被打歪了, 心态爆炸地输掉了比赛", "捕鱼被鱼吃了, 输掉了比赛", "打的是个几把", "直接进行一个比赛的输"];
879
+ var LOSE_POSITIVE = ["无力回天输掉了比赛", "尽力了, 但还是输了比赛", "背靠世界树, 虽败犹荣", "带不动队友, 输了比赛", "又输了, 很难受, 宁愿输的是我"];
880
+
881
+ // src/index.ts
882
+ var import_koishi2 = require("koishi");
883
+ var name = "dota2tracker";
884
+ var usage = "DOTA2Bot插件-提供自动追踪群友的最新对局的功能(需群友绑定),以及一系列查询功能。";
885
+ var inject = ["database", "puppeteer", "cron"];
886
+ var Config = import_koishi.Schema.object({
887
+ STRATZ_API_TOKEN: import_koishi.Schema.string().required().description("※必须。stratz.com的API TOKEN,可在 https://stratz.com/api 获取")
888
+ });
889
+ var pendingMatches = [];
890
+ var random = new import_koishi2.Random(() => Math.random());
891
+ async function apply(ctx, config) {
892
+ CONFIGS.STRATZ_API.TOKEN = config.STRATZ_API_TOKEN;
893
+ ctx.command("订阅本群", "订阅后还需玩家在本群绑定SteamID").usage("订阅后还需玩家在本群绑定SteamID,BOT将订阅本群中已绑定玩家的新比赛数据,在STRATZ比赛解析完成后将比赛数据生成为图片战报发布至本群中。").action(async ({ session }) => {
894
+ if (session.guild) {
895
+ let currentGuild = (await ctx.database.get("dt_subscribed_guilds", { guildId: session.event.guild.id, platform: session.event.platform }))[0];
896
+ if (currentGuild)
897
+ session.send("本群已订阅,无需重复订阅。");
898
+ else {
899
+ ctx.database.create("dt_subscribed_guilds", { guildId: session.event.guild.id, platform: session.event.platform });
900
+ session.send("订阅成功。");
901
+ }
902
+ }
903
+ });
904
+ ctx.command("取消订阅", "取消订阅本群").action(async ({ session }) => {
905
+ if (session.guild) {
906
+ let cancelingGuild = (await ctx.database.get("dt_subscribed_guilds", { guildId: session.event.guild.id, platform: session.event.platform }))[0];
907
+ if (cancelingGuild) {
908
+ ctx.database.remove("dt_subscribed_guilds", session.event.guild.id);
909
+ session.send("取消订阅成功。");
910
+ return;
911
+ }
912
+ } else
913
+ session.send("本群尚未订阅,无需取消订阅。");
914
+ });
915
+ ctx.command("绑定 <steam_id> [nick_name]", "绑定SteamID,并起一个别名(也可以不起)").usage("将你的SteamID与你的账号绑定,若本群已订阅将会实时获取你的新比赛数据发布至群中。").example("-绑定 123456789").example("-绑定 123456789 张三").action(async ({ session }, steam_id, nick_name) => {
916
+ if (session.guild) {
917
+ if (!steam_id || !/^\d{1,11}$/.test(steam_id)) {
918
+ session.send("SteamID无效。");
919
+ return;
920
+ }
921
+ let sessionPlayer = (await ctx.database.get("dt_subscribed_players", { guildId: session.event.guild.id, platform: session.event.platform, userId: session.event.user.id }))[0];
922
+ if (sessionPlayer) {
923
+ session.send(
924
+ `
925
+ 你已绑定,无需重复绑定。
926
+ 以下是你的个人信息:
927
+ ID:${sessionPlayer.userId}
928
+ 别名:${sessionPlayer.nickName || ""}
929
+ SteamID:${sessionPlayer.steamId}`.replace(/\n\s+/g, " ")
930
+ );
931
+ return;
932
+ }
933
+ let verifyRes = await playerIsInvalid(steam_id);
934
+ if (!verifyRes.isInvalid) {
935
+ session.send(`绑定失败,${verifyRes.reason}`);
936
+ return;
937
+ }
938
+ session.send(
939
+ `
940
+ 绑定成功,
941
+ ID:${session.event.user.id}
942
+ 别名:${nick_name || ""}
943
+ SteamID:${steam_id}`.replace(/\n\s+/g, " ")
944
+ );
945
+ ctx.database.create("dt_subscribed_players", { userId: session.event.user.id, guildId: session.event.guild.id, platform: session.event.platform, steamId: parseInt(steam_id), nickName: nick_name || null });
946
+ }
947
+ });
948
+ ctx.command("取消绑定", "取消绑定你的个人信息").action(async ({ session }) => {
949
+ if (session.guild) {
950
+ let sessionPlayer = (await ctx.database.get("dt_subscribed_players", { guildId: session.event.guild.id, platform: session.event.platform, userId: session.event.user.id }))[0];
951
+ if (sessionPlayer) {
952
+ await ctx.database.remove("dt_subscribed_players", sessionPlayer.id);
953
+ session.send("取消绑定成功。");
954
+ } else
955
+ session.send("尚未绑定,无需取消绑定。");
956
+ }
957
+ });
958
+ ctx.command("改名 <nick_name>", "修改绑定时设定的别名").example("-改名 李四").action(async ({ session }, nick_name) => {
959
+ if (session.guild) {
960
+ let sessionPlayer = (await ctx.database.get("dt_subscribed_players", { guildId: session.event.guild.id, platform: session.event.platform, userId: session.event.user.id }))[0];
961
+ if (sessionPlayer) {
962
+ if (!nick_name) {
963
+ session.send("请输入你的别名。");
964
+ return;
965
+ }
966
+ sessionPlayer.nickName = nick_name;
967
+ await ctx.database.set("dt_subscribed_players", sessionPlayer.id, { nickName: sessionPlayer.nickName });
968
+ session.send(`改名成功,现在你叫${nick_name}了。`);
969
+ } else {
970
+ session.send("请先绑定,绑定时即可设定别名。");
971
+ }
972
+ }
973
+ });
974
+ ctx.command("查询群友", "查询本群已绑定的玩家").action(async ({ session }) => {
975
+ if (session.guild) {
976
+ let queryRes = await ctx.database.get("dt_subscribed_players", { guildId: session.event.guild.id });
977
+ session.send("开发中,未来此功能会重写。\n" + queryRes.map((item) => `${item.nickName ?? ""},ID:${item.userId},SteamID:${item.steamId}`).join("\n"));
978
+ }
979
+ });
980
+ ctx.command("查询比赛 <match_id>", "查询比赛ID").usage("查询指定比赛ID的比赛数据,生成图片发布。").example("-查询比赛 1234567890").action(async ({ session }, match_id) => {
981
+ if (!match_id) {
982
+ session.send("请输入比赛ID。");
983
+ return;
984
+ }
985
+ if (!/^\d{10}$/.test(match_id)) {
986
+ session.send("比赛ID无效。");
987
+ return;
988
+ }
989
+ session.send("正在搜索对局详情,请稍后...");
990
+ try {
991
+ let match;
992
+ let queryLocal = await ctx.database.get("dt_previous_query_results", match_id, ["data"]);
993
+ if (queryLocal.length > 0) {
994
+ match = queryLocal[0].data;
995
+ ctx.database.set("dt_previous_query_results", match.id, { queryTime: /* @__PURE__ */ new Date() });
996
+ } else {
997
+ let queryRes = await query(MATCH_INFO(match_id));
998
+ if (queryRes.status == 200) {
999
+ match = getFormattedMatchData(queryRes.data.data.match);
1000
+ }
1001
+ }
1002
+ if (match.parsedDateTime) {
1003
+ session.send(await ctx.puppeteer.render(genMatchImageHTML(match)));
1004
+ ctx.database.upsert("dt_previous_query_results", (row) => [{ matchId: match.id, data: match, queryTime: /* @__PURE__ */ new Date() }]);
1005
+ } else {
1006
+ pendingMatches.push({ matchId: match_id, platform: session.event.platform, guildId: session.event.guild.id });
1007
+ session.send("比赛尚未解析,将在解析完成后发布。");
1008
+ }
1009
+ } catch (error) {
1010
+ console.error(error);
1011
+ session.send("获取比赛信息失败。");
1012
+ }
1013
+ });
1014
+ ctx.command("查询最近比赛 [input_data]", "查询玩家的最近比赛").usage("查询指定玩家的最近一场比赛的比赛数据,生成图片发布。\n参数可输入该玩家的SteamID或已在本群绑定玩家的别名,无参数时尝试查询调用指令玩家的SteamID").example("-查询最近比赛 123456789").example("-查询最近比赛 张三").action(async ({ session }, input_data) => {
1015
+ if (session.guild) {
1016
+ let sessionPlayer;
1017
+ if (!input_data) {
1018
+ sessionPlayer = (await ctx.database.get("dt_subscribed_players", { guildId: session.event.guild.id, platform: session.event.platform, userId: session.event.user.id }))[0];
1019
+ if (!sessionPlayer) {
1020
+ session.send("无参数时默认从已绑定SteamID玩家中寻找你的信息,但你似乎并没有绑定。\n请在本群绑定SteamID。(可输入【-绑定 -h】获取帮助)\n或在指令后跟上希望查询的SteamID或已绑定玩家的别名。");
1021
+ return;
1022
+ }
1023
+ }
1024
+ let flagBindedPlayer = sessionPlayer || (await ctx.database.get("dt_subscribed_players", { guildId: session.event.guild.id, platform: session.event.platform, nickName: input_data }))[0];
1025
+ if (!(flagBindedPlayer || /^\d{1,11}$/.test(input_data))) {
1026
+ session.send("SteamID不合法并且未在本群找到此玩家。");
1027
+ return;
1028
+ }
1029
+ let lastMatchId = 0;
1030
+ try {
1031
+ session.send("正在搜索对局详情,请稍后...");
1032
+ let queryRes = await query(PLAYERS_LASTMATCH([parseInt(flagBindedPlayer ? flagBindedPlayer.steamId : input_data)]));
1033
+ lastMatchId = queryRes.data.data.players[0].matches[0].id;
1034
+ } catch {
1035
+ session.send("获取玩家最近比赛失败。");
1036
+ return;
1037
+ }
1038
+ try {
1039
+ let match;
1040
+ let queryLocal = await ctx.database.get("dt_previous_query_results", lastMatchId, ["data"]);
1041
+ if (queryLocal.length > 0) {
1042
+ match = queryLocal[0].data;
1043
+ } else {
1044
+ let queryRes = await query(MATCH_INFO(lastMatchId));
1045
+ if (queryRes.status == 200) {
1046
+ match = getFormattedMatchData(queryRes.data.data.match);
1047
+ }
1048
+ }
1049
+ if (match.parsedDateTime) {
1050
+ session.send(await ctx.puppeteer.render(genMatchImageHTML(match)));
1051
+ ctx.database.upsert("dt_previous_query_results", (row) => [{ matchId: match.id, data: match, queryTime: /* @__PURE__ */ new Date() }]);
1052
+ } else {
1053
+ pendingMatches.push({ matchId: lastMatchId, platform: session.event.platform, guildId: session.event.guild.id });
1054
+ session.send("比赛尚未解析,将在解析完成后发布。");
1055
+ }
1056
+ } catch (error) {
1057
+ console.error(error);
1058
+ session.send("获取比赛信息失败。");
1059
+ }
1060
+ }
1061
+ });
1062
+ ctx.command("查询玩家 <input_data>", "查询玩家信息").usage("查询指定玩家的个人信息与最近战绩,生成图片发布。\n参数可输入该玩家的SteamID或已在本群绑定玩家的别名,无参数时尝试查询调用指令玩家的SteamID").example("-查询玩家 123456789").example("-查询玩家 张三").action(async ({ session }, input_data) => {
1063
+ if (session.guild) {
1064
+ let sessionPlayer;
1065
+ if (!input_data) {
1066
+ sessionPlayer = (await ctx.database.get("dt_subscribed_players", { guildId: session.event.guild.id, platform: session.event.platform, userId: session.event.user.id }))[0];
1067
+ if (!sessionPlayer) {
1068
+ session.send("无参数时默认从已绑定SteamID玩家中寻找你的信息,但你似乎并没有绑定。\n请在本群绑定SteamID。(可输入【-绑定 -h】获取帮助)\n或在指令后跟上希望查询的SteamID或已绑定玩家的别名。");
1069
+ return;
1070
+ }
1071
+ }
1072
+ let flagBindedPlayer = sessionPlayer || (await ctx.database.get("dt_subscribed_players", { guildId: session.event.guild.id, platform: session.event.platform, nickName: input_data }))[0];
1073
+ if (!(flagBindedPlayer || /^\d{1,11}$/.test(input_data))) {
1074
+ session.send("SteamID不合法并且未在本群找到此玩家。");
1075
+ return;
1076
+ }
1077
+ session.send("正在获取玩家数据,请稍后...");
1078
+ let steamId = flagBindedPlayer?.steamId ?? input_data;
1079
+ let player;
1080
+ try {
1081
+ let queryRes = await query(PLAYER_INFO_WITH_25_MATCHES(steamId));
1082
+ if (queryRes.status == 200) {
1083
+ player = queryRes.data.data.player;
1084
+ } else
1085
+ throw 0;
1086
+ let queryRes2 = await query(PLAYER_EXTRA_INFO(steamId, player.matchCount, Object.keys(dotaconstants3.heroes).length));
1087
+ if (queryRes2.status == 200) {
1088
+ let playerExtra = queryRes2.data.data.player;
1089
+ let filteredDotaPlus = {};
1090
+ playerExtra.dotaPlus.forEach((item) => {
1091
+ if (!filteredDotaPlus[item.heroId] || filteredDotaPlus[item.heroId].level < item.level) {
1092
+ filteredDotaPlus[item.heroId] = {
1093
+ heroId: item.heroId,
1094
+ level: item.level
1095
+ };
1096
+ }
1097
+ });
1098
+ playerExtra.heroesPerformance.forEach((hero) => {
1099
+ if (filteredDotaPlus[hero.hero.id]) {
1100
+ filteredDotaPlus[hero.hero.id].shortName = hero.hero.shortName;
1101
+ filteredDotaPlus[hero.hero.id].winCount = hero.winCount;
1102
+ filteredDotaPlus[hero.hero.id].matchCount = hero.matchCount;
1103
+ }
1104
+ });
1105
+ player.dotaPlus = Object.values(filteredDotaPlus);
1106
+ player.dotaPlus.sort((a, b) => {
1107
+ if (b.level !== a.level) {
1108
+ return b.level - a.level;
1109
+ }
1110
+ return a.heroId - b.heroId;
1111
+ });
1112
+ player.heroesPerformanceTop10 = playerExtra.heroesPerformance.slice(0, 10);
1113
+ } else
1114
+ throw 0;
1115
+ session.send(await ctx.puppeteer.render(genPlayerHTML(player)));
1116
+ } catch (error) {
1117
+ console.error(error);
1118
+ session.send("获取玩家信息失败。");
1119
+ }
1120
+ }
1121
+ });
1122
+ ctx.command("查询英雄 <input_data>", "查询英雄技能/面板信息").usage("查询英雄的技能说明与各项数据,生成图片发布。\n参数可输入英雄ID、英雄名、英雄常用别名").example("-查询英雄 15").example("-查询英雄 雷泽").example("-查询英雄 电魂").action(async ({ session }, input_data) => {
1123
+ if (input_data) {
1124
+ let dc_heroes = Object.values(dotaconstants3.heroes).map((hero) => ({ id: hero["id"], name: hero["name"], shortName: hero["name"].match(/^npc_dota_hero_(.+)$/)[1] }));
1125
+ let cn_heroes = Object.keys(HEROES_CHINESE).map((key) => ({
1126
+ id: parseInt(key),
1127
+ names_cn: HEROES_CHINESE[key]
1128
+ }));
1129
+ const mergedMap = /* @__PURE__ */ new Map();
1130
+ [dc_heroes, cn_heroes].forEach((array) => {
1131
+ array.forEach((item) => {
1132
+ const existingItem = mergedMap.get(item.id);
1133
+ if (existingItem)
1134
+ mergedMap.set(item.id, { ...existingItem, ...item });
1135
+ else
1136
+ mergedMap.set(item.id, item);
1137
+ });
1138
+ });
1139
+ let heroes3 = Array.from(mergedMap.values());
1140
+ let findingHero = heroes3.find((hero) => hero.names_cn.includes(input_data) || hero.shortName === input_data.toLowerCase() || hero.id == input_data);
1141
+ if (!findingHero) {
1142
+ session.send("未找到输入的英雄,请确认后重新输入。");
1143
+ return;
1144
+ }
1145
+ try {
1146
+ let AbilitiesConstantsCN;
1147
+ let queryRes = await query(CURRENT_GAMEVERSION());
1148
+ if (queryRes.status == 200) {
1149
+ let queryConstants = queryRes.data.data.constants;
1150
+ AbilitiesConstantsCN = (await ctx.database.get("dt_constants_abilities_cn", [1]))[0];
1151
+ if (!AbilitiesConstantsCN || AbilitiesConstantsCN.gameVersionsId < queryConstants.gameVersions[0].id) {
1152
+ session.send("初次使用或版本更新,正在更新英雄技能数据中……");
1153
+ let queryRes2 = await query(ALL_ABILITIES_CHINESE_NAME());
1154
+ if (queryRes2.status == 200) {
1155
+ AbilitiesConstantsCN.data = queryRes2.data.data.constants;
1156
+ await ctx.database.upsert("dt_constants_abilities_cn", (row) => [
1157
+ { id: 1, data: AbilitiesConstantsCN, gameVersionId: queryConstants.gameVersions[0].id, gameVersionName: queryConstants.gameVersions[0].name }
1158
+ ]);
1159
+ } else
1160
+ throw 0;
1161
+ }
1162
+ } else
1163
+ throw 0;
1164
+ let queryRes3 = await query(HERO_INFO(findingHero.id));
1165
+ if (queryRes3.status == 200) {
1166
+ let hero = queryRes3.data.data.constants.hero;
1167
+ hero.talents.forEach((talent) => talent.name_cn = AbilitiesConstantsCN.data.abilities.find((item) => item.id == talent.abilityId).language.displayName);
1168
+ await session.send(await ctx.puppeteer.render(genHeroHTML(hero)));
1169
+ } else
1170
+ throw 0;
1171
+ } catch (error) {
1172
+ console.error(error);
1173
+ session.send("获取数据失败");
1174
+ return;
1175
+ }
1176
+ } else {
1177
+ session.send("请输入参数。");
1178
+ }
1179
+ });
1180
+ ctx.command("查询英雄对战 <input_data:string>", "查询英雄近一周的最佳搭档与最佳克星英雄").usage("根据输入英雄查询最近一周比赛数据(传奇~万古分段)中与该英雄组合胜率最高英雄和与该英雄对抗胜率最低英雄。\n参数可输入英雄ID、英雄名、英雄常用别名").option("limit", "-l <value:number> 返回英雄个数(默认值 5)", { fallback: 5 }).option("filter", "-f <value:number> 过滤场数过低的组合(单位:%,默认值0.75)", { fallback: 0.5 }).example("-查询英雄对战 敌法师 (无额外参数默认返回5个英雄,过滤舍弃场次占比0.75%以下)").example("-查询英雄对战 敌法师 -l=10 -f=1 (返回10个英雄,过滤舍弃场次占比1%以下)").example("-查询英雄对战 敌法师 -l 10 -f 1 (等同于上例,参数接空格也可使用)").action(async ({ session, options }, input_data) => {
1181
+ if (input_data) {
1182
+ let dc_heroes = Object.values(dotaconstants3.heroes).map((hero) => ({ id: hero["id"], name: hero["name"], shortName: hero["name"].match(/^npc_dota_hero_(.+)$/)[1] }));
1183
+ let cn_heroes = Object.keys(HEROES_CHINESE).map((key) => ({
1184
+ id: parseInt(key),
1185
+ names_cn: HEROES_CHINESE[key]
1186
+ }));
1187
+ const mergedMap = /* @__PURE__ */ new Map();
1188
+ [dc_heroes, cn_heroes].forEach((array) => {
1189
+ array.forEach((item) => {
1190
+ const existingItem = mergedMap.get(item.id);
1191
+ if (existingItem)
1192
+ mergedMap.set(item.id, { ...existingItem, ...item });
1193
+ else
1194
+ mergedMap.set(item.id, item);
1195
+ });
1196
+ });
1197
+ let heroes3 = Array.from(mergedMap.values());
1198
+ let findingHero = heroes3.find((hero) => hero.names_cn.includes(input_data) || hero.shortName === input_data.toLowerCase() || hero.id == input_data);
1199
+ if (!findingHero) {
1200
+ session.send("未找到输入的英雄,请确认后重新输入。");
1201
+ return;
1202
+ }
1203
+ try {
1204
+ let queryRes = await query(HERO_MATCHUP_WINRATE(findingHero.id));
1205
+ if (queryRes.status == 200) {
1206
+ let heroStats = queryRes.data.data.heroStats;
1207
+ let withTopFive = heroStats.matchUp[0].with.filter((item) => item.matchCount / heroStats.matchUp[0].matchCountWith > Math.max(0, Math.min(5, options.filter)) / 100).map((item) => {
1208
+ const winRate = item.winCount / item.matchCount;
1209
+ return { ...item, winRate: winRate.toFixed(3) };
1210
+ }).sort((a, b) => b.winRate - a.winRate).slice(0, Math.max(1, Math.min(Object.keys(dotaconstants3.heroes).length - 1, options.limit)));
1211
+ let vsBottomFive = heroStats.matchUp[0].vs.filter((item) => item.matchCount / heroStats.matchUp[0].matchCountVs > Math.max(0, Math.min(5, options.filter)) / 100).map((item) => {
1212
+ const winRate = item.winCount / item.matchCount;
1213
+ return { ...item, winRate: winRate.toFixed(3) };
1214
+ }).sort((a, b) => a.winRate - b.winRate).slice(0, Math.max(1, Math.min(Object.keys(dotaconstants3.heroes).length - 1, options.limit)));
1215
+ session.send(
1216
+ `你查询的英雄是${HEROES_CHINESE[heroStats.matchUp[0].heroId][0]}(ID:${heroStats.matchUp[0].heroId}),
1217
+ 以下是7天内传奇-万古分段比赛数据总结而来的搭档与克制关系
1218
+ 最佳搭档(组合胜率前${options.limit}):${withTopFive.map((item) => `${HEROES_CHINESE[item.heroId2][0]}(胜率${(item.winRate * 100).toFixed(1)}%)`).join("、")}
1219
+ 最佳克星(对抗胜率倒${options.limit}):${vsBottomFive.map((item) => `${HEROES_CHINESE[item.heroId2][0]}(胜率${(item.winRate * 100).toFixed(1)}%)`).join("、")}`
1220
+ );
1221
+ }
1222
+ } catch (error) {
1223
+ console.error(error);
1224
+ session.send("获取数据失败");
1225
+ return;
1226
+ }
1227
+ }
1228
+ });
1229
+ ctx.on("ready", async () => {
1230
+ const tables = await ctx.database.tables;
1231
+ if (!("dt_subscribed_guilds" in tables)) {
1232
+ ctx.model.extend("dt_subscribed_guilds", { id: "unsigned", guildId: "string", platform: "string" }, { autoInc: true });
1233
+ }
1234
+ if (!("dt_subscribed_players" in tables)) {
1235
+ ctx.model.extend("dt_subscribed_players", { id: "unsigned", userId: "string", guildId: "string", platform: "string", steamId: "integer", nickName: "string" }, { autoInc: true });
1236
+ }
1237
+ if (!("dt_sended_match_id" in tables)) {
1238
+ ctx.model.extend("dt_sended_match_id", { matchId: "unsigned", sendTime: "timestamp" }, { primary: "matchId" });
1239
+ }
1240
+ if (!("dt_previous_query_results" in tables)) {
1241
+ ctx.model.extend("dt_previous_query_results", { matchId: "unsigned", data: "json", queryTime: "timestamp" }, { primary: "matchId" });
1242
+ }
1243
+ if (!("dt_constants_abilities_cn" in tables)) {
1244
+ ctx.model.extend("dt_constants_abilities_cn", { id: "unsigned", data: "json", gameVersionId: "unsigned", gameVersionName: "string" }, { primary: "id" });
1245
+ }
1246
+ ctx.cron("0 */6 * * *", () => {
1247
+ const oneMonthAgo = (0, import_moment.default)().subtract(1, "months").toDate();
1248
+ ctx.database.remove("dt_sended_match_id", { sendTime: { $lt: oneMonthAgo } });
1249
+ ctx.database.remove("dt_previous_query_results", { queryTime: { $lt: oneMonthAgo } });
1250
+ });
1251
+ ctx.cron("* * * * *", async function() {
1252
+ const scanningMatches = [...pendingMatches];
1253
+ const subscribedGuilds = await ctx.database.get("dt_subscribed_guilds", void 0);
1254
+ const subscribedPlayersInGuild = (await ctx.database.get("dt_subscribed_players", void 0)).filter((player) => subscribedGuilds.some((guild) => guild.guildId == player.guildId));
1255
+ if (subscribedPlayersInGuild.length > 0) {
1256
+ let queryRes = await query(
1257
+ PLAYERS_LASTMATCH(
1258
+ subscribedPlayersInGuild.map((player) => player.steamId).filter(function(value, index, self) {
1259
+ return self.indexOf(value) === index;
1260
+ })
1261
+ )
1262
+ );
1263
+ for (let player of queryRes.data.data.players) {
1264
+ let lastMatch = player.matches[0];
1265
+ if ((await ctx.database.get("dt_sended_match_id", { matchId: lastMatch.id })).length)
1266
+ continue;
1267
+ if (import_moment.default.unix(lastMatch.startDateTime).isBefore((0, import_moment.default)().subtract(1, "days")))
1268
+ continue;
1269
+ for (let subscribed_player of subscribedPlayersInGuild) {
1270
+ if (player.steamAccount.id == subscribed_player.steamId) {
1271
+ scanningMatches.push({ matchId: lastMatch.id, platform: subscribed_player.platform, guildId: subscribed_player.guildId });
1272
+ ctx.logger.info(`追踪到来自群组${subscribed_player.platform}:${subscribed_player.guildId}-用户${subscribed_player.nickName ?? ""}(${subscribed_player.steamId})的尚未播报过的最新比赛 ${lastMatch.id}。`);
1273
+ }
1274
+ }
1275
+ }
1276
+ }
1277
+ if (scanningMatches.length > 0) {
1278
+ const pendingMatch = scanningMatches[0];
1279
+ try {
1280
+ let match;
1281
+ let queryLocal = await ctx.database.get("dt_previous_query_results", pendingMatch.matchId, ["data"]);
1282
+ if (queryLocal.length > 0) {
1283
+ match = queryLocal[0].data;
1284
+ ctx.database.set("dt_previous_query_results", match.id, { queryTime: /* @__PURE__ */ new Date() });
1285
+ } else {
1286
+ let queryRes = await query(MATCH_INFO(pendingMatch.matchId));
1287
+ if (queryRes.status == 200) {
1288
+ match = queryRes.data.data.match.parsedDateTime ? getFormattedMatchData(queryRes.data.data.match) : queryRes.data.data.match;
1289
+ }
1290
+ }
1291
+ if (match.parsedDateTime || import_moment.default.unix(match.startDateTime).isBefore((0, import_moment.default)().subtract(1, "years"))) {
1292
+ const commingMatches = scanningMatches.filter((item) => item.matchId == match.id);
1293
+ const realCommingMatches = commingMatches.filter((commingMatch, index, self) => index === self.findIndex((t) => t.guildId === commingMatch.guildId && t.platform === commingMatch.platform));
1294
+ let broadMatchMessage = "";
1295
+ const img = await ctx.puppeteer.render(genMatchImageHTML(match));
1296
+ for (let comming of realCommingMatches) {
1297
+ let commingSubscribedPlayers = subscribedPlayersInGuild.filter((item) => item.platform == comming.platform && item.guildId == comming.guildId);
1298
+ let idsToFind = commingSubscribedPlayers.map((item) => item.steamId);
1299
+ let broadPlayers = match.players.filter((item) => idsToFind.includes(item.steamAccountId));
1300
+ for (let player of broadPlayers) {
1301
+ let broadPlayerMessage = `${player.steamAccount.name}的${random.pick(HEROES_CHINESE[player.hero.id])}`;
1302
+ if (player.isRadiant == match.didRadiantWin) {
1303
+ if (player.deathContribution < 0.2 || player.killContribution > 0.75 || player.heroDamage / player.networth > 1.5 || player.towerDamage > 1e4 || player.imp > 0)
1304
+ broadPlayerMessage += random.pick(WIN_POSITIVE);
1305
+ else
1306
+ broadPlayerMessage += random.pick(WIN_NEGATIVE);
1307
+ } else {
1308
+ if (player.deathContribution < 0.25 || player.killContribution > 0.75 || player.heroDamage / player.networth > 1.25 || player.towerDamage > 5e3 || player.imp > 0)
1309
+ broadPlayerMessage += random.pick(LOSE_POSITIVE);
1310
+ else
1311
+ broadPlayerMessage += random.pick(LOSE_NEGATIVE);
1312
+ }
1313
+ broadPlayerMessage += `。
1314
+ KDA:${((player.kills + player.assists) / Math.max(1, player.deaths)).toFixed(2)} [${player.kills}/${player.deaths}/${player.assists}],GPM/XPM:${player.goldPerMinute}/${player.experiencePerMinute},补刀数:${player.numLastHits}/${player.numDenies},伤害/塔伤:${player.heroDamage}/${player.towerDamage},参战/参葬率:${(player.killContribution * 100).toFixed(2)}%/${(player.deathContribution * 100).toFixed(2)}%`;
1315
+ broadMatchMessage += broadPlayerMessage + "\n";
1316
+ }
1317
+ await ctx.broadcast([`${comming.platform}:${comming.guildId}`], broadMatchMessage + img);
1318
+ }
1319
+ ctx.database.upsert("dt_previous_query_results", (row) => [{ matchId: match.id, data: match, queryTime: /* @__PURE__ */ new Date() }]);
1320
+ ctx.database.create("dt_sended_match_id", { matchId: match.id, sendTime: /* @__PURE__ */ new Date() });
1321
+ } else
1322
+ ctx.logger.info("比赛 %d 尚未解析完成,继续等待。", match.id);
1323
+ } catch (error) {
1324
+ console.error(error);
1325
+ }
1326
+ }
1327
+ });
1328
+ });
1329
+ ctx.on("dispose", async () => {
1330
+ });
1331
+ }
1332
+ __name(apply, "apply");
1333
+ function genMatchImageHTML(match) {
1334
+ let $ = cheerio.load(import_fs2.default.readFileSync(`./node_modules/@sjtdev/koishi-plugin-${name}/template/match.html`, "utf-8"));
1335
+ let kcndcStyle = {
1336
+ kc: function(num) {
1337
+ let red = (255 - num * 255 / 100).toFixed(2);
1338
+ return `rgb(255,${red},${red})`;
1339
+ },
1340
+ dc: function(num) {
1341
+ let gray = ((50 - Math.min(num, 50)) * (255 / 50)).toFixed(2);
1342
+ return `rgb(${gray},${gray},${gray})`;
1343
+ }
1344
+ };
1345
+ const laneSVG = {
1346
+ stomp: `<svg viewBox="0 0 24 24" class="hitagi__sc-1apuy4g-0 hmhZOG"><path d="M8.05731 22.3674L9.60454 22.8002L11.5974 21.6551L12.043 20.0773L13.5902 20.51L15.583 19.3649L16.0287 17.7871L17.5759 18.2199L19.5687 17.0748L20.0143 15.4969L21.5615 15.9297L23.5544 14.7846L24 13.2068L23.4492 12.2014L7.50651 21.3621L8.05731 22.3674ZM12.1328 3.50265L11.0312 1.49196C10.8798 1.21549 10.5316 1.11811 10.2576 1.27556L0.29345 7.00098C0.0194354 7.15843 -0.0808273 7.51346 0.0706444 7.78993L1.44766 10.3033L11.91 4.29159C12.184 4.13414 12.2843 3.77912 12.1328 3.50265ZM18.3935 8.4063L14.1658 9.60458L12.4221 10.6065C12.2851 10.6853 12.111 10.6366 12.0353 10.4983L11.7599 9.99565C11.6842 9.85742 11.7343 9.6799 11.8713 9.60118L13.615 8.59924L13.0642 7.59389L11.3205 8.59584C11.1835 8.67456 11.0094 8.62587 10.9337 8.48765L10.6583 7.98497C10.5826 7.84673 10.6327 7.66922 10.7697 7.5905L12.5134 6.58855L11.9626 5.58321L1.99846 11.3086L6.9557 20.3567L22.8984 11.196L22.2615 10.0336C21.5024 8.64813 19.9073 7.97847 18.3935 8.4063Z"></path></svg>`,
1347
+ victory: `<svg viewBox="0 0 512 512"><path d="M198.844 64.75c-.985 0-1.974.03-2.97.094-15.915 1.015-32.046 11.534-37.78 26.937-34.072 91.532-51.085 128.865-61.5 222.876 14.633 13.49 31.63 26.45 50.25 38.125l66.406-196.467 17.688 5.968L163.28 362.5c19.51 10.877 40.43 20.234 62 27.28l75.407-201.53 17.5 6.53-74.937 200.282c19.454 5.096 39.205 8.2 58.78 8.875L381.345 225.5l17.094 7.594-75.875 170.656c21.82-1.237 43.205-5.768 63.437-14.28 43.317-53.844 72.633-109.784 84.5-172.69 5.092-26.992-14.762-53.124-54.22-54.81l-6.155-.282-2.188-5.75c-8.45-22.388-19.75-30.093-31.5-32.47-11.75-2.376-25.267 1.535-35.468 7.376l-13.064 7.47-.906-15c-.99-16.396-10.343-29.597-24.313-35.626-13.97-6.03-33.064-5.232-54.812 9.906l-10.438 7.25-3.812-12.125c-6.517-20.766-20.007-27.985-34.78-27.97zM103.28 188.344C71.143 233.448 47.728 299.56 51.407 359.656c27.54 21.84 54.61 33.693 80.063 35.438 14.155.97 27.94-1.085 41.405-6.438-35.445-17.235-67.36-39.533-92.594-63.53l-3.343-3.157.5-4.595c5.794-54.638 13.946-91.5 25.844-129.03z"/></svg>`,
1348
+ fail: `<svg viewBox="0 0 36 36"><path fill="#ff6961" d="M36 32a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4h28a4 4 0 0 1 4 4v28z"></path><circle fill="#FFF" cx="27" cy="7" r="3"></circle><path fill="#FFF" d="M13.06 13.06l2.367-2.366l3.859 1.158l-2.635 2.847a10.018 10.018 0 0 1 4.392 3.379l5.017-5.017a1.5 1.5 0 0 0-.63-2.497l-9.999-3a1.495 1.495 0 0 0-1.492.376l-3 3a1.5 1.5 0 1 0 2.121 2.12zm16.065 4.949a1.496 1.496 0 0 0-1.262-.503l-6.786.617a9.966 9.966 0 0 1 1.464 2.879l3.548-.322l-1.554 6.995a1.499 1.499 0 1 0 2.928.65l2-9a1.5 1.5 0 0 0-.338-1.316zM13 16a8 8 0 1 0 0 16a8 8 0 0 0 0-16zm0 14a6 6 0 1 1 .002-12.002A6 6 0 0 1 13 30z"></path></svg>`,
1349
+ stomped: `<svg viewBox="-1 0 19 19"><path d="M16.417 9.579A7.917 7.917 0 1 1 8.5 1.662a7.917 7.917 0 0 1 7.917 7.917zm-2.458 2.96a.396.396 0 0 0-.396-.397h-.667a1.527 1.527 0 0 0-1.249-1.114.777.777 0 0 0 .014-.145V9.378a.794.794 0 0 0-.792-.792H8.201a2.984 2.984 0 0 0-1.682-.516l-.11.002V7.42h2.997a.396.396 0 1 0 0-.792H6.41v-1.3a.396.396 0 0 0-.396-.397H4.891a.396.396 0 0 0 0 .792h.727V8.21a2.997 2.997 0 1 0 3.836 3.466h.71a1.526 1.526 0 1 0 2.732 1.26h.667a.396.396 0 0 0 .396-.397zM8.078 9.507a2.205 2.205 0 1 1-1.559-.646 2.19 2.19 0 0 1 1.559.646zm4.078 3.03a.734.734 0 1 1-.733-.734.735.735 0 0 1 .733.733z"/></svg>`,
1350
+ tie: `<svg fill="#fff" viewBox="0 0 512.001 512.001"><g><g><path d="M120.988,239.868c-4.496,10.625-5.122,20.183-5.157,20.811c-0.267,4.607,3.243,8.547,7.849,8.829 c4.618,0.29,8.574-3.228,8.873-7.833c0.265-4.771,2.339-13.092,5.884-19.44C137.421,242.113,141.397,242.649,120.988,239.868z"/></g></g><g><g><path d="M391.178,255.418c-0.211,8.054-2.458,17.62-6.74,28.398c-1.708,4.299,0.393,9.168,4.692,10.875 c4.293,1.708,9.167-0.39,10.875-4.692c5.103-12.842,7.74-24.392,7.943-34.581H391.178z"/></g></g><g><g><path d="M164.769,210.51c1.046,3.339,1.397,6.953,0.893,10.65c-0.293,2.146-0.857,4.188-1.648,6.1c0,0,51.266,3.416,198.065,3.949 c-0.086-6.331,2.19-12.199,6.244-16.732C217.627,214.046,164.769,210.51,164.769,210.51z"/></g></g><g><g><circle cx="37.179" cy="128.669" r="29.491"/></g></g><g><g><path d="M510.146,391.511l-37.916-66.985c14.35-49.173,20.678-68.137,20.678-68.137l8.949-67.014 c1.502-10.977-6.248-21.075-17.235-22.468l-18.183-2.305c-10.984-1.393-20.996,6.445-22.293,17.431l-1.884,15.955l28.718-21.317 l-37.91,42.278h-46.432c-6.571,0-11.898,5.328-11.898,11.898c0,6.57,5.328,11.898,11.898,11.898h51.744 c3.381,0,6.601-1.438,8.859-3.956l41.456-46.234l-32.023,54.694c-5.28,9.018-14.374,8.169-18.293,8.167c-1.959,0-3.31,0-5.295,0 c-0.399,0.898,3.152-7.399-24.44,57.181c-0.548,1.284-0.907,2.642-1.06,4.031l-8.934,80.338 c-0.939,8.447,5.667,15.857,14.208,15.857c7.179,0,13.361-5.401,14.172-12.701l8.702-78.244l21.512-50.353l-14.121,50.463 c-1.158,3.756-0.718,7.823,1.218,11.243l40.949,72.345c3.885,6.864,12.596,9.276,19.459,5.392 C511.615,407.085,514.03,398.373,510.146,391.511z"/></g></g><g><g><circle cx="464.865" cy="128.702" r="29.491"/></g></g><g><g><path d="M142.923,206.051l-59.556-8.118l-39.135-18.451l13.626,2.292c-1.422-10.945-11.411-18.577-22.254-17.202l-18.182,2.305 C6.43,168.271-1.315,178.374,0.186,189.345l9.12,68.689l21.865,70.857l5.829,70.795c0.646,7.848,7.527,13.705,15.401,13.057 c7.859-0.647,13.705-7.542,13.058-15.401l-5.956-72.345c-0.084-1.031-0.281-2.05-0.585-3.039l-14.123-50.463l21.514,50.353 l8.702,78.244c0.873,7.86,7.96,13.486,15.768,12.612c7.838-0.871,13.483-7.931,12.612-15.768l-8.934-80.338 c-0.154-1.388-0.511-2.747-1.06-4.032l-27.336-61.43l-2.945-24.951l-29.029-25.179l40.79,19.231 c1.097,0.517,2.266,0.862,3.468,1.027l61.369,8.365c6.521,0.887,12.509-3.68,13.396-10.183 C153.994,212.936,149.435,206.939,142.923,206.051z"/></g></g></svg>`
1351
+ };
1352
+ let matchInfo_html = `
1353
+ <img src="${getImageUrl("flag_radiant")}" alt="" class="flag radiant${match.didRadiantWin ? " won" : ""}" style="order: 1" />
1354
+ <img src="${getImageUrl("flag_dire")}" alt="" class="flag dire${match.didRadiantWin ? "" : " won"}" style="order: 3" />
1355
+ <p class="won${match.didRadiantWin ? " radiant" : ""}">获胜</p>
1356
+ <div class="details" style="order: 2">
1357
+ <p>比赛编号:<span class="match_id">${match.id}</span></p>
1358
+ <p>模式:<span class="mode">${lobbyTypes[match.lobbyType] || match.lobbyType}/${gameMode[match.gameMode] || match.gameMode}</span></p>
1359
+ <p>服务器:<span class="server">${region[match.regionId]}</span></p>
1360
+ <p>起始时间:<span class="start_time">${(0, import_moment.default)(new Date(match.startDateTime * 1e3)).format("YYYY-MM-DD HH:mm:ss").slice(2)}</span></p>
1361
+ <img src="${getImageUrl("star_" + match.rank?.toString().split("")[1])}" alt="" class="star">
1362
+ <img src="${getImageUrl("medal_" + match.rank?.toString().split("")[0])}" alt="" class="rank">
1363
+ <p>结束时间:<span class="end_time">${(0, import_moment.default)(new Date(match.endDateTime * 1e3)).format("YYYY-MM-DD HH:mm:ss").slice(2)}</span></p>
1364
+ <div class="score">
1365
+ <p class="score radiant">${match.radiantKillsCount}</p>
1366
+ <p class="time">${sec2time(match.durationSeconds)}</p>
1367
+ <p class="score dire">${match.direKillsCount}</p>
1368
+ </div>
1369
+ </div>
1370
+ `;
1371
+ $(".match_info").html(matchInfo_html);
1372
+ let players_html = { radiant: "", dire: "" };
1373
+ match.players.forEach((player) => {
1374
+ players_html[player.isRadiant ? "radiant" : "dire"] += sanitizeHTML`
1375
+ <div class="player${player.hero.id == 80 ? " bear" : ""}${player.leaverStatus != "NONE" && player.leaverStatus != "DISCONNECTED" ? " giveup" : ""}" style="order:${player.position?.slice(-1)}">
1376
+ <div class="hero">
1377
+ <div class="player_avatar">
1378
+ <img alt="" src="${getImageUrl(player.hero.shortName, "heroes" /* Heroes */)}" />
1379
+ <p class="party_line${player.partyId != null ? " party_" + match.party[player.partyId] : ""}"></p>
1380
+ <p class="party_mark${player.partyId != null ? " party_" + match.party[player.partyId] : ""}"></p>
1381
+ <p class="position p${Math.floor(player.order / 4) + 1}">${player.isRandom ? "随机" : `第<span>${player.order ? player.order + 1 : "-"}</span>手`}<br/>${position[player.position?.slice(-1)]}</p>
1382
+ <p class="level">${player.level}</p>
1383
+ </div>
1384
+ <div class="player_info">
1385
+ <summary class="player_name">${player.steamAccount.name}</summary>
1386
+ <summary class="player_performance">
1387
+ <span class="kda">${player.kills}/${player.deaths}/${player.assists}</span>&nbsp;&nbsp;
1388
+ <span class="kc" style="color:${kcndcStyle.kc(player.killContribution * 100)}">${Math.floor(player.killContribution * 100)}%</span>&nbsp;&nbsp;
1389
+ <span class="dc" style="color:${kcndcStyle.dc(player.deathContribution * 100)}">${Math.floor(player.deathContribution * 100)}%</span></summary>
1390
+ <summary class="player_net"><span class="networth">${player.networth}</span>&emsp;<span class="score">${(player.heroDamage / player.networth)?.toFixed(2)}</span></summary>
1391
+ </div>
1392
+ <div class="player_lane ${player.laneResult}">
1393
+ ${laneSVG[player.laneResult]}
1394
+ </div>
1395
+ <div class="player_rank">
1396
+ ${player.steamAccount.seasonRank ? `
1397
+ <div class="rank">
1398
+ <img class="medal" src="${getImageUrl(
1399
+ "medal_" + (player.steamAccount.seasonLeaderboardRank ? player.steamAccount.seasonLeaderboardRank <= 100 ? player.steamAccount.seasonLeaderboardRank <= 10 ? "8c" : "8b" : player.steamAccount.seasonRank.toString().split("")[0] : player.steamAccount.seasonRank.toString().split("")[0])
1400
+ )}" alt="" />
1401
+ ${!player.steamAccount.seasonLeaderboardRank ? `
1402
+ <img class="star" src="${getImageUrl("star_" + player.steamAccount.seasonRank.toString().split("")[1])}" alt="" />` : `
1403
+ <p>${player.steamAccount.seasonLeaderboardRank}</p>`}
1404
+ </div>` : `
1405
+ <div class="norank">
1406
+ <img class="medal" src="${getImageUrl("medal_0")}" alt="" />
1407
+ </div>`}
1408
+ <div class="dotaPlusLevel"${!player.dotaPlus ? ` style="display:none"` : ""}>
1409
+ <img src="${getImageUrl("hero_badge_" + (player.dotaPlus ? Math.ceil((player.dotaPlus?.level + 1) / 6) : 1))}" alt="" class="badge">
1410
+ <p class="level">${player.dotaPlus?.level}</p>
1411
+ </div>
1412
+ </div>
1413
+ </div>
1414
+ <div class="titles">
1415
+ ${player.titles.map((item) => `<span style="color: ${item.color};">${item.name}</span>`).join("&nbsp;")}
1416
+ </div>
1417
+ ${player.hero.id != 80 ? `
1418
+ <div class="items">
1419
+ <div class="items_normal">
1420
+ ${player.items.map(
1421
+ (item) => item ? `
1422
+ <div class="item${item.isRecipe ? " recipe" : ""}">
1423
+ <img src="${getImageUrl(item.name, "items" /* Items */)}" alt="" />
1424
+ <p class="time">${sec2time(item.time)}</p>
1425
+ </div>` : `
1426
+ <div class="item" style="visibility:hidden"}">
1427
+ <img src="${getImageUrl("blink", "items" /* Items */)}" alt="" />
1428
+ <p class="time">--:--</p>
1429
+ </div>`
1430
+ ).join("")}
1431
+ </div>
1432
+ <div class="items_backpack">
1433
+ ${player.backpacks.map(
1434
+ (item) => item ? `
1435
+ <div class="item back${item.isRecipe ? " recipe" : ""}">
1436
+ <img src="${getImageUrl(item.name, "items" /* Items */)}" alt="" />
1437
+ <p class="time">${sec2time(item.time)}</p>
1438
+ </div>` : `
1439
+ <div class="item back" style="visibility:hidden"}">
1440
+ <img src="${getImageUrl("blink", "items" /* Items */)}" alt="" />
1441
+ <p class="time">--:--</p>
1442
+ </div>`
1443
+ ).join("")}
1444
+ </div>
1445
+ <div class="item neutral" style="background-image: url(${getImageUrl(dotaconstants3.item_ids[player.neutral0Id], "items" /* Items */)})"></div>
1446
+ </div>
1447
+ <div class="buffs">
1448
+ <section>
1449
+ ${player.stats?.matchPlayerBuffEvent?.map(
1450
+ (buff) => `
1451
+ <div class="buff">
1452
+ <img src="${getImageUrl(dotaconstants3[buff.abilityId ? "ability_ids" : "item_ids"][buff.abilityId ?? buff.itemId], buff.abilityId ? "abilities" /* Abilities */ : "items" /* Items */)}" alt="" />
1453
+ <p>${buff.stackCount ?? ""}</p>
1454
+ </div>`
1455
+ ).join("")}
1456
+ </section>
1457
+ <section>
1458
+ <div class="support_item"${player.supportItemsCount[30] > 0 ? "" : ' style="display:none"'}>
1459
+ <img src="${getImageUrl("gem", "items" /* Items */)}" alt="" />
1460
+ <p>${player.supportItemsCount[30]}</p>
1461
+ </div>
1462
+ <div class="support_item"${player.supportItemsCount[40] > 0 ? "" : ' style="display:none"'}>
1463
+ <img src="${getImageUrl("dust", "items" /* Items */)}" alt="" />
1464
+ <p>${player.supportItemsCount[40]}</p>
1465
+ </div>
1466
+ <div class="support_item"${player.supportItemsCount[42] > 0 ? "" : ' style="display:none"'}>
1467
+ <img src="${getImageUrl("ward_observer", "items" /* Items */)}" alt="" />
1468
+ <p>${player.supportItemsCount[42]}</p>
1469
+ </div>
1470
+ <div class="support_item"${player.supportItemsCount[43] > 0 ? "" : ' style="display:none"'}>
1471
+ <img src="${getImageUrl("ward_sentry", "items" /* Items */)}" alt="" />
1472
+ <p>${player.supportItemsCount[43]}</p>
1473
+ </div>
1474
+ <div class="support_item"${player.supportItemsCount[188] > 0 ? "" : ' style="display:none"'}>
1475
+ <img src="${getImageUrl("smoke_of_deceit", "items" /* Items */)}" alt="" />
1476
+ <p>${player.supportItemsCount[188]}</p>
1477
+ </div>
1478
+ </section>
1479
+ </div>` : `
1480
+ <div class="items_buffs master">
1481
+ <div class="items master">
1482
+ ${player.items.map(
1483
+ (item) => item ? `
1484
+ <div class="item${item.isRecipe ? " recipe" : ""}">
1485
+ <img src="${getImageUrl(item.name, "items" /* Items */)}" alt="" />
1486
+ <p class="time">${sec2time(item.time)}</p>
1487
+ </div>` : `
1488
+ <div class="item" style="visibility:hidden"}">
1489
+ <img src="${getImageUrl("blink", "items" /* Items */)}" alt="" />>
1490
+ <p class="time">--:--</p>
1491
+ </div>`
1492
+ ).join("")}
1493
+ ${player.backpacks.map(
1494
+ (item) => item ? `
1495
+ <div class="item back${item.isRecipe ? " recipe" : ""}">
1496
+ <img src="${getImageUrl(item.name, "items" /* Items */)}" alt="" />
1497
+ <p class="time">${sec2time(item.time)}</p>
1498
+ </div>` : `
1499
+ <div class="item back" style="visibility:hidden"}">
1500
+ <img src="${getImageUrl("blink", "items" /* Items */)}" alt="" />
1501
+ <p class="time">--:--</p>
1502
+ </div>`
1503
+ ).join("")}
1504
+ <div class="item neutral">
1505
+ <img src="${getImageUrl(dotaconstants3.item_ids[player.neutral0Id], "items" /* Items */)}" alt="" />
1506
+ </div>
1507
+ </div>
1508
+ <div class="buffs master">
1509
+ ${player.stats?.matchPlayerBuffEvent?.map(
1510
+ (buff) => `
1511
+ <div class="buff">
1512
+ <img src="${getImageUrl(dotaconstants3[buff.abilityId ? "ability_ids" : "item_ids"][buff.abilityId ?? buff.itemId], buff.abilityId ? "abilities" /* Abilities */ : "items" /* Items */)}" alt="" />
1513
+ <p>${buff.stackCount ?? ""}</p>
1514
+ </div>`
1515
+ ).join("")}
1516
+ </div>
1517
+ </div>
1518
+ <div class="items_buffs slave">
1519
+ <div class="items slave">
1520
+ ${player.unitItems.map(
1521
+ (item) => item ? `
1522
+ <div class="item${item.isRecipe ? " recipe" : ""}">
1523
+ <img src="${getImageUrl(item.name, "items" /* Items */)}" alt="" />
1524
+ <p class="time">${sec2time(item.time)}</p>
1525
+ </div>` : `
1526
+ <div class="item" style="visibility:hidden"}">
1527
+ <img src="${getImageUrl("blink", "items" /* Items */)}" alt="" />>
1528
+ <p class="time">--:--</p>
1529
+ </div>`
1530
+ ).join("")}
1531
+ ${player.unitBackpacks.map(
1532
+ (item) => item ? `
1533
+ <div class="item back${item.isRecipe ? " recipe" : ""}">
1534
+ <img src="${getImageUrl(item.name, "items" /* Items */)}" alt="" />
1535
+ <p class="time">${sec2time(item.time)}</p>
1536
+ </div>` : `
1537
+ <div class="item back" style="visibility:hidden"}">
1538
+ <img src="${getImageUrl("blink", "items" /* Items */)}" alt="" />
1539
+ <p class="time">--:--</p>
1540
+ </div>`
1541
+ ).join("")}
1542
+ <div class="item neutral">
1543
+ <img src="${getImageUrl(dotaconstants3.item_ids[player.additionalUnit.neutral0Id], "items" /* Items */)}" alt="" />
1544
+ </div>
1545
+ </div>
1546
+ <div class="buffs_supportItems slave">
1547
+ <div class="buffs">
1548
+ <!-- 无有效API获取熊灵buff -->
1549
+ </div>
1550
+ <div class="support_items">
1551
+ <div class="support_item"${player.supportItemsCount[30] > 0 ? "" : ' style="display:none"'}>
1552
+ <img src="${getImageUrl("gem", "items" /* Items */)}" alt="" />
1553
+ <p>${player.supportItemsCount[30]}</p>
1554
+ </div>
1555
+ <div class="support_item"${player.supportItemsCount[40] > 0 ? "" : ' style="display:none"'}>
1556
+ <img src="${getImageUrl("dust", "items" /* Items */)}" alt="" />
1557
+ <p>${player.supportItemsCount[40]}</p>
1558
+ </div>
1559
+ <div class="support_item"${player.supportItemsCount[42] > 0 ? "" : ' style="display:none"'}>
1560
+ <img src="${getImageUrl("ward_observer", "items" /* Items */)}" alt="" />
1561
+ <p>${player.supportItemsCount[42]}</p>
1562
+ </div>
1563
+ <div class="support_item"${player.supportItemsCount[43] > 0 ? "" : ' style="display:none"'}>
1564
+ <img src="${getImageUrl("ward_sentry", "items" /* Items */)}" alt="" />
1565
+ <p>${player.supportItemsCount[43]}</p>
1566
+ </div>
1567
+ <div class="support_item"${player.supportItemsCount[188] > 0 ? "" : ' style="display:none"'}>
1568
+ <img src="${getImageUrl("smoke_of_deceit", "items" /* Items */)}" alt="" />
1569
+ <p>${player.supportItemsCount[188]}</p>
1570
+ </div>
1571
+ </div>
1572
+ </div>
1573
+ </div>`}
1574
+ <div class="details">
1575
+ <section>英雄伤害:<span class="hero_damage">${player.heroDamage}</span></section>
1576
+ <section>建筑伤害:<span class="building_damage">${player.towerDamage}</span></section>
1577
+ <section>受到伤害(减免后):<span class="tak">${player.stats?.heroDamageReport?.receivedTotal.physicalDamage + player.stats?.heroDamageReport?.receivedTotal.magicalDamage + player.stats?.heroDamageReport?.receivedTotal.pureDamage}</span></section>
1578
+ <section>补刀:<span class="lh">${player.numLastHits}</span>/<span class="dn">${player.numDenies}</span></section>
1579
+ <section>GPM/XPM:<span class="gpm">${player.goldPerMinute}</span>/<span class="xpm">${player.experiencePerMinute}</span></section>
1580
+ <section>治疗量:<span class="heal">${player.heroHealing}</span></section>
1581
+ <section>控制时间:<span class="building_damage">${(player.stats?.heroDamageReport?.dealtTotal.stunDuration / 100).toFixed(2)}/${(player.stats?.heroDamageReport?.dealtTotal.slowDuration / 100).toFixed(2)}/${(player.stats?.heroDamageReport?.dealtTotal.disableDuration / 100).toFixed(2)}</span>s</section>
1582
+ </div>
1583
+ </div>`;
1584
+ });
1585
+ $(".radiant_players").html(players_html.radiant);
1586
+ $(".dire_players").html(players_html.dire);
1587
+ $(".ban_list").html(
1588
+ match.pickBans.filter((hero) => !hero.isPick).map((hero) => `<div class="ban_hero"><img src="${getImageUrl(/^npc_dota_hero_(?<name>.+)$/.exec(dotaconstants3.heroes[hero.bannedHeroId].name)[1], "heroes" /* Heroes */)}" alt="" /></div>`).join("")
1589
+ );
1590
+ if (process.env.NODE_ENV === "development")
1591
+ import_fs2.default.writeFileSync("./node_modules/@sjtdev/koishi-plugin-dota2tracker/temp.html", $.html());
1592
+ return $.html();
1593
+ }
1594
+ __name(genMatchImageHTML, "genMatchImageHTML");
1595
+ function genHeroHTML(hero) {
1596
+ let $ = cheerio.load(import_fs2.default.readFileSync(`./node_modules/@sjtdev/koishi-plugin-${name}/template/hero.html`, "utf-8"));
1597
+ let html = `
1598
+ <div class="hero" id="${hero.id}">
1599
+ <img src="${getImageUrl(hero.shortName, "heroes" /* Heroes */)}" alt="" />
1600
+ <img class="pri_attr" src="${getImageUrl(primary_attrs[dotaconstants3.heroes[hero.id].primary_attr], "icons" /* Icons */)}" alt="" />
1601
+ <div class="info">
1602
+ <p class="name">${hero.language.displayName}</p>
1603
+ <p class="roles">
1604
+ ${hero.roles.map((item) => `<span class="role level${item.level}">${roles[item.roleId]}</span>`).join("")}
1605
+ </p>
1606
+ <p class="attrs">
1607
+ <span class="str">${dotaconstants3.heroes[hero.id].base_str} <span class="gain">+${dotaconstants3.heroes[hero.id].str_gain.toFixed(1)}</span></span>
1608
+ <span class="agi">${dotaconstants3.heroes[hero.id].base_agi} <span class="gain">+${dotaconstants3.heroes[hero.id].agi_gain.toFixed(1)}</span></span>
1609
+ <span class="int">${dotaconstants3.heroes[hero.id].base_int} <span class="gain">+${dotaconstants3.heroes[hero.id].int_gain.toFixed(1)}</span></span>
1610
+ </p>
1611
+ </div>
1612
+ </div>
1613
+ <div class="details">
1614
+ <div class="hype_talents">
1615
+ <div class="hype">
1616
+ ${hero.language.hype}
1617
+ </div>
1618
+ <div class="talents">
1619
+ <div class="talent">
1620
+ <div class="left">${hero.talents[7].name_cn}</div>
1621
+ <div class="level">25</div>
1622
+ <div class="right">${hero.talents[6].name_cn}</div>
1623
+ </div>
1624
+ <div class="talent">
1625
+ <div class="left">${hero.talents[5].name_cn}</div>
1626
+ <div class="level">20</div>
1627
+ <div class="right">${hero.talents[4].name_cn}</div>
1628
+ </div>
1629
+ <div class="talent">
1630
+ <div class="left">${hero.talents[3].name_cn}</div>
1631
+ <div class="level">15</div>
1632
+ <div class="right">${hero.talents[2].name_cn}</div>
1633
+ </div>
1634
+ <div class="talent">
1635
+ <div class="left">${hero.talents[1].name_cn}</div>
1636
+ <div class="level">10</div>
1637
+ <div class="right">${hero.talents[0].name_cn}</div>
1638
+ </div>
1639
+ </div>
1640
+ </div>
1641
+ <table class="list">
1642
+ <tbody>
1643
+ <tr>
1644
+ <td>初始生命值</td>
1645
+ <td>${dotaconstants3.heroes[hero.id].base_health + dotaconstants3.heroes[hero.id].base_str * 22}</td>
1646
+ </tr>
1647
+ <tr>
1648
+ <td>初始生命回复</td>
1649
+ <td>${dotaconstants3.heroes[hero.id].base_health_regen}</td>
1650
+ </tr>
1651
+ <tr>
1652
+ <td>初始魔法值</td>
1653
+ <td>${dotaconstants3.heroes[hero.id].base_mana + dotaconstants3.heroes[hero.id].base_int * 12}</td>
1654
+ </tr>
1655
+ <tr>
1656
+ <td>初始魔法回复</td>
1657
+ <td>${dotaconstants3.heroes[hero.id].base_mana_regen}</td>
1658
+ </tr>
1659
+ <tr>
1660
+ <td>初始攻击力</td>
1661
+ <td>${dotaconstants3.heroes[hero.id].base_mr + Math.round(
1662
+ dotaconstants3.heroes[hero.id].primary_attr == "all" ? (dotaconstants3.heroes[hero.id].base_str + dotaconstants3.heroes[hero.id].base_agi + dotaconstants3.heroes[hero.id].base_int) * 0.7 : dotaconstants3.heroes[hero.id]["base_" + dotaconstants3.heroes[hero.id].primary_attr]
1663
+ )}(${dotaconstants3.heroes[hero.id].base_attack_min + Math.round(
1664
+ dotaconstants3.heroes[hero.id].primary_attr == "all" ? (dotaconstants3.heroes[hero.id].base_str + dotaconstants3.heroes[hero.id].base_agi + dotaconstants3.heroes[hero.id].base_int) * 0.7 : dotaconstants3.heroes[hero.id]["base_" + dotaconstants3.heroes[hero.id].primary_attr]
1665
+ )}~${dotaconstants3.heroes[hero.id].base_attack_max + Math.round(
1666
+ dotaconstants3.heroes[hero.id].primary_attr == "all" ? (dotaconstants3.heroes[hero.id].base_str + dotaconstants3.heroes[hero.id].base_agi + dotaconstants3.heroes[hero.id].base_int) * 0.7 : dotaconstants3.heroes[hero.id]["base_" + dotaconstants3.heroes[hero.id].primary_attr]
1667
+ )})</td>
1668
+ </tr>
1669
+ <tr>
1670
+ <td>基础攻击间隔</td>
1671
+ <td>${dotaconstants3.heroes[hero.id].attack_rate.toFixed(1)}</td>
1672
+ </tr>
1673
+ <tr>
1674
+ <td>基础攻击前摇</td>
1675
+ <td>${dotaconstants3.heroes[hero.id].attack_point.toFixed(1)}</td>
1676
+ </tr>
1677
+ <tr>
1678
+ <td>攻击范围</td>
1679
+ <td>${dotaconstants3.heroes[hero.id].attack_range}</td>
1680
+ </tr>
1681
+ <tr>
1682
+ <td>护甲</td>
1683
+ <td>${(Math.round((dotaconstants3.heroes[hero.id].base_armor + dotaconstants3.heroes[hero.id].base_agi * 0.167) * 10) / 10).toFixed(1)}</td>
1684
+ </tr>
1685
+ <tr>
1686
+ <td>移动速度</td>
1687
+ <td>${dotaconstants3.heroes[hero.id].move_speed}</td>
1688
+ </tr>
1689
+ <tr>
1690
+ <td>视野范围</td>
1691
+ <td>${dotaconstants3.heroes[hero.id].day_vision}(${dotaconstants3.heroes[hero.id].night_vision})</td>
1692
+ </tr>
1693
+ </tbody>
1694
+ </table>
1695
+ </div>
1696
+ <div class="skills">
1697
+ ${hero.abilities.filter((item) => dotaconstants3.abilities[item.ability.name].behavior != "Hidden").map(
1698
+ (item) => `
1699
+ <div class="skill">
1700
+ <p class="title">${item.ability.language.displayName}</p>
1701
+ ${item.ability.stat.isGrantedByScepter ? `<svg class="scepter" viewBox="0 0 19 20" fill="hsla(0,0%,100%,0.16)" width="24"><path d="M4.795 14.99a2.06 2.06 0 00-.96-.388c-1.668-.204-2.506.518-3.107 1.008.464.128.879.364.867.97 2.347-1.605 4.159.84 2.415 2.666-.14.147.65.929.767.718.203-.365.79-1.064 1.445-1.064.964 0 1.529.68 1.823.838.267.144.793-.372.642-.675-.03-.06-.229-.204-.569-.438-1.407-.197-1.935-1.093-2.37-2.026-.276-.593-.503-1.206-.953-1.61zm9.41 0a2.06 2.06 0 01.96-.388c1.668-.204 2.507.518 3.107 1.008-.464.128-.879.364-.867.97-2.347-1.605-4.158.84-2.415 2.666.14.147-.65.929-.768.718-.202-.365-.79-1.064-1.444-1.064-.965 0-1.529.68-1.823.838-.267.144-.793-.372-.642-.675.03-.06.229-.204.569-.438 1.407-.197 1.935-1.093 2.37-2.026.276-.593.503-1.206.953-1.61zm-3.919-2.211c0-.233-.175-.423-.392-.423h-.788c-.217 0-.392.19-.392.423v5.665c0 .232.175.421.392.421h.788c.216 0 .392-.189.392-.421v-5.665zm-1.989 2.543c-.553-.139-2.074-.563-2.702-1.17-.814-.784-1.107-3.135-2.655-3.52-1.29-.32-2.448.27-2.924 1.05-.06.101.055.241.252.178 2.786-.884 2.957 1.674 2.672 2.215a.275.275 0 00-.024.057c.87-.106 1.462.043 1.893.328.447.294.732.738.975 1.231.515 1.042.822 2.335 2.513 2.512v-2.88zm2.406 0c.553-.139 2.074-.563 2.703-1.17.812-.784 1.106-3.135 2.654-3.52 1.29-.32 2.448.27 2.924 1.05.06.101-.055.241-.252.178-2.786-.884-2.957 1.674-2.672 2.215a.27.27 0 01.024.057c-.87-.106-1.462.043-1.893.328-.447.294-.732.738-.975 1.231-.515 1.042-.822 2.335-2.513 2.512v-2.88z" fill="hsla(0,0%,100%,0.6)"></path><path d="M9.753.093a.39.39 0 00-.506 0C8.461.747 6.08 2.946 5.515 3.417a.434.434 0 00-.15.262c-.162.895-.949 4.817-1.12 5.764a.46.46 0 00.067.333c.37.564 1.665 2.752 2.071 3.37a.404.404 0 00.336.187h.768c.19 0 .356-.14.4-.337l.35-1.577a.416.416 0 01.399-.336h1.728c.19 0 .356.139.399.336l.35 1.577a.416.416 0 00.4.337h.768c.133 0 .259-.07.336-.187.406-.618 1.7-2.806 2.07-3.37a.457.457 0 00.067-.333c-.17-.947-.957-4.87-1.118-5.764a.435.435 0 00-.15-.262C12.92 2.946 10.537.747 9.752.093z" fill="url(#activeAghanimScepterGradient)"></path><defs><radialGradient id="activeAghanimScepterGradient" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(9.03623 10.4684) rotate(-90) scale(9.38905 7.0456)"><stop stop-color="#00CEFF"></stop><stop offset="1" stop-color="#3443C4"></stop></radialGradient></defs></svg>` : ""}
1702
+ ${item.ability.stat.isGrantedByShard ? `<svg class="shard" viewBox="0 0 19 10" fill="hsla(0,0%,100%,0.16)" width="24"><path d="M0.259504 4.54746C0.272981 4.60325 0.326002 4.64198 0.386194 4.64198C0.831857 4.62418 2.60461 4.45628 3.91732 2.90727C4.49956 2.22054 4.37916 1.21884 3.64777 0.671532C2.91819 0.125197 1.85256 0.238284 1.27032 0.924899C-0.0423919 2.47305 0.17864 4.13525 0.259504 4.54746Z" fill="url(#activeAghanimLeftShardGradient)"></path><path d="M9.46713 9.98081C9.42698 10.0064 9.37572 10.0064 9.33559 9.98081C8.88968 9.67166 6.33212 7.75166 6.33212 4.38581C6.33212 2.96661 7.70742 1.81406 9.40136 1.81406C11.0953 1.81406 12.4706 2.96661 12.4706 4.38581C12.4706 7.75166 9.91303 9.67166 9.46713 9.98081Z" fill="url(#activeAghanimMidShardGradient)"></path><path d="M18.6888 4.54746C18.6753 4.60325 18.6232 4.64198 18.5631 4.64198C18.1173 4.62418 16.3445 4.45628 15.0317 2.90727C14.4494 2.22054 14.5697 1.21884 15.3003 0.671532C16.0308 0.125197 17.0966 0.238284 17.6788 0.924899C18.9917 2.47305 18.7707 4.13525 18.6888 4.54746Z" fill="url(#activeAghanimRightShardGradient)"></path><defs><radialGradient id="activeAghanimMidShardGradient" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(9.01787 2.49983) rotate(90) scale(7.50029 5.21143)"><stop stop-color="#00CEFF"></stop><stop offset="1" stop-color="#3443C4"></stop></radialGradient><radialGradient id="activeAghanimLeftShardGradient" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(3.98746 0.625367) rotate(128.66) scale(6.00315 4.79432)"><stop stop-color="#00CEFF"></stop><stop offset="1" stop-color="#3443C4"></stop></radialGradient><radialGradient id="activeAghanimRightShardGradient" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(14.2996 0.625367) rotate(51.3402) scale(6.00316 4.7942)"><stop stop-color="#00CEFF"></stop><stop offset="1" stop-color="#3443C4"></stop></radialGradient></defs></svg>` : ""}
1703
+ <div class="img_stats">
1704
+ <img src="${getImageUrl(item.ability.name, "abilities" /* Abilities */)}" alt="" />
1705
+ <div class="stats">
1706
+ <p class="behavior">技能:${(Array.isArray(dotaconstants3.abilities[item.ability.name].behavior) ? dotaconstants3.abilities[item.ability.name].behavior : [dotaconstants3.abilities[item.ability.name].behavior]).filter((beh) => beh !== "Hidden" || !(item.ability.stat.isGrantedByShard || item.ability.stat.isGrantedByScepter)).map((beh) => behavior[beh]).join("/")}</p>
1707
+ ${dotaconstants3.abilities[item.ability.name].target_team ? `<p class="target_team">影响:${(Array.isArray(dotaconstants3.abilities[item.ability.name].target_team) ? dotaconstants3.abilities[item.ability.name].target_team : [dotaconstants3.abilities[item.ability.name].target_team]).map((tt) => target_team[tt]).join("/")}</p>` : ""}
1708
+ ${!Array.isArray(dotaconstants3.abilities[item.ability.name].dmg_type) && dotaconstants3.abilities[item.ability.name].dmg_type ? `<p class="dmg_type ${dotaconstants3.abilities[item.ability.name].dmg_type}">伤害类型:</p>` : ""}
1709
+ ${dotaconstants3.abilities[item.ability.name].dispellable ? `<p class="dispellable ${dotaconstants3.abilities[item.ability.name].dispellable == "Strong Dispels Only" ? "Strong" : dotaconstants3.abilities[item.ability.name].dispellable}">能否驱散:</p>` : ""}
1710
+ ${!Array.isArray(dotaconstants3.abilities[item.ability.name].bkbpierce) && dotaconstants3.abilities[item.ability.name].bkbpierce ? `<p class="bkbpierce">无视减益免疫: ${dotaconstants3.abilities[item.ability.name].bkbpierce == "Yes" ? "是" : "否"}</p>` : ""}
1711
+ </div>
1712
+ </div>
1713
+ ${item.ability.language.description.map((desc) => `<p class="description">${desc}</p>`).join("")}
1714
+ ${item.ability.language.aghanimDescription ? `<p class="aghanim_description"><span class="title"><svg viewBox="0 0 19 20" fill="hsla(0,0%,100%,0.16)" width="24"><path d="M4.795 14.99a2.06 2.06 0 00-.96-.388c-1.668-.204-2.506.518-3.107 1.008.464.128.879.364.867.97 2.347-1.605 4.159.84 2.415 2.666-.14.147.65.929.767.718.203-.365.79-1.064 1.445-1.064.964 0 1.529.68 1.823.838.267.144.793-.372.642-.675-.03-.06-.229-.204-.569-.438-1.407-.197-1.935-1.093-2.37-2.026-.276-.593-.503-1.206-.953-1.61zm9.41 0a2.06 2.06 0 01.96-.388c1.668-.204 2.507.518 3.107 1.008-.464.128-.879.364-.867.97-2.347-1.605-4.158.84-2.415 2.666.14.147-.65.929-.768.718-.202-.365-.79-1.064-1.444-1.064-.965 0-1.529.68-1.823.838-.267.144-.793-.372-.642-.675.03-.06.229-.204.569-.438 1.407-.197 1.935-1.093 2.37-2.026.276-.593.503-1.206.953-1.61zm-3.919-2.211c0-.233-.175-.423-.392-.423h-.788c-.217 0-.392.19-.392.423v5.665c0 .232.175.421.392.421h.788c.216 0 .392-.189.392-.421v-5.665zm-1.989 2.543c-.553-.139-2.074-.563-2.702-1.17-.814-.784-1.107-3.135-2.655-3.52-1.29-.32-2.448.27-2.924 1.05-.06.101.055.241.252.178 2.786-.884 2.957 1.674 2.672 2.215a.275.275 0 00-.024.057c.87-.106 1.462.043 1.893.328.447.294.732.738.975 1.231.515 1.042.822 2.335 2.513 2.512v-2.88zm2.406 0c.553-.139 2.074-.563 2.703-1.17.812-.784 1.106-3.135 2.654-3.52 1.29-.32 2.448.27 2.924 1.05.06.101-.055.241-.252.178-2.786-.884-2.957 1.674-2.672 2.215a.27.27 0 01.024.057c-.87-.106-1.462.043-1.893.328-.447.294-.732.738-.975 1.231-.515 1.042-.822 2.335-2.513 2.512v-2.88z" fill="hsla(0,0%,100%,0.6)"></path><path d="M9.753.093a.39.39 0 00-.506 0C8.461.747 6.08 2.946 5.515 3.417a.434.434 0 00-.15.262c-.162.895-.949 4.817-1.12 5.764a.46.46 0 00.067.333c.37.564 1.665 2.752 2.071 3.37a.404.404 0 00.336.187h.768c.19 0 .356-.14.4-.337l.35-1.577a.416.416 0 01.399-.336h1.728c.19 0 .356.139.399.336l.35 1.577a.416.416 0 00.4.337h.768c.133 0 .259-.07.336-.187.406-.618 1.7-2.806 2.07-3.37a.457.457 0 00.067-.333c-.17-.947-.957-4.87-1.118-5.764a.435.435 0 00-.15-.262C12.92 2.946 10.537.747 9.752.093z" fill="url(#activeAghanimScepterGradient)"></path><defs><radialGradient id="activeAghanimScepterGradient" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(9.03623 10.4684) rotate(-90) scale(9.38905 7.0456)"><stop stop-color="#00CEFF"></stop><stop offset="1" stop-color="#3443C4"></stop></radialGradient></defs></svg>
1715
+ &nbsp;阿哈利姆神杖</span><span class="desc">${item.ability.language.aghanimDescription}</span></p>` : ""}
1716
+ ${item.ability.language.shardDescription ? `<p class="aghanim_description"><span class="title"><svg viewBox="0 0 19 10" fill="hsla(0,0%,100%,0.16)" width="24"><path d="M0.259504 4.54746C0.272981 4.60325 0.326002 4.64198 0.386194 4.64198C0.831857 4.62418 2.60461 4.45628 3.91732 2.90727C4.49956 2.22054 4.37916 1.21884 3.64777 0.671532C2.91819 0.125197 1.85256 0.238284 1.27032 0.924899C-0.0423919 2.47305 0.17864 4.13525 0.259504 4.54746Z" fill="url(#activeAghanimLeftShardGradient)"></path><path d="M9.46713 9.98081C9.42698 10.0064 9.37572 10.0064 9.33559 9.98081C8.88968 9.67166 6.33212 7.75166 6.33212 4.38581C6.33212 2.96661 7.70742 1.81406 9.40136 1.81406C11.0953 1.81406 12.4706 2.96661 12.4706 4.38581C12.4706 7.75166 9.91303 9.67166 9.46713 9.98081Z" fill="url(#activeAghanimMidShardGradient)"></path><path d="M18.6888 4.54746C18.6753 4.60325 18.6232 4.64198 18.5631 4.64198C18.1173 4.62418 16.3445 4.45628 15.0317 2.90727C14.4494 2.22054 14.5697 1.21884 15.3003 0.671532C16.0308 0.125197 17.0966 0.238284 17.6788 0.924899C18.9917 2.47305 18.7707 4.13525 18.6888 4.54746Z" fill="url(#activeAghanimRightShardGradient)"></path><defs><radialGradient id="activeAghanimMidShardGradient" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(9.01787 2.49983) rotate(90) scale(7.50029 5.21143)"><stop stop-color="#00CEFF"></stop><stop offset="1" stop-color="#3443C4"></stop></radialGradient><radialGradient id="activeAghanimLeftShardGradient" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(3.98746 0.625367) rotate(128.66) scale(6.00315 4.79432)"><stop stop-color="#00CEFF"></stop><stop offset="1" stop-color="#3443C4"></stop></radialGradient><radialGradient id="activeAghanimRightShardGradient" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(14.2996 0.625367) rotate(51.3402) scale(6.00316 4.7942)"><stop stop-color="#00CEFF"></stop><stop offset="1" stop-color="#3443C4"></stop></radialGradient></defs></svg>
1717
+ &nbsp;阿哈利姆魔晶</span><span class="desc">${item.ability.language.shardDescription}</span></p>` : ""}
1718
+ <div class="notes"${!item.ability.language.notes.length ? ` style="display:none;"` : ""}>
1719
+ ${item.ability.language.notes.map((note) => `<p>${note}</p>`).join("")}
1720
+ </div>
1721
+ <div class="attributes">
1722
+ ${item.ability.language.attributes.map((attr) => {
1723
+ const parts = attr.split(":");
1724
+ return `<p><span class="item">${parts[0]}</span><span class="values">${parts[1]}</span></p>`;
1725
+ }).join("")}
1726
+ </div>
1727
+ <p>
1728
+ ${dotaconstants3.abilities[item.ability.name].cd ? `<span class="cooldown"> ${(Array.isArray(dotaconstants3.abilities[item.ability.name].cd) ? dotaconstants3.abilities[item.ability.name].cd : [dotaconstants3.abilities[item.ability.name].cd]).join(
1729
+ " / "
1730
+ )} </span>` : ""}
1731
+ ${dotaconstants3.abilities[item.ability.name].mc ? `<span class="mana_cost"> ${(Array.isArray(dotaconstants3.abilities[item.ability.name].mc) ? dotaconstants3.abilities[item.ability.name].mc : [dotaconstants3.abilities[item.ability.name].mc]).join(
1732
+ " / "
1733
+ )} </span>` : ""}
1734
+ </p>
1735
+ <p class="lore"${!item.ability.language.lore ? ` style="display:none;"` : ""}>${item.ability.language.lore}</p>
1736
+ </div>
1737
+ `
1738
+ ).join("")}
1739
+ </div>
1740
+ <div class="lore">
1741
+ ${hero.language.lore}
1742
+ </div>
1743
+ `;
1744
+ $(".wrapper").html(html);
1745
+ if (process.env.NODE_ENV === "development")
1746
+ import_fs2.default.writeFileSync("./node_modules/@sjtdev/koishi-plugin-dota2tracker/temp.html", $.html());
1747
+ return $.html();
1748
+ }
1749
+ __name(genHeroHTML, "genHeroHTML");
1750
+ function genPlayerHTML(player) {
1751
+ let $ = cheerio.load(import_fs2.default.readFileSync(`./node_modules/@sjtdev/koishi-plugin-${name}/template/player.html`, "utf-8"));
1752
+ const guildLevel = /* @__PURE__ */ __name((percent) => {
1753
+ if (percent <= 25) {
1754
+ return "Copper";
1755
+ } else if (percent <= 50) {
1756
+ return "Silver";
1757
+ } else if (percent <= 75) {
1758
+ return "Gold";
1759
+ } else {
1760
+ return "Diamond";
1761
+ }
1762
+ }, "guildLevel");
1763
+ const laneSVG = {
1764
+ stomp: `<svg viewBox="0 0 24 24" class="hitagi__sc-1apuy4g-0 hmhZOG"><path d="M8.05731 22.3674L9.60454 22.8002L11.5974 21.6551L12.043 20.0773L13.5902 20.51L15.583 19.3649L16.0287 17.7871L17.5759 18.2199L19.5687 17.0748L20.0143 15.4969L21.5615 15.9297L23.5544 14.7846L24 13.2068L23.4492 12.2014L7.50651 21.3621L8.05731 22.3674ZM12.1328 3.50265L11.0312 1.49196C10.8798 1.21549 10.5316 1.11811 10.2576 1.27556L0.29345 7.00098C0.0194354 7.15843 -0.0808273 7.51346 0.0706444 7.78993L1.44766 10.3033L11.91 4.29159C12.184 4.13414 12.2843 3.77912 12.1328 3.50265ZM18.3935 8.4063L14.1658 9.60458L12.4221 10.6065C12.2851 10.6853 12.111 10.6366 12.0353 10.4983L11.7599 9.99565C11.6842 9.85742 11.7343 9.6799 11.8713 9.60118L13.615 8.59924L13.0642 7.59389L11.3205 8.59584C11.1835 8.67456 11.0094 8.62587 10.9337 8.48765L10.6583 7.98497C10.5826 7.84673 10.6327 7.66922 10.7697 7.5905L12.5134 6.58855L11.9626 5.58321L1.99846 11.3086L6.9557 20.3567L22.8984 11.196L22.2615 10.0336C21.5024 8.64813 19.9073 7.97847 18.3935 8.4063Z"></path></svg>`,
1765
+ victory: `<svg viewBox="0 0 512 512"><path d="M198.844 64.75c-.985 0-1.974.03-2.97.094-15.915 1.015-32.046 11.534-37.78 26.937-34.072 91.532-51.085 128.865-61.5 222.876 14.633 13.49 31.63 26.45 50.25 38.125l66.406-196.467 17.688 5.968L163.28 362.5c19.51 10.877 40.43 20.234 62 27.28l75.407-201.53 17.5 6.53-74.937 200.282c19.454 5.096 39.205 8.2 58.78 8.875L381.345 225.5l17.094 7.594-75.875 170.656c21.82-1.237 43.205-5.768 63.437-14.28 43.317-53.844 72.633-109.784 84.5-172.69 5.092-26.992-14.762-53.124-54.22-54.81l-6.155-.282-2.188-5.75c-8.45-22.388-19.75-30.093-31.5-32.47-11.75-2.376-25.267 1.535-35.468 7.376l-13.064 7.47-.906-15c-.99-16.396-10.343-29.597-24.313-35.626-13.97-6.03-33.064-5.232-54.812 9.906l-10.438 7.25-3.812-12.125c-6.517-20.766-20.007-27.985-34.78-27.97zM103.28 188.344C71.143 233.448 47.728 299.56 51.407 359.656c27.54 21.84 54.61 33.693 80.063 35.438 14.155.97 27.94-1.085 41.405-6.438-35.445-17.235-67.36-39.533-92.594-63.53l-3.343-3.157.5-4.595c5.794-54.638 13.946-91.5 25.844-129.03z"/></svg>`,
1766
+ fail: `<svg viewBox="0 0 36 36"><path fill="#ff6961" d="M36 32a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4h28a4 4 0 0 1 4 4v28z"></path><circle fill="#FFF" cx="27" cy="7" r="3"></circle><path fill="#FFF" d="M13.06 13.06l2.367-2.366l3.859 1.158l-2.635 2.847a10.018 10.018 0 0 1 4.392 3.379l5.017-5.017a1.5 1.5 0 0 0-.63-2.497l-9.999-3a1.495 1.495 0 0 0-1.492.376l-3 3a1.5 1.5 0 1 0 2.121 2.12zm16.065 4.949a1.496 1.496 0 0 0-1.262-.503l-6.786.617a9.966 9.966 0 0 1 1.464 2.879l3.548-.322l-1.554 6.995a1.499 1.499 0 1 0 2.928.65l2-9a1.5 1.5 0 0 0-.338-1.316zM13 16a8 8 0 1 0 0 16a8 8 0 0 0 0-16zm0 14a6 6 0 1 1 .002-12.002A6 6 0 0 1 13 30z"></path></svg>`,
1767
+ stomped: `<svg viewBox="-1 0 19 19"><path d="M16.417 9.579A7.917 7.917 0 1 1 8.5 1.662a7.917 7.917 0 0 1 7.917 7.917zm-2.458 2.96a.396.396 0 0 0-.396-.397h-.667a1.527 1.527 0 0 0-1.249-1.114.777.777 0 0 0 .014-.145V9.378a.794.794 0 0 0-.792-.792H8.201a2.984 2.984 0 0 0-1.682-.516l-.11.002V7.42h2.997a.396.396 0 1 0 0-.792H6.41v-1.3a.396.396 0 0 0-.396-.397H4.891a.396.396 0 0 0 0 .792h.727V8.21a2.997 2.997 0 1 0 3.836 3.466h.71a1.526 1.526 0 1 0 2.732 1.26h.667a.396.396 0 0 0 .396-.397zM8.078 9.507a2.205 2.205 0 1 1-1.559-.646 2.19 2.19 0 0 1 1.559.646zm4.078 3.03a.734.734 0 1 1-.733-.734.735.735 0 0 1 .733.733z"/></svg>`,
1768
+ tie: `<svg fill="#fff" viewBox="0 0 512.001 512.001"><g><g><path d="M120.988,239.868c-4.496,10.625-5.122,20.183-5.157,20.811c-0.267,4.607,3.243,8.547,7.849,8.829 c4.618,0.29,8.574-3.228,8.873-7.833c0.265-4.771,2.339-13.092,5.884-19.44C137.421,242.113,141.397,242.649,120.988,239.868z"/></g></g><g><g><path d="M391.178,255.418c-0.211,8.054-2.458,17.62-6.74,28.398c-1.708,4.299,0.393,9.168,4.692,10.875 c4.293,1.708,9.167-0.39,10.875-4.692c5.103-12.842,7.74-24.392,7.943-34.581H391.178z"/></g></g><g><g><path d="M164.769,210.51c1.046,3.339,1.397,6.953,0.893,10.65c-0.293,2.146-0.857,4.188-1.648,6.1c0,0,51.266,3.416,198.065,3.949 c-0.086-6.331,2.19-12.199,6.244-16.732C217.627,214.046,164.769,210.51,164.769,210.51z"/></g></g><g><g><circle cx="37.179" cy="128.669" r="29.491"/></g></g><g><g><path d="M510.146,391.511l-37.916-66.985c14.35-49.173,20.678-68.137,20.678-68.137l8.949-67.014 c1.502-10.977-6.248-21.075-17.235-22.468l-18.183-2.305c-10.984-1.393-20.996,6.445-22.293,17.431l-1.884,15.955l28.718-21.317 l-37.91,42.278h-46.432c-6.571,0-11.898,5.328-11.898,11.898c0,6.57,5.328,11.898,11.898,11.898h51.744 c3.381,0,6.601-1.438,8.859-3.956l41.456-46.234l-32.023,54.694c-5.28,9.018-14.374,8.169-18.293,8.167c-1.959,0-3.31,0-5.295,0 c-0.399,0.898,3.152-7.399-24.44,57.181c-0.548,1.284-0.907,2.642-1.06,4.031l-8.934,80.338 c-0.939,8.447,5.667,15.857,14.208,15.857c7.179,0,13.361-5.401,14.172-12.701l8.702-78.244l21.512-50.353l-14.121,50.463 c-1.158,3.756-0.718,7.823,1.218,11.243l40.949,72.345c3.885,6.864,12.596,9.276,19.459,5.392 C511.615,407.085,514.03,398.373,510.146,391.511z"/></g></g><g><g><circle cx="464.865" cy="128.702" r="29.491"/></g></g><g><g><path d="M142.923,206.051l-59.556-8.118l-39.135-18.451l13.626,2.292c-1.422-10.945-11.411-18.577-22.254-17.202l-18.182,2.305 C6.43,168.271-1.315,178.374,0.186,189.345l9.12,68.689l21.865,70.857l5.829,70.795c0.646,7.848,7.527,13.705,15.401,13.057 c7.859-0.647,13.705-7.542,13.058-15.401l-5.956-72.345c-0.084-1.031-0.281-2.05-0.585-3.039l-14.123-50.463l21.514,50.353 l8.702,78.244c0.873,7.86,7.96,13.486,15.768,12.612c7.838-0.871,13.483-7.931,12.612-15.768l-8.934-80.338 c-0.154-1.388-0.511-2.747-1.06-4.032l-27.336-61.43l-2.945-24.951l-29.029-25.179l40.79,19.231 c1.097,0.517,2.266,0.862,3.468,1.027l61.369,8.365c6.521,0.887,12.509-3.68,13.396-10.183 C153.994,212.936,149.435,206.939,142.923,206.051z"/></g></g></svg>`
1769
+ };
1770
+ const outcomeCounts = {
1771
+ victory: 0,
1772
+ stomp: 0,
1773
+ fail: 0,
1774
+ stomped: 0,
1775
+ tie: 0
1776
+ };
1777
+ const processLaneOutcome = /* @__PURE__ */ __name((outcome) => {
1778
+ switch (outcome) {
1779
+ case "RADIANT_VICTORY":
1780
+ return { radiant: "victory", dire: "fail" };
1781
+ case "RADIANT_STOMP":
1782
+ return { radiant: "stomp", dire: "stomped" };
1783
+ case "DIRE_VICTORY":
1784
+ return { radiant: "fail", dire: "victory" };
1785
+ case "DIRE_STOMP":
1786
+ return { radiant: "stomped", dire: "stomp" };
1787
+ default:
1788
+ return { radiant: "tie", dire: "tie" };
1789
+ }
1790
+ }, "processLaneOutcome");
1791
+ let nearMatchCount = 25, nearWinCount = 0, streak = 0;
1792
+ player.matches.forEach((match) => {
1793
+ const innerPlayer = match.players[0];
1794
+ nearWinCount += match.didRadiantWin == innerPlayer.isRadiant ? 1 : 0;
1795
+ const didWin = match.didRadiantWin === innerPlayer.isRadiant;
1796
+ if (!player.streak) {
1797
+ if (streak != 0) {
1798
+ if (didWin && streak > 0)
1799
+ streak++;
1800
+ else if (!didWin && streak < 0)
1801
+ streak--;
1802
+ else
1803
+ player.streak = streak;
1804
+ } else
1805
+ streak = didWin ? 1 : -1;
1806
+ }
1807
+ const laneResult = {
1808
+ top: processLaneOutcome(match.topLaneOutcome),
1809
+ mid: processLaneOutcome(match.midLaneOutcome),
1810
+ bottom: processLaneOutcome(match.bottomLaneOutcome)
1811
+ };
1812
+ let laneKey = "mid";
1813
+ if (innerPlayer.lane === "SAFE_LANE") {
1814
+ laneKey = innerPlayer.isRadiant ? "bottom" : "top";
1815
+ } else if (innerPlayer.lane === "OFF_LANE") {
1816
+ laneKey = innerPlayer.isRadiant ? "top" : "bottom";
1817
+ }
1818
+ match.laneResult = laneResult[laneKey][innerPlayer.isRadiant ? "radiant" : "dire"];
1819
+ if (match.laneResult in outcomeCounts) {
1820
+ outcomeCounts[match.laneResult]++;
1821
+ }
1822
+ });
1823
+ const playerHTML = `
1824
+ <div class="avatar"><img src="${player.steamAccount.avatar}" alt="" /></div>
1825
+ <div class="info">
1826
+ <p class="name">${player.steamAccount.name}${player.guildMember ? ` <span class="guild ${guildLevel(player.guildMember.guild.currentPercentile)}">[${player.guildMember.guild.tag}]</span></p>` : ""}
1827
+ <p class="matches"><span>场次:${player.matchCount}(<span class="win">${player.winCount}</span>/<span class="lose">${player.matchCount - player.winCount}</span>)</span>胜率:<span style="color:${winRateColor(
1828
+ player.winCount / player.matchCount
1829
+ )};">${(player.winCount / player.matchCount * 100).toFixed(2)}%</span></p>
1830
+ <p class="matches"><span>最近25场:<span class="win">${nearWinCount}</span>/<span class="lose">${nearMatchCount - nearWinCount}</span></span><span>胜率:<span style="color:${winRateColor(nearWinCount / nearMatchCount)};">${(nearWinCount / nearMatchCount * 100).toFixed(2)}%</span></span><span>评分:${player.performance.imp}</span></span></p>
1831
+ <p class="matches"><span>对线:<span class="victory">${outcomeCounts.victory + outcomeCounts.stomp}(<span class="stomp">${outcomeCounts.stomp}</span>)</span>-<span class="tie">${outcomeCounts.tie}</span>-<span class="fail">${outcomeCounts.fail + outcomeCounts.stomped}(<span class="stomped">${outcomeCounts.stomped}</span>)</span></span><span>线优:<span style="color:${winRateColor(
1832
+ (outcomeCounts.victory + outcomeCounts.stomp + outcomeCounts.tie / 2) / (outcomeCounts.victory + outcomeCounts.stomp + outcomeCounts.tie + outcomeCounts.fail + outcomeCounts.stomped)
1833
+ )};">${((outcomeCounts.victory + outcomeCounts.stomp) / (outcomeCounts.victory + outcomeCounts.stomp + outcomeCounts.fail + outcomeCounts.stomped) * 100).toFixed(2)}%</span></span></p>
1834
+ </div>
1835
+ ${player.steamAccount.seasonRank ? `
1836
+ <div class="rank">
1837
+ <img class="medal" src="${getImageUrl(
1838
+ "medal_" + (player.steamAccount.seasonLeaderboardRank ? player.steamAccount.seasonLeaderboardRank <= 100 ? player.steamAccount.seasonLeaderboardRank <= 10 ? "8c" : "8b" : player.steamAccount.seasonRank.toString().split("")[0] : player.steamAccount.seasonRank.toString().split("")[0])
1839
+ )}" alt="" />
1840
+ ${!player.steamAccount.seasonLeaderboardRank ? `
1841
+ <img class="star" src="${getImageUrl("star_" + player.steamAccount.seasonRank.toString().split("")[1])}" alt="" />` : `
1842
+ <p>${player.steamAccount.seasonLeaderboardRank}</p>`}
1843
+ </div>` : `
1844
+ <div class="rank">
1845
+ <img class="medal" src="${getImageUrl("medal_0")}" alt="" />
1846
+ </div>`}`;
1847
+ const heroesCountPixels = 800 - ($(".tip:not(.row):not(.win_count):not(.lose_count)").length + 1) * 40;
1848
+ const highestCountsTotal = {
1849
+ winCount: Math.max(...player.heroesPerformanceTop10.map((hero) => hero.winCount)),
1850
+ loseCount: Math.max(...player.heroesPerformanceTop10.map((hero) => hero.matchCount - hero.winCount))
1851
+ };
1852
+ const pixelOfPerMatchInTotal = heroesCountPixels / (highestCountsTotal.winCount + highestCountsTotal.loseCount);
1853
+ const highestCountsNear = {
1854
+ winCount: Math.max(...player.heroesPerformance?.filter((hero) => hero.matchCount > 1)?.map((hero) => hero.winCount)),
1855
+ loseCount: Math.max(...player.heroesPerformance?.filter((hero) => hero.matchCount > 1)?.map((hero) => hero.matchCount - hero.winCount))
1856
+ };
1857
+ const nearAdjustmentFactor = Math.min(highestCountsTotal.winCount / (highestCountsTotal.winCount + highestCountsTotal.loseCount), highestCountsTotal.loseCount / (highestCountsTotal.winCount + highestCountsTotal.loseCount));
1858
+ const pixelOfPerMatchInNear = heroesCountPixels / (highestCountsNear?.winCount + highestCountsNear?.loseCount) * nearAdjustmentFactor;
1859
+ const heroesTotalHTML = player.heroesPerformanceTop10.map(
1860
+ (hero) => `
1861
+ <span><img alt="" src="${getImageUrl(hero.hero.shortName, "heroes/icons" /* HeroIcons */)}" /></span>
1862
+ <span class="count">${hero.matchCount}</span>
1863
+ <span class="win_rate">${(hero.winCount / hero.matchCount * 100).toFixed(0)}%</span>
1864
+ <span class="imp">${(hero.imp > 0 ? "+" : "") + hero.imp}</span>
1865
+ <span class="win" style="${hero.winCount == 0 ? "visibility:hidden;" : ""}width: ${hero.winCount * pixelOfPerMatchInTotal}px">${hero.winCount}</span>
1866
+ <span class="lose" style="${hero.matchCount - hero.winCount == 0 ? "visibility:hidden;" : ""}width: ${(hero.matchCount - hero.winCount) * pixelOfPerMatchInTotal}px">${hero.matchCount - hero.winCount}</span>`
1867
+ ).join("") + player.heroesPerformance.filter((hero) => hero.matchCount > 1).map(
1868
+ (hero, index) => `
1869
+ <span style="order:${index + 1};"><img alt="" src="${getImageUrl(hero.hero.shortName, "heroes/icons" /* HeroIcons */)}" /></span>
1870
+ <span style="order:${index + 1};" class="count">${hero.matchCount}</span>
1871
+ <span style="order:${index + 1};" class="win_rate">${(hero.winCount / hero.matchCount * 100).toFixed(0)}%</span>
1872
+ <span style="order:${index + 1};" class="imp">${(hero.imp > 0 ? "+" : "") + hero.imp}</span>
1873
+ <span class="win" style="order:${index + 1};${hero.winCount == 0 ? "visibility:hidden;" : ""}width: ${hero.winCount * pixelOfPerMatchInNear}px">${hero.winCount}</span>
1874
+ <span class="lose" style="order:${index + 1};${hero.matchCount - hero.winCount == 0 ? "visibility:hidden;" : ""}width: ${(hero.matchCount - hero.winCount) * pixelOfPerMatchInNear}px">${hero.matchCount - hero.winCount}</span>`
1875
+ ).join("");
1876
+ const streakHTML = `<div class="streak" style="box-shadow:none;color:${winRateColor((player.streak + 10) / 20)};">${Math.abs(player.streak) + (player.streak > 0 ? "连胜" : "连败")}</div>`;
1877
+ const matchesHTML = player.matches.map(
1878
+ (match) => `
1879
+ <tr class="match ${match.didRadiantWin == match.players[0].isRadiant ? "win" : "lose"}">
1880
+ <td>${match.id}</td>
1881
+ <td>
1882
+ <p>${lobbyTypes[match.lobbyType] || match.lobbyType}</p>
1883
+ <p>${gameMode[match.gameMode] || match.gameMode}</p>
1884
+ </td>
1885
+ <td><img alt="" src="${getImageUrl(match.players[0].hero.shortName, "heroes/icons" /* HeroIcons */)}" /></td>
1886
+ <td style="line-height: 20px">
1887
+ <p>${((match.players[0].kills + match.players[0].assists) / Math.max(1, match.players[0].deaths)).toFixed(2)} (${((match.players[0].kills + match.players[0].assists) / (match.players[0].isRadiant ? match.radiantKills.reduce((acc, cva) => acc + cva, 0) : match.direKills.reduce((acc, cva) => acc + cva, 0)) * 100).toFixed(0)}%)</p>
1888
+ <p>${match.players[0].kills}/${match.players[0].deaths}/${match.players[0].assists}</p>
1889
+ </td>
1890
+ <td>
1891
+ <div class="player_lane ${match.laneResult}">${laneSVG[match.laneResult]}</div>
1892
+ </td>
1893
+ <td style="line-height: 20px">${(0, import_moment.default)(new Date(match.endDateTime * 1e3)).format("YYYY-MM-DD HH:mm:ss").slice(2)}</td>
1894
+ <td>${sec2time(match.durationSeconds)}</td>
1895
+ <td>${(match.players[0].imp > 0 ? "+" : "") + match.players[0].imp}</td>
1896
+ <td><img class="medal" src="${getImageUrl("medal_" + match.rank.toString().split("")[0])}" style="width: 100%" /></td>
1897
+ </tr>`
1898
+ ).join("");
1899
+ const dotaPlusHTML = player.dotaPlus.map(
1900
+ (hero) => `
1901
+ <div class="hero">
1902
+ <img src="${getImageUrl(hero.shortName, "heroes" /* Heroes */)}" alt="" />
1903
+ <div class="level"><img src="${getImageUrl("hero_badge_" + Math.ceil((hero.level + 1) / 6))}" alt="" /><span>${hero.level}</span></div>
1904
+ <span>${(hero.winCount / hero.matchCount * 100).toFixed(2)}%</span>
1905
+ <span>${hero.matchCount}</span>
1906
+ </div>`
1907
+ ).join("");
1908
+ $(".player").html(playerHTML);
1909
+ $(".heroes > span:not(.tip)").remove();
1910
+ $(".heroes .tip.near").before(heroesTotalHTML);
1911
+ if (player.streak > 1 || player.streak < -1)
1912
+ $(".streak").replaceWith(streakHTML);
1913
+ $(".matches tbody").html(matchesHTML);
1914
+ $(".plus").html(dotaPlusHTML);
1915
+ if (process.env.NODE_ENV === "development")
1916
+ import_fs2.default.writeFileSync("./node_modules/@sjtdev/koishi-plugin-dota2tracker/temp.html", $.html());
1917
+ return $.html();
1918
+ }
1919
+ __name(genPlayerHTML, "genPlayerHTML");
1920
+ async function playerIsInvalid(steamAccountId) {
1921
+ try {
1922
+ let queryRes = await query(VERIFYING_PLAYER(steamAccountId));
1923
+ if (queryRes.status == 200) {
1924
+ if (queryRes.data.data.player.matchCount != null)
1925
+ return { isInvalid: true };
1926
+ else
1927
+ return { isInvalid: false, reason: "SteamID无效或无任何场次。" };
1928
+ }
1929
+ } catch (error) {
1930
+ console.error(error);
1931
+ return { isInvalid: false, reason: "网络状况不佳SteamID验证失败,请稍后重试。" };
1932
+ }
1933
+ }
1934
+ __name(playerIsInvalid, "playerIsInvalid");
1935
+ function sec2time(sec) {
1936
+ return sec ? (sec < 0 ? "-" : "") + Math.floor(Math.abs(sec) / 60) + ":" + ("00" + Math.abs(sec) % 60).slice(-2) : "--:--";
1937
+ }
1938
+ __name(sec2time, "sec2time");
1939
+ function winRateColor(value) {
1940
+ value = value * 100;
1941
+ value = Math.max(0, Math.min(100, value));
1942
+ let red, green, blue;
1943
+ if (value <= 50) {
1944
+ let scale = Math.round(255 * (value / 50));
1945
+ red = 255;
1946
+ green = scale;
1947
+ blue = scale;
1948
+ } else {
1949
+ let scale = Math.round(255 * ((value - 50) / 50));
1950
+ red = 255 - scale;
1951
+ green = 255;
1952
+ blue = 255 - scale;
1953
+ }
1954
+ const toHex = /* @__PURE__ */ __name((color) => color.toString(16).padStart(2, "0").toUpperCase(), "toHex");
1955
+ return `#${toHex(red)}${toHex(green)}${toHex(blue)}`;
1956
+ }
1957
+ __name(winRateColor, "winRateColor");
1958
+ function sanitizeHTML(strings, ...values) {
1959
+ return strings.reduce((result, string, i) => {
1960
+ let value = values[i] ?? "--";
1961
+ return result + string + (i < values.length ? value : "");
1962
+ }, "");
1963
+ }
1964
+ __name(sanitizeHTML, "sanitizeHTML");
1965
+ // Annotate the CommonJS export names for ESM import in node:
1966
+ 0 && (module.exports = {
1967
+ Config,
1968
+ apply,
1969
+ inject,
1970
+ name,
1971
+ usage
1972
+ });