@sjtdev/koishi-plugin-dota2tracker 1.2.8-pre → 1.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/changelog.md CHANGED
@@ -1,11 +1,34 @@
1
+ # 1.2.8
2
+ **新增**:
3
+ - 新增周报功能,效果等同日报。
4
+ - 日报与周报中新增可关闭总结中显示组合的功能。
5
+
6
+ **改进**:
7
+ - 现在`查询玩家`与`查询最近比赛`指令可在私聊状态下使用,必须提供SteamID参数。
8
+ - 将战报中的播报评语由随机选取改为固定种子:比赛ID+玩家SteamID+玩家位置,确保在不同调用时刻、次数及平台下,对同一场比赛中的玩家评语保持一致。
9
+ - `查询玩家`指令图片中,玩家近期比赛表中未解析的场次参战率由"?%"改为估算值,显示为"≈xx%"。
10
+ - `查询英雄`指令图片中,由于命石带来的属性数值无法确定作用方式(增加或是替换等),去除命石属性前的“+”,请结合命石说明自行判断。(7.37版本更新带来的API变动,导致使用指令查询某些英雄报错问题,暂无法解决)
11
+ - 比赛图片模板`match_2`中,现在冠绝排名数字位置更准确了。
12
+ - 比赛图片模板`match_2`中,比赛段位可以显示星级了。
13
+
14
+ <details>
15
+ <summary>为什么未解析的比赛需要“估算”?</summary>
16
+ 比赛未解析无法获取“团队击杀数”,若是将己方所有玩家的击杀数相加,则会漏掉那些由小兵、防御塔等非玩家单位击杀数;若是将敌方所有玩家的死亡数相加,又会多出送野、自杀等不应算在己方战果中的计数。<br>目前程序采用将己方所有击杀数累加的方式来估算参战率,可能会略高于实际值。
17
+ </details>
18
+
19
+ <details>
20
+ <summary><b>1.2.8-pre更新日志</b></summary>
21
+
1
22
  ### 1.2.8-pre
2
23
  **改进**:
3
24
  - 现在`查询玩家`可在私聊状态使用,必须提供SteamID参数。
4
25
  - 将战报中的播报评语由随机选取改为固定种子:比赛ID+玩家SteamID,确保在不同调用时刻、次数及平台下,对同一场比赛中的玩家评语保持一致。
5
26
  - `查询玩家`指令图片中,玩家近期比赛表中未解析的场次参战率由"?%"改为估算值,显示为"≈xx%"
6
- <details>
7
- <summary>为什么是“估算”?</summary>
8
- 比赛未解析无法获取“团队击杀数”,若是将己方所有玩家的击杀数相加,则会漏掉那些由小兵、防御塔等非玩家单位击杀数;若是将敌方所有玩家的死亡数相加,又会多出送野、自杀等不应算在己方战果中的计数。<br>目前程序采用将己方所有击杀数累加的方式来估算参战率
27
+
28
+ ##### pre.2
29
+ **改进**:
30
+ - 比赛图片模板`match_2`中,现在冠绝排名数字位置更准确了。
31
+ - 比赛图片模板`match_2`中,比赛段位可以显示星级了。
9
32
  </details>
10
33
 
11
34
  # 1.2.7
