@sjtdev/koishi-plugin-dota2tracker 1.2.19 → 1.2.20-pre.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -60,474 +60,8 @@ __export(utils_exports, {
60
60
  winRateColor: () => winRateColor
61
61
  });
62
62
  var import_fs = __toESM(require("fs"));
63
- var dotaconstants2 = __toESM(require("dotaconstants"));
64
- var import_path = __toESM(require("path"));
65
-
66
- // src/queries.ts
67
- var queries_exports = {};
68
- __export(queries_exports, {
69
- ALL_ABILITIES_CHINESE_NAME: () => ALL_ABILITIES_CHINESE_NAME,
70
- CURRENT_GAMEVERSION: () => CURRENT_GAMEVERSION,
71
- HERO_INFO: () => HERO_INFO,
72
- HERO_MATCHUP_WINRATE: () => HERO_MATCHUP_WINRATE,
73
- MATCH_INFO: () => MATCH_INFO,
74
- PLAYERS_INFO_WITH_10_MATCHES_FOR_GUILD: () => PLAYERS_INFO_WITH_10_MATCHES_FOR_GUILD,
75
- PLAYERS_LASTMATCH_RANKINFO: () => PLAYERS_LASTMATCH_RANKINFO,
76
- PLAYERS_MATCHES_FOR_DAILY: () => PLAYERS_MATCHES_FOR_DAILY,
77
- PLAYER_EXTRA_INFO: () => PLAYER_EXTRA_INFO,
78
- PLAYER_INFO_WITH_25_MATCHES: () => PLAYER_INFO_WITH_25_MATCHES,
79
- REQUEST_MATCH_DATA_ANALYSIS: () => REQUEST_MATCH_DATA_ANALYSIS,
80
- VERIFYING_PLAYER: () => VERIFYING_PLAYER
81
- });
82
63
  var dotaconstants = __toESM(require("dotaconstants"));
83
- function MATCH_INFO(matchId) {
84
- return `
85
- {
86
- match(id: ${matchId}) {
87
- id
88
- didRadiantWin
89
- lobbyType
90
- gameMode
91
- regionId
92
- parsedDateTime
93
- startDateTime
94
- endDateTime
95
- actualRank
96
- rank
97
- averageRank
98
- durationSeconds
99
- topLaneOutcome
100
- midLaneOutcome
101
- bottomLaneOutcome
102
- radiantKills
103
- direKills
104
- players {
105
- steamAccountId
106
- steamAccount {
107
- name
108
- }
109
- level
110
- hero {
111
- id
112
- name
113
- shortName
114
- facets {
115
- facetId
116
- }
117
- }
118
- variant
119
- dotaPlus {
120
- level
121
- }
122
- leaverStatus
123
- partyId
124
- position
125
- playerSlot
126
- lane
127
- imp
128
- kills
129
- deaths
130
- assists
131
- isRadiant
132
- networth
133
- steamAccount {
134
- seasonRank
135
- seasonLeaderboardRank
136
- }
137
- item0Id
138
- item1Id
139
- item2Id
140
- item3Id
141
- item4Id
142
- item5Id
143
- backpack0Id
144
- backpack1Id
145
- backpack2Id
146
- neutral0Id
147
- stats {
148
- matchPlayerBuffEvent {
149
- abilityId
150
- itemId
151
- stackCount
152
- }
153
- }
154
- heroDamage
155
- towerDamage
156
- stats {
157
- heroDamageReport {
158
- receivedTotal {
159
- physicalDamage
160
- magicalDamage
161
- pureDamage
162
- }
163
- }
164
- }
165
- numLastHits
166
- numDenies
167
- goldPerMinute
168
- experiencePerMinute
169
- heroHealing
170
- stats {
171
- itemPurchases{
172
- itemId
173
- time
174
- }
175
- campStack
176
- heroDamageReport {
177
- dealtTotal {
178
- stunDuration
179
- stunCount
180
- slowDuration
181
- slowCount
182
- disableDuration
183
- disableCount
184
- }
185
- }
186
- }
187
- additionalUnit {
188
- item0Id
189
- item1Id
190
- item2Id
191
- item3Id
192
- item4Id
193
- item5Id
194
- backpack0Id
195
- backpack1Id
196
- backpack2Id
197
- neutral0Id
198
- }
199
- isRandom
200
- }
201
- pickBans {
202
- isPick
203
- bannedHeroId
204
- heroId
205
- order
206
- }
207
- }
208
- constants {
209
- facets (language: S_CHINESE) {
210
- name
211
- id
212
- color
213
- icon
214
- language {
215
- displayName
216
- }
217
- }
218
- }
219
- }
220
-
221
- `;
222
- }
223
- __name(MATCH_INFO, "MATCH_INFO");
224
- function PLAYERS_MATCHES_FOR_DAILY(steamAccountIds, seconds) {
225
- return `
226
- {
227
- players(steamAccountIds:[${steamAccountIds.join(",")}]) {
228
- steamAccount{id name avatar}
229
- matches(request:{startDateTime:${seconds} take:50}){
230
- id
231
- didRadiantWin
232
- parsedDateTime
233
- startDateTime
234
- players {
235
- kills
236
- deaths
237
- assists
238
- imp
239
- isRadiant
240
- steamAccount {
241
- id
242
- }
243
- }
244
- }
245
- }
246
- }
247
- `;
248
- }
249
- __name(PLAYERS_MATCHES_FOR_DAILY, "PLAYERS_MATCHES_FOR_DAILY");
250
- function VERIFYING_PLAYER(steamAccountId) {
251
- return `
252
- {
253
- player(steamAccountId: ${steamAccountId}) {
254
- matchCount
255
- }
256
- }
257
-
258
- `;
259
- }
260
- __name(VERIFYING_PLAYER, "VERIFYING_PLAYER");
261
- function PLAYERS_LASTMATCH_RANKINFO(steamAccountIds) {
262
- return `
263
- {
264
- players(steamAccountIds:[${steamAccountIds.join(",")}]) {
265
- steamAccount{
266
- id
267
- name
268
- avatar
269
- seasonRank
270
- seasonLeaderboardRank
271
- }
272
- matches(request:{take:1}){
273
- id
274
- parsedDateTime
275
- startDateTime
276
- players{
277
- steamAccount{
278
- id
279
- }
280
- }
281
- }
282
- }
283
- }
284
-
285
- `;
286
- }
287
- __name(PLAYERS_LASTMATCH_RANKINFO, "PLAYERS_LASTMATCH_RANKINFO");
288
- function PLAYER_INFO_WITH_25_MATCHES(steamAccountId, heroId) {
289
- return `
290
- {
291
- player(steamAccountId: ${steamAccountId}) {
292
- steamAccount {
293
- avatar
294
- name
295
- seasonRank
296
- seasonLeaderboardRank
297
- id
298
- }
299
- guildMember {
300
- guild {
301
- tag
302
- }
303
- }
304
- matchCount
305
- winCount
306
- performance {
307
- imp
308
- }
309
- heroesPerformance(take: 25, request: {matchGroupOrderBy: WIN_COUNT take: 25 ${heroId ? "heroIds:" + heroId : ""}}) {
310
- hero {
311
- id
312
- shortName
313
- }
314
- imp
315
- winCount
316
- matchCount
317
- }
318
- matches(request: {take: 25 ${heroId ? "heroIds:" + heroId : ""}}) {
319
- id
320
- rank
321
- lobbyType
322
- gameMode
323
- startDateTime
324
- parsedDateTime
325
- durationSeconds
326
- didRadiantWin
327
- topLaneOutcome
328
- midLaneOutcome
329
- bottomLaneOutcome
330
- radiantKills
331
- direKills
332
- players {
333
- steamAccount {
334
- id
335
- }
336
- isRadiant
337
- lane
338
- kills
339
- deaths
340
- assists
341
- position
342
- award
343
- imp
344
- hero {
345
- id
346
- shortName
347
- }
348
- }
349
- }
350
- }
351
-
352
- }
353
-
354
- `;
355
- }
356
- __name(PLAYER_INFO_WITH_25_MATCHES, "PLAYER_INFO_WITH_25_MATCHES");
357
- function PLAYER_EXTRA_INFO(steamAccountId, matchCount, totalHeroCount, heroId) {
358
- return `{
359
- player(steamAccountId: ${steamAccountId}) {
360
- heroesPerformance(take: ${totalHeroCount}, request: {matchGroupOrderBy: MATCH_COUNT, take: ${matchCount} ${heroId ? "heroIds:" + heroId : ""}}) {
361
- hero {
362
- id
363
- shortName
364
- }
365
- winCount
366
- matchCount
367
- imp
368
- }
369
- dotaPlus {
370
- heroId
371
- level
372
- }
373
- }
374
- }
375
- `;
376
- }
377
- __name(PLAYER_EXTRA_INFO, "PLAYER_EXTRA_INFO");
378
- function PLAYERS_INFO_WITH_10_MATCHES_FOR_GUILD(steamAccountIds) {
379
- return `{
380
- players(steamAccountIds: [${steamAccountIds.join()}]) {
381
- steamAccount {
382
- id
383
- avatar
384
- name
385
- seasonRank
386
- }
387
- matches(request: {take: 10}) {
388
- didRadiantWin
389
- startDateTime
390
- players {
391
- isRadiant
392
- kills
393
- deaths
394
- assists
395
- steamAccount {
396
- id
397
- }
398
- hero {
399
- shortName
400
- }
401
- imp
402
- }
403
- }
404
- }
405
- }
406
- `;
407
- }
408
- __name(PLAYERS_INFO_WITH_10_MATCHES_FOR_GUILD, "PLAYERS_INFO_WITH_10_MATCHES_FOR_GUILD");
409
- function CURRENT_GAMEVERSION() {
410
- return `
411
- {
412
- constants {
413
- gameVersions{name id}
414
- }
415
- }
416
-
417
- `;
418
- }
419
- __name(CURRENT_GAMEVERSION, "CURRENT_GAMEVERSION");
420
- function ALL_ABILITIES_CHINESE_NAME() {
421
- return `
422
- {
423
- constants {
424
- abilities(language:S_CHINESE){
425
- id
426
- language{displayName}
427
- }
428
- gameVersions{name id}
429
- }
430
- }
431
-
432
- `;
433
- }
434
- __name(ALL_ABILITIES_CHINESE_NAME, "ALL_ABILITIES_CHINESE_NAME");
435
- function HERO_INFO(heroId) {
436
- return `
437
- {
438
- constants {
439
- hero(id: ${heroId}, language: S_CHINESE) {
440
- id
441
- name
442
- shortName
443
- aliases
444
- roles {
445
- roleId
446
- level
447
- }
448
- language {
449
- displayName
450
- lore
451
- hype
452
- }
453
- abilities {
454
- ability(language: S_CHINESE) {
455
- name
456
- language {
457
- displayName
458
- description
459
- attributes
460
- lore
461
- aghanimDescription
462
- shardDescription
463
- notes
464
- }
465
- stat {
466
- type
467
- behavior
468
- unitTargetType
469
- unitTargetTeam
470
- unitTargetFlags
471
- unitDamageType
472
- cooldown
473
- manaCost
474
- spellImmunity
475
- isOnCastbar
476
- isGrantedByShard
477
- isGrantedByScepter
478
- hasShardUpgrade
479
- hasScepterUpgrade
480
- }
481
- }
482
- }
483
- talents {
484
- abilityId
485
- slot
486
- }
487
- }
488
- }
489
- }
490
-
491
- `;
492
- }
493
- __name(HERO_INFO, "HERO_INFO");
494
- function HERO_MATCHUP_WINRATE(heroId) {
495
- return `
496
- {
497
- heroStats {
498
- matchUp(heroId: ${heroId}, take: ${Object.keys(dotaconstants.heroes).length - 1},bracketBasicIds:LEGEND_ANCIENT) {
499
- heroId
500
- matchCountWith
501
- matchCountVs
502
- with {
503
- heroId1
504
- winRateHeroId1
505
- heroId2
506
- winRateHeroId2
507
- winCount
508
- matchCount
509
- }
510
- vs {
511
- heroId1
512
- winRateHeroId1
513
- heroId2
514
- winRateHeroId2
515
- winCount
516
- matchCount
517
- }
518
- }
519
- }
520
- }
521
-
522
- `;
523
- }
524
- __name(HERO_MATCHUP_WINRATE, "HERO_MATCHUP_WINRATE");
525
- function REQUEST_MATCH_DATA_ANALYSIS(matchId) {
526
- return `stratz{matchRetry(id:${matchId})}`;
527
- }
528
- __name(REQUEST_MATCH_DATA_ANALYSIS, "REQUEST_MATCH_DATA_ANALYSIS");
529
-
530
- // src/utils.ts
64
+ var import_path = __toESM(require("path"));
531
65
  var CONFIGS = { STRATZ_API: { URL: "https://api.stratz.com/graphql", TOKEN: "" } };
532
66
  var http = null;
533
67
  var setTimeout;
@@ -536,40 +70,46 @@ function init(newHttp, newSetTimeout) {
536
70
  setTimeout = newSetTimeout;
537
71
  }
538
72
  __name(init, "init");
539
- async function fetchData(query_str) {
540
- return await http.post(CONFIGS.STRATZ_API.URL, query_str, {
73
+ async function fetchData(query2) {
74
+ return await http.post(CONFIGS.STRATZ_API.URL, JSON.stringify(query2), {
541
75
  responseType: "json",
542
76
  headers: {
543
77
  "User-Agent": "STRATZ_API",
544
- "Content-Type": "application/graphql",
78
+ "Content-Type": "application/json",
545
79
  Authorization: `Bearer ${CONFIGS.STRATZ_API.TOKEN}`
546
80
  }
547
81
  });
548
82
  }
549
83
  __name(fetchData, "fetchData");
550
- async function query(query_func, ...args) {
551
- if (query_func.name.startsWith("PLAYERS") && args[0].length > 5) {
552
- const playerIds = args[0];
84
+ async function query(queryName, variables) {
85
+ if (queryName.startsWith("Players") && variables?.steamAccountIds.length > 5) {
86
+ const playerIds = variables?.steamAccountIds ?? [];
553
87
  const chunkSize = 5;
554
88
  let allPlayers = [];
555
89
  for (let i = 0; i < playerIds.length; i += chunkSize) {
556
90
  const chunk = playerIds.slice(i, i + chunkSize);
557
- const query_str = query_func(chunk, ...args.slice(1));
558
- const result = await new Promise((resolve) => setTimeout(async () => resolve(await fetchData(query_str)), 100));
559
- if (result.errors) throw { errors: result.errors };
91
+ variables.steamAccountIds = chunk;
92
+ const query_str = loadGraphqlFile(queryName);
93
+ const result = await new Promise((resolve) => setTimeout(async () => resolve(await fetchData({ query: query_str, variables })), 200));
94
+ if (result?.errors) throw { errors: result.errors };
560
95
  if (result.data && result.data.players) {
561
96
  allPlayers = allPlayers.concat(result.data.players);
562
97
  }
563
98
  }
564
- return { data: { players: allPlayers } };
99
+ return { players: allPlayers };
565
100
  } else {
566
- const query_str = query_func(...args);
567
- const result = await fetchData(query_str);
101
+ const query_str = loadGraphqlFile(queryName);
102
+ const result = await fetchData({ query: query_str, variables });
568
103
  if (result.errors) throw { errors: result.errors };
569
- return result || {};
104
+ return result.data;
570
105
  }
571
106
  }
572
107
  __name(query, "query");
108
+ function loadGraphqlFile(queryName) {
109
+ const filepath = `./node_modules/@sjtdev/koishi-plugin-dota2tracker/queries/${queryName}.graphql`;
110
+ return import_fs.default.readFileSync(filepath, { encoding: "utf-8" }).replace(/[\r\n]+/g, " ");
111
+ }
112
+ __name(loadGraphqlFile, "loadGraphqlFile");
573
113
  async function queryHeroFromValve(heroId) {
574
114
  return (await http.get(`https://www.dota2.com/datafeed/herodata?language=schinese&hero_id=${heroId}`)).result.data.heroes[0];
575
115
  }
@@ -611,7 +151,8 @@ function getImageUrl(image, type = "local" /* Local */, format = "png" /* png */
611
151
  }
612
152
  __name(getImageUrl, "getImageUrl");
613
153
  function getFormattedMatchData(data) {
614
- const { match, constants } = data;
154
+ const match = data.match;
155
+ const constants = data.constants;
615
156
  ["radiant", "dire"].forEach((team) => {
616
157
  match[team] = { killsCount: match[team + "Kills"]?.reduce((acc, cva) => acc + cva, 0) ?? 0, damageReceived: 0, heroDamage: 0, networth: 0, experience: 0 };
617
158
  });
@@ -680,7 +221,10 @@ function getFormattedMatchData(data) {
680
221
  }
681
222
  return acc;
682
223
  }, {});
683
- player.stats.matchPlayerBuffEvent.splice(0, player.stats.matchPlayerBuffEvent.length, ...Object.values(maxStackCountsByAbilityOrItem));
224
+ player.buffs = Object.entries(maxStackCountsByAbilityOrItem).map(([key, event]) => ({
225
+ key,
226
+ event
227
+ }));
684
228
  }
685
229
  switch (player.lane) {
686
230
  case "SAFE_LANE":
@@ -741,8 +285,8 @@ function getFormattedMatchData(data) {
741
285
  const itemId = player[key];
742
286
  if (itemId === void 0 || itemId === null) {
743
287
  player.items.push(null);
744
- } else if (dotaconstants2.item_ids[itemId]) {
745
- const name2 = dotaconstants2.item_ids[itemId];
288
+ } else if (dotaconstants.item_ids[itemId]) {
289
+ const name2 = dotaconstants.item_ids[itemId];
746
290
  const isRecipe = name2.startsWith(prefix);
747
291
  const cleanName = isRecipe ? name2.substring(prefix.length) : name2;
748
292
  player.items.push({
@@ -760,8 +304,8 @@ function getFormattedMatchData(data) {
760
304
  const itemId = player[key];
761
305
  if (itemId === void 0 || itemId === null) {
762
306
  player.backpacks.push(null);
763
- } else if (dotaconstants2.item_ids[itemId]) {
764
- const name2 = dotaconstants2.item_ids[itemId];
307
+ } else if (dotaconstants.item_ids[itemId]) {
308
+ const name2 = dotaconstants.item_ids[itemId];
765
309
  const isRecipe = name2.startsWith(prefix);
766
310
  const cleanName = isRecipe ? name2.substring(prefix.length) : name2;
767
311
  player.backpacks.push({
@@ -783,8 +327,8 @@ function getFormattedMatchData(data) {
783
327
  const itemId = player.additionalUnit[key];
784
328
  if (itemId === void 0 || itemId === null) {
785
329
  player.unitItems.push(null);
786
- } else if (dotaconstants2.item_ids[itemId]) {
787
- const name2 = dotaconstants2.item_ids[itemId];
330
+ } else if (dotaconstants.item_ids[itemId]) {
331
+ const name2 = dotaconstants.item_ids[itemId];
788
332
  const isRecipe = name2.startsWith(prefix2);
789
333
  const cleanName = isRecipe ? name2.substring(prefix2.length) : name2;
790
334
  player.unitItems.push({
@@ -802,8 +346,8 @@ function getFormattedMatchData(data) {
802
346
  const itemId = player.additionalUnit[key];
803
347
  if (itemId === void 0 || itemId === null) {
804
348
  player.unitBackpacks.push(null);
805
- } else if (dotaconstants2.item_ids[itemId]) {
806
- const name2 = dotaconstants2.item_ids[itemId];
349
+ } else if (dotaconstants.item_ids[itemId]) {
350
+ const name2 = dotaconstants.item_ids[itemId];
807
351
  const isRecipe = name2.startsWith(prefix2);
808
352
  const cleanName = isRecipe ? name2.substring(prefix2.length) : name2;
809
353
  player.unitBackpacks.push({
@@ -926,7 +470,7 @@ function winRateColor(value) {
926
470
  __name(winRateColor, "winRateColor");
927
471
  async function playerisValid(steamAccountId) {
928
472
  try {
929
- let queryRes = await query(VERIFYING_PLAYER, steamAccountId);
473
+ let queryRes = await query("VerifyingPlayer", { steamAccountId });
930
474
  if (queryRes.data.player.matchCount != null) return { isValid: true };
931
475
  else return { isValid: false, reason: "SteamID无效或无任何场次。" };
932
476
  } catch (error) {
@@ -979,7 +523,7 @@ __name(formatHeroDesc, "formatHeroDesc");
979
523
  var import_fs2 = __toESM(require("fs"));
980
524
  var import_path2 = __toESM(require("path"));
981
525
  var import_moment = __toESM(require("moment"));
982
- var dotaconstants3 = __toESM(require("dotaconstants"));
526
+ var dotaconstants2 = __toESM(require("dotaconstants"));
983
527
 
984
528
  // src/dotaconstants_add.json
985
529
  var dotaconstants_add_exports = {};
@@ -1418,10 +962,10 @@ async function apply(ctx, config) {
1418
962
  memberList = await session.bot?.getGuildMemberList(session.event.channel.id);
1419
963
  } catch (error) {
1420
964
  }
1421
- async function getUsers(subscribedPlayers2, utils, queries, memberList2) {
1422
- const playerSteamIds = subscribedPlayers2.map((player) => player.steamId);
1423
- const queryResult = await utils.query(queries.PLAYERS_INFO_WITH_10_MATCHES_FOR_GUILD, playerSteamIds);
1424
- const playersInfo = queryResult.data.players;
965
+ async function getUsers(subscribedPlayers2, memberList2) {
966
+ const playerSteamIds = { steamAccountIds: subscribedPlayers2.map((player) => player.steamId) };
967
+ const queryResult = await query("PlayersInfoWith10MatchesForGuild", { steamAccountIds: playerSteamIds });
968
+ const playersInfo = queryResult.players;
1425
969
  const users2 = [];
1426
970
  for (const subscribedPlayer of subscribedPlayers2) {
1427
971
  const queryPlayer = playersInfo.find((player) => player.steamAccount.id == subscribedPlayer.steamId);
@@ -1431,7 +975,7 @@ async function apply(ctx, config) {
1431
975
  return users2;
1432
976
  }
1433
977
  __name(getUsers, "getUsers");
1434
- const users = await getUsers(subscribedPlayers, utils_exports, queries_exports, memberList);
978
+ const users = await getUsers(subscribedPlayers, memberList);
1435
979
  session.send(await ctx.puppeteer.render(genImageHTML(users, "guild_member" /* GuildMember */, "guild_member" /* GuildMember */)));
1436
980
  } catch (error) {
1437
981
  ctx.logger.error(error);
@@ -1442,18 +986,17 @@ async function apply(ctx, config) {
1442
986
  });
1443
987
  async function queryMatchAndSend(session, matchId) {
1444
988
  try {
1445
- let match;
989
+ let matchQuery;
1446
990
  let queryLocal = await ctx.database.get("dt_previous_query_results", matchId, ["data"]);
1447
991
  if (queryLocal.length > 0) {
1448
- match = queryLocal[0].data;
1449
- ctx.database.set("dt_previous_query_results", match.id, { queryTime: /* @__PURE__ */ new Date() });
1450
- } else {
1451
- match = getFormattedMatchData((await query(MATCH_INFO, matchId)).data);
1452
- }
992
+ matchQuery = queryLocal[0].data;
993
+ ctx.database.set("dt_previous_query_results", matchQuery.match.id, { queryTime: /* @__PURE__ */ new Date() });
994
+ } else matchQuery = await query("MatchInfo", { matchId });
995
+ if (matchQuery.match.parsedDateTime)
996
+ ctx.database.upsert("dt_previous_query_results", (row) => [{ matchId: matchQuery.match.id, data: matchQuery, queryTime: /* @__PURE__ */ new Date() }]);
997
+ let match = getFormattedMatchData(matchQuery);
1453
998
  if (match && (match.parsedDateTime || import_moment.default.unix(match.endDateTime).isBefore((0, import_moment.default)().subtract(config.dataParsingTimeoutMinutes, "minutes")))) {
1454
999
  session.send((ctx.config.urlInMessageType.some((type) => type == "match") ? "https://stratz.com/matches/" + matchId : "") + await ctx.puppeteer.render(genImageHTML(match, config.template_match, "match" /* Match */)));
1455
- if (match.parsedDateTime)
1456
- ctx.database.upsert("dt_previous_query_results", (row) => [{ matchId: match.id, data: match, queryTime: /* @__PURE__ */ new Date() }]);
1457
1000
  } else {
1458
1001
  pendingMatches.push({ matchId, guilds: [{ platform: session.event.platform, guildId: session.event.channel.id, players: [] }] });
1459
1002
  session.send("比赛尚未解析,将在解析完成后发布。");
@@ -1461,7 +1004,7 @@ async function apply(ctx, config) {
1461
1004
  } catch (error) {
1462
1005
  ctx.logger.error(error);
1463
1006
  session.send("获取比赛信息失败。");
1464
- ctx.database.remove("dt_previous_query_results", { matchId: parseInt(matchId) });
1007
+ await ctx.database.remove("dt_previous_query_results", { matchId });
1465
1008
  }
1466
1009
  }
1467
1010
  __name(queryMatchAndSend, "queryMatchAndSend");
@@ -1495,7 +1038,7 @@ async function apply(ctx, config) {
1495
1038
  let lastMatchId = 0;
1496
1039
  try {
1497
1040
  session.send("正在搜索对局详情,请稍后...");
1498
- lastMatchId = (await query(PLAYERS_LASTMATCH_RANKINFO, [parseInt(flagBindedPlayer?.steamId ?? input_data)])).data.players[0].matches[0].id;
1041
+ lastMatchId = (await query("PlayersLastmatchRankinfo", { steamAccountIds: [parseInt(flagBindedPlayer?.steamId ?? input_data)] })).players[0].matches[0].id;
1499
1042
  } catch (error) {
1500
1043
  session.send("获取玩家最近比赛失败。");
1501
1044
  ctx.logger.error(error);
@@ -1526,8 +1069,13 @@ async function apply(ctx, config) {
1526
1069
  let steamId = flagBindedPlayer?.steamId ?? input_data;
1527
1070
  let player;
1528
1071
  try {
1529
- player = (await query(PLAYER_INFO_WITH_25_MATCHES, steamId, hero?.id)).data.player;
1530
- let playerExtra = (await query(PLAYER_EXTRA_INFO, steamId, player.matchCount, Object.keys(dotaconstants3.heroes).length, hero?.id)).data.player;
1072
+ player = (await query("PlayerInfoWith25Matches", { steamAccountId: steamId, heroIds: hero?.id })).player;
1073
+ let playerExtra = (await query("PlayerExtraInfo", {
1074
+ steamAccountId: steamId,
1075
+ matchCount: player.matchCount,
1076
+ totalHeroCount: Object.keys(dotaconstants2.heroes).length,
1077
+ heroIds: hero?.id
1078
+ })).player;
1531
1079
  let filteredDotaPlus = {};
1532
1080
  playerExtra.dotaPlus.forEach((item) => {
1533
1081
  if (!filteredDotaPlus[item.heroId] || filteredDotaPlus[item.heroId].level < item.level) {
@@ -1675,15 +1223,15 @@ async function apply(ctx, config) {
1675
1223
  return;
1676
1224
  }
1677
1225
  try {
1678
- let heroStats = (await query(HERO_MATCHUP_WINRATE, hero.id)).data.heroStats;
1226
+ let heroStats = (await query("HeroMatchupWinrate", { heroId: hero.id, take: Object.keys(dotaconstants2.heroes).length - 1 })).heroStats;
1679
1227
  let withTopFive = heroStats.matchUp[0].with.filter((item) => item.matchCount / heroStats.matchUp[0].matchCountWith > Math.max(0, Math.min(5, options.filter)) / 100).map((item) => {
1680
1228
  const winRate = item.winCount / item.matchCount;
1681
1229
  return { ...item, winRate: winRate.toFixed(3) };
1682
- }).sort((a, b) => b.winRate - a.winRate).slice(0, Math.max(1, Math.min(Object.keys(dotaconstants3.heroes).length - 1, options.limit)));
1230
+ }).sort((a, b) => b.winRate - a.winRate).slice(0, Math.max(1, Math.min(Object.keys(dotaconstants2.heroes).length - 1, options.limit)));
1683
1231
  let vsBottomFive = heroStats.matchUp[0].vs.filter((item) => item.matchCount / heroStats.matchUp[0].matchCountVs > Math.max(0, Math.min(5, options.filter)) / 100).map((item) => {
1684
1232
  const winRate = item.winCount / item.matchCount;
1685
1233
  return { ...item, winRate: winRate.toFixed(3) };
1686
- }).sort((a, b) => a.winRate - b.winRate).slice(0, Math.max(1, Math.min(Object.keys(dotaconstants3.heroes).length - 1, options.limit)));
1234
+ }).sort((a, b) => a.winRate - b.winRate).slice(0, Math.max(1, Math.min(Object.keys(dotaconstants2.heroes).length - 1, options.limit)));
1687
1235
  session.send(
1688
1236
  `你查询的英雄是${HEROES_CHINESE[heroStats.matchUp[0].heroId][0]}(ID:${heroStats.matchUp[0].heroId}),
1689
1237
  以下是7天内传奇-万古分段比赛数据总结而来的搭档与克制关系
@@ -1699,7 +1247,7 @@ async function apply(ctx, config) {
1699
1247
  });
1700
1248
  function findingHero(input) {
1701
1249
  if (!input) return;
1702
- let dc_heroes = Object.values(dotaconstants3.heroes).map((hero) => ({
1250
+ let dc_heroes = Object.values(dotaconstants2.heroes).map((hero) => ({
1703
1251
  id: hero["id"],
1704
1252
  name: hero["name"],
1705
1253
  shortName: hero["name"].match(/^npc_dota_hero_(.+)$/)[1]
@@ -1716,8 +1264,8 @@ async function apply(ctx, config) {
1716
1264
  else mergedMap.set(item.id, item);
1717
1265
  });
1718
1266
  });
1719
- let heroes3 = Array.from(mergedMap.values());
1720
- return heroes3.find((hero) => hero.names_cn.some((cn) => cn.toLowerCase() == input.toLowerCase()) || hero.shortName === input.toLowerCase() || hero.id == input);
1267
+ let heroes2 = Array.from(mergedMap.values());
1268
+ return heroes2.find((hero) => hero.names_cn.some((cn) => cn.toLowerCase() == input.toLowerCase()) || hero.shortName === input.toLowerCase() || hero.id == input);
1721
1269
  }
1722
1270
  __name(findingHero, "findingHero");
1723
1271
  ctx.on("ready", async () => {
@@ -1749,7 +1297,7 @@ async function apply(ctx, config) {
1749
1297
  const subscribedPlayersSteamIds = subscribedPlayersInGuild.map((player) => player.steamId).filter(function(value, index, self) {
1750
1298
  return self.indexOf(value) === index;
1751
1299
  });
1752
- const players = (await query(PLAYERS_LASTMATCH_RANKINFO, subscribedPlayersSteamIds)).data.players;
1300
+ const players = (await query("PlayersLastmatchRankinfo", { steamAccountIds: subscribedPlayersSteamIds })).players;
1753
1301
  const lastMatches = players.map((player) => player.matches[0]).filter((item, index, self) => index === self.findIndex((t) => t.id === item.id)).filter((match) => import_moment.default.unix(match.startDateTime).isAfter((0, import_moment.default)().subtract(1, "days"))).filter((match) => !pendingMatches.some((pendingMatch) => pendingMatch.matchId == match.id));
1754
1302
  const sendedMatchesIds = (await ctx.database.get("dt_sended_match_id", { matchId: lastMatches.map((match) => match.id) }, ["matchId"])).map((match) => match.matchId);
1755
1303
  lastMatches.filter((match) => !sendedMatchesIds.includes(match.id)).forEach((match) => {
@@ -1764,7 +1312,7 @@ async function apply(ctx, config) {
1764
1312
  });
1765
1313
  });
1766
1314
  pendingMatches.push({ matchId: match.id, guilds: tempGuilds });
1767
- query(REQUEST_MATCH_DATA_ANALYSIS, match.id);
1315
+ query("RequestMatchDataAnalysis", { matchId: match.id });
1768
1316
  ctx.logger.info(
1769
1317
  tempGuilds.map((guild) => `追踪到来自群组${guild.platform}:${guild.guildId}的用户${guild.players.map((player) => `[${player.nickName ?? ""}(${player.steamId})]`).join("、")}的尚未播报过的最新比赛 ${match.id}。`).join("")
1770
1318
  );
@@ -1824,13 +1372,16 @@ async function apply(ctx, config) {
1824
1372
  const now = (0, import_moment.default)();
1825
1373
  const pendingMatch = pendingMatches[(now.hours() * 60 + now.minutes()) % pendingMatches.length];
1826
1374
  try {
1827
- let match;
1375
+ let matchQuery;
1828
1376
  let queryLocal = await ctx.database.get("dt_previous_query_results", pendingMatch.matchId, ["data"]);
1829
1377
  if (queryLocal.length > 0) {
1830
- match = queryLocal[0].data;
1831
- ctx.database.set("dt_previous_query_results", match.id, { queryTime: /* @__PURE__ */ new Date() });
1832
- } else match = getFormattedMatchData((await query(MATCH_INFO, pendingMatch.matchId)).data);
1833
- if (match.parsedDateTime || import_moment.default.unix(match.endDateTime).isBefore(now.subtract(config.dataParsingTimeoutMinutes, "minutes"))) {
1378
+ matchQuery = queryLocal[0].data;
1379
+ ctx.database.set("dt_previous_query_results", matchQuery.match.id, { queryTime: /* @__PURE__ */ new Date() });
1380
+ } else matchQuery = await query("MatchInfo", { matchId: pendingMatch.matchId });
1381
+ if (matchQuery.match.parsedDateTime || import_moment.default.unix(matchQuery.match.endDateTime).isBefore(now.subtract(config.dataParsingTimeoutMinutes, "minutes"))) {
1382
+ if (matchQuery.match.parsedDateTime)
1383
+ ctx.database.upsert("dt_previous_query_results", (row) => [{ matchId: matchQuery.match.id, data: matchQuery, queryTime: /* @__PURE__ */ new Date() }]);
1384
+ let match = getFormattedMatchData(matchQuery);
1834
1385
  pendingMatches = pendingMatches.filter((item) => item.matchId != match.id);
1835
1386
  const img = await ctx.puppeteer.render(genImageHTML(match, config.template_match, "match" /* Match */));
1836
1387
  for (let commingGuild of pendingMatch.guilds) {
@@ -1856,10 +1407,8 @@ KDA:${((player.kills + player.assists) / (player.deaths || 1)).toFixed(2)} [${
1856
1407
  await ctx.broadcast([`${commingGuild.platform}:${commingGuild.guildId}`], broadMatchMessage + (ctx.config.urlInMessageType.some((type) => type == "match") ? "https://stratz.com/matches/" + match.id : "") + img);
1857
1408
  ctx.logger.info(`${match.id}${match.parsedDateTime ? "已解析," : "已结束超过1小时仍未被解析,放弃等待解析直接"}生成图片并发布于${commingGuild.platform}:${commingGuild.guildId}。`);
1858
1409
  }
1859
- if (match.parsedDateTime)
1860
- ctx.database.upsert("dt_previous_query_results", (row) => [{ matchId: match.id, data: match, queryTime: /* @__PURE__ */ new Date() }]);
1861
1410
  ctx.database.create("dt_sended_match_id", { matchId: match.id, sendTime: /* @__PURE__ */ new Date() });
1862
- } else ctx.logger.info("比赛 %d 尚未解析完成,继续等待。", match.id);
1411
+ } else ctx.logger.info("比赛 %d 尚未解析完成,继续等待。", matchQuery.match.id);
1863
1412
  } catch (error) {
1864
1413
  ctx.logger.error(error);
1865
1414
  ctx.database.remove("dt_previous_query_results", { matchId: pendingMatch.matchId });
@@ -1870,11 +1419,10 @@ KDA:${((player.kills + player.assists) / (player.deaths || 1)).toFixed(2)} [${
1870
1419
  async function report(timeAgo, title, showCombi) {
1871
1420
  const subscribedGuilds = await ctx.database.get("dt_subscribed_guilds", void 0);
1872
1421
  const subscribedPlayersInGuild = (await ctx.database.get("dt_subscribed_players", void 0)).filter((player) => subscribedGuilds.some((guild) => guild.guildId == player.guildId));
1873
- const players = (await query(
1874
- PLAYERS_MATCHES_FOR_DAILY,
1875
- subscribedPlayersInGuild.map((player) => player.steamId).filter((value, index, self) => self.indexOf(value) === index),
1876
- timeAgo
1877
- )).data.players.filter((player) => player.matches.length > 0);
1422
+ const players = (await query("PlayersMatchesForDaily", {
1423
+ steamAccountIds: subscribedPlayersInGuild.map((player) => player.steamId).filter((value, index, self) => self.indexOf(value) === index),
1424
+ seconds: timeAgo
1425
+ })).players.filter((player) => player.matches.length > 0);
1878
1426
  const matches = players.map((player) => player.matches.map((match) => match)).flat().filter((item, index, self) => index === self.findIndex((t) => t.id === item.id));
1879
1427
  for (let subPlayer of subscribedPlayersInGuild) {
1880
1428
  let player = players.find((player2) => subPlayer.steamId == player2.steamAccount.id);
@@ -1961,7 +1509,7 @@ function genImageHTML(data, template, type) {
1961
1509
  ImageType,
1962
1510
  ImageFormat,
1963
1511
  d2a: dotaconstants_add_exports,
1964
- dotaconstants: dotaconstants3,
1512
+ dotaconstants: dotaconstants2,
1965
1513
  moment: import_moment.default,
1966
1514
  escapeHTML: /* @__PURE__ */ __name(function escapeHTML(str) {
1967
1515
  if (str == null) return "";
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "@sjtdev/koishi-plugin-dota2tracker",
3
3
  "description": "koishi插件-追踪群友的DOTA2对局",
4
- "version": "1.2.19",
4
+ "version": "1.2.20-pre.2",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
8
8
  "lib",
9
9
  "dist",
10
10
  "images",
11
- "template"
11
+ "template",
12
+ "queries"
12
13
  ],
13
14
  "contributors": [
14
15
  "sjtdev <sh1j1n9ta0@foxmail.com>"
@@ -0,0 +1,14 @@
1
+ query AllAbilitiesChineseName {
2
+ constants {
3
+ abilities(language: S_CHINESE) {
4
+ id
5
+ language {
6
+ displayName
7
+ }
8
+ }
9
+ gameVersions {
10
+ name
11
+ id
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,8 @@
1
+ query CurrentGameversion {
2
+ constants {
3
+ gameVersions {
4
+ name
5
+ id
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,53 @@
1
+ query HeroInfo($heroId: Short!) {
2
+ constants {
3
+ hero(id: $heroId, language: S_CHINESE) {
4
+ id
5
+ name
6
+ shortName
7
+ aliases
8
+ roles {
9
+ roleId
10
+ level
11
+ }
12
+ language {
13
+ displayName
14
+ lore
15
+ hype
16
+ }
17
+ abilities {
18
+ ability(language: S_CHINESE) {
19
+ name
20
+ language {
21
+ displayName
22
+ description
23
+ attributes
24
+ lore
25
+ aghanimDescription
26
+ shardDescription
27
+ notes
28
+ }
29
+ stat {
30
+ type
31
+ behavior
32
+ unitTargetType
33
+ unitTargetTeam
34
+ unitTargetFlags
35
+ unitDamageType
36
+ cooldown
37
+ manaCost
38
+ spellImmunity
39
+ isOnCastbar
40
+ isGrantedByShard
41
+ isGrantedByScepter
42
+ hasShardUpgrade
43
+ hasScepterUpgrade
44
+ }
45
+ }
46
+ }
47
+ talents {
48
+ abilityId
49
+ slot
50
+ }
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,25 @@
1
+ query HeroMatchupWinrate($heroId: Short!, $take: Int!) {
2
+ heroStats {
3
+ matchUp(heroId: $heroId, take: $take, bracketBasicIds: LEGEND_ANCIENT) {
4
+ heroId
5
+ matchCountWith
6
+ matchCountVs
7
+ with {
8
+ heroId1
9
+ winRateHeroId1
10
+ heroId2
11
+ winRateHeroId2
12
+ winCount
13
+ matchCount
14
+ }
15
+ vs {
16
+ heroId1
17
+ winRateHeroId1
18
+ heroId2
19
+ winRateHeroId2
20
+ winCount
21
+ matchCount
22
+ }
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,135 @@
1
+ query MatchInfo($matchId: Long!) {
2
+ match(id: $matchId) {
3
+ id
4
+ didRadiantWin
5
+ lobbyType
6
+ gameMode
7
+ regionId
8
+ parsedDateTime
9
+ startDateTime
10
+ endDateTime
11
+ actualRank
12
+ rank
13
+ averageRank
14
+ durationSeconds
15
+ topLaneOutcome
16
+ midLaneOutcome
17
+ bottomLaneOutcome
18
+ radiantKills
19
+ direKills
20
+ players {
21
+ steamAccountId
22
+ steamAccount {
23
+ name
24
+ }
25
+ level
26
+ hero {
27
+ id
28
+ name
29
+ shortName
30
+ facets {
31
+ facetId
32
+ }
33
+ }
34
+ variant
35
+ dotaPlus {
36
+ level
37
+ }
38
+ leaverStatus
39
+ partyId
40
+ position
41
+ playerSlot
42
+ lane
43
+ imp
44
+ kills
45
+ deaths
46
+ assists
47
+ isRadiant
48
+ networth
49
+ steamAccount {
50
+ seasonRank
51
+ seasonLeaderboardRank
52
+ }
53
+ item0Id
54
+ item1Id
55
+ item2Id
56
+ item3Id
57
+ item4Id
58
+ item5Id
59
+ backpack0Id
60
+ backpack1Id
61
+ backpack2Id
62
+ neutral0Id
63
+ stats {
64
+ matchPlayerBuffEvent {
65
+ abilityId
66
+ itemId
67
+ stackCount
68
+ }
69
+ }
70
+ heroDamage
71
+ towerDamage
72
+ stats {
73
+ heroDamageReport {
74
+ receivedTotal {
75
+ physicalDamage
76
+ magicalDamage
77
+ pureDamage
78
+ }
79
+ }
80
+ }
81
+ numLastHits
82
+ numDenies
83
+ goldPerMinute
84
+ experiencePerMinute
85
+ heroHealing
86
+ stats {
87
+ itemPurchases {
88
+ itemId
89
+ time
90
+ }
91
+ campStack
92
+ heroDamageReport {
93
+ dealtTotal {
94
+ stunDuration
95
+ stunCount
96
+ slowDuration
97
+ slowCount
98
+ disableDuration
99
+ disableCount
100
+ }
101
+ }
102
+ }
103
+ additionalUnit {
104
+ item0Id
105
+ item1Id
106
+ item2Id
107
+ item3Id
108
+ item4Id
109
+ item5Id
110
+ backpack0Id
111
+ backpack1Id
112
+ backpack2Id
113
+ neutral0Id
114
+ }
115
+ isRandom
116
+ }
117
+ pickBans {
118
+ isPick
119
+ bannedHeroId
120
+ heroId
121
+ order
122
+ }
123
+ }
124
+ constants {
125
+ facets(language: S_CHINESE) {
126
+ name
127
+ id
128
+ color
129
+ icon
130
+ language {
131
+ displayName
132
+ }
133
+ }
134
+ }
135
+ }
@@ -0,0 +1,17 @@
1
+ query PlayerExtraInfo($steamAccountId: Long!, $totalHeroCount: Int!, $matchCount: Int!, $heroIds: [Short]) {
2
+ player(steamAccountId: $steamAccountId) {
3
+ heroesPerformance(take: $totalHeroCount, request: { matchGroupOrderBy: MATCH_COUNT, take: $matchCount, heroIds: $heroIds }) {
4
+ hero {
5
+ id
6
+ shortName
7
+ }
8
+ winCount
9
+ matchCount
10
+ imp
11
+ }
12
+ dotaPlus {
13
+ heroId
14
+ level
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,62 @@
1
+ query PlayerInfoWith25Matches($steamAccountId: Long!, $heroIds: [Short]) {
2
+ player(steamAccountId: $steamAccountId) {
3
+ steamAccount {
4
+ avatar
5
+ name
6
+ seasonRank
7
+ seasonLeaderboardRank
8
+ id
9
+ }
10
+ guildMember {
11
+ guild {
12
+ tag
13
+ }
14
+ }
15
+ matchCount
16
+ winCount
17
+ performance {
18
+ imp
19
+ }
20
+ heroesPerformance(take: 25, request: { matchGroupOrderBy: WIN_COUNT, take: 25, heroIds: $heroIds }) {
21
+ hero {
22
+ id
23
+ shortName
24
+ }
25
+ imp
26
+ winCount
27
+ matchCount
28
+ }
29
+ matches(request: { take: 25, heroIds: $heroIds }) {
30
+ id
31
+ rank
32
+ lobbyType
33
+ gameMode
34
+ startDateTime
35
+ parsedDateTime
36
+ durationSeconds
37
+ didRadiantWin
38
+ topLaneOutcome
39
+ midLaneOutcome
40
+ bottomLaneOutcome
41
+ radiantKills
42
+ direKills
43
+ players {
44
+ steamAccount {
45
+ id
46
+ }
47
+ isRadiant
48
+ lane
49
+ kills
50
+ deaths
51
+ assists
52
+ position
53
+ award
54
+ imp
55
+ hero {
56
+ id
57
+ shortName
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,27 @@
1
+ query PlayersInfoWith10MatchesForGuild($steamAccountIds: [Long]!) {
2
+ players(steamAccountIds: $steamAccountIds) {
3
+ steamAccount {
4
+ id
5
+ avatar
6
+ name
7
+ seasonRank
8
+ }
9
+ matches(request: { take: 10 }) {
10
+ didRadiantWin
11
+ startDateTime
12
+ players {
13
+ isRadiant
14
+ kills
15
+ deaths
16
+ assists
17
+ steamAccount {
18
+ id
19
+ }
20
+ hero {
21
+ shortName
22
+ }
23
+ imp
24
+ }
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,21 @@
1
+ query PlayersLastmatchRankinfo($steamAccountIds: [Long]!) {
2
+ players(steamAccountIds: $steamAccountIds) {
3
+ steamAccount {
4
+ id
5
+ name
6
+ avatar
7
+ seasonRank
8
+ seasonLeaderboardRank
9
+ }
10
+ matches(request: { take: 1 }) {
11
+ id
12
+ parsedDateTime
13
+ startDateTime
14
+ players {
15
+ steamAccount {
16
+ id
17
+ }
18
+ }
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,25 @@
1
+ query PlayersMatchesForDaily($steamAccountIds: [Long]!, $seconds: Long!) {
2
+ players(steamAccountIds: $steamAccountIds) {
3
+ steamAccount {
4
+ id
5
+ name
6
+ avatar
7
+ }
8
+ matches(request: { startDateTime: $seconds, take: 50 }) {
9
+ id
10
+ didRadiantWin
11
+ parsedDateTime
12
+ startDateTime
13
+ players {
14
+ kills
15
+ deaths
16
+ assists
17
+ imp
18
+ isRadiant
19
+ steamAccount {
20
+ id
21
+ }
22
+ }
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,5 @@
1
+ query RequestMatchDataAnalysis($matchId: Long!) {
2
+ stratz {
3
+ matchRetry(id: $matchId)
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ query VerifyingPlayer($steamAccountId: Long!) {
2
+ player(steamAccountId: $steamAccountId) {
3
+ matchCount
4
+ }
5
+ }
package/readme.md CHANGED
@@ -53,11 +53,6 @@ DOTA2Bot插件-提供自动追踪群友的最新对局的功能(需群友绑
53
53
  展示见[wiki](https://github.com/sjtdev/koishi-plugin-dota2tracker/wiki)
54
54
  生成图片已使用ejs模板实现,所有模板都在[template]文件夹下,若是有大佬想自己设计模板欢迎联系我完善数据接口。(当前有很多在模板中后处理的数据,不是很友好)
55
55
 
56
- ### 其他问题
57
- 已知问题:当本插件更新版本后,查询已经查询过的比赛数据可能查询失败。
58
- * 原因:插件的缓存功能,查询过的比赛数据会缓存至本地以节省API调用次数与服务器流量,再次查询时优先调用本地数据也会提高处理速度。当插件更新后若新增数据的处理或API查询字段,会导致旧的缓存内缺失部分数据引发生成时报错。(播报比赛战报应该不会有这个问题)
59
- * 解决:目前在生成失败时会清除缓存,再查询一次就好了。
60
-
61
56
  ## 灵感来源&鸣谢
62
57
  * 感谢[SonodaHanami](https://github.com/SonodaHanami)大佬的[Steam_watcher](https://github.com/SonodaHanami/Steam_watcher)项目,是本插件最重要的灵感来源,并授权提供了战报称号系统、英雄代称与播报信息的代码数据。
63
58
  * 也受到了[koishi-plugin-dota2track(npm)](https://www.npmjs.com/package/koishi-plugin-dota2track)的启发,为我的代码提供了一些思路。