@sjtdev/koishi-plugin-dota2tracker 1.0.3 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/lib/index.js +559 -1038
  2. package/package.json +2 -1
  3. package/readme.md +9 -7
  4. package/template/{hero.html → hero/hero_1.ejs} +543 -516
  5. package/template/images/logo_dire.png +0 -0
  6. package/template/images/logo_radiant.png +0 -0
  7. package/template/images/scepter.png +0 -0
  8. package/template/images/scepter_0.png +0 -0
  9. package/template/images/scepter_1.png +0 -0
  10. package/template/images/shard.png +0 -0
  11. package/template/images/shard_0.png +0 -0
  12. package/template/images/shard_1.png +0 -0
  13. package/template/match/match_1.ejs +997 -0
  14. package/template/match/match_2.ejs +440 -0
  15. package/template/player/player_1.ejs +543 -0
  16. package/template/match.html +0 -1027
  17. package/template/player.html +0 -414
  18. /package/{images → template/images}/flag_dire.png +0 -0
  19. /package/{images → template/images}/flag_radiant.png +0 -0
  20. /package/{images → template/images}/hero_badge_1.png +0 -0
  21. /package/{images → template/images}/hero_badge_2.png +0 -0
  22. /package/{images → template/images}/hero_badge_3.png +0 -0
  23. /package/{images → template/images}/hero_badge_4.png +0 -0
  24. /package/{images → template/images}/hero_badge_5.png +0 -0
  25. /package/{images → template/images}/hero_badge_6.png +0 -0
  26. /package/{images → template/images}/medal_0.png +0 -0
  27. /package/{images → template/images}/medal_1.png +0 -0
  28. /package/{images → template/images}/medal_2.png +0 -0
  29. /package/{images → template/images}/medal_3.png +0 -0
  30. /package/{images → template/images}/medal_4.png +0 -0
  31. /package/{images → template/images}/medal_5.png +0 -0
  32. /package/{images → template/images}/medal_6.png +0 -0
  33. /package/{images → template/images}/medal_7.png +0 -0
  34. /package/{images → template/images}/medal_8.png +0 -0
  35. /package/{images → template/images}/medal_8b.png +0 -0
  36. /package/{images → template/images}/medal_8c.png +0 -0
  37. /package/{images → template/images}/star_0.png +0 -0
  38. /package/{images → template/images}/star_1.png +0 -0
  39. /package/{images → template/images}/star_2.png +0 -0
  40. /package/{images → template/images}/star_3.png +0 -0
  41. /package/{images → template/images}/star_4.png +0 -0
  42. /package/{images → template/images}/star_5.png +0 -0
package/lib/index.js CHANGED
@@ -40,259 +40,26 @@ module.exports = __toCommonJS(src_exports);
40
40
  var import_koishi = require("koishi");
41
41
 
42
42
  // src/utils.ts
43
+ var utils_exports = {};
44
+ __export(utils_exports, {
45
+ CONFIGS: () => CONFIGS,
46
+ ImageType: () => ImageType,
47
+ formatNumber: () => formatNumber,
48
+ getFormattedMatchData: () => getFormattedMatchData,
49
+ getImageUrl: () => getImageUrl,
50
+ playerisValid: () => playerisValid,
51
+ query: () => query,
52
+ readDirectoryFilesSync: () => readDirectoryFilesSync,
53
+ sec2time: () => sec2time,
54
+ winRateColor: () => winRateColor
55
+ });
43
56
  var import_axios = __toESM(require("axios"));
44
57
  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");
58
+ var dotaconstants2 = __toESM(require("dotaconstants"));
59
+ var import_path = __toESM(require("path"));
293
60
 
294
61
  // src/queries.ts