package/lib/index.js CHANGED
@@ -117,6 +117,7 @@ function MATCH_INFO(matchId) {
117
117
  leaverStatus
118
118
  partyId
119
119
  position
120
+ playerSlot
120
121
  lane
121
122
  imp
122
123
  kills
@@ -1213,7 +1214,19 @@ var Config = import_koishi.Schema.intersect([
1213
1214
  import_koishi.Schema.union([
1214
1215
  import_koishi.Schema.object({
1215
1216
  dailyReportSwitch: import_koishi.Schema.const(true).required(),
1216
- dailyReportHours: import_koishi.Schema.number().min(0).max(23).default(6).description("日报时间小时")
1217
+ dailyReportHours: import_koishi.Schema.number().min(0).max(23).default(6).description("日报时间小时"),
1218
+ dailyReportShowCombi: import_koishi.Schema.boolean().default(true).description("日报是否显示组合")
1219
+ }),
1220
+ import_koishi.Schema.object({})
1221
+ ]),
1222
+ import_koishi.Schema.object({
1223
+ weeklyReportSwitch: import_koishi.Schema.boolean().default(false).description("周报功能").experimental()
1224
+ }),
1225
+ import_koishi.Schema.union([
1226
+ import_koishi.Schema.object({
1227
+ weeklyReportSwitch: import_koishi.Schema.const(true).required(),
1228
+ weeklyReportDayHours: import_koishi.Schema.tuple([import_koishi.Schema.number().min(1).max(7), import_koishi.Schema.number().min(0).max(23)]).default([1, 10]).description("周报发布于周(几)的(几)点"),
1229
+ weeklyReportShowCombi: import_koishi.Schema.boolean().default(true).description("周报是否显示组合")
1217
1230
  }),
1218
1231
  import_koishi.Schema.object({})
1219
1232
  ]),
@@ -1397,7 +1410,7 @@ async function apply(ctx, config) {
1397
1410
  queryMatchAndSend(session, match_id);
1398
1411
  });
1399
1412
  ctx.command("查询最近比赛 [input_data]", "查询玩家的最近比赛").usage("查询指定玩家的最近一场比赛的比赛数据,生成图片发布。\n参数可输入该玩家的SteamID或已在本群绑定玩家的别名,无参数时尝试查询调用指令玩家的SteamID").example("查询最近比赛 123456789").example("查询最近比赛 张三").action(async ({ session }, input_data) => {
1400
- if (session.guild) {
1413
+ if (session.guild || !session.guild && input_data) {
1401
1414
  let sessionPlayer;
1402
1415
  if (!input_data) {
1403
1416
  sessionPlayer = (await ctx.database.get("dt_subscribed_players", { guildId: session.event.channel.id, platform: session.event.platform, userId: session.event.user.id }))[0];
@@ -1420,6 +1433,8 @@ async function apply(ctx, config) {
1420
1433
  return;
1421
1434
  }
1422
1435
  queryMatchAndSend(session, lastMatchId);
1436
+ } else {
1437
+ session.send("<p>指令调用失败。</p><p>当前不属于群聊状态,必须提供指定玩家的SteamID。</p>");
1423
1438
  }
1424
1439
  });
1425
1440
  ctx.command("查询玩家 <input_data>", "查询玩家信息,可指定英雄").usage("查询指定玩家的个人信息与最近战绩,生成图片发布。\n参数可输入该玩家的SteamID或已在本群绑定玩家的别名,无参数时尝试查询调用指令玩家的SteamID").option("hero", "-o <value:string> 查询玩家指定英雄使用情况(同其他英雄查询,可用简称与ID)").example("查询玩家 123456789").example("查询玩家 张三").example("查询玩家 张三 hero 敌法师").action(async ({ session, options }, input_data) => {
@@ -1663,81 +1678,13 @@ async function apply(ctx, config) {
1663
1678
  if (config.dailyReportSwitch) {
1664
1679
  ctx.cron(`0 ${config.dailyReportHours} * * *`, async function() {
1665
1680
  const oneDayAgo = (0, import_moment.default)().subtract(1, "days").unix();
1666
- const subscribedGuilds = await ctx.database.get("dt_subscribed_guilds", void 0);
1667
- const subscribedPlayersInGuild = (await ctx.database.get("dt_subscribed_players", void 0)).filter((player) => subscribedGuilds.some((guild) => guild.guildId == player.guildId));
1668
- const players = (await query(
1669
- MATCHES_FOR_DAILY(
1670
- subscribedPlayersInGuild.map((player) => player.steamId).filter((value, index, self) => self.indexOf(value) === index),
1671
- oneDayAgo
1672
- )
1673
- )).data.players.filter((player) => player.matches.length > 0);
1674
- const matches = players.map((player) => player.matches.map((match) => match)).flat().filter((item, index, self) => index === self.findIndex((t) => t.id === item.id));
1675
- for (let subPlayer of subscribedPlayersInGuild) {
1676
- let player = players.find((player2) => subPlayer.steamId == player2.steamAccount.id);
1677
- if (!player)
1678
- continue;
1679
- let guildMember;
1680
- try {
1681
- guildMember = await ctx.bots.find((bot) => bot.platform == subPlayer.platform)?.getGuildMember(subPlayer.guildId, subPlayer.userId);
1682
- } catch (error) {
1683
- ctx.logger.error("获取群组信息失败。" + error);
1684
- }
1685
- subPlayer.name = subPlayer.nickName || (guildMember?.nick ?? players.find((player2) => player2.steamAccount.id == subPlayer.steamId)?.steamAccount.name);
1686
- player.winCount = player.matches.filter((match) => match.didRadiantWin == match.players.find((innerPlayer) => innerPlayer.steamAccount.id == player.steamAccount.id).isRadiant).length;
1687
- player.loseCount = player.matches.length - player.winCount;
1688
- player.avgKills = roundToDecimalPlaces(player.matches.reduce((acc, match) => acc + match.players.find((innerPlayer) => innerPlayer.steamAccount.id == player.steamAccount.id).kills, 0) / player.matches.length, 2);
1689
- player.avgDeaths = roundToDecimalPlaces(player.matches.reduce((acc, match) => acc + match.players.find((innerPlayer) => innerPlayer.steamAccount.id == player.steamAccount.id).deaths, 0) / player.matches.length, 2);
1690
- player.avgAssists = roundToDecimalPlaces(
1691
- player.matches.reduce((acc, match) => acc + match.players.find((innerPlayer) => innerPlayer.steamAccount.id == player.steamAccount.id).assists, 0) / player.matches.length,
1692
- 2
1693
- );
1694
- player.avgKDA = roundToDecimalPlaces((player.avgKills + player.avgAssists) / (player.avgDeaths || 1), 2);
1695
- player.avgImp = roundToDecimalPlaces(player.matches.reduce((acc, match) => acc + match.players.find((innerPlayer) => innerPlayer.steamAccount.id == player.steamAccount.id).imp, 0) / player.matches.length, 0);
1696
- subPlayer = Object.assign(subPlayer, player);
1697
- }
1698
- for (let guild of subscribedGuilds) {
1699
- const currentsubscribedPlayers = subscribedPlayersInGuild.filter((player) => player.platform == guild.platform && player.guildId == guild.guildId && player.matches?.length);
1700
- if (currentsubscribedPlayers.length) {
1701
- const currentsubscribedPlayersIds = currentsubscribedPlayers.map((player) => player.steamId);
1702
- const combinationsMap = /* @__PURE__ */ new Map();
1703
- matches.forEach((match) => {
1704
- const sortedPlayerIds = match.players.map((player) => player.steamAccount.id).filter((id) => currentsubscribedPlayersIds.includes(id)).sort((a, b) => a - b);
1705
- const key = sortedPlayerIds.join(",");
1706
- if (!combinationsMap.has(key)) {
1707
- const players2 = currentsubscribedPlayers.filter((subPlayer) => sortedPlayerIds.includes(subPlayer.steamId));
1708
- if (players2.length > 0) {
1709
- const name2 = players2.map((subPlayer) => subPlayer.name).join("/");
1710
- combinationsMap.set(key, {
1711
- players: players2,
1712
- name: name2,
1713
- winCount: match.didRadiantWin == match.players.find((innerPlayer) => innerPlayer.steamAccount.id == players2[0].steamId).isRadiant ? 1 : 0,
1714
- matches: [match]
1715
- });
1716
- }
1717
- } else {
1718
- const combi = combinationsMap.get(key);
1719
- combi.matches.push(match);
1720
- combi.winCount += match.didRadiantWin == match.players.find((innerPlayer) => innerPlayer.steamAccount.id == combi.players[0].steamId).isRadiant ? 1 : 0;
1721
- }
1722
- });
1723
- const combinations = Array.from(combinationsMap.values());
1724
- try {
1725
- await ctx.broadcast(
1726
- [`${guild.platform}:${guild.guildId}`],
1727
- `昨日总结:
1728
- ${currentsubscribedPlayers.map(
1729
- (player) => `${player.name}: ${player.winCount}胜${player.loseCount}负 胜率${Math.round(player.winCount / player.matches.length * 100)}%,平均KDA: [${player.avgKills}/${player.avgDeaths}/${player.avgAssists}](${player.avgKDA}),平均表现: ${player.avgImp > 0 ? "+" : ""}${player.avgImp}`
1730
- ).join("\n")}
1731
- ${combinations.map((combi) => `组合[${combi.name}]: ${combi.winCount}胜${combi.matches.length - combi.winCount}负 胜率${Math.round(combi.winCount / combi.matches.length * 100)}%`).join("\n")}`.replace(
1732
- /\s*\n\s*/g,
1733
- "\n"
1734
- )
1735
- );
1736
- } catch (error) {
1737
- ctx.logger.error(error);
1738
- }
1739
- }
1740
- }
1681
+ await report(oneDayAgo, "昨日总结", config.dailyReportShowCombi);
1682
+ });
1683
+ }
1684
+ if (config.weeklyReportSwitch) {
1685
+ ctx.cron(`0 ${config.weeklyReportDayHours[1]} * * ${config.weeklyReportDayHours[0]}`, async function() {
1686
+ const oneWeekAgo = (0, import_moment.default)().subtract(1, "weeks").unix();
1687
+ await report(oneWeekAgo, "上周总结", config.weeklyReportShowCombi);
1741
1688
  });
1742
1689
  }
1743
1690
  ctx.cron("* * * * *", async function() {
@@ -1786,15 +1733,16 @@ async function apply(ctx, config) {
1786
1733
  let idsToFind = commingGuild.players.map((player) => player.steamId);
1787
1734
  let broadPlayers = match.players.filter((item) => idsToFind.includes(item.steamAccountId));
1788
1735
  for (let player of broadPlayers) {
1789
- const random2 = new import_koishi2.Random(() => simpleHashToSeed(match.id, player.steamAccountId));
1736
+ const random2 = new import_koishi2.Random(() => simpleHashToSeed(`${match.id}-${player.steamAccountId}-${player.playerSlot}`));
1790
1737
  let broadPlayerMessage = `${player.steamAccount.name}的${random2.pick(HEROES_CHINESE[player.hero.id])}`;
1738
+ console.log([player.deathContribution, player.killContribution]);
1791
1739
  if (player.isRadiant == match.didRadiantWin) {
1792
1740
  if (player.deathContribution < 0.2 || player.killContribution > 0.75 || player.heroDamage / player.networth > 1.5 || player.towerDamage > 1e4 || player.imp > 0)
1793
1741
  broadPlayerMessage += random2.pick(WIN_POSITIVE);
1794
1742
  else
1795
1743
  broadPlayerMessage += random2.pick(WIN_NEGATIVE);
1796
1744
  } else {
1797
- if (player.deathContribution < 0.25 || player.killContribution > 0.75 || player.heroDamage / player.networth > 1.25 || player.towerDamage > 5e3 || player.imp > 0)
1745
+ if (player.deathContribution < 0.25 || player.killContribution > 0.75 || player.heroDamage / player.networth > 1 || player.towerDamage > 5e3 || player.imp > 0)
1798
1746
  broadPlayerMessage += random2.pick(LOSE_POSITIVE);
1799
1747
  else
1800
1748
  broadPlayerMessage += random2.pick(LOSE_NEGATIVE);
@@ -1804,7 +1752,7 @@ KDA:${((player.kills + player.assists) / (player.deaths || 1)).toFixed(2)} [${
1804
1752
  broadMatchMessage += broadPlayerMessage + "\n";
1805
1753
  }
1806
1754
  await ctx.broadcast([`${commingGuild.platform}:${commingGuild.guildId}`], broadMatchMessage + (ctx.config.urlInMessageType.some((type) => type == "match") ? "https://stratz.com/matches/" + match.id : "") + img);
1807
- ctx.logger.info(`${match.id}${match.parsedDateTime ? "已解析," : "已结束超过1小时仍未被解析,放弃解析直接"}生成图片并发布于${commingGuild.platform}:${commingGuild.guildId}。`);
1755
+ ctx.logger.info(`${match.id}${match.parsedDateTime ? "已解析," : "已结束超过1小时仍未被解析,放弃等待解析直接"}生成图片并发布于${commingGuild.platform}:${commingGuild.guildId}。`);
1808
1756
  }
1809
1757
  if (match.parsedDateTime)
1810
1758
  ctx.database.upsert("dt_previous_query_results", (row) => [{ matchId: match.id, data: match, queryTime: /* @__PURE__ */ new Date() }]);
@@ -1818,6 +1766,79 @@ KDA:${((player.kills + player.assists) / (player.deaths || 1)).toFixed(2)} [${
1818
1766
  }
1819
1767
  });
1820
1768
  });
1769
+ async function report(timeAgo, title, showCombi) {
1770
+ const subscribedGuilds = await ctx.database.get("dt_subscribed_guilds", void 0);
1771
+ const subscribedPlayersInGuild = (await ctx.database.get("dt_subscribed_players", void 0)).filter((player) => subscribedGuilds.some((guild) => guild.guildId == player.guildId));
1772
+ const players = (await query(
1773
+ MATCHES_FOR_DAILY(
1774
+ subscribedPlayersInGuild.map((player) => player.steamId).filter((value, index, self) => self.indexOf(value) === index),
1775
+ timeAgo
1776
+ )
1777
+ )).data.players.filter((player) => player.matches.length > 0);
1778
+ const matches = players.map((player) => player.matches.map((match) => match)).flat().filter((item, index, self) => index === self.findIndex((t) => t.id === item.id));
1779
+ for (let subPlayer of subscribedPlayersInGuild) {
1780
+ let player = players.find((player2) => subPlayer.steamId == player2.steamAccount.id);
1781
+ if (!player)
1782
+ continue;
1783
+ let guildMember;
1784
+ try {
1785
+ guildMember = await ctx.bots.find((bot) => bot.platform == subPlayer.platform)?.getGuildMember(subPlayer.guildId, subPlayer.userId);
1786
+ } catch (error) {
1787
+ ctx.logger.error("获取群组信息失败。" + error);
1788
+ }
1789
+ subPlayer.name = subPlayer.nickName || (guildMember?.nick ?? players.find((player2) => player2.steamAccount.id == subPlayer.steamId)?.steamAccount.name);
1790
+ player.winCount = player.matches.filter((match) => match.didRadiantWin == match.players.find((innerPlayer) => innerPlayer.steamAccount.id == player.steamAccount.id).isRadiant).length;
1791
+ player.loseCount = player.matches.length - player.winCount;
1792
+ player.avgKills = roundToDecimalPlaces(player.matches.reduce((acc, match) => acc + match.players.find((innerPlayer) => innerPlayer.steamAccount.id == player.steamAccount.id).kills, 0) / player.matches.length, 2);
1793
+ player.avgDeaths = roundToDecimalPlaces(player.matches.reduce((acc, match) => acc + match.players.find((innerPlayer) => innerPlayer.steamAccount.id == player.steamAccount.id).deaths, 0) / player.matches.length, 2);
1794
+ player.avgAssists = roundToDecimalPlaces(player.matches.reduce((acc, match) => acc + match.players.find((innerPlayer) => innerPlayer.steamAccount.id == player.steamAccount.id).assists, 0) / player.matches.length, 2);
1795
+ player.avgKDA = roundToDecimalPlaces((player.avgKills + player.avgAssists) / (player.avgDeaths || 1), 2);
1796
+ player.avgImp = roundToDecimalPlaces(player.matches.reduce((acc, match) => acc + match.players.find((innerPlayer) => innerPlayer.steamAccount.id == player.steamAccount.id).imp, 0) / player.matches.length, 0);
1797
+ subPlayer = Object.assign(subPlayer, player);
1798
+ }
1799
+ for (let guild of subscribedGuilds) {
1800
+ const currentsubscribedPlayers = subscribedPlayersInGuild.filter((player) => player.platform == guild.platform && player.guildId == guild.guildId && player.matches?.length);
1801
+ if (currentsubscribedPlayers.length) {
1802
+ const currentsubscribedPlayersIds = currentsubscribedPlayers.map((player) => player.steamId);
1803
+ const combinationsMap = /* @__PURE__ */ new Map();
1804
+ matches.forEach((match) => {
1805
+ const sortedPlayerIds = match.players.map((player) => player.steamAccount.id).filter((id) => currentsubscribedPlayersIds.includes(id)).sort((a, b) => a - b);
1806
+ const key = sortedPlayerIds.join(",");
1807
+ if (!combinationsMap.has(key)) {
1808
+ const players2 = currentsubscribedPlayers.filter((subPlayer) => sortedPlayerIds.includes(subPlayer.steamId));
1809
+ if (players2.length > 0) {
1810
+ const name2 = players2.map((subPlayer) => subPlayer.name).join("/");
1811
+ combinationsMap.set(key, {
1812
+ players: players2,
1813
+ name: name2,
1814
+ winCount: match.didRadiantWin == match.players.find((innerPlayer) => innerPlayer.steamAccount.id == players2[0].steamId).isRadiant ? 1 : 0,
1815
+ matches: [match]
1816
+ });
1817
+ }
1818
+ } else {
1819
+ const combi = combinationsMap.get(key);
1820
+ combi.matches.push(match);
1821
+ combi.winCount += match.didRadiantWin == match.players.find((innerPlayer) => innerPlayer.steamAccount.id == combi.players[0].steamId).isRadiant ? 1 : 0;
1822
+ }
1823
+ });
1824
+ const combinations = Array.from(combinationsMap.values());
1825
+ try {
1826
+ await ctx.broadcast(
1827
+ [`${guild.platform}:${guild.guildId}`],
1828
+ `${title}:
1829
+ ${currentsubscribedPlayers.map(
1830
+ (player) => `${player.name}: ${player.winCount}胜${player.loseCount}负 胜率${Math.round(player.winCount / player.matches.length * 100)}%,平均KDA: [${player.avgKills}/${player.avgDeaths}/${player.avgAssists}](${player.avgKDA}),平均表现: ${player.avgImp > 0 ? "+" : ""}${player.avgImp}`
1831
+ ).join("\n")}
1832
+ ${showCombi ? combinations.map((combi) => `组合[${combi.name}]: ${combi.winCount}胜${combi.matches.length - combi.winCount}负 胜率${Math.round(combi.winCount / combi.matches.length * 100)}%`).join("\n") : ""}`.replace(/\s*\n\s*/g, "\n")
1833
+ );
1834
+ ctx.logger.info(`发布日报于${guild.platform}:${guild.guildId}`);
1835
+ } catch (error) {
1836
+ ctx.logger.error(error);
1837
+ }
1838
+ }
1839
+ }
1840
+ }
1841
+ __name(report, "report");
1821
1842
  ctx.on("dispose", async () => {
1822
1843
  });
1823
1844
  }
@@ -1844,9 +1865,8 @@ function genImageHTML(data, template, type) {
1844
1865
  return result;
1845
1866
  }
1846
1867
  __name(genImageHTML, "genImageHTML");
1847
- function simpleHashToSeed(matchId, playerId) {
1848
- const input = `${matchId}-${playerId}`;
1849
- const encoded = btoa(input);
1868
+ function simpleHashToSeed(inputString) {
1869
+ const encoded = btoa(inputString);
1850
1870
  let total = 0;
1851
1871
  for (let i = 0; i < encoded.length; i++) {
1852
1872
  total += encoded.charCodeAt(i);
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.8-pre",
4
+ "version": "1.2.8",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
@@ -26,7 +26,7 @@
26
26
  "dota2"
27
27
  ],
28
28
  "dependencies": {
29
- "dotaconstants": "^8.8.0",
29
+ "dotaconstants": "^8.10.0",
30
30
  "ejs": "^3.1.10",
31
31
  "moment": "^2.30.1"
32
32
  },
package/readme.md CHANGED
@@ -12,6 +12,8 @@ DOTA2Bot插件-提供自动追踪群友的最新对局的功能(需群友绑
12
12
  在希望推送战报信息的群组(或频道)使用指令`订阅本群`,玩家可使用指令`绑定`来将自身账号与Steam账号绑定,bot会尝试追踪已订阅群组(或频道)中的绑定玩家的最新对局信息。
13
13
  其他查询功能见下方指令说明。
14
14
  **直接调用help指令可获取更详细的说明,调用【指令 -h】还会有用法示例。(例如:订阅本群 -h)**
15
+ **本插件使用的所有SteamID均为SteamID3类型(即DOTA2游戏内个人页面显示的好友ID与stratz登录后个人页面链接中显示的ID),有关SteamID类型见 https://steamid.tatlead.com/ (由[issue](../../issues/1)提供,感谢这位用户)**
16
+
15
17
  **更新日志见[changelog](changelog.md)**
16
18
 
17
19
  ### 指令
@@ -683,7 +683,7 @@
683
683
  `).join("")}
684
684
  </div>
685
685
  <div class="skills">
686
- ${hero.abilities//.filter((item) => dotaconstants.abilities[item.name].behavior != "Hidden")
686
+ ${hero.abilities//.filter((item) => dotaconstants.abilities[item.name]?.behavior != "Hidden")
687
687
  .map((item) => `
688
688
  <div class="skill" data-innate="${item.ability_is_innate&&!item.ability_is_facet}">
689
689
  <p class="title">
@@ -695,26 +695,20 @@
695
695
  <div class="img_stats">
696
696
  <img src="${utils.getImageUrl(item.name, ImageType.Abilities)}" onerror="this.onerror=null; this.src='${utils.getImageUrl(`innate_icon`,ImageType.Icons)}';"/>
697
697
  <div class="stats">
698
- <p class="behavior">技能:${(Array.isArray(dotaconstants.abilities[item.name].behavior) ? dotaconstants.abilities[item.name].behavior : [dotaconstants.abilities[item.name].behavior])
698
+ <p class="behavior">技能:${[].concat(dotaconstants.abilities[item.name]?.behavior)
699
699
  .filter((beh) => beh !== "Hidden" || !(item.ability_is_granted_by_shard || item.ability_is_granted_by_scepter))
700
- .map((beh) => d2a.behavior[beh])
701
- .join("/")}</p>
702
- ${dotaconstants.abilities[item.name].target_team
703
- ? `<p class="target_team">影响:${(Array.isArray(dotaconstants.abilities[item.name].target_team)
704
- ? dotaconstants.abilities[item.name].target_team
705
- : [dotaconstants.abilities[item.name].target_team])
706
- .map((tt) => d2a.target_team[tt])
707
- .join("/")}</p>`
708
- : ""}
709
- ${!Array.isArray(dotaconstants.abilities[item.name].dmg_type) && dotaconstants.abilities[item.name].dmg_type
710
- ? `<p class="dmg_type ${dotaconstants.abilities[item.name].dmg_type}">伤害类型:</p>`
711
- : ""}
712
- ${dotaconstants.abilities[item.name].dispellable
713
- ? `<p class="dispellable ${dotaconstants.abilities[item.name].dispellable == "Strong Dispels Only" ? "Strong" : dotaconstants.abilities[item.name].dispellable}">能否驱散:</p>`
714
- : ""}
715
- ${!Array.isArray(dotaconstants.abilities[item.name].bkbpierce) && dotaconstants.abilities[item.name].bkbpierce
716
- ? `<p class="bkbpierce">无视减益免疫: ${dotaconstants.abilities[item.name].bkbpierce == "Yes" ? "是" : "否"}</p>`
717
- : ""}
700
+ .map((beh) => d2a.behavior[beh]).join("/")}
701
+ </p>
702
+ ${dotaconstants.abilities[item.name]?.target_team ?
703
+ `<p class="target_team">影响:${[].concat(dotaconstants.abilities[item.name]?.target_team)
704
+ .map((tt) => d2a.target_team[tt]).join("/")}
705
+ </p>` : ""}
706
+ ${!Array.isArray(dotaconstants.abilities[item.name]?.dmg_type) && dotaconstants.abilities[item.name]?.dmg_type ?
707
+ `<p class="dmg_type ${dotaconstants.abilities[item.name]?.dmg_type}">伤害类型:</p>` : ""}
708
+ ${dotaconstants.abilities[item.name]?.dispellable ?
709
+ `<p class="dispellable ${dotaconstants.abilities[item.name]?.dispellable == "Strong Dispels Only" ? "Strong" : dotaconstants.abilities[item.name]?.dispellable}">能否驱散:</p>` : ""}
710
+ ${!Array.isArray(dotaconstants.abilities[item.name]?.bkbpierce) && dotaconstants.abilities[item.name]?.bkbpierce ?
711
+ `<p class="bkbpierce">无视减益免疫: ${dotaconstants.abilities[item.name]?.bkbpierce == "Yes" ? "是" : ""}</p>` : ""}
718
712
  </div>
719
713
  </div>
720
714
  <p class="description">${item.desc_loc}</p>
@@ -768,7 +762,7 @@
768
762
  </span>` : "" }
769
763
  ${sv.facet_bonus.name ?
770
764
  `<span class="alternative facet">
771
- <span class="facet"><span class="name_back type_${hero.facets.find(facet=>facet.name==sv.facet_bonus.name).color}"><img src="${utils.getImageUrl(hero.facets.find(facet=>facet.name==sv.facet_bonus.name).icon, ImageType.IconsFacets)}" />${sv.facet_bonus.values.map(value => (value > 0 ? '<span class="plus">+</span>' : "") + value + (sv.is_percentage ? "%" : "")).join(" / ")}</span></span>
765
+ <span class="facet"><span class="name_back type_${hero.facets.find(facet=>facet.name==sv.facet_bonus.name).color}"><img src="${utils.getImageUrl(hero.facets.find(facet=>facet.name==sv.facet_bonus.name).icon, ImageType.IconsFacets)}" />${sv.facet_bonus.values.map(value => value + (sv.is_percentage ? "%" : "")).join(" / ")}</span></span>
772
766
  </span>` : "" }
773
767
  ${sv.bonuses.length ?
774
768
  `<span class="alternative talent">
@@ -49,6 +49,18 @@
49
49
  color: #ffb400;
50
50
  }
51
51
 
52
+ nav > .rank {
53
+ width: 48px;
54
+ height: 48px;
55
+ position: relative;
56
+ }
57
+
58
+ nav > .rank > img {
59
+ width: 48px;
60
+ height: 48px;
61
+ position: absolute;
62
+ }
63
+
52
64
  .radiant {
53
65
  color: #3c9028;
54
66
  }
@@ -232,9 +244,9 @@
232
244
  .player > .rank > p {
233
245
  position: absolute;
234
246
  width: 100%;
235
- bottom: 3px;
247
+ bottom: 1.5px;
236
248
  text-align: center;
237
- font-size: 9px;
249
+ font-size: 8px;
238
250
  color: #fff;
239
251
  text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000; /* 文字描边 */
240
252
  }
@@ -450,7 +462,8 @@
450
462
  <p><%-d2a.lobbyTypes[match.lobbyType] || match.lobbyType%>/<%-d2a.gameMode[match.gameMode] || match.gameMode%></p>
451
463
  </div>
452
464
  <div class="rank">
453
- <img width="48" height="48" src="<%-utils.getImageUrl('medal_' + match.rank?.toString().split('')[0])%>" alt="" />
465
+ <img src="<%-utils.getImageUrl('medal_' + (match.rank?.toString().split('')[0] ?? '0'))%>" alt="" />
466
+ <img style="z-index: 1;" src="<%-utils.getImageUrl('star_' + (match.rank?.toString().split('')[1] ?? '0'))%>" alt="" />
454
467
  </div>
455
468
  </nav>
456
469
  <section class="match_result">