@sjtdev/koishi-plugin-dota2tracker 1.5.0-pre.3 → 1.5.0-pre.4

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
@@ -610,7 +610,7 @@ var require_zh_CN_command = __commonJS({
610
610
  // src/locales/zh-CN.template.yml
611
611
  var require_zh_CN_template = __commonJS({
612
612
  "src/locales/zh-CN.template.yml"(exports2, module2) {
613
- module2.exports = { dota2tracker: { template: { radiant: "天辉", dire: "夜魇", won: "获胜", lost: "失败", match_id_: "比赛编号:{0}", game_mode_: "模式:{0}", start_time_: "起始时间:{0}", end_time_: "结束时间:{0}", pick_order: "第{0}手", random: "随机", hero_damage_: "英雄伤害:{0}", building_damage_: "建筑伤害:{0}", damage_received_: "受到伤害:{0}", lasthit_: "补刀:{0}", deny_: "反补:{0}", "lh/dn_": "补刀:{0}/{1}", GPM: "GPM", XPM: "XPM", heal_: "治疗量:{0}", crowd_control_duration_: "控制时间:{0}", "GPM/XPM_": "GPM/XPM:{0}", lane: "对线", lane_: "对线:", lane_advantage: "对线优势", lane_disadvantage: "对线劣势", lane_stomp: "对线碾压", lane_stomped: "对线被碾", lane_tie: "对线平手", lane_jungle: "野区霸主", analysis_successful: "录像分析成功", analysis_incomplete: "分析结果不完整", kill: "击杀", kill_contribution_: "参战率:{0}", position: "位置", position_: "位置:", position_1: "优势路", position_2: "中路", position_3: "烈士路", position_4: "采灵芝", position_5: "工具人", position_undefined: "?", total_damage: "总伤害", total_gold: "总经济", total_experience: "总经验", radiant_won: "天辉获胜", dire_won: "夜魇获胜", duration_: "持续时间:{0}", region_: "地区:{0}", match_count_: "场次:", last25matches_: "最近25场:", winrate_: "胜率:", imp_: "表现:", lane_advantage_rate_: "线优率:", top10_: "全期场次前十的英雄:", hero: "英雄", all_matches_: "全期场次:", match_count: "场次", winrate: "胜率", imp: "表现", win_count: "胜场", lose_count: "败场", recently_heroes: "近期使用场次大于1的英雄:", recently_positions: "近25场各个位置的表现:", winning_streak: "连胜", losing_streak: "连败", id: "ID", mode: "模式", kda_kc: "KDA(参战率)", time: "时间", duration: "时长", rank: "段位", un_parsed: "(未解析)", combined_win_loss_summary: "组合胜负情况:", yesterdays_summary: "昨日总结", last_weeks_summary: "上周总结", report_won: "胜", report_lost: "负", report_winrate: "胜率", anonymous_player_1: "数据未公开", anonymous_player_2: "背景仅供展示目的,不属于{player}的数据。", rank_fun_up_message: "热烈祝贺群友 {avatar}{name} 在天梯中再获进步,<br/>由 {prev} 升为 {curr},再接再厉,再创辉煌!", rank_fun_down_message: "{avatar}<br/>寄", titles: { MVP: "MVP-#FFA500", Soul: "魂-#66CCFF", Rich: "富-#FFD700", Wise: "睿-#8888FF", Controller: "控-#FF00FF", Nuker: "爆-#CC0088", Breaker: "破-#DD0000", Ghost: "鬼-#CCCCCC", Assister: "助-#006400", Demolisher: "拆-#FEDCBA", Healer: "奶-#00FF00", Tank: "耐-#84A1C7", Idle: "摸-#DDDDDD" }, situation: "局势", networth: "财产总和", experience: "经验" } } };
613
+ module2.exports = { dota2tracker: { template: { radiant: "天辉", dire: "夜魇", won: "获胜", lost: "失败", match_id_: "比赛编号:{0}", game_mode_: "模式:{0}", start_time_: "起始时间:{0}", end_time_: "结束时间:{0}", pick_order: "第{0}手", random: "随机", hero_damage_: "英雄伤害:{0}", building_damage_: "建筑伤害:{0}", damage_received_: "受到伤害:{0}", lasthit_: "补刀:{0}", deny_: "反补:{0}", "lh/dn_": "补刀:{0}/{1}", GPM: "GPM", XPM: "XPM", heal_: "治疗量:{0}", crowd_control_duration_: "控制时间:{0}", "GPM/XPM_": "GPM/XPM:{0}", lane: "对线", lane_: "对线:", lane_advantage: "对线优势", lane_disadvantage: "对线劣势", lane_stomp: "对线碾压", lane_stomped: "对线被碾", lane_tie: "对线平手", lane_jungle: "野区霸主", analysis_successful: "录像分析成功", analysis_incomplete: "分析结果不完整", kill: "击杀", kill_contribution_: "参战率:{0}", position: "位置", position_: "位置:", position_1: "优势路", position_2: "中路", position_3: "烈士路", position_4: "采灵芝", position_5: "工具人", position_undefined: "?", total_damage: "总伤害", total_gold: "总经济", total_experience: "总经验", radiant_won: "天辉获胜", dire_won: "夜魇获胜", duration_: "持续时间:{0}", region_: "地区:{0}", match_count_: "场次:", last25matches_: "最近25场:", winrate_: "胜率:", imp_: "表现:", lane_advantage_rate_: "线优率:", top10_: "全期场次前十的英雄:", hero: "英雄", all_matches_: "全期场次:", match_count: "场次", winrate: "胜率", imp: "表现", win_count: "胜场", lose_count: "败场", recently_heroes: "近期使用场次大于1的英雄:", recently_positions: "近25场各个位置的表现:", winning_streak: "连胜", losing_streak: "连败", id: "ID", mode: "模式", kda_kc: "KDA(参战率)", time: "时间", duration: "时长", rank: "段位", un_parsed: "(未解析)", combined_win_loss_summary: "组合胜负情况:", yesterdays_summary: "昨日总结", last_weeks_summary: "上周总结", report_won: "胜", report_lost: "负", report_winrate: "胜率", anonymous_player_1: "数据未公开", anonymous_player_2: "背景仅供展示目的,不属于{player}的数据。", rank_fun_up_message: "热烈祝贺群友 {avatar}{name} 在天梯中再获进步,<br/>由 {prev} 升为 {curr},再接再厉,再创辉煌!", rank_fun_down_message: "{avatar}<br/>寄", titles: { MVP: "MVP-#FFA500", Soul: "魂-#66CCFF", Rich: "富-#FFD700", Wise: "睿-#8888FF", Controller: "控-#FF00FF", Nuker: "爆-#CC0088", Breaker: "破-#DD0000", Ghost: "鬼-#CCCCCC", Assister: "助-#006400", Demolisher: "拆-#FEDCBA", Healer: "奶-#00FF00", Tank: "耐-#84A1C7", Idle: "摸-#DDDDDD" }, situation: "局势", networth: "财产总和", experience: "经验", OUTCOME_MAP: { RADIANT_VICTORY: "天辉优势", RADIANT_STOMP: "天辉碾压", DIRE_VICTORY: "夜魇优势", DIRE_STOMP: "夜魇碾压", TIE: "势均力敌" }, lane_top: "上路", lane_mid: "中路", lane_bottom: "下路" } } };
614
614
  }
615
615
  });
616
616
 
@@ -1716,7 +1716,7 @@ async function apply(ctx, config) {
1716
1716
  }).then((response) => ctx.logger.info($t(GlobalLanguageTag, `dota2tracker.logger.parse_request_${response.stratz.matchRetry ? "sent" : "failed"}`, { matchId: matchQuery.match.id })));
1717
1717
  }
