@sjtdev/koishi-plugin-dota2tracker 1.2.16 → 1.2.18-beta.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.
package/lib/index.js CHANGED
@@ -50,13 +50,13 @@ __export(utils_exports, {
50
50
  formatNumber: () => formatNumber,
51
51
  getFormattedMatchData: () => getFormattedMatchData,
52
52
  getImageUrl: () => getImageUrl,
53
+ init: () => init,
53
54
  playerisValid: () => playerisValid,
54
55
  query: () => query,
55
56
  queryHeroFromValve: () => queryHeroFromValve,
56
57
  readDirectoryFilesSync: () => readDirectoryFilesSync,
57
58
  roundToDecimalPlaces: () => roundToDecimalPlaces,
58
59
  sec2time: () => sec2time,
59
- setHttp: () => setHttp,
60
60
  winRateColor: () => winRateColor
61
61
  });
62
62
  var import_fs = __toESM(require("fs"));
@@ -70,12 +70,13 @@ __export(queries_exports, {
70
70
  CURRENT_GAMEVERSION: () => CURRENT_GAMEVERSION,
71
71
  HERO_INFO: () => HERO_INFO,
72
72
  HERO_MATCHUP_WINRATE: () => HERO_MATCHUP_WINRATE,
73
- MATCHES_FOR_DAILY: () => MATCHES_FOR_DAILY,
74
73
  MATCH_INFO: () => MATCH_INFO,
75
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,
76
77
  PLAYER_EXTRA_INFO: () => PLAYER_EXTRA_INFO,
77
78
  PLAYER_INFO_WITH_25_MATCHES: () => PLAYER_INFO_WITH_25_MATCHES,
78
- PLAYER_LASTMATCH: () => PLAYER_LASTMATCH,
79
+ REQUEST_MATCH_DATA_ANALYSIS: () => REQUEST_MATCH_DATA_ANALYSIS,
79
80
  VERIFYING_PLAYER: () => VERIFYING_PLAYER
80
81
  });
81
82
  var dotaconstants = __toESM(require("dotaconstants"));