295
- var dotaconstants2 = __toESM(require("dotaconstants"));
62
+ var dotaconstants = __toESM(require("dotaconstants"));
296
63
  function MATCH_INFO(matchId) {
297
64
  return `
298
65
  {
@@ -498,6 +265,7 @@ function PLAYER_INFO_WITH_25_MATCHES(steamAccountId) {
498
265
  kills
499
266
  deaths
500
267
  assists
268
+ position
501
269
  award
502
270
  imp
503
271
  hero {
@@ -531,133 +299,474 @@ function PLAYER_EXTRA_INFO(steamAccountId, matchCount, totalHeroCount) {
531
299
  }
532
300
  }
533
301
  }
534
- `;
302
+ `;
303
+ }
304
+ __name(PLAYER_EXTRA_INFO, "PLAYER_EXTRA_INFO");
305
+ function CURRENT_GAMEVERSION() {
306
+ return `
307
+ {
308
+ constants {
309
+ gameVersions{name id}
310
+ }
311
+ }
312
+
313
+ `;
314
+ }
315
+ __name(CURRENT_GAMEVERSION, "CURRENT_GAMEVERSION");
316
+ function ALL_ABILITIES_CHINESE_NAME() {
317
+ return `
318
+ {
319
+ constants {
320
+ abilities(language:S_CHINESE){
321
+ id
322
+ language{displayName}
323
+ }
324
+ gameVersions{name id}
325
+ }
326
+ }
327
+
328
+ `;
329
+ }
330
+ __name(ALL_ABILITIES_CHINESE_NAME, "ALL_ABILITIES_CHINESE_NAME");
331
+ function HERO_INFO(heroId) {
332
+ return `
333
+ {
334
+ constants {
335
+ hero(id: ${heroId}, language: S_CHINESE) {
336
+ id
337
+ name
338
+ shortName
339
+ aliases
340
+ roles {
341
+ roleId
342
+ level
343
+ }
344
+ language {
345
+ displayName
346
+ lore
347
+ hype
348
+ }
349
+ abilities {
350
+ ability(language: S_CHINESE) {
351
+ name
352
+ language {
353
+ displayName
354
+ description
355
+ attributes
356
+ lore
357
+ aghanimDescription
358
+ shardDescription
359
+ notes
360
+ }
361
+ stat {
362
+ type
363
+ behavior
364
+ unitTargetType
365
+ unitTargetTeam
366
+ unitTargetFlags
367
+ unitDamageType
368
+ cooldown
369
+ manaCost
370
+ spellImmunity
371
+ isOnCastbar
372
+ isGrantedByShard
373
+ isGrantedByScepter
374
+ hasShardUpgrade
375
+ hasScepterUpgrade
376
+ }
377
+ }
378
+ }
379
+ talents {
380
+ abilityId
381
+ slot
382
+ }
383
+ }
384
+ }
385
+ }
386
+
387
+ `;
388
+ }
389
+ __name(HERO_INFO, "HERO_INFO");
390
+ function HERO_MATCHUP_WINRATE(heroId) {
391
+ return `
392
+ {
393
+ heroStats {
394
+ matchUp(heroId: ${heroId}, take: ${Object.keys(dotaconstants.heroes).length - 1},bracketBasicIds:LEGEND_ANCIENT) {
395
+ heroId
396
+ matchCountWith
397
+ matchCountVs
398
+ with {
399
+ heroId1
400
+ winRateHeroId1
401
+ heroId2
402
+ winRateHeroId2
403
+ winCount
404
+ matchCount
405
+ }
406
+ vs {
407
+ heroId1
408
+ winRateHeroId1
409
+ heroId2
410
+ winRateHeroId2
411
+ winCount
412
+ matchCount
413
+ }
414
+ }
415
+ }
416
+ }
417
+
418
+ `;
419
+ }
420
+ __name(HERO_MATCHUP_WINRATE, "HERO_MATCHUP_WINRATE");
421
+
422
+ // src/utils.ts
423
+ var CONFIGS = { STRATZ_API: { URL: "https://api.stratz.com/graphql", TOKEN: "" } };
424
+ async function query(query_str) {
425
+ return await import_axios.default.post(CONFIGS.STRATZ_API.URL, query_str, {
426
+ headers: {
427
+ "Content-Type": "application/graphql",
428
+ Authorization: `Bearer ${CONFIGS.STRATZ_API.TOKEN}`
429
+ }
430
+ });
431
+ }
432
+ __name(query, "query");
433
+ var ImageType = /* @__PURE__ */ ((ImageType2) => {
434
+ ImageType2["Icons"] = "icons";
435
+ ImageType2["Heroes"] = "heroes";
436
+ ImageType2["HeroIcons"] = "heroes/icons";
437
+ ImageType2["Items"] = "items";
438
+ ImageType2["Abilities"] = "abilities";
439
+ ImageType2["Local"] = "local";
440
+ return ImageType2;
441
+ })(ImageType || {});
442
+ function getImageUrl(image, type = "local" /* Local */) {
443
+ if (type === "local" /* Local */) {
444
+ try {
445
+ const imageData = import_fs.default.readFileSync(`./node_modules/@sjtdev/koishi-plugin-dota2tracker/template/images/${image}.png`);
446
+ const base64Data = imageData.toString("base64");
447
+ return `data:image/png;base64,${base64Data}`;
448
+ } catch (error) {
449
+ console.error(error);
450
+ return "";
451
+ }
452
+ } else
453
+ return `https://cdn.cloudflare.steamstatic.com/apps/dota2/images/dota_react/${type}/${image}.png`;
454
+ }
455
+ __name(getImageUrl, "getImageUrl");
456
+ function getFormattedMatchData(match) {
457
+ ["radiant", "dire"].forEach((team) => {
458
+ match[team] = { killsCount: match[team + "Kills"]?.reduce((acc, cva) => acc + cva, 0) ?? 0, damageReceived: 0, heroDamage: 0, networth: 0, experience: 0 };
459
+ });
460
+ match.party = {};
461
+ let party_index = 0;
462
+ const party_mark = ["I", "II", "III", "IV"];
463
+ let heroOrderList = {};
464
+ for (let hero of match.pickBans) {
465
+ if (hero.isPick)
466
+ heroOrderList[hero.heroId] = hero.order;
467
+ }
468
+ let processLaneOutcome = /* @__PURE__ */ __name(function(outcome) {
469
+ switch (outcome) {
470
+ case "RADIANT_VICTORY":
471
+ return { radiant: "victory", dire: "fail" };
472
+ case "RADIANT_STOMP":
473
+ return { radiant: "stomp", dire: "stomped" };
474
+ case "DIRE_VICTORY":
475
+ return { radiant: "fail", dire: "victory" };
476
+ case "DIRE_STOMP":
477
+ return { radiant: "stomped", dire: "stomp" };
478
+ default:
479
+ return { radiant: "tie", dire: "tie" };
480
+ }
481
+ }, "processLaneOutcome");
482
+ let laneResult = { top: {}, mid: {}, bottom: {} };
483
+ laneResult.top = processLaneOutcome(match.topLaneOutcome);
484
+ laneResult.mid = processLaneOutcome(match.midLaneOutcome);
485
+ laneResult.bottom = processLaneOutcome(match.bottomLaneOutcome);
486
+ match.players.forEach((player) => {
487
+ player.team = player.isRadiant ? "radiant" : "dire";
488
+ player.rank = {
489
+ medal: parseInt(player.steamAccount.seasonRank?.toString().split("")[0] ?? 0),
490
+ star: parseInt(player.steamAccount.seasonRank?.toString().split("")[1] ?? 0),
491
+ leaderboard: player.steamAccount.seasonLeaderboardRank,
492
+ inTop100: player.steamAccount.seasonLeaderboardRank ? player.steamAccount.seasonLeaderboardRank <= 10 ? "8c" : player.steamAccount.seasonLeaderboardRank <= 100 ? "8b" : void 0 : void 0
493
+ };
494
+ player.killContribution = (player.kills + player.assists) / match[player.team].killsCount;
495
+ player.deathContribution = player.deaths / match[player.team === "radiant" ? "dire" : player.team].killsCount;
496
+ player.damageReceived = player.stats?.heroDamageReport?.receivedTotal.physicalDamage + player.stats?.heroDamageReport?.receivedTotal.magicalDamage + player.stats?.heroDamageReport?.receivedTotal.pureDamage;
497
+ match[player.team].heroDamage = (match[player.team].heroDamage ?? 0) + player.heroDamage;
498
+ match[player.team].damageReceived = (match[player.team].damageReceived ?? 0) + player.damageReceived;
499
+ match[player.team].networth += player.networth;
500
+ match[player.team].experience += Math.floor(player.experiencePerMinute / 60 * match.durationSeconds);
501
+ player.titles = [];
502
+ player.mvpScore = // 计算MVP分数
503
+ 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;
504
+ player.order = heroOrderList[player.hero.id];
505
+ if (player.partyId != null) {
506
+ if (!match.party[player.partyId])
507
+ match.party[player.partyId] = party_mark[party_index++];
508
+ }
509
+ const maxStackCountsByAbilityOrItem = player.stats.matchPlayerBuffEvent.reduce((acc, event) => {
510
+ const key = event.abilityId !== null ? `ability-${event.abilityId}` : `item-${event.itemId}`;
511
+ if (!acc[key] || event.stackCount > acc[key].stackCount) {
512
+ acc[key] = event;
513
+ }
514
+ return acc;
515
+ }, {});
516
+ player.stats.matchPlayerBuffEvent.splice(0, player.stats.matchPlayerBuffEvent.length, ...Object.values(maxStackCountsByAbilityOrItem));
517
+ switch (player.lane) {
518
+ case "SAFE_LANE":
519
+ player.laneResult = laneResult[player.isRadiant ? "bottom" : "top"][player.team];
520
+ break;
521
+ case "OFF_LANE":
522
+ player.laneResult = laneResult[!player.isRadiant ? "bottom" : "top"][player.team];
523
+ break;
524
+ default:
525
+ player.laneResult = laneResult.mid[player.team];
526
+ break;
527
+ }
528
+ let items_timelist = {};
529
+ player.supportItemsCount = { 30: 0, 40: 0, 42: 0, 43: 0, 188: 0 };
530
+ if (player.playbackData) {
531
+ for (let item of player.playbackData.purchaseEvents) {
532
+ items_timelist[item.itemId] = item.time;
533
+ if (item.itemId == 42 || item.itemId == 43)
534
+ items_timelist[218] = item.time;
535
+ switch (item.itemId) {
536
+ case 30:
537
+ case 40:
538
+ case 42:
539
+ case 43:
540
+ case 188:
541
+ player.supportItemsCount[item.itemId]++;
542
+ break;
543
+ }
544
+ }
545
+ }
546
+ player.items = [];
547
+ player.backpacks = [];
548
+ const prefix = "recipe_";
549
+ for (let i = 0; i <= 5; i++) {
550
+ const key = `item${i}Id`;
551
+ const itemId = player[key];
552
+ if (itemId === void 0 || itemId === null) {
553
+ player.items.push(null);
554
+ } else if (dotaconstants2.item_ids[itemId]) {
555
+ const name2 = dotaconstants2.item_ids[itemId];
556
+ const isRecipe = name2.startsWith(prefix);
557
+ const cleanName = isRecipe ? name2.substring(prefix.length) : name2;
558
+ player.items.push({
559
+ id: itemId,
560
+ name: cleanName,
561
+ time: items_timelist[itemId],
562
+ isRecipe
563
+ });
564
+ } else {
565
+ player.items.push(null);
566
+ }
567
+ }
568
+ for (let i = 0; i <= 2; i++) {
569
+ const key = `backpack${i}Id`;
570
+ const itemId = player[key];
571
+ if (itemId === void 0 || itemId === null) {
572
+ player.backpacks.push(null);
573
+ } else if (dotaconstants2.item_ids[itemId]) {
574
+ const name2 = dotaconstants2.item_ids[itemId];
575
+ const isRecipe = name2.startsWith(prefix);
576
+ const cleanName = isRecipe ? name2.substring(prefix.length) : name2;
577
+ player.backpacks.push({
578
+ id: itemId,
579
+ name: cleanName,
580
+ time: items_timelist[itemId],
581
+ isRecipe
582
+ });
583
+ } else {
584
+ player.backpacks.push(null);
585
+ }
586
+ }
587
+ if (player.additionalUnit) {
588
+ player.unitItems = [];
589
+ player.unitBackpacks = [];
590
+ const prefix2 = "recipe_";
591
+ for (let i = 0; i <= 5; i++) {
592
+ const key = `item${i}Id`;
593
+ const itemId = player.additionalUnit[key];
594
+ if (itemId === void 0 || itemId === null) {
595
+ player.unitItems.push(null);
596
+ } else if (dotaconstants2.item_ids[itemId]) {
597
+ const name2 = dotaconstants2.item_ids[itemId];
598
+ const isRecipe = name2.startsWith(prefix2);
599
+ const cleanName = isRecipe ? name2.substring(prefix2.length) : name2;
600
+ player.unitItems.push({
601
+ id: itemId,
602
+ name: cleanName,
603
+ time: items_timelist[itemId],
604
+ isRecipe
605
+ });
606
+ } else {
607
+ player.unitItems.push(null);
608
+ }
609
+ }
610
+ for (let i = 0; i <= 2; i++) {
611
+ const key = `backpack${i}Id`;
612
+ const itemId = player.additionalUnit[key];
613
+ if (itemId === void 0 || itemId === null) {
614
+ player.unitBackpacks.push(null);
615
+ } else if (dotaconstants2.item_ids[itemId]) {
616
+ const name2 = dotaconstants2.item_ids[itemId];
617
+ const isRecipe = name2.startsWith(prefix2);
618
+ const cleanName = isRecipe ? name2.substring(prefix2.length) : name2;
619
+ player.unitBackpacks.push({
620
+ id: itemId,
621
+ name: cleanName,
622
+ time: items_timelist[itemId],
623
+ isRecipe
624
+ });
625
+ } else {
626
+ player.unitBackpacks.push(null);
627
+ }
628
+ }
629
+ }
630
+ });
631
+ let ComparisonMode;
632
+ ((ComparisonMode2) => {
633
+ ComparisonMode2["Max"] = "max";
634
+ ComparisonMode2["Min"] = "min";
635
+ })(ComparisonMode || (ComparisonMode = {}));
636
+ function findMaxByProperty(primaryProperty, secondaryProperty = null, players = match.players, primaryMode = "max" /* Max */, secondaryMode = "max" /* Max */) {
637
+ return players.reduce((result, player) => {
638
+ const primaryComparison = primaryMode === "max" /* Max */ ? player[primaryProperty] > result[primaryProperty] : player[primaryProperty] < result[primaryProperty];
639
+ const secondaryComparison = secondaryMode === "max" /* Max */ ? player[secondaryProperty] > result[secondaryProperty] : player[secondaryProperty] < result[secondaryProperty];
640
+ if (primaryComparison) {
641
+ return player;
642
+ } else if (player[primaryProperty] === result[primaryProperty] && secondaryProperty && secondaryComparison) {
643
+ return player;
644
+ }
645
+ return result;
646
+ });
647
+ }
648
+ __name(findMaxByProperty, "findMaxByProperty");
649
+ findMaxByProperty(
650
+ "mvpScore",
651
+ void 0,
652
+ match.players.filter((player) => match.didRadiantWin == player.isRadiant)
653
+ ).titles.push({ name: "MVP", color: "#FFA500" });
654
+ findMaxByProperty(
655
+ "mvpScore",
656
+ void 0,
657
+ match.players.filter((player) => match.didRadiantWin != player.isRadiant)
658
+ ).titles.push({ name: "魂", color: "#6cf" });
659
+ findMaxByProperty("networth").titles.push({ name: "富", color: "#FFD700" });
660
+ findMaxByProperty("experiencePerMinute").titles.push({ name: "睿", color: "#8888FF" });
661
+ match.players.reduce(
662
+ (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
663
+ ).titles.push({ name: "控", color: "#FF00FF" });
664
+ findMaxByProperty("heroDamage").titles.push({ name: "爆", color: "#CC0088" });
665
+ findMaxByProperty("kills", "heroDamage").titles.push({ name: "破", color: "#DD0000" });
666
+ findMaxByProperty("deaths", "networth", void 0, void 0, "min" /* Min */).titles.push({ name: "鬼", color: "#CCCCCC" });
667
+ findMaxByProperty("assists", "heroDamage").titles.push({ name: "助", color: "#006400" });
668
+ findMaxByProperty("towerDamage", "heroDamage").titles.push({ name: "拆", color: "#FEDCBA" });
669
+ findMaxByProperty("heroHealing").titles.push({ name: "奶", color: "#00FF00" });
670
+ match.players.reduce(
671
+ (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
672
+ ).titles.push({ name: "耐", color: "#84A1C7" });
673
+ match.players.reduce((lowest, player) => {
674
+ const currentContribution = (player.kills + player.assists) / match[player.team].KillsCount;
675
+ const lowestContribution = (lowest.kills + lowest.assists) / match[lowest.team].KillsCount;
676
+ if (currentContribution < lowestContribution) {
677
+ return player;
678
+ } else if (currentContribution === lowestContribution) {
679
+ const currentPlayerScore = player.kills + player.assists;
680
+ const lowestPlayerScore = lowest.kills + lowest.assists;
681
+ if (currentPlayerScore < lowestPlayerScore) {
682
+ return player;
683
+ } else if (currentPlayerScore === lowestPlayerScore) {
684
+ return player.heroDamage < lowest.heroDamage ? player : lowest;
685
+ }
686
+ }
687
+ return lowest;
688
+ }).titles.push({ name: "摸", color: "#DDDDDD" });
689
+ return match;
535
690
  }
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
- `;
691
+ __name(getFormattedMatchData, "getFormattedMatchData");
692
+ function sec2time(sec) {
693
+ return sec ? (sec < 0 ? "-" : "") + Math.floor(Math.abs(sec) / 60) + ":" + ("00" + Math.abs(sec) % 60).slice(-2) : "--:--";
546
694
  }
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
- `;
695
+ __name(sec2time, "sec2time");
696
+ function formatNumber(num) {
697
+ return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
561
698
  }
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
- `;
699
+ __name(formatNumber, "formatNumber");
700
+ function readDirectoryFilesSync(directoryPath) {
701
+ try {
702
+ const files = import_fs.default.readdirSync(directoryPath);
703
+ const fileNames = files.map((file) => import_path.default.basename(file, import_path.default.extname(file)));
704
+ return fileNames;
705
+ } catch (error) {
706
+ console.error("Error reading directory:", error);
707
+ return [];
708
+ }
620
709
  }
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
- `;
710
+ __name(readDirectoryFilesSync, "readDirectoryFilesSync");
711
+ function winRateColor(value) {
712
+ value = value * 100;
713
+ value = Math.max(0, Math.min(100, value));
714
+ let red, green, blue;
715
+ if (value <= 50) {
716
+ let scale = Math.round(255 * (value / 50));
717
+ red = 255;
718
+ green = scale;
719
+ blue = scale;
720
+ } else {
721
+ let scale = Math.round(255 * ((value - 50) / 50));
722
+ red = 255 - scale;
723
+ green = 255;
724
+ blue = 255 - scale;
725
+ }
726
+ const toHex = /* @__PURE__ */ __name((color) => color.toString(16).padStart(2, "0").toUpperCase(), "toHex");
727
+ return `#${toHex(red)}${toHex(green)}${toHex(blue)}`;
651
728
  }
652
- __name(HERO_MATCHUP_WINRATE, "HERO_MATCHUP_WINRATE");
729
+ __name(winRateColor, "winRateColor");
730
+ async function playerisValid(steamAccountId) {
731
+ try {
732
+ let queryRes = await query(VERIFYING_PLAYER(steamAccountId));
733
+ if (queryRes.status == 200) {
734
+ if (queryRes.data.data.player.matchCount != null)
735
+ return { isValid: true };
736
+ else
737
+ return { isValid: false, reason: "SteamID无效或无任何场次。" };
738
+ }
739
+ } catch (error) {
740
+ console.error(error);
741
+ return { isValid: false, reason: "网络状况不佳SteamID验证失败,请稍后重试。" };
742
+ }
743
+ }
744
+ __name(playerisValid, "playerisValid");
653
745
 
654
746
  // src/index.ts
655
747
  var import_fs2 = __toESM(require("fs"));
656
- var cheerio = __toESM(require("cheerio"));
657
748
  var import_moment = __toESM(require("moment"));
658
749
  var dotaconstants3 = __toESM(require("dotaconstants"));
659
750
 
660
751
  // src/dotaconstants_add.json
752
+ var dotaconstants_add_exports = {};
753
+ __export(dotaconstants_add_exports, {
754
+ HEROES_CHINESE: () => HEROES_CHINESE,
755
+ LOSE_NEGATIVE: () => LOSE_NEGATIVE,
756
+ LOSE_POSITIVE: () => LOSE_POSITIVE,
757
+ WIN_NEGATIVE: () => WIN_NEGATIVE,
758
+ WIN_POSITIVE: () => WIN_POSITIVE,
759
+ behavior: () => behavior,
760
+ default: () => dotaconstants_add_default,
761
+ gameMode: () => gameMode,
762
+ lobbyTypes: () => lobbyTypes,
763
+ position: () => position,
764
+ primary_attrs: () => primary_attrs,
765
+ rank: () => rank,
766
+ region: () => region,
767
+ roles: () => roles,
768
+ target_team: () => target_team
769
+ });
661
770
  var gameMode = {
662
771
  NONE: "",
663
772
  ALL_PICK: "全英雄选择",
@@ -732,6 +841,17 @@ var position = {
732
841
  "4": "采灵芝",
733
842
  "5": "工具人"
734
843
  };
844
+ var rank = {
845
+ "0": "未知",
846
+ "1": "先锋",
847
+ "2": "卫士",
848
+ "3": "中军",
849
+ "4": "统帅",
850
+ "5": "传奇",
851
+ "6": "万古流芳",
852
+ "7": "超凡入圣",
853
+ "8": "冠绝一世"
854
+ };
735
855
  var roles = { CARRY: "核心", ESCAPE: "逃生", NUKER: "爆发", INITIATOR: "先手", DURABLE: "耐久", DISABLER: "控制", JUNGLER: "打野", SUPPORT: "辅助", PUSHER: "推进" };
736
856
  var primary_attrs = { all: "hero_universal", str: "hero_strength", agi: "hero_agility", int: "hero_intelligence" };
737
857
  var behavior = {
@@ -877,15 +997,40 @@ var WIN_NEGATIVE = ["侥幸赢得了比赛", "走狗屎运赢得了比赛", "躺
877
997
  var WIN_POSITIVE = ["带领团队走向了胜利", "暴打对面后赢得了胜利", " CARRY全场赢得了胜利", "把对面当猪宰了, 赢得了胜利", "又赢了, 这游戏就是这么枯燥, 且乏味", "直接进行一个比赛的赢"];
878
998
  var LOSE_NEGATIVE = ["被人按在地上摩擦, 输掉了这场比赛", "悲惨地输掉了比赛", "头都被打歪了, 心态爆炸地输掉了比赛", "捕鱼被鱼吃了, 输掉了比赛", "打的是个几把", "直接进行一个比赛的输"];
879
999
  var LOSE_POSITIVE = ["无力回天输掉了比赛", "尽力了, 但还是输了比赛", "背靠世界树, 虽败犹荣", "带不动队友, 输了比赛", "又输了, 很难受, 宁愿输的是我"];
1000
+ var dotaconstants_add_default = {
1001
+ gameMode,
1002
+ lobbyTypes,
1003
+ region,
1004
+ position,
1005
+ rank,
1006
+ roles,
1007
+ primary_attrs,
1008
+ behavior,
1009
+ target_team,
1010
+ HEROES_CHINESE,
1011
+ WIN_NEGATIVE,
1012
+ WIN_POSITIVE,
1013
+ LOSE_NEGATIVE,
1014
+ LOSE_POSITIVE
1015
+ };
880
1016
 
881
1017
  // src/index.ts
882
1018
  var import_koishi2 = require("koishi");
1019
+ var ejs = __toESM(require("ejs"));
1020
+ var import_path2 = __toESM(require("path"));
883
1021
  var name = "dota2tracker";
884
1022
  var usage = "DOTA2Bot插件-提供自动追踪群友的最新对局的功能(需群友绑定),以及一系列查询功能。";
885
1023
  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
- });
1024
+ var Config = import_koishi.Schema.intersect([
1025
+ import_koishi.Schema.object({
1026
+ STRATZ_API_TOKEN: import_koishi.Schema.string().required().description("※必须。stratz.com的API TOKEN,可在 https://stratz.com/api 获取")
1027
+ }).description("基础设置"),
1028
+ import_koishi.Schema.object({
1029
+ template_match: import_koishi.Schema.union([...readDirectoryFilesSync(`./node_modules/@sjtdev/koishi-plugin-${name}/template/match`)]).default("match_1").description("生成比赛信息图片使用的模板,见 https://github.com/sjtdev/koishi-plugin-dota2tracker/wiki 有模板展示。"),
1030
+ template_player: import_koishi.Schema.union([...readDirectoryFilesSync(`./node_modules/@sjtdev/koishi-plugin-${name}/template/player`)]).default("player_1").description("生成玩家信息图片使用的模板。(目前仅有一张模板)"),
1031
+ template_hero: import_koishi.Schema.union([...readDirectoryFilesSync(`./node_modules/@sjtdev/koishi-plugin-${name}/template/hero`)]).default("hero_1").description("生成英雄信息图片使用的模板。(目前仅有一张模板)")
1032
+ }).description("模板设置")
1033
+ ]);
889
1034
  var pendingMatches = [];
890
1035
  var random = new import_koishi2.Random(() => Math.random());
891
1036
  async function apply(ctx, config) {
@@ -930,8 +1075,8 @@ async function apply(ctx, config) {
930
1075
  );
931
1076
  return;
932
1077
  }
933
- let verifyRes = await playerIsInvalid(steam_id);
934
- if (!verifyRes.isInvalid) {
1078
+ let verifyRes = await playerisValid(steam_id);
1079
+ if (!verifyRes.isValid) {
935
1080
  session.send(`绑定失败,${verifyRes.reason}`);
936
1081
  return;
937
1082
  }
@@ -977,39 +1122,45 @@ async function apply(ctx, config) {
977
1122
  session.send("开发中,未来此功能会重写。\n" + queryRes.map((item) => `${item.nickName ?? ""},ID:${item.userId},SteamID:${item.steamId}`).join("\n"));
978
1123
  }
979
1124
  });
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("正在搜索对局详情,请稍后...");
1125
+ async function queryAndDisplayMatch(session, matchId) {
990
1126
  try {
991
1127
  let match;
992
- let queryLocal = await ctx.database.get("dt_previous_query_results", match_id, ["data"]);
1128
+ let queryLocal = await ctx.database.get("dt_previous_query_results", matchId, ["data"]);
993
1129
  if (queryLocal.length > 0) {
994
1130
  match = queryLocal[0].data;
995
1131
  ctx.database.set("dt_previous_query_results", match.id, { queryTime: /* @__PURE__ */ new Date() });
996
1132
  } else {
997
- let queryRes = await query(MATCH_INFO(match_id));
1133
+ let queryRes = await query(MATCH_INFO(matchId));
998
1134
  if (queryRes.status == 200) {
999
1135
  match = getFormattedMatchData(queryRes.data.data.match);
1000
1136
  }
1001
1137
  }
1002
- if (match.parsedDateTime) {
1003
- session.send(await ctx.puppeteer.render(genMatchImageHTML(match)));
1138
+ if (match && match.parsedDateTime) {
1139
+ session.send(await ctx.puppeteer.render(genImageHTML(match, config.template_match, "match" /* Match */)));
1004
1140
  ctx.database.upsert("dt_previous_query_results", (row) => [{ matchId: match.id, data: match, queryTime: /* @__PURE__ */ new Date() }]);
1005
1141
  } else {
1006
- pendingMatches.push({ matchId: match_id, platform: session.event.platform, guildId: session.event.guild.id });
1142
+ pendingMatches.push({ matchId, platform: session.event.platform, guildId: session.event.guild.id });
1007
1143
  session.send("比赛尚未解析,将在解析完成后发布。");
1008
1144
  }
1009
1145
  } catch (error) {
1010
1146
  console.error(error);
1011
1147
  session.send("获取比赛信息失败。");
1148
+ ctx.database.remove("dt_previous_query_results", { matchId: parseInt(matchId) });
1149
+ }
1150
+ }
1151
+ __name(queryAndDisplayMatch, "queryAndDisplayMatch");
1152
+ ctx.command("查询比赛 <match_id>", "查询比赛ID").usage("查询指定比赛ID的比赛数据,生成图片发布。").example("-查询比赛 1234567890").action(async ({ session }, match_id) => {
1153
+ if (!match_id) {
1154
+ session.send("请输入比赛ID。");
1155
+ return;
1156
+ }
1157
+ JSON.stringify;
1158
+ if (!/^\d{10}$/.test(match_id)) {
1159
+ session.send("比赛ID无效。");
1160
+ return;
1012
1161
  }
1162
+ session.send("正在搜索对局详情,请稍后...");
1163
+ queryAndDisplayMatch(session, match_id);
1013
1164
  });
1014
1165
  ctx.command("查询最近比赛 [input_data]", "查询玩家的最近比赛").usage("查询指定玩家的最近一场比赛的比赛数据,生成图片发布。\n参数可输入该玩家的SteamID或已在本群绑定玩家的别名,无参数时尝试查询调用指令玩家的SteamID").example("-查询最近比赛 123456789").example("-查询最近比赛 张三").action(async ({ session }, input_data) => {
1015
1166
  if (session.guild) {
@@ -1035,28 +1186,7 @@ async function apply(ctx, config) {
1035
1186
  session.send("获取玩家最近比赛失败。");
1036
1187
  return;
1037
1188
  }
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
- }
1189
+ queryAndDisplayMatch(session, lastMatchId);
1060
1190
  }
1061
1191
  });
1062
1192
  ctx.command("查询玩家 <input_data>", "查询玩家信息").usage("查询指定玩家的个人信息与最近战绩,生成图片发布。\n参数可输入该玩家的SteamID或已在本群绑定玩家的别名,无参数时尝试查询调用指令玩家的SteamID").example("-查询玩家 123456789").example("-查询玩家 张三").action(async ({ session }, input_data) => {
@@ -1112,7 +1242,7 @@ async function apply(ctx, config) {
1112
1242
  player.heroesPerformanceTop10 = playerExtra.heroesPerformance.slice(0, 10);
1113
1243
  } else
1114
1244
  throw 0;
1115
- session.send(await ctx.puppeteer.render(genPlayerHTML(player)));
1245
+ session.send(await ctx.puppeteer.render(genImageHTML(player, config.template_player, "player" /* Player */)));
1116
1246
  } catch (error) {
1117
1247
  console.error(error);
1118
1248
  session.send("获取玩家信息失败。");
@@ -1165,7 +1295,7 @@ async function apply(ctx, config) {
1165
1295
  if (queryRes3.status == 200) {
1166
1296
  let hero = queryRes3.data.data.constants.hero;
1167
1297
  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)));
1298
+ await session.send(await ctx.puppeteer.render(genImageHTML(hero, config.template_hero, "hero" /* Hero */)));
1169
1299
  } else
1170
1300
  throw 0;
1171
1301
  } catch (error) {
@@ -1292,7 +1422,7 @@ async function apply(ctx, config) {
1292
1422
  const commingMatches = scanningMatches.filter((item) => item.matchId == match.id);
1293
1423
  const realCommingMatches = commingMatches.filter((commingMatch, index, self) => index === self.findIndex((t) => t.guildId === commingMatch.guildId && t.platform === commingMatch.platform));
1294
1424
  let broadMatchMessage = "";
1295
- const img = await ctx.puppeteer.render(genMatchImageHTML(match));
1425
+ const img = await ctx.puppeteer.render(genImageHTML(match, config.template_match, "match" /* Match */));
1296
1426
  for (let comming of realCommingMatches) {
1297
1427
  let commingSubscribedPlayers = subscribedPlayersInGuild.filter((item) => item.platform == comming.platform && item.guildId == comming.guildId);
1298
1428
  let idsToFind = commingSubscribedPlayers.map((item) => item.steamId);
@@ -1311,7 +1441,7 @@ async function apply(ctx, config) {
1311
1441
  broadPlayerMessage += random.pick(LOSE_NEGATIVE);
1312
1442
  }
1313
1443
  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)}%`;
1444
+ KDA:${((player.kills + player.assists) / (player.deaths || 1)).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
1445
  broadMatchMessage += broadPlayerMessage + "\n";
1316
1446
  }
1317
1447
  await ctx.broadcast([`${comming.platform}:${comming.guildId}`], broadMatchMessage + img);
@@ -1322,6 +1452,7 @@ KDA:${((player.kills + player.assists) / Math.max(1, player.deaths)).toFixed(2
1322
1452
  ctx.logger.info("比赛 %d 尚未解析完成,继续等待。", match.id);
1323
1453
  } catch (error) {
1324
1454
  console.error(error);
1455
+ ctx.database.remove("dt_previous_query_results", { matchId: pendingMatch.matchId });
1325
1456
  }
1326
1457
  }
1327
1458
  });
@@ -1330,638 +1461,28 @@ KDA:${((player.kills + player.assists) / Math.max(1, player.deaths)).toFixed(2
1330
1461
  });
1331
1462
  }
1332
1463
  __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
1464
+ function genImageHTML(data, template, type) {
1465
+ const templatePath = import_path2.default.join(`./node_modules/@sjtdev/koishi-plugin-${name}/template/${type}`, template + ".ejs");
1466
+ const templateData = {
1467
+ data,
1468
+ utils: utils_exports,
1469
+ ImageType,
1470
+ d2a: dotaconstants_add_exports,
1471
+ dotaconstants: dotaconstants3,
1472
+ moment: import_moment.default
1776
1473
  };
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
- }
1474
+ let result = "";
1475
+ ejs.renderFile(templatePath, templateData, (err, html) => {
1476
+ if (err)
1477
+ throw err;
1478
+ else
1479
+ result = html;
1822
1480
  });
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
1481
  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
- }, "");
1482
+ import_fs2.default.writeFileSync("./node_modules/@sjtdev/koishi-plugin-dota2tracker/temp.html", result);
1483
+ return result;
1963
1484
  }
1964
- __name(sanitizeHTML, "sanitizeHTML");
1485
+ __name(genImageHTML, "genImageHTML");
1965
1486
  // Annotate the CommonJS export names for ESM import in node:
1966
1487
  0 && (module.exports = {
1967
1488
  Config,