1718
1718
  } catch (error) {
1719
- session.send(session.text(".query_failed"));
1719
+ session.send(session.text("commands.dota2tracker.query-match.messages.query_failed"));
1720
1720
  ctx.logger.error(error);
1721
1721
  }
1722
1722
  });
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.5.0-pre.3",
4
+ "version": "1.5.0-pre.4",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
@@ -70,6 +70,10 @@ query MatchInfo($matchId: Long!) {
70
70
  stackCount
71
71
  }
72
72
  networthPerMinute
73
+ experiencePerMinute
74
+ killEvents{time}
75
+ deathEvents{time}
76
+ assistEvents{time}
73
77
  }
74
78
  heroDamage
75
79
  towerDamage
@@ -89,11 +89,11 @@ nav > .rank > img {
89
89
  flex-direction: column;
90
90
  }
91
91
 
92
- .panel {
92
+ .players .panel {
93
93
  padding: 0 10px;
94
94
  }
95
95
 
96
- .panel {
96
+ .players .panel {
97
97
  padding-top: 4px;
98
98
  height: 40px;
99
99
  border-top: 3px solid #fff;
@@ -101,21 +101,21 @@ nav > .rank > img {
101
101
  display: grid;
102
102
  grid-template-columns: 32px 56px 378px repeat(4,1fr);
103
103
  }
104
- .panel.radiant {
104
+ .players .panel.radiant {
105
105
  border-color: #3c9028;
106
106
  }
107
- .panel.dire {
107
+ .players .panel.dire {
108
108
  border-color: #9c3628;
109
109
  }
110
- .panel p{
110
+ .players .panel p{
111
111
  line-height: 16px;
112
112
  margin-left: 8px;
113
113
  }
114
- .panel .win{
114
+ .players .panel .win{
115
115
  font-size: 20px;
116
116
  line-height: 32px;
117
117
  }
118
- .panel .data{
118
+ .players .panel .data{
119
119
  color: #aaa;
120
120
  }
121
121
 
@@ -0,0 +1,131 @@
1
+ html {
2
+ overflow: visible;
3
+ }
4
+
5
+ body {
6
+ /* width: 1200px; */
7
+ display: flex;
8
+ flex-direction: column;
9
+ overflow: visible;
10
+ }
11
+
12
+ #regular {
13
+ width: 800px;
14
+ }
15
+
16
+ #extra {
17
+ display: flex;
18
+ width: 800px;
19
+ flex-direction: row;
20
+ }
21
+
22
+ #charts {
23
+ display: flex;
24
+ flex-direction: column;
25
+ width: 50%;
26
+ }
27
+
28
+ #charts > svg {
29
+ font-size: 10px;
30
+ }
31
+
32
+ #charts > .title {
33
+ width: 100%;
34
+ display: flex;
35
+ justify-content: space-between;
36
+ }
37
+
38
+ #charts > .title > .logo {
39
+ display: flex;
40
+ padding: 0 25px;
41
+ }
42
+
43
+ #charts > .title > .logo > img {
44
+ width: 20px;
45
+ }
46
+
47
+ #extra > .tip {
48
+ width: 100%;
49
+ line-height: 4;
50
+ text-align: center;
51
+ color: #ccc;
52
+ }
53
+
54
+ .lane_outcome {
55
+ display: flex;
56
+ flex-direction: column;
57
+ width: 386px;
58
+ margin-right: 14px;
59
+ }
60
+
61
+ .lane_outcome > .title {
62
+ width: 100%;
63
+ text-align: center;
64
+ padding: 0;
65
+ margin: 0;
66
+ line-height: 50px;
67
+ }
68
+
69
+ .lane_outcome .panel {
70
+ display: flex;
71
+ flex-direction: column;
72
+ height: 100%;
73
+ justify-content: space-evenly;
74
+ }
75
+
76
+ .lane_outcome .panel .lane {
77
+ display: flex;
78
+ flex-direction: column;
79
+ width: 100%;
80
+ }
81
+
82
+ .lane_outcome .panel .lane > .title {
83
+ width: 100%;
84
+ text-align: center;
85
+ height: 32px;
86
+ }
87
+
88
+ .lane_outcome .panel .lane > .title > p:nth-child(1){
89
+ font-size: 12px;
90
+ }
91
+
92
+ .lane_outcome .panel .lane > .title > p:nth-child(2){
93
+ font-size: 14px;
94
+ }
95
+
96
+ .lane_outcome .panel .details {
97
+ display: grid;
98
+ grid-template-columns: 24px 32px 44px auto 44px 32px 24px;
99
+ grid-template-rows: 1fr;
100
+ gap: 1px;
101
+ font-size: 12px;
102
+ height: 32px;
103
+ line-height: 32px;
104
+ align-items: center;
105
+ }
106
+
107
+ .lane_outcome .panel .lane img.hero{
108
+ width: 24px;
109
+ height: 24px;
110
+ }
111
+
112
+ .lane_outcome .panel .lane .kda {
113
+ height: 100%;
114
+ line-height: 32px;
115
+ text-align: center;
116
+ font-size: 10px;
117
+ }
118
+
119
+ .lane_outcome .panel .lane .graph {
120
+ height: 32px;
121
+ display: flex;
122
+ align-items: center;
123
+ }
124
+
125
+ .graph svg {
126
+ shape-rendering: crispEdges;
127
+ }
128
+ .graph text {
129
+ dominant-baseline: middle; /* 垂直居中 */
130
+ white-space: nowrap; /* 防止换行 */
131
+ }
@@ -0,0 +1,143 @@
1
+ <% const match = data; %>
2
+ <%
3
+ function generateLevelCircle(xp) {
4
+ const XP_LEVELS = dotaconstants.xp_level;
5
+ let level = XP_LEVELS.findIndex(l => xp < l) - 1;
6
+ if (level < 0) level = XP_LEVELS.length - 1;
7
+ const currentXP = xp - XP_LEVELS[level];
8
+ const nextXP = XP_LEVELS[level+1] - XP_LEVELS[level];
9
+ const percentage = currentXP / nextXP * 100;
10
+ // return { level, progress: currentXP / nextXP };
11
+ const config = {
12
+ radius: 12,
13
+ strokeColor: '#ffd700',
14
+ bgColor: '#eee',
15
+ strokeWidth: 2,
16
+ svgSize: 32,
17
+ showBackground: true,
18
+ // 新增文字配置
19
+ showText: true, // 是否显示文字
20
+ textColor: '#333', // 文字颜色
21
+ fontSize: 12 * 0.8,
22
+ fontWeight: 'bold', // 字体粗细
23
+ // ...options
24
+ };
25
+
26
+ const center = config.svgSize / 2;
27
+ const circumference = 2 * Math.PI * config.radius;
28
+ const progress = circumference * (percentage / 100);
29
+ const gap = circumference - progress;
30
+
31
+ return `
32
+ <svg width="${config.svgSize}" height="${config.svgSize}" viewBox="0 0 ${config.svgSize} ${config.svgSize}">
33
+ ${config.showBackground ? `
34
+ <circle cx="${center}" cy="${center}" r="${config.radius}"
35
+ fill="none" stroke="${config.bgColor}"
36
+ stroke-width="${config.strokeWidth}"/>
37
+ ` : ''}
38
+
39
+ <circle cx="${center}" cy="${center}" r="${config.radius}"
40
+ fill="none" stroke="${config.strokeColor}"
41
+ stroke-width="${config.strokeWidth}"
42
+ stroke-dasharray="${progress} ${gap}"
43
+ stroke-linecap="round"
44
+ transform="rotate(-90 ${center} ${center})"/>
45
+
46
+ ${config.showText ? `
47
+ <!-- 居中文字 -->
48
+ <text x="${center}" y="${center}"
49
+ text-anchor="middle" // 水平居中
50
+ dominant-baseline="middle" // 垂直居中
51
+ fill="${config.textColor}"
52
+ font-size="${config.fontSize}px"
53
+ font-weight="${config.fontWeight}"
54
+ font-family="Arial, sans-serif">
55
+ ${level}
56
+ </text>
57
+ ` : ''}
58
+ </svg>`;
59
+ }
60
+
61
+ function generateDetails(rivals) {
62
+ const svgHeight = 32;
63
+
64
+ rivals.sort((a, b) => b.isRadiant - a.isRadiant);
65
+ const gold = { radiant: rivals[0].stats.networthPerMinute.at(Math.min(10, rivals[0].stats.networthPerMinute.length - 1)), dire: rivals[1].stats.networthPerMinute.at(Math.min(10, rivals[1].stats.networthPerMinute.length - 1)) };
66
+ const kills = { radiant: rivals[0].stats.killEvents?.filter(e => e.time <= 600).length || 0, dire: rivals[1].stats.killEvents?.filter(e => e.time <= 600).length || 0 }
67
+ const deaths = { radiant: rivals[0].stats.deathEvents?.filter(e => e.time <= 600).length || 0, dire: rivals[1].stats.deathEvents?.filter(e => e.time <= 600).length || 0 }
68
+ const assists = { radiant: rivals[0].stats.assistEvents?.filter(e => e.time <= 600).length || 0, dire: rivals[1].stats.assistEvents?.filter(e => e.time <= 600).length || 0 }
69
+ const goldSplit = gold.radiant / (gold.radiant + gold.dire);
70
+ return `
71
+ <div class="details">
72
+ <img src="${utils.getImageUrl(rivals[0].hero.shortName, ImageType.HeroIcons)}" class="hero radiant" />
73
+ ${generateLevelCircle(rivals[0].stats.experiencePerMinute.slice(0, Math.min(11, rivals[0].stats.experiencePerMinute.length - 1)).reduce((a, b) => a + b, 0))}
74
+ <p class="kda">${kills.radiant}/${deaths.radiant}/${assists.radiant}</p>
75
+ <div class="graph">
76
+ <svg width="100%" height="${svgHeight}" viewBox="0 0 180 ${svgHeight}">
77
+
78
+ <!-- 经济差 -->
79
+ <g transform="translate(0,0)">
80
+ <rect y="9" width="180" height="14" fill="#9c3628"/>
81
+ <rect y="9" width="${180 * goldSplit}" height="14" fill="#3c9028"/>
82
+ <line x1="${180 * goldSplit}" y1="9" x2="${180 * goldSplit}" y2="25"
83
+ stroke="#fff" stroke-width="1"/>
84
+
85
+ <!-- 左侧数值(天辉) -->
86
+ <text x="5" y="50%"
87
+ dominant-baseline="middle"
88
+ fill="#fff"
89
+ font-size="10"
90
+ text-anchor="start">${gold.radiant}</text>
91
+
92
+ <!-- 右侧数值(夜魇) -->
93
+ <text x="175" y="50%"
94
+ dominant-baseline="middle"
95
+ fill="#fff"
96
+ font-size="10"
97
+ text-anchor="end">${gold.dire}</text>
98
+
99
+ <!-- 差值(上方) -->
100
+ <text x="${180 * goldSplit}"
101
+ y="5px"
102
+ dominant-baseline="middle"
103
+ fill="${gold.radiant > gold.dire ? "#3c9028" : "#9c3628"}"
104
+ font-size="10"
105
+ text-anchor="middle">+${Math.abs(gold.dire - gold.radiant)}</text>
106
+ </g>
107
+ </svg>
108
+ </div>
109
+ <p class="kda">${kills.dire}/${deaths.dire}/${assists.dire}</p>
110
+ ${generateLevelCircle(rivals[1].stats.experiencePerMinute.slice(0, Math.min(11, rivals[1].stats.experiencePerMinute.length - 1)).reduce((a, b) => a + b, 0))}
111
+ <img src="${utils.getImageUrl(rivals[1].hero.shortName, ImageType.HeroIcons)}" class="hero dire" />
112
+ </div>
113
+ `
114
+ }
115
+ %>
116
+ <div class="lane_outcome">
117
+ <h4 class="title">对线</h4>
118
+ <div class="panel">
119
+ <div class="lane">
120
+ <div class="title">
121
+ <p><%= $t("dota2tracker.template.lane_top") %></p>
122
+ <p class="<%= match.topLaneOutcome.split("_")[0].toLowerCase() %>"><%= $t("dota2tracker.template.OUTCOME_MAP."+match.topLaneOutcome) %></p>
123
+ </div>
124
+ <%- generateDetails(match.players.filter(p => (p.position.slice(-1) == 3 && p.isRadiant) || (p.position.slice(-1) == 1 && !p.isRadiant))) %>
125
+ <%- generateDetails(match.players.filter(p => (p.position.slice(-1) == 4 && p.isRadiant) || (p.position.slice(-1) == 5 && !p.isRadiant))) %>
126
+ </div>
127
+ <div class="lane">
128
+ <div class="title">
129
+ <p><%= $t("dota2tracker.template.lane_mid") %></p>
130
+ <p class="<%= match.midLaneOutcome.split("_")[0].toLowerCase() %>"><%= $t("dota2tracker.template.OUTCOME_MAP."+match.midLaneOutcome) %></p>
131
+ </div>
132
+ <%- generateDetails(match.players.filter(p => p.position.slice(-1) == 2)) %>
133
+ </div>
134
+ <div class="lane">
135
+ <div class="title">
136
+ <p><%= $t("dota2tracker.template.lane_bottom") %></p>
137
+ <p class="<%= match.bottomLaneOutcome.split("_")[0].toLowerCase() %>"><%= $t("dota2tracker.template.OUTCOME_MAP."+match.bottomLaneOutcome) %></p>
138
+ </div>
139
+ <%- generateDetails(match.players.filter(p => (p.position.slice(-1) == 1 && p.isRadiant) || (p.position.slice(-1) == 3 && !p.isRadiant))) %>
140
+ <%- generateDetails(match.players.filter(p => (p.position.slice(-1) == 5 && p.isRadiant) || (p.position.slice(-1) == 4 && !p.isRadiant))) %>
141
+ </div>
142
+ </div>
143
+ </div>
@@ -7,14 +7,15 @@
7
7
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css" />
8
8
  <%- "<style>" %>
9
9
  <%- include("./match_2/original.css") %>
10
- <%- include("./match_2/extra.css") %>
10
+ <%- include("./match_2+/extra.css") %>
11
11
  <%- "</style>" %>
12
12
  </head>
13
13
  <body>
14
14
  <section id="regular"><%- include('./match_2/original') %></section>
15
15
  <section id="extra">
16
16
  <% if (data.parsedDateTime && data.radiantNetworthLeads && data.radiantExperienceLeads) { %>
17
- <%- include('./match_2/charts') %>
17
+ <%- include('./match_2+/charts') %>
18
+ <%- include('./match_2+/lane_outcome') %>
18
19
  <% } else { %>
19
20
  <div class="tip">比赛未解析或无局势信息,无法展示更多数据。</div>
20
21
  <% } %>
@@ -1,51 +0,0 @@
1
- html {
2
- overflow: visible;
3
- }
4
-
5
- body {
6
- /* width: 1200px; */
7
- display: flex;
8
- flex-direction: column;
9
- overflow: visible;
10
- }
11
-
12
- #regular {
13
- width: 800px;
14
- }
15
-
16
- #extra {
17
- display: flex;
18
- width: 800px;
19
- }
20
-
21
- #charts {
22
- display: flex;
23
- flex-direction: row;
24
- width: 100%;
25
- }
26
-
27
- #charts > svg {
28
- font-size: 10px;
29
- }
30
-
31
- #charts > .title {
32
- width: 100%;
33
- display: flex;
34
- justify-content: space-between;
35
- }
36
-
37
- #charts > .title > .logo {
38
- display: flex;
39
- padding: 0 25px;
40
- }
41
-
42
- #charts > .title > .logo > img{
43
- width: 20px;
44
- }
45
-
46
- #extra > .tip {
47
- width: 100%;
48
- line-height: 4;
49
- text-align: center;
50
- color: #ccc;
51
- }
File without changes