@sjtdev/koishi-plugin-dota2tracker 1.2.8-pre.2 → 1.2.9-pre
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 +29 -4
- package/lib/index.js +106 -83
- package/package.json +2 -2
- package/readme.md +2 -0
- package/template/hero/hero_1.ejs +38 -30
- package/template/match/match_2.ejs +2 -2
package/changelog.md
CHANGED
|
@@ -1,17 +1,42 @@
|
|
|
1
|
+
### 1.2.9-pre
|
|
2
|
+
**修复**:
|
|
3
|
+
- `查询英雄`:尝试修复DOTA2 7.37版本更新后API变动导致部分英雄查询报错。
|
|
4
|
+
|
|
5
|
+
**改进**:
|
|
6
|
+
- `查询英雄`:命石技能的技能名栏位添加对应命石图标与背景色用于标识。
|
|
7
|
+
|
|
8
|
+
# 1.2.8
|
|
9
|
+
**新增**:
|
|
10
|
+
- 新增周报功能,效果等同日报。
|
|
11
|
+
- 日报与周报中新增可关闭总结中显示组合的功能。
|
|
12
|
+
|
|
13
|
+
**改进**:
|
|
14
|
+
- 现在`查询玩家`与`查询最近比赛`指令可在私聊状态下使用,必须提供SteamID参数。
|
|
15
|
+
- 将战报中的播报评语由随机选取改为固定种子:比赛ID+玩家SteamID+玩家位置,确保在不同调用时刻、次数及平台下,对同一场比赛中的玩家评语保持一致。
|
|
16
|
+
- `查询玩家`指令图片中,玩家近期比赛表中未解析的场次参战率由"?%"改为估算值,显示为"≈xx%"。
|
|
17
|
+
- `查询英雄`指令图片中,由于命石带来的属性数值无法确定作用方式(增加或是替换等),去除命石属性前的“+”,请结合命石说明自行判断。(7.37版本更新带来的API变动,导致使用指令查询某些英雄报错问题,暂无法解决)
|
|
18
|
+
- 比赛图片模板`match_2`中,现在冠绝排名数字位置更准确了。
|
|
19
|
+
- 比赛图片模板`match_2`中,比赛段位可以显示星级了。
|
|
20
|
+
|
|
21
|
+
<details>
|
|
22
|
+
<summary>为什么未解析的比赛需要“估算”?</summary>
|
|
23
|
+
比赛未解析无法获取“团队击杀数”,若是将己方所有玩家的击杀数相加,则会漏掉那些由小兵、防御塔等非玩家单位击杀数;若是将敌方所有玩家的死亡数相加,又会多出送野、自杀等不应算在己方战果中的计数。<br>目前程序采用将己方所有击杀数累加的方式来估算参战率,可能会略高于实际值。
|
|
24
|
+
</details>
|
|
25
|
+
|
|
26
|
+
<details>
|
|
27
|
+
<summary><b>1.2.8-pre更新日志</b></summary>
|
|
28
|
+
|
|
1
29
|
### 1.2.8-pre
|
|
2
30
|
**改进**:
|
|
3
31
|
- 现在`查询玩家`可在私聊状态使用,必须提供SteamID参数。
|
|
4
32
|
- 将战报中的播报评语由随机选取改为固定种子:比赛ID+玩家SteamID,确保在不同调用时刻、次数及平台下,对同一场比赛中的玩家评语保持一致。
|
|
5
33
|
- `查询玩家`指令图片中,玩家近期比赛表中未解析的场次参战率由"?%"改为估算值,显示为"≈xx%"
|
|
6
|
-
<details>
|
|
7
|
-
<summary>为什么未解析的比赛需要“估算”?</summary>
|
|
8
|
-
比赛未解析无法获取“团队击杀数”,若是将己方所有玩家的击杀数相加,则会漏掉那些由小兵、防御塔等非玩家单位击杀数;若是将敌方所有玩家的死亡数相加,又会多出送野、自杀等不应算在己方战果中的计数。<br>目前程序采用将己方所有击杀数累加的方式来估算参战率,可能会略高于实际值
|
|
9
|
-
</details>
|
|
10
34
|
|
|
11
35
|
##### pre.2
|
|
12
36
|
**改进**:
|
|
13
37
|
- 比赛图片模板`match_2`中,现在冠绝排名数字位置更准确了。
|
|
14
38
|
- 比赛图片模板`match_2`中,比赛段位可以显示星级了。
|
|
39
|
+
</details>
|
|
15
40
|
|
|
16
41
|
# 1.2.7
|
|
17
42
|
##### 于1.2.7-beta中尝试修复的功能均已正常工作,正式发布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) => {
|
|
@@ -1516,6 +1531,7 @@ async function apply(ctx, config) {
|
|
|
1516
1531
|
else
|
|
1517
1532
|
hero.facets[i].description_loc = formatHeroDesc(hero.facets[i].description_loc, ab.special_values, "facet" /* Facet */);
|
|
1518
1533
|
ab.ability_is_facet = true;
|
|
1534
|
+
ab.facet = hero.facets[i];
|
|
1519
1535
|
hero.abilities.push(ab);
|
|
1520
1536
|
});
|
|
1521
1537
|
}
|
|
@@ -1523,6 +1539,9 @@ async function apply(ctx, config) {
|
|
|
1523
1539
|
const all_special_values = [...hero.abilities.flatMap((ab) => ab.special_values), ...hero.facet_abilities.flatMap((fas) => fas.abilities.flatMap((fa) => fa.special_values))];
|
|
1524
1540
|
hero.abilities.forEach((ab) => {
|
|
1525
1541
|
ab.facets_loc.forEach((facet, i) => {
|
|
1542
|
+
i = i + (hero.facets.length - ab.facets_loc.length);
|
|
1543
|
+
if (i < 0)
|
|
1544
|
+
return;
|
|
1526
1545
|
if (facet) {
|
|
1527
1546
|
if (!hero.facets[i].abilities)
|
|
1528
1547
|
hero.facets[i].abilities = [];
|
|
@@ -1663,81 +1682,13 @@ async function apply(ctx, config) {
|
|
|
1663
1682
|
if (config.dailyReportSwitch) {
|
|
1664
1683
|
ctx.cron(`0 ${config.dailyReportHours} * * *`, async function() {
|
|
1665
1684
|
const oneDayAgo = (0, import_moment.default)().subtract(1, "days").unix();
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
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
|
-
}
|
|
1685
|
+
await report(oneDayAgo, "昨日总结", config.dailyReportShowCombi);
|
|
1686
|
+
});
|
|
1687
|
+
}
|
|
1688
|
+
if (config.weeklyReportSwitch) {
|
|
1689
|
+
ctx.cron(`0 ${config.weeklyReportDayHours[1]} * * ${config.weeklyReportDayHours[0]}`, async function() {
|
|
1690
|
+
const oneWeekAgo = (0, import_moment.default)().subtract(1, "weeks").unix();
|
|
1691
|
+
await report(oneWeekAgo, "上周总结", config.weeklyReportShowCombi);
|
|
1741
1692
|
});
|
|
1742
1693
|
}
|
|
1743
1694
|
ctx.cron("* * * * *", async function() {
|
|
@@ -1786,7 +1737,7 @@ async function apply(ctx, config) {
|
|
|
1786
1737
|
let idsToFind = commingGuild.players.map((player) => player.steamId);
|
|
1787
1738
|
let broadPlayers = match.players.filter((item) => idsToFind.includes(item.steamAccountId));
|
|
1788
1739
|
for (let player of broadPlayers) {
|
|
1789
|
-
const random2 = new import_koishi2.Random(() => simpleHashToSeed(match.id
|
|
1740
|
+
const random2 = new import_koishi2.Random(() => simpleHashToSeed(`${match.id}-${player.steamAccountId}-${player.playerSlot}`));
|
|
1790
1741
|
let broadPlayerMessage = `${player.steamAccount.name}的${random2.pick(HEROES_CHINESE[player.hero.id])}`;
|
|
1791
1742
|
if (player.isRadiant == match.didRadiantWin) {
|
|
1792
1743
|
if (player.deathContribution < 0.2 || player.killContribution > 0.75 || player.heroDamage / player.networth > 1.5 || player.towerDamage > 1e4 || player.imp > 0)
|
|
@@ -1794,7 +1745,7 @@ async function apply(ctx, config) {
|
|
|
1794
1745
|
else
|
|
1795
1746
|
broadPlayerMessage += random2.pick(WIN_NEGATIVE);
|
|
1796
1747
|
} else {
|
|
1797
|
-
if (player.deathContribution < 0.25 || player.killContribution > 0.75 || player.heroDamage / player.networth > 1
|
|
1748
|
+
if (player.deathContribution < 0.25 || player.killContribution > 0.75 || player.heroDamage / player.networth > 1 || player.towerDamage > 5e3 || player.imp > 0)
|
|
1798
1749
|
broadPlayerMessage += random2.pick(LOSE_POSITIVE);
|
|
1799
1750
|
else
|
|
1800
1751
|
broadPlayerMessage += random2.pick(LOSE_NEGATIVE);
|
|
@@ -1804,7 +1755,7 @@ KDA:${((player.kills + player.assists) / (player.deaths || 1)).toFixed(2)} [${
|
|
|
1804
1755
|
broadMatchMessage += broadPlayerMessage + "\n";
|
|
1805
1756
|
}
|
|
1806
1757
|
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
|
|
1758
|
+
ctx.logger.info(`${match.id}${match.parsedDateTime ? "已解析," : "已结束超过1小时仍未被解析,放弃等待解析直接"}生成图片并发布于${commingGuild.platform}:${commingGuild.guildId}。`);
|
|
1808
1759
|
}
|
|
1809
1760
|
if (match.parsedDateTime)
|
|
1810
1761
|
ctx.database.upsert("dt_previous_query_results", (row) => [{ matchId: match.id, data: match, queryTime: /* @__PURE__ */ new Date() }]);
|
|
@@ -1818,6 +1769,79 @@ KDA:${((player.kills + player.assists) / (player.deaths || 1)).toFixed(2)} [${
|
|
|
1818
1769
|
}
|
|
1819
1770
|
});
|
|
1820
1771
|
});
|
|
1772
|
+
async function report(timeAgo, title, showCombi) {
|
|
1773
|
+
const subscribedGuilds = await ctx.database.get("dt_subscribed_guilds", void 0);
|
|
1774
|
+
const subscribedPlayersInGuild = (await ctx.database.get("dt_subscribed_players", void 0)).filter((player) => subscribedGuilds.some((guild) => guild.guildId == player.guildId));
|
|
1775
|
+
const players = (await query(
|
|
1776
|
+
MATCHES_FOR_DAILY(
|
|
1777
|
+
subscribedPlayersInGuild.map((player) => player.steamId).filter((value, index, self) => self.indexOf(value) === index),
|
|
1778
|
+
timeAgo
|
|
1779
|
+
)
|
|
1780
|
+
)).data.players.filter((player) => player.matches.length > 0);
|
|
1781
|
+
const matches = players.map((player) => player.matches.map((match) => match)).flat().filter((item, index, self) => index === self.findIndex((t) => t.id === item.id));
|
|
1782
|
+
for (let subPlayer of subscribedPlayersInGuild) {
|
|
1783
|
+
let player = players.find((player2) => subPlayer.steamId == player2.steamAccount.id);
|
|
1784
|
+
if (!player)
|
|
1785
|
+
continue;
|
|
1786
|
+
let guildMember;
|
|
1787
|
+
try {
|
|
1788
|
+
guildMember = await ctx.bots.find((bot) => bot.platform == subPlayer.platform)?.getGuildMember(subPlayer.guildId, subPlayer.userId);
|
|
1789
|
+
} catch (error) {
|
|
1790
|
+
ctx.logger.error("获取群组信息失败。" + error);
|
|
1791
|
+
}
|
|
1792
|
+
subPlayer.name = subPlayer.nickName || (guildMember?.nick ?? players.find((player2) => player2.steamAccount.id == subPlayer.steamId)?.steamAccount.name);
|
|
1793
|
+
player.winCount = player.matches.filter((match) => match.didRadiantWin == match.players.find((innerPlayer) => innerPlayer.steamAccount.id == player.steamAccount.id).isRadiant).length;
|
|
1794
|
+
player.loseCount = player.matches.length - player.winCount;
|
|
1795
|
+
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);
|
|
1796
|
+
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);
|
|
1797
|
+
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);
|
|
1798
|
+
player.avgKDA = roundToDecimalPlaces((player.avgKills + player.avgAssists) / (player.avgDeaths || 1), 2);
|
|
1799
|
+
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);
|
|
1800
|
+
subPlayer = Object.assign(subPlayer, player);
|
|
1801
|
+
}
|
|
1802
|
+
for (let guild of subscribedGuilds) {
|
|
1803
|
+
const currentsubscribedPlayers = subscribedPlayersInGuild.filter((player) => player.platform == guild.platform && player.guildId == guild.guildId && player.matches?.length);
|
|
1804
|
+
if (currentsubscribedPlayers.length) {
|
|
1805
|
+
const currentsubscribedPlayersIds = currentsubscribedPlayers.map((player) => player.steamId);
|
|
1806
|
+
const combinationsMap = /* @__PURE__ */ new Map();
|
|
1807
|
+
matches.forEach((match) => {
|
|
1808
|
+
const sortedPlayerIds = match.players.map((player) => player.steamAccount.id).filter((id) => currentsubscribedPlayersIds.includes(id)).sort((a, b) => a - b);
|
|
1809
|
+
const key = sortedPlayerIds.join(",");
|
|
1810
|
+
if (!combinationsMap.has(key)) {
|
|
1811
|
+
const players2 = currentsubscribedPlayers.filter((subPlayer) => sortedPlayerIds.includes(subPlayer.steamId));
|
|
1812
|
+
if (players2.length > 0) {
|
|
1813
|
+
const name2 = players2.map((subPlayer) => subPlayer.name).join("/");
|
|
1814
|
+
combinationsMap.set(key, {
|
|
1815
|
+
players: players2,
|
|
1816
|
+
name: name2,
|
|
1817
|
+
winCount: match.didRadiantWin == match.players.find((innerPlayer) => innerPlayer.steamAccount.id == players2[0].steamId).isRadiant ? 1 : 0,
|
|
1818
|
+
matches: [match]
|
|
1819
|
+
});
|
|
1820
|
+
}
|
|
1821
|
+
} else {
|
|
1822
|
+
const combi = combinationsMap.get(key);
|
|
1823
|
+
combi.matches.push(match);
|
|
1824
|
+
combi.winCount += match.didRadiantWin == match.players.find((innerPlayer) => innerPlayer.steamAccount.id == combi.players[0].steamId).isRadiant ? 1 : 0;
|
|
1825
|
+
}
|
|
1826
|
+
});
|
|
1827
|
+
const combinations = Array.from(combinationsMap.values());
|
|
1828
|
+
try {
|
|
1829
|
+
await ctx.broadcast(
|
|
1830
|
+
[`${guild.platform}:${guild.guildId}`],
|
|
1831
|
+
`${title}:
|
|
1832
|
+
${currentsubscribedPlayers.map(
|
|
1833
|
+
(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}`
|
|
1834
|
+
).join("\n")}
|
|
1835
|
+
${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")
|
|
1836
|
+
);
|
|
1837
|
+
ctx.logger.info(`发布日报于${guild.platform}:${guild.guildId}`);
|
|
1838
|
+
} catch (error) {
|
|
1839
|
+
ctx.logger.error(error);
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
__name(report, "report");
|
|
1821
1845
|
ctx.on("dispose", async () => {
|
|
1822
1846
|
});
|
|
1823
1847
|
}
|
|
@@ -1844,9 +1868,8 @@ function genImageHTML(data, template, type) {
|
|
|
1844
1868
|
return result;
|
|
1845
1869
|
}
|
|
1846
1870
|
__name(genImageHTML, "genImageHTML");
|
|
1847
|
-
function simpleHashToSeed(
|
|
1848
|
-
const
|
|
1849
|
-
const encoded = btoa(input);
|
|
1871
|
+
function simpleHashToSeed(inputString) {
|
|
1872
|
+
const encoded = btoa(inputString);
|
|
1850
1873
|
let total = 0;
|
|
1851
1874
|
for (let i = 0; i < encoded.length; i++) {
|
|
1852
1875
|
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.
|
|
4
|
+
"version": "1.2.9-pre",
|
|
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.
|
|
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
|
### 指令
|
package/template/hero/hero_1.ejs
CHANGED
|
@@ -204,7 +204,8 @@
|
|
|
204
204
|
gap: 10px; /* 可选:设置项目之间的间距 */
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
.facet
|
|
207
|
+
.facets > .facet ,
|
|
208
|
+
.skill > .facet {
|
|
208
209
|
flex: 1 1 calc(50% - 10px); /* 每行两个项目 */
|
|
209
210
|
box-sizing: border-box; /* 包含padding和border在宽度和高度的计算中 */
|
|
210
211
|
background-color: #181f24;
|
|
@@ -212,10 +213,11 @@
|
|
|
212
213
|
border: 1px solid #2b2f33;
|
|
213
214
|
}
|
|
214
215
|
|
|
215
|
-
.facet:nth-child(odd):last-child {
|
|
216
|
+
.facets > .facet:nth-child(odd):last-child {
|
|
216
217
|
flex-basis: 100%; /* 最后一个奇数项目占据整行 */
|
|
217
218
|
}
|
|
218
|
-
.facet > .name_back
|
|
219
|
+
.facets > .facet > .name_back ,
|
|
220
|
+
.skill > .facet > .name_back {
|
|
219
221
|
position: absolute;
|
|
220
222
|
height: 50px;
|
|
221
223
|
width: 100%;
|
|
@@ -343,6 +345,8 @@
|
|
|
343
345
|
background-color: #1f272b;
|
|
344
346
|
padding: 8px;
|
|
345
347
|
font-weight: 100;
|
|
348
|
+
height: auto;
|
|
349
|
+
width: auto;
|
|
346
350
|
}
|
|
347
351
|
.skill > .title > .name {
|
|
348
352
|
font-family: "KaiTi", "楷体", "楷体_GB2312", "STKaiti", serif;
|
|
@@ -357,6 +361,10 @@
|
|
|
357
361
|
background-color: #5b93d1;
|
|
358
362
|
}
|
|
359
363
|
|
|
364
|
+
.skill > .title.name_back > img{
|
|
365
|
+
width: 16px;
|
|
366
|
+
}
|
|
367
|
+
|
|
360
368
|
.skill img.scepter,
|
|
361
369
|
.skill img.shard {
|
|
362
370
|
position: absolute;
|
|
@@ -683,10 +691,11 @@
|
|
|
683
691
|
`).join("")}
|
|
684
692
|
</div>
|
|
685
693
|
<div class="skills">
|
|
686
|
-
${hero.abilities//.filter((item) => dotaconstants.abilities[item.name]
|
|
694
|
+
${hero.abilities//.filter((item) => dotaconstants.abilities[item.name]?.behavior != "Hidden")
|
|
687
695
|
.map((item) => `
|
|
688
|
-
<div class="skill" data-innate="${item.ability_is_innate&&!item.ability_is_facet}">
|
|
689
|
-
<p class="title">
|
|
696
|
+
<div class="skill${item.facet?' facet':''}" data-innate="${item.ability_is_innate&&!item.ability_is_facet}">
|
|
697
|
+
<p class="title${item.facet?(' name_back type_' + item.facet?.color):''}">
|
|
698
|
+
${item.facet?`<img src="${utils.getImageUrl(item.facet?.icon, ImageType.IconsFacets)}">`:""}
|
|
690
699
|
<span class="name">${item.name_loc}</span>
|
|
691
700
|
${item.ability_is_innate&&!item.ability_is_facet?`<span class="is_innate">先天技能</span>`:""}
|
|
692
701
|
${item.ability_is_granted_by_scepter ?`<img src="${utils.getImageUrl("scepter")}" class="scepter">`:""}
|
|
@@ -695,30 +704,29 @@
|
|
|
695
704
|
<div class="img_stats">
|
|
696
705
|
<img src="${utils.getImageUrl(item.name, ImageType.Abilities)}" onerror="this.onerror=null; this.src='${utils.getImageUrl(`innate_icon`,ImageType.Icons)}';"/>
|
|
697
706
|
<div class="stats">
|
|
698
|
-
<p class="behavior">技能:${
|
|
707
|
+
<p class="behavior">技能:${[].concat(dotaconstants.abilities[item.name]?.behavior)
|
|
699
708
|
.filter((beh) => beh !== "Hidden" || !(item.ability_is_granted_by_shard || item.ability_is_granted_by_scepter))
|
|
700
|
-
.map((beh) => d2a.behavior[beh])
|
|
701
|
-
|
|
702
|
-
${dotaconstants.abilities[item.name]
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
${
|
|
710
|
-
|
|
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
|
-
: ""}
|
|
709
|
+
.map((beh) => d2a.behavior[beh]).join("/")}
|
|
710
|
+
</p>
|
|
711
|
+
${dotaconstants.abilities[item.name]?.target_team ?
|
|
712
|
+
`<p class="target_team">影响:${[].concat(dotaconstants.abilities[item.name]?.target_team)
|
|
713
|
+
.map((tt) => d2a.target_team[tt]).join("/")}
|
|
714
|
+
</p>` : ""}
|
|
715
|
+
${!Array.isArray(dotaconstants.abilities[item.name]?.dmg_type) && dotaconstants.abilities[item.name]?.dmg_type ?
|
|
716
|
+
`<p class="dmg_type ${dotaconstants.abilities[item.name]?.dmg_type}">伤害类型:</p>` : ""}
|
|
717
|
+
${dotaconstants.abilities[item.name]?.dispellable ?
|
|
718
|
+
`<p class="dispellable ${dotaconstants.abilities[item.name]?.dispellable == "Strong Dispels Only" ? "Strong" : dotaconstants.abilities[item.name]?.dispellable}">能否驱散:</p>` : ""}
|
|
719
|
+
${!Array.isArray(dotaconstants.abilities[item.name]?.bkbpierce) && dotaconstants.abilities[item.name]?.bkbpierce ?
|
|
720
|
+
`<p class="bkbpierce">无视减益免疫: ${dotaconstants.abilities[item.name]?.bkbpierce == "Yes" ? "是" : "否"}</p>` : ""}
|
|
718
721
|
</div>
|
|
719
722
|
</div>
|
|
720
723
|
<p class="description">${item.desc_loc}</p>
|
|
721
|
-
${item.facets_loc
|
|
724
|
+
${item.facets_loc
|
|
725
|
+
.reduce((acc, facet_loc, index) => {
|
|
726
|
+
index = index + (hero.facets.length - item.facets_loc.length);
|
|
727
|
+
if (index >= 0) acc.push(facet_loc);
|
|
728
|
+
return acc;},[])
|
|
729
|
+
.map((facet_loc,index)=>(facet_loc!=""?`
|
|
722
730
|
<div class="facet">
|
|
723
731
|
<div class="name_back type_${hero.facets[index].color}"></div>
|
|
724
732
|
<div class="name_line type_${hero.facets[index].color}"></div>
|
|
@@ -733,12 +741,12 @@
|
|
|
733
741
|
</div>
|
|
734
742
|
</div>
|
|
735
743
|
`:"")).join("")}
|
|
736
|
-
${item.ability_has_scepter&&!item.ability_is_granted_by_scepter
|
|
744
|
+
${item.ability_has_scepter&&!item.ability_is_granted_by_scepter&&item.scepter_loc
|
|
737
745
|
? `<p class="aghanim_description">
|
|
738
746
|
<span class="title"><img src="${utils.getImageUrl("scepter")}"class="scepter">阿哈利姆神杖</span>
|
|
739
747
|
<span class="desc">${item.scepter_loc}</span>
|
|
740
748
|
</p>` : ""}
|
|
741
|
-
${item.ability_has_shard&&!item.ability_is_granted_by_shard
|
|
749
|
+
${item.ability_has_shard&&!item.ability_is_granted_by_shard&&item.shard_loc
|
|
742
750
|
? `<p class="aghanim_description">
|
|
743
751
|
<span class="title"><img src="${utils.getImageUrl("shard")}"class="shard">阿哈利姆魔晶</span>
|
|
744
752
|
<span class="desc">${item.shard_loc}</span>
|
|
@@ -766,9 +774,9 @@
|
|
|
766
774
|
`<span class="alternative shard">
|
|
767
775
|
<img src="${utils.getImageUrl("shard")}"/>${sv.values_shard.map(value => (value > 0 ? '<span class="plus">+</span>' : "") + value + (sv.is_percentage ? "%" : "")).join(" / ")}
|
|
768
776
|
</span>` : "" }
|
|
769
|
-
${sv.facet_bonus.name ?
|
|
777
|
+
${sv.facet_bonus.name && hero.facets.some(facet=>facet.name==sv.facet_bonus.name) ?
|
|
770
778
|
`<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 =>
|
|
779
|
+
<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
780
|
</span>` : "" }
|
|
773
781
|
${sv.bonuses.length ?
|
|
774
782
|
`<span class="alternative talent">
|
|
@@ -244,9 +244,9 @@
|
|
|
244
244
|
.player > .rank > p {
|
|
245
245
|
position: absolute;
|
|
246
246
|
width: 100%;
|
|
247
|
-
bottom:
|
|
247
|
+
bottom: 1.5px;
|
|
248
248
|
text-align: center;
|
|
249
|
-
font-size:
|
|
249
|
+
font-size: 8px;
|
|
250
250
|
color: #fff;
|
|
251
251
|
text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000; /* 文字描边 */
|
|
252
252
|
}
|