@@ -220,10 +221,10 @@ function MATCH_INFO(matchId) {
220
221
  `;
221
222
  }
222
223
  __name(MATCH_INFO, "MATCH_INFO");
223
- function MATCHES_FOR_DAILY(steamAccountIds, seconds) {
224
+ function PLAYERS_MATCHES_FOR_DAILY(steamAccountIds, seconds) {
224
225
  return `
225
226
  {
226
- players(steamAccountIds:${JSON.stringify(steamAccountIds)}) {
227
+ players(steamAccountIds:[${steamAccountIds.join(",")}]) {
227
228
  steamAccount{id name avatar}
228
229
  matches(request:{startDateTime:${seconds} take:50}){
229
230
  id
@@ -245,7 +246,7 @@ function MATCHES_FOR_DAILY(steamAccountIds, seconds) {
245
246
  }
246
247
  `;
247
248
  }
248
- __name(MATCHES_FOR_DAILY, "MATCHES_FOR_DAILY");
249
+ __name(PLAYERS_MATCHES_FOR_DAILY, "PLAYERS_MATCHES_FOR_DAILY");
249
250
  function VERIFYING_PLAYER(steamAccountId) {
250
251
  return `
251
252
  {
@@ -257,29 +258,33 @@ function VERIFYING_PLAYER(steamAccountId) {
257
258
  `;
258
259
  }
259
260
  __name(VERIFYING_PLAYER, "VERIFYING_PLAYER");
260
- function PLAYER_LASTMATCH(steamAccountId) {
261
+ function PLAYERS_LASTMATCH_RANKINFO(steamAccountIds) {
261
262
  return `
262
- {
263
- player(steamAccountId: ${steamAccountId}) {
264
- steamAccount {
265
- id
266
- }
267
- matches(request: {take: 1}) {
268
- id
269
- parsedDateTime
270
- startDateTime
271
- players {
272
- steamAccount {
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
273
  id
274
+ parsedDateTime
275
+ startDateTime
276
+ players{
277
+ steamAccount{
278
+ id
279
+ }
280
+ }
274
281
  }
275
282
  }
276
283
  }
277
- }
278
- }
279
-
280
- `;
284
+
285
+ `;
281
286
  }
282
- __name(PLAYER_LASTMATCH, "PLAYER_LASTMATCH");
287
+ __name(PLAYERS_LASTMATCH_RANKINFO, "PLAYERS_LASTMATCH_RANKINFO");
283
288
  function PLAYER_INFO_WITH_25_MATCHES(steamAccountId, heroId) {
284
289
  return `
285
290
  {
@@ -517,15 +522,20 @@ function HERO_MATCHUP_WINRATE(heroId) {
517
522
  `;
518
523
  }
519
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");
520
529
 
521
530
  // src/utils.ts
522
531
  var CONFIGS = { STRATZ_API: { URL: "https://api.stratz.com/graphql", TOKEN: "" } };
523
532
  var http = null;
524
- function setHttp(newHttp) {
533
+ function init(newHttp, setTimeout2) {
525
534
  http = newHttp;
535
+ setTimeout2 = setTimeout2;
526
536
  }
527
- __name(setHttp, "setHttp");
528
- async function query(query_str) {
537
+ __name(init, "init");
538
+ async function fetchData(query_str) {
529
539
  return await http.post(CONFIGS.STRATZ_API.URL, query_str, {
530
540
  responseType: "json",
531
541
  headers: {
@@ -535,6 +545,27 @@ async function query(query_str) {
535
545
  }
536
546
  });
537
547
  }
548
+ __name(fetchData, "fetchData");
549
+ async function query(query_func, ...args) {
550
+ if (query_func.name.startsWith("PLAYERS") && args[0].length > 5) {
551
+ const playerIds = args[0];
552
+ const chunkSize = 5;
553
+ let allPlayers = [];
554
+ for (let i = 0; i < playerIds.length; i += chunkSize) {
555
+ const chunk = playerIds.slice(i, i + chunkSize);
556
+ const query_str = query_func(chunk, ...args.slice(1));
557
+ const result = await new Promise((resolve) => setTimeout(() => resolve(fetchData(query_str)), 100));
558
+ if (result.data && result.data.players) {
559
+ allPlayers = allPlayers.concat(result.data.players);
560
+ }
561
+ }
562
+ return { data: { players: allPlayers } };
563
+ } else {
564
+ const query_str = query_func(...args);
565
+ const result = await fetchData(query_str);
566
+ return result || {};
567
+ }
568
+ }
538
569
  __name(query, "query");
539
570
  async function queryHeroFromValve(heroId) {
540
571
  return (await http.get(`https://www.dota2.com/datafeed/herodata?language=schinese&hero_id=${heroId}`)).result.data.heroes[0];
@@ -566,7 +597,7 @@ function getImageUrl(image, type = "local" /* Local */, format = "png" /* png */
566
597
  if (type === "local" /* Local */) {
567
598
  try {
568
599
  if (format === "svg" /* svg */) return import_fs.default.readFileSync(`./node_modules/@sjtdev/koishi-plugin-dota2tracker/template/images/${image}.svg`);
569
- const imageData = import_fs.default.readFileSync(`./node_modules/@sjtdev/koishi-plugin-dota2tracker/template/images/${image}.png`);
600
+ const imageData = import_fs.default.readFileSync(`./node_modules/@sjtdev/koishi-plugin-dota2tracker/template/images/${image}.${format}`);
570
601
  const base64Data = imageData.toString("base64");
571
602
  return `data:image/png;base64,${base64Data}`;
572
603
  } catch (error) {
@@ -1235,6 +1266,20 @@ var Config = import_koishi.Schema.intersect([
1235
1266
  ])
1236
1267
  ).role("checkbox").description("在消息中附带链接,<br/>请选择消息类型:")
1237
1268
  }).description("基础设置"),
1269
+ import_koishi.Schema.intersect([
1270
+ import_koishi.Schema.object({
1271
+ rankBroadSwitch: import_koishi.Schema.boolean().default(false).description("段位变动播报")
1272
+ }),
1273
+ import_koishi.Schema.union([
1274
+ import_koishi.Schema.object({
1275
+ rankBroadSwitch: import_koishi.Schema.const(true).required(),
1276
+ rankBroadStar: import_koishi.Schema.boolean().default(true).description("星级变动播报"),
1277
+ rankBroadLeader: import_koishi.Schema.boolean().default(true).description("冠绝名次变动播报"),
1278
+ rankBroadFun: import_koishi.Schema.boolean().default(false).description("整活播报模板")
1279
+ }),
1280
+ import_koishi.Schema.object({})
1281
+ ])
1282
+ ]),
1238
1283
  import_koishi.Schema.intersect([
1239
1284
  import_koishi.Schema.object({
1240
1285
  dailyReportSwitch: import_koishi.Schema.boolean().default(false).description("日报功能")
@@ -1269,7 +1314,7 @@ var pendingMatches = [];
1269
1314
  var random = new import_koishi2.Random(() => Math.random());
1270
1315
  async function apply(ctx, config) {
1271
1316
  CONFIGS.STRATZ_API.TOKEN = config.STRATZ_API_TOKEN;
1272
- setHttp(ctx.http);
1317
+ init(ctx.http, ctx.setTimeout);
1273
1318
  ctx.command("订阅本群", "订阅后还需玩家在本群绑定SteamID").usage("订阅后还需玩家在本群绑定SteamID,BOT将订阅本群中已绑定玩家的新比赛数据,在STRATZ比赛解析完成后将比赛数据生成为图片战报发布至本群中。").action(async ({ session }) => {
1274
1319
  if (session.guild) {
1275
1320
  let currentGuild = (await ctx.database.get("dt_subscribed_guilds", { guildId: session.event.channel.id, platform: session.event.platform }))[0];
@@ -1365,15 +1410,19 @@ async function apply(ctx, config) {
1365
1410
  }
1366
1411
  if (subscribedPlayers.length <= 20) {
1367
1412
  try {
1368
- const memberList = await session.bot.getGuildMemberList(session.event.channel.id);
1413
+ let memberList;
1414
+ try {
1415
+ memberList = await session.bot?.getGuildMemberList(session.event.channel.id);
1416
+ } catch (error) {
1417
+ }
1369
1418
  async function getUsers(subscribedPlayers2, utils, queries, memberList2) {
1370
1419
  const playerSteamIds = subscribedPlayers2.map((player) => player.steamId);
1371
- const queryResult = await utils.query(queries.PLAYERS_INFO_WITH_10_MATCHES_FOR_GUILD(playerSteamIds));
1420
+ const queryResult = await utils.query(queries.PLAYERS_INFO_WITH_10_MATCHES_FOR_GUILD, playerSteamIds);
1372
1421
  const playersInfo = queryResult.data.players;
1373
1422
  const users2 = [];
1374
1423
  for (const subscribedPlayer of subscribedPlayers2) {
1375
1424
  const queryPlayer = playersInfo.find((player) => player.steamAccount.id == subscribedPlayer.steamId);
1376
- const queryMember = memberList2.data.find((member) => member.user?.id == subscribedPlayer.userId);
1425
+ const queryMember = memberList2?.data.find((member) => member.user?.id == subscribedPlayer.userId);
1377
1426
  users2.push({ ...subscribedPlayer, ...queryPlayer, ...queryMember });
1378
1427
  }
1379
1428
  return users2;
@@ -1396,7 +1445,7 @@ async function apply(ctx, config) {
1396
1445
  match = queryLocal[0].data;
1397
1446
  ctx.database.set("dt_previous_query_results", match.id, { queryTime: /* @__PURE__ */ new Date() });
1398
1447
  } else {
1399
- match = getFormattedMatchData((await query(MATCH_INFO(matchId))).data);
1448
+ match = getFormattedMatchData((await query(MATCH_INFO, matchId)).data);
1400
1449
  }
1401
1450
  if (match && (match.parsedDateTime || import_moment.default.unix(match.endDateTime).isBefore((0, import_moment.default)().subtract(config.dataParsingTimeoutMinutes, "minutes")))) {
1402
1451
  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 */)));
@@ -1418,7 +1467,6 @@ async function apply(ctx, config) {
1418
1467
  session.send("请输入比赛ID。");
1419
1468
  return;
1420
1469
  }
1421
- JSON.stringify;
1422
1470
  if (!/^\d{10}$/.test(match_id)) {
1423
1471
  session.send("比赛ID无效。");
1424
1472
  return;
@@ -1444,9 +1492,10 @@ async function apply(ctx, config) {
1444
1492
  let lastMatchId = 0;
1445
1493
  try {
1446
1494
  session.send("正在搜索对局详情,请稍后...");
1447
- lastMatchId = (await query(PLAYER_LASTMATCH(parseInt(flagBindedPlayer?.steamId ?? input_data)))).data.player.matches[0].id;
1448
- } catch {
1495
+ lastMatchId = (await query(PLAYERS_LASTMATCH_RANKINFO, [parseInt(flagBindedPlayer?.steamId ?? input_data)])).data.players[0].matches[0].id;
1496
+ } catch (error) {
1449
1497
  session.send("获取玩家最近比赛失败。");
1498
+ ctx.logger.error(error);
1450
1499
  return;
1451
1500
  }
1452
1501
  queryMatchAndSend(session, lastMatchId);
@@ -1474,8 +1523,8 @@ async function apply(ctx, config) {
1474
1523
  let steamId = flagBindedPlayer?.steamId ?? input_data;
1475
1524
  let player;
1476
1525
  try {
1477
- player = (await query(PLAYER_INFO_WITH_25_MATCHES(steamId, hero?.id))).data.player;
1478
- let playerExtra = (await query(PLAYER_EXTRA_INFO(steamId, player.matchCount, Object.keys(dotaconstants3.heroes).length, hero?.id))).data.player;
1526
+ player = (await query(PLAYER_INFO_WITH_25_MATCHES, steamId, hero?.id)).data.player;
1527
+ let playerExtra = (await query(PLAYER_EXTRA_INFO, steamId, player.matchCount, Object.keys(dotaconstants3.heroes).length, hero?.id)).data.player;
1479
1528
  let filteredDotaPlus = {};
1480
1529
  playerExtra.dotaPlus.forEach((item) => {
1481
1530
  if (!filteredDotaPlus[item.heroId] || filteredDotaPlus[item.heroId].level < item.level) {
@@ -1623,7 +1672,7 @@ async function apply(ctx, config) {
1623
1672
  return;
1624
1673
  }
1625
1674
  try {
1626
- let heroStats = (await query(HERO_MATCHUP_WINRATE(hero.id))).data.heroStats;
1675
+ let heroStats = (await query(HERO_MATCHUP_WINRATE, hero.id)).data.heroStats;
1627
1676
  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) => {
1628
1677
  const winRate = item.winCount / item.matchCount;
1629
1678
  return { ...item, winRate: winRate.toFixed(3) };
@@ -1669,19 +1718,10 @@ async function apply(ctx, config) {
1669
1718
  }
1670
1719
  __name(findingHero, "findingHero");
1671
1720
  ctx.on("ready", async () => {
1672
- const tables = await ctx.database.tables;
1673
- if (!("dt_subscribed_guilds" in tables)) {
1674
- ctx.model.extend("dt_subscribed_guilds", { id: "unsigned", guildId: "string", platform: "string" }, { autoInc: true });
1675
- }
1676
- if (!("dt_subscribed_players" in tables)) {
1677
- ctx.model.extend("dt_subscribed_players", { id: "unsigned", userId: "string", guildId: "string", platform: "string", steamId: "integer", nickName: "string" }, { autoInc: true });
1678
- }
1679
- if (!("dt_sended_match_id" in tables)) {
1680
- ctx.model.extend("dt_sended_match_id", { matchId: "unsigned", sendTime: "timestamp" }, { primary: "matchId" });
1681
- }
1682
- if (!("dt_previous_query_results" in tables)) {
1683
- ctx.model.extend("dt_previous_query_results", { matchId: "unsigned", data: "json", queryTime: "timestamp" }, { primary: "matchId" });
1684
- }
1721
+ ctx.model.extend("dt_subscribed_guilds", { id: "unsigned", guildId: "string", platform: "string" }, { autoInc: true });
1722
+ ctx.model.extend("dt_subscribed_players", { id: "unsigned", userId: "string", guildId: "string", platform: "string", steamId: "integer", nickName: "string", rank: "json" }, { autoInc: true });
1723
+ ctx.model.extend("dt_sended_match_id", { matchId: "unsigned", sendTime: "timestamp" }, { primary: "matchId" });
1724
+ ctx.model.extend("dt_previous_query_results", { matchId: "unsigned", data: "json", queryTime: "timestamp" }, { primary: "matchId" });
1685
1725
  ctx.cron("0 */6 * * *", () => {
1686
1726
  const oneMonthAgo = (0, import_moment.default)().subtract(1, "months").toDate();
1687
1727
  ctx.database.remove("dt_sended_match_id", { sendTime: { $lt: oneMonthAgo } });
@@ -1706,10 +1746,7 @@ async function apply(ctx, config) {
1706
1746
  const subscribedPlayersSteamIds = subscribedPlayersInGuild.map((player) => player.steamId).filter(function(value, index, self) {
1707
1747
  return self.indexOf(value) === index;
1708
1748
  });
1709
- const players = [];
1710
- for (let id of subscribedPlayersSteamIds) {
1711
- players.push((await query(PLAYER_LASTMATCH(id))).data.player);
1712
- }
1749
+ const players = (await query(PLAYERS_LASTMATCH_RANKINFO, subscribedPlayersSteamIds)).data.players;
1713
1750
  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));
1714
1751
  const sendedMatchesIds = (await ctx.database.get("dt_sended_match_id", { matchId: lastMatches.map((match) => match.id) }, ["matchId"])).map((match) => match.matchId);
1715
1752
  lastMatches.filter((match) => !sendedMatchesIds.includes(match.id)).forEach((match) => {
@@ -1724,10 +1761,61 @@ async function apply(ctx, config) {
1724
1761
  });
1725
1762
  });
1726
1763
  pendingMatches.push({ matchId: match.id, guilds: tempGuilds });
1764
+ query(REQUEST_MATCH_DATA_ANALYSIS, match.id);
1727
1765
  ctx.logger.info(
1728
1766
  tempGuilds.map((guild) => `追踪到来自群组${guild.platform}:${guild.guildId}的用户${guild.players.map((player) => `[${player.nickName ?? ""}(${player.steamId})]`).join("、")}的尚未播报过的最新比赛 ${match.id}。`).join("")
1729
1767
  );
1730
1768
  });
1769
+ const rankMap = players.reduce((map, player) => {
1770
+ map[player.steamAccount.id] = { rank: player.steamAccount.seasonRank, leader: player.steamAccount.seasonLeaderboardRank };
1771
+ return map;
1772
+ }, {});
1773
+ for (let subPlayer of subscribedPlayersInGuild) {
1774
+ if (subPlayer.rank.rank !== rankMap[subPlayer.steamId].rank || subPlayer.rank.leader !== rankMap[subPlayer.steamId].board) {
1775
+ if (Object.keys(subPlayer.rank).length != 0) {
1776
+ if (config.rankBroadSwitch) {
1777
+ const ranks = ["prevRank", "currRank"].reduce((acc, key) => {
1778
+ const source = key === "prevRank" ? subPlayer.rank : rankMap[subPlayer.steamId];
1779
+ acc[key] = {
1780
+ medal: parseInt(source.rank?.toString().split("")[0] ?? "0"),
1781
+ star: parseInt(source.rank?.toString().split("")[1] ?? "0"),
1782
+ leader: source.leader,
1783
+ inTop100: source.leader ? source.leader <= 10 ? "8c" : source.leader <= 100 ? "8b" : void 0 : void 0
1784
+ };
1785
+ return acc;
1786
+ }, {});
1787
+ const prevRank = ranks["prevRank"];
1788
+ const currRank = ranks["currRank"];
1789
+ if (prevRank.medal !== currRank.medal || prevRank.star !== currRank.star && config.rankBroadStar || prevRank.leader !== currRank.leader && config.rankBroadLeader) {
1790
+ const guildMember = await ctx.bots.find((bot) => bot.platform == subPlayer.platform)?.getGuildMember?.(subPlayer.guildId, subPlayer.userId);
1791
+ const name2 = subPlayer.nickName ?? guildMember?.nick ?? players.find((player) => player.steamAccount.id == subPlayer.steamId)?.steamAccount.name ?? subPlayer.steamId;
1792
+ if (config.rankBroadFun === true) {
1793
+ const img = await ctx.puppeteer.render(
1794
+ genImageHTML(
1795
+ {
1796
+ name: name2,
1797
+ avatar: guildMember?.avatar ?? players.find((player) => subPlayer.steamId == player.steamAccount.id).steamAccount.avatar,
1798
+ isRising: rankMap[subPlayer.steamId].rank > subPlayer.rank.rank || rankMap[subPlayer.steamId].rank == subPlayer.rank.rank && rankMap[subPlayer.steamId].leader < subPlayer.rank.leader || rankMap[subPlayer.steamId].leader > 0 && subPlayer.rank.leader == null,
1799
+ prevRank,
1800
+ currRank
1801
+ },
1802
+ "rank" + (config.rankBroadFun ? "_fun" : ""),
1803
+ "rank" /* Rank */
1804
+ )
1805
+ );
1806
+ await ctx.broadcast([`${subPlayer.platform}:${subPlayer.guildId}`], img);
1807
+ } else {
1808
+ const message = `群友 ${name2} 段位变动:${rank[prevRank.medal]}${prevRank.star} → ${rank[currRank.medal]}${currRank.star} `;
1809
+ const img = await ctx.puppeteer.render(genImageHTML(currRank, "rank" + (config.rankBroadFun ? "2" : ""), "rank" /* Rank */));
1810
+ await ctx.broadcast([`${subPlayer.platform}:${subPlayer.guildId}`], message + img);
1811
+ }
1812
+ ctx.logger.info(`向 ${subPlayer.platform}:${subPlayer.guildId} 发布段位变动播报信息。`);
1813
+ }
1814
+ }
1815
+ }
1816
+ ctx.database.set("dt_subscribed_players", subPlayer.id, { rank: rankMap[subPlayer.steamId] });
1817
+ }
1818
+ }
1731
1819
  }
1732
1820
  if (pendingMatches.length > 0) {
1733
1821
  const now = (0, import_moment.default)();
@@ -1738,7 +1826,7 @@ async function apply(ctx, config) {
1738
1826
  if (queryLocal.length > 0) {
1739
1827
  match = queryLocal[0].data;
1740
1828
  ctx.database.set("dt_previous_query_results", match.id, { queryTime: /* @__PURE__ */ new Date() });
1741
- } else match = getFormattedMatchData((await query(MATCH_INFO(pendingMatch.matchId))).data);
1829
+ } else match = getFormattedMatchData((await query(MATCH_INFO, pendingMatch.matchId)).data);
1742
1830
  if (match.parsedDateTime || import_moment.default.unix(match.endDateTime).isBefore(now.subtract(config.dataParsingTimeoutMinutes, "minutes"))) {
1743
1831
  pendingMatches = pendingMatches.filter((item) => item.matchId != match.id);
1744
1832
  const img = await ctx.puppeteer.render(genImageHTML(match, config.template_match, "match" /* Match */));
@@ -1780,10 +1868,9 @@ KDA:${((player.kills + player.assists) / (player.deaths || 1)).toFixed(2)} [${
1780
1868
  const subscribedGuilds = await ctx.database.get("dt_subscribed_guilds", void 0);
1781
1869
  const subscribedPlayersInGuild = (await ctx.database.get("dt_subscribed_players", void 0)).filter((player) => subscribedGuilds.some((guild) => guild.guildId == player.guildId));
1782
1870
  const players = (await query(
1783
- MATCHES_FOR_DAILY(
1784
- subscribedPlayersInGuild.map((player) => player.steamId).filter((value, index, self) => self.indexOf(value) === index),
1785
- timeAgo
1786
- )
1871
+ PLAYERS_MATCHES_FOR_DAILY,
1872
+ subscribedPlayersInGuild.map((player) => player.steamId).filter((value, index, self) => self.indexOf(value) === index),
1873
+ timeAgo
1787
1874
  )).data.players.filter((player) => player.matches.length > 0);
1788
1875
  const matches = players.map((player) => player.matches.map((match) => match)).flat().filter((item, index, self) => index === self.findIndex((t) => t.id === item.id));
1789
1876
  for (let subPlayer of subscribedPlayersInGuild) {
@@ -1874,6 +1961,7 @@ function genImageHTML(data, template, type) {
1874
1961
  dotaconstants: dotaconstants3,
1875
1962
  moment: import_moment.default,
1876
1963
  escapeHTML: /* @__PURE__ */ __name(function escapeHTML(str) {
1964
+ if (str == null) return "";
1877
1965
  return str.replace(/[&<>"']/g, function(match) {
1878
1966
  const escape = {
1879
1967
  "&": "&amp;",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sjtdev/koishi-plugin-dota2tracker",
3
3
  "description": "koishi插件-追踪群友的DOTA2对局",
4
- "version": "1.2.16",
4
+ "version": "1.2.18-beta.1",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
Binary file
Binary file
@@ -0,0 +1,35 @@
1
+ <% const rank = data; %>
2
+ <head>
3
+ <style>
4
+ html,
5
+ body {
6
+ width: 128px;
7
+ height: 128px;
8
+ margin: 0;
9
+ padding: 0;
10
+ background-color: #0000;
11
+ }
12
+ div {
13
+ width: 128px;
14
+ height: 128px;
15
+ position: relative;
16
+ }
17
+ div > * {
18
+ position: absolute;
19
+ width: 100%;
20
+ }
21
+ p {
22
+ margin: 0;
23
+ text-align: center;
24
+ color: #fff;
25
+ bottom: 8px;
26
+ font-size: 20px;
27
+ text-shadow: -2px -2px 0 rgba(0, 0, 0, 0.5), 2px -2px 0 rgba(0, 0, 0, 0.5), -2px 2px 0 rgba(0, 0, 0, 0.5), 2px 2px 0 rgba(0, 0, 0, 0.5); /* 描边效果 */
28
+ }
29
+ </style>
30
+ </head>
31
+ <div>
32
+ <img src="<%= utils.getImageUrl('medal_' + (rank.inTop100 ?? rank.medal)) %>" />
33
+ <img src="<%= utils.getImageUrl("star_" + rank.star) %>" />
34
+ <p><%= rank.leader %></p>
35
+ </div>
@@ -0,0 +1,108 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Document</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ }
12
+ html {
13
+ width: 1024px;
14
+ height: 768px;
15
+ }
16
+ <%= ["xi","bei"].map(kind=>`body.${kind} {background: url(${utils.getImageUrl(kind, undefined, "jpg")}) no-repeat center center / cover;}`).join("\n") %>
17
+ .wrapper {
18
+ padding: 80px;
19
+ padding-top: 180px;
20
+ }
21
+ .wrapper > p {
22
+ font-size: 32px;
23
+ line-height: 48px;
24
+ height: 48px;
25
+ text-align: center;
26
+ }
27
+ body.xi p.bei,
28
+ body.bei p.xi {
29
+ display: none;
30
+ }
31
+ .wrapper > p > span {
32
+ margin: 0 10px;
33
+ }
34
+ .wrapper > p > span.prev {
35
+ color: gray;
36
+ }
37
+ .wrapper > p > span.curr {
38
+ font-weight: bold;
39
+ color: red;
40
+ }
41
+
42
+ .wrapper > p > img {
43
+ width: 48px;
44
+ height: 48px;
45
+ vertical-align: middle;
46
+ line-height: 48px;
47
+ }
48
+
49
+ .ranks {
50
+ margin-top: 40px;
51
+ display: flex;
52
+ width: 100%;
53
+ height: 256px;
54
+ justify-content: space-evenly;
55
+ }
56
+
57
+ div.rank {
58
+ position: relative;
59
+ width: 256px;
60
+ height: 256px;
61
+ }
62
+
63
+ div.rank > img {
64
+ position: absolute;
65
+ }
66
+
67
+ div.rank.prev {
68
+ filter: grayscale(1);
69
+ }
70
+ div.rank.curr {
71
+ transform: scale(1.25);
72
+ }
73
+ div.rank p {
74
+ font-size: 36px;
75
+ bottom: 20px;
76
+ width: 100%;
77
+ text-align: center;
78
+ color: #fff;
79
+ position: absolute;
80
+ }
81
+ </style>
82
+ </head>
83
+ <% const {name, avatar, isRising, prevRank, currRank} = data; %>
84
+ <body class="<%= isRising ? "xi" : "bei" %>">
85
+ <div class="wrapper">
86
+ <p class="xi">热烈祝贺群友 <img src="<%= avatar %>" /><%= name %> 在天梯中再获进步,</p>
87
+ <p class="xi">由
88
+ <span class="rank prev"><%= d2a.rank[prevRank.medal] %><%= prevRank.leader ?? prevRank.star %></span>升为
89
+ <span class="rank curr"><%= d2a.rank[currRank.medal] %><%= currRank.leader ?? currRank.star %></span>,再接再厉,再创辉煌!
90
+ </p>
91
+ <p class="bei"><img src="<%= avatar %>" /></p>
92
+ <p class="bei">寄</p>
93
+ <div class="ranks">
94
+ <div class="rank prev">
95
+ <img src="<%= utils.getImageUrl('medal_' +(prevRank.inTop100 ?? prevRank.medal)) %>" alt="" />
96
+ <img src="<%= utils.getImageUrl('star_' + prevRank.star) %>" alt="" />
97
+ <p><%= prevRank.leader ?? "" %></p>
98
+ </div>
99
+ <div class="rank curr">
100
+ <img src="<%= utils.getImageUrl('medal_' +(currRank.inTop100 ?? currRank.medal)) %>" alt="" />
101
+ <img src="<%= utils.getImageUrl('star_' + currRank.star) %>" alt="" />
102
+ <p><%= currRank.leader ?? "" %></p>
103
+ </div>
104
+ </div>
105
+ <p style="text-align: right; margin-top: 40px;">—— <%= moment().format('YYYY/MM/DD HH点mm分') %></p>
106
+ </div>
107
+ </body>
108
+ </html>
@@ -0,0 +1,27 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>获取文件目录</title>
7
+ <script>
8
+ function showDirectory() {
9
+ // 获取当前页面的 URL
10
+ const url = window.location.href;
11
+ // 使用 URL 对象来解析当前 URL
12
+ const urlObj = new URL(url);
13
+ // 获取目录部分
14
+ const directory = urlObj.pathname.substring(0, urlObj.pathname.lastIndexOf('/'));
15
+ // 输出目录
16
+ document.getElementById('directory').innerText = directory;
17
+
18
+
19
+ console.log(directory)
20
+ }
21
+ </script>
22
+ </head>
23
+ <body onload="showDirectory()">
24
+ <h1>当前文件所在目录</h1>
25
+ <p id="directory"></p>
26
+ </body>
27
+ </html>