@sjtdev/koishi-plugin-dota2tracker 1.3.1-beta.1 → 1.4.0

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
@@ -38,14 +38,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
38
38
  // src/locales/en-US.schema.yml
39
39
  var require_en_US_schema = __commonJS({
40
40
  "src/locales/en-US.schema.yml"(exports2, module2) {
41
- module2.exports = { _config: { base: { $desc: "Basic Settings", STRATZ_API_TOKEN: "Required. API TOKEN from stratz.com, available at https://stratz.com/api.", dataParsingTimeoutMinutes: "Time to wait for match data parsing (in minutes). If the data parsing time exceeds the waiting time, the report will be generated directly without waiting for the parsing to complete.", urlInMessageType: { $desc: "Include links in messages, <br/>please select the message type:", $inner: ["Include stratz match page link in match query and report messages", "Include stratz player page link in player information query messages", "Include Dota Encyclopedia hero page link in hero data query messages"] } }, rank: { rankBroadSwitch: "Rank change broadcast", rankBroadStar: "Star change broadcast", rankBroadLeader: "Leaderboard rank change broadcast", rankBroadFun: "Fun broadcast template" }, report: { $desc: "Summary Settings", dailyReportSwitch: "Daily Report Function", dailyReportHours: "Daily report time in hours", dailyReportShowCombi: "Show combinations in daily report", weeklyReportSwitch: "Weekly Report Function", weeklyReportDayHours: "Weekly report published on (day) at (hour)", weeklyReportShowCombi: "Show combinations in weekly report" }, template: { $desc: "Template Settings", template_match: "Template used to generate match information images, see https://github.com/sjtdev/koishi-plugin-dota2tracker/wiki for template display.", template_player: "Template used to generate player information images. (Currently only one template available)", template_hero: "Template used to generate hero information images. (Currently only one template available)" } } };
41
+ module2.exports = { _config: { base: { $desc: "Basic Settings", STRATZ_API_TOKEN: "Required. API TOKEN from stratz.com, available at https://stratz.com/api.", dataParsingTimeoutMinutes: "Time to wait for match data parsing (in minutes). If the data parsing time exceeds the waiting time, the report will be generated directly without waiting for the parsing to complete.", urlInMessageType: { $desc: "Include links in messages, <br/>please select the message type:", $inner: ["Include stratz match page link in match query and report messages", "Include stratz player page link in player information query messages", "Include Dota Encyclopedia hero page link in hero data query messages"] } }, rank: { rankBroadSwitch: "Rank change broadcast", rankBroadStar: "Star change broadcast", rankBroadLeader: "Leaderboard rank change broadcast", rankBroadFun: "Fun broadcast template" }, report: { $desc: "Summary Settings", dailyReportSwitch: "Daily Report Function", dailyReportHours: "Daily report time in hours", dailyReportShowCombi: "Show combinations in daily report", weeklyReportSwitch: "Weekly Report Function", weeklyReportDayHours: "Weekly report published on (day) at (hour)", weeklyReportShowCombi: "Show combinations in weekly report" }, template: { $desc: "Template Settings", template_match: "Template used to generate match information images, see https://github.com/sjtdev/koishi-plugin-dota2tracker/wiki for template display.", template_player: "Template used to generate player information images. (Currently only one template available)", template_hero: "Template used to generate hero information images. (Currently only one template available)", playerRankEstimate: "Estimate the rank of players without a rank in the player template <br>Estimated rank will be displayed as a gray image" } } };
42
42
  }
43
43
  });
44
44
 
45
45
  // src/locales/zh-CN.schema.yml
46
46
  var require_zh_CN_schema = __commonJS({
47
47
  "src/locales/zh-CN.schema.yml"(exports2, module2) {
48
- module2.exports = { _config: { base: { $desc: "基础设置", STRATZ_API_TOKEN: "※必须。stratz.com的API TOKEN,可在 https://stratz.com/api 获取。", dataParsingTimeoutMinutes: "等待比赛数据解析的时间(单位:分钟)。如果数据解析时间超过等待时间,将直接生成战报而不再等待解析完成。", urlInMessageType: { $desc: "在消息中附带链接,<br/>请选择消息类型:", $inner: ["在查询比赛与战报消息中附带stratz比赛页面链接", "在查询玩家信息消息中附带stratz玩家页面链接", "在查询英雄数据消息中附带刀塔百科对应英雄页面链接"] } }, rank: { rankBroadSwitch: "段位变动播报", rankBroadStar: "星级变动播报", rankBroadLeader: "冠绝名次变动播报", rankBroadFun: "整活播报模板" }, report: { $desc: "总结设置", dailyReportSwitch: "日报功能", dailyReportHours: "日报时间小时", dailyReportShowCombi: "日报是否显示组合", weeklyReportSwitch: "周报功能", weeklyReportDayHours: "周报发布于周(几)的(几)点", weeklyReportShowCombi: "周报是否显示组合" }, template: { $desc: "模板设置", template_match: "生成比赛信息图片使用的模板,见 https://github.com/sjtdev/koishi-plugin-dota2tracker/wiki 有模板展示。", template_player: "生成玩家信息图片使用的模板。(目前仅有一张模板)", template_hero: "生成英雄信息图片使用的模板。(目前仅有一张模板)" } } };
48
+ module2.exports = { _config: { base: { $desc: "基础设置", STRATZ_API_TOKEN: "※必须。stratz.com的API TOKEN,可在 https://stratz.com/api 获取。", dataParsingTimeoutMinutes: "等待比赛数据解析的时间(单位:分钟)。如果数据解析时间超过等待时间,将直接生成战报而不再等待解析完成。", urlInMessageType: { $desc: "在消息中附带链接,<br/>请选择消息类型:", $inner: ["在查询比赛与战报消息中附带stratz比赛页面链接", "在查询玩家信息消息中附带stratz玩家页面链接", "在查询英雄数据消息中附带刀塔百科对应英雄页面链接"] } }, rank: { rankBroadSwitch: "段位变动播报", rankBroadStar: "星级变动播报", rankBroadLeader: "冠绝名次变动播报", rankBroadFun: "整活播报模板" }, report: { $desc: "总结设置", dailyReportSwitch: "日报功能", dailyReportHours: "日报时间小时", dailyReportShowCombi: "日报是否显示组合", weeklyReportSwitch: "周报功能", weeklyReportDayHours: "周报发布于周(几)的(几)点", weeklyReportShowCombi: "周报是否显示组合" }, template: { $desc: "模板设置", template_match: "生成比赛信息图片使用的模板,见 https://github.com/sjtdev/koishi-plugin-dota2tracker/wiki 有模板展示。", template_player: "生成玩家信息图片使用的模板。(目前仅有一张模板)", template_hero: "生成英雄信息图片使用的模板。(目前仅有一张模板)", playerRankEstimate: "在player模板中对没有段位的玩家进行段位估算 <br>估算的段位将以灰色图片显示" } } };
49
49
  }
50
50
  });
51
51
 
@@ -347,7 +347,7 @@ var require_zh_CN_constants = __commonJS({
347
347
  ALL_PICK_RANKED: "全英雄选择",
348
348
  TURBO: "加速模式",
349
349
  MUTATION: "",
350
- UNKNOWN: ""
350
+ UNKNOWN: "未知"
351
351
  },
352
352
  lobby_types: {
353
353
  INVALID: "",
@@ -582,14 +582,14 @@ var require_zh_CN_constants = __commonJS({
582
582
  // src/locales/en-US.command.yml
583
583
  var require_en_US_command = __commonJS({
584
584
  "src/locales/en-US.command.yml"(exports2, module2) {
585
- module2.exports = { commands: { dota2tracker: { subscribe: { description: "After subscribing, players need to bind their Steam ID to this group.", usage: "After subscribing, players need to bind their Steam ID to this group. BOT will subscribe to the new game data of bound players in this group. After the STRATZ game analysis is completed, the game data will be generated into a picture battle report and published to this group.", messages: { subscribe_success: "Subscription successful.", subscribed: "This Channel has been subscribed, no need to subscribe again." } }, unsubscribe: { description: "Unsubscribe from this group.", messages: { unsubscribe_success: "Unsubscription successful.", not_subscribed: "This Channel has not been subscribed yet, so there is no need to unsubscribe." } }, bind: { description: "Bind your SteamID and optionally set a nickname.", usage: "Bind your SteamID to your account. If the group is subscribed, your new match data will be posted in the group in real-time.", examples: 'bind 123456789\nbind 123456789 John\nbind 123456789 "John Doe"', messages: { steam_id_invalid: "Invalid SteamID.", bind_success: "Binding successful,\nID: {userId}\nNickname: {nickName}\nSteamID: {steamId}", bind_failed: "Binding failed, {0}", reason_without_match: "Invalid SteamID or no matches found.", reason_fetch_failed: "Poor network conditions or other reasons prevented the verification of the SteamID. Please try again later.", already_binded: "You are already bound, no need to bind again.\nHere is your personal information:\nID: {userId}\nNickname: {nickName}\nSteamID: {steamId}", nick_name_too_long: "Nickname is too long, please limit it to 20 characters or less. (It can also be left blank)" } }, unbind: { description: "Unbind your personal information.", messages: { unbind_success: "Unbinding successful.", not_binded: "Not bound, no need to unbind." } }, rename: { description: "Change the nickname set during binding.", examples: 'rename John\nrename "John Doe"', messages: { rename_success: "Rename successful, now you are called {nick_name}.", empty_input: "Please enter your nickname.", not_binded: "Please bind first, you can set a nickname during binding.", nick_name_too_long: "Nickname is too long, please limit it to 20 characters." } }, "query-members": { description: "Query the players bound in this group.", messages: { no_members: "No players bound in this group.", query_failed: "Failed to query group members." } }, "query-match": { description: "Query the match data of the specified match ID and generate a picture.", options: { parse: "Whether to wait for match data parsing" }, examples: "query-match 1234567890\nquery-match 1234567890 -p\nquery-match 1234567890 --parse", messages: { empty_input: "Please enter the match ID.", match_id_invalid: "Invalid match ID.", querying_match: "Searching for match details, please wait...", query_failed: "Failed to get match data.", waiting_for_parse: "Match data has not been parsed yet, a parse request has been sent to the server. The battle report will be sent once parsing is complete or times out." } }, "query-recent-match": { description: "Query the most recent match data and generate a picture.", options: { parse: "Whether to wait for match data parsing" }, usage: "Query the most recent match data of the specified player and generate a picture.\nThe parameter can be the player's SteamID or the nickname of a player bound in this group. If no parameter is provided, it will try to query the SteamID of the command caller.", examples: "query-recent-match\nquery-recent-match 123456789\nquery-recent-match John\nquery-recent-match 123456789 -p\nquery-recent-match John --parse", messages: { not_binded: "By default, it tries to find your information from the bound SteamID players, but it seems you are not bound.\nPlease bind your SteamID in this group. (You can enter [-bind -h] for help)\nOr follow the command with the SteamID or nickname of the player you want to query.", steam_id_invalid: "Invalid SteamID and the player was not found in this group.", querying_match: "Searching for match details, please wait...", query_failed: "Failed to get the player's recent match.", not_in_group: "Command failed.\nCurrently not in a group chat, you must provide the specified player's SteamID." } }, "query-player": { description: "Query the player's personal information, optionally specify a hero.", options: { hero: "Query the player's usage of the specified hero (same as querying a hero, can use nickname or ID)" }, usage: "Query the personal information of the specified player and generate a picture, optionally specify a hero.\nThe parameter can be the player's SteamID or the nickname of a player bound in this group. If no parameter is provided, it will try to query the SteamID of the command caller.", examples: "query-player\nquery-player 123456789\nquery-player John\nquery-player John --hero Anti-Mage\nquery-player John -o Anti-Mage", messages: { not_binded: "By default, it tries to find your information from the bound SteamID players, but it seems you are not bound.\nPlease bind your SteamID in this group. (You can enter [bind -h] for help)\nOr follow the command with the SteamID or nickname of the player you want to query.", steam_id_invalid: "Invalid SteamID and the player was not found in this group.", querying_player: "Retrieving player data, please wait...", query_failed: "Failed to get player information.", not_in_group: "Command failed.\nCurrently not in a group chat, you must provide the specified player's SteamID." } }, "query-hero": { description: "Query hero skills/stats information.", options: { random: "Randomly select a hero." }, usage: "Query the hero's skill descriptions and various stats, generate a picture.\nThe parameter can be the hero's ID, name, or common nickname.", examples: "query-hero 15\nquery-hero Razor\nquery-hero -r", messages: { not_found: "Hero not found, please confirm and re-enter.", querying_hero: "Retrieving hero data, please wait...", query_failed: "Failed to get hero data.", empty_input: "Please enter a parameter." } } } } };
585
+ module2.exports = { commands: { dota2tracker: { subscribe: { description: "After subscribing, players need to bind their Steam ID to this group.", usage: "After subscribing, players need to bind their Steam ID to this group. BOT will subscribe to the new game data of bound players in this group. After the STRATZ game analysis is completed, the game data will be generated into a picture battle report and published to this group.", messages: { subscribe_success: "Subscription successful.", subscribed: "This Channel has been subscribed, no need to subscribe again." } }, unsubscribe: { description: "Unsubscribe from this group.", messages: { unsubscribe_success: "Unsubscription successful.", not_subscribed: "This Channel has not been subscribed yet, so there is no need to unsubscribe." } }, bind: { description: "Bind your SteamID and optionally set a nickname.", usage: "Bind your SteamID to your account. If the group is subscribed, your new match data will be posted in the group in real-time.", examples: 'bind 123456789\nbind 123456789 John\nbind 123456789 "John Doe"', messages: { steam_id_invalid: "Invalid SteamID.", bind_success: "Binding successful,\nID: {userId}\nNickname: {nickName}\nSteamID: {steamId}", bind_failed: "Binding failed, {0}", reason_without_match: "Invalid SteamID or no matches found.", reason_fetch_failed: "Poor network conditions or other reasons prevented the verification of the SteamID. Please try again later.", already_binded: "You are already bound, no need to bind again.\nHere is your personal information:\nID: {userId}\nNickname: {nickName}\nSteamID: {steamId}", nick_name_too_long: "Nickname is too long, please limit it to 20 characters or less. (It can also be left blank)", is_anonymous: 'Please note: Your Steam player data is not public, and you will not be able to use the main functions of the BOT, such as "battle report tracking," "query-recent-match commands," etc.\nIf you need to make data public, please set it to public in the DOTA2 game settings.' } }, unbind: { description: "Unbind your personal information.", messages: { unbind_success: "Unbinding successful.", not_binded: "Not bound, no need to unbind." } }, rename: { description: "Change the nickname set during binding.", examples: 'rename John\nrename "John Doe"', messages: { rename_success: "Rename successful, now you are called {nick_name}.", empty_input: "Please enter your nickname.", not_binded: "Please bind first, you can set a nickname during binding.", nick_name_too_long: "Nickname is too long, please limit it to 20 characters." } }, "query-members": { description: "Query the players bound in this group.", messages: { no_members: "No players bound in this group.", query_failed: "Failed to query group members." } }, "query-match": { description: "Query the match data of the specified match ID and generate a picture.", options: { parse: "Whether to wait for match data parsing" }, examples: "query-match 1234567890\nquery-match 1234567890 -p\nquery-match 1234567890 --parse", messages: { empty_input: "Please enter the match ID.", match_id_invalid: "Invalid match ID.", querying_match: "Searching for match details, please wait...", query_failed: "Failed to get match data.", waiting_for_parse: "Match data has not been parsed yet, a parse request has been sent to the server. The battle report will be sent once parsing is complete or times out." } }, "query-recent-match": { description: "Query the most recent match data and generate a picture.", options: { parse: "Whether to wait for match data parsing" }, usage: "Query the most recent match data of the specified player and generate a picture.\nThe parameter can be the player's SteamID or the nickname of a player bound in this group. If no parameter is provided, it will try to query the SteamID of the command caller.", examples: "query-recent-match\nquery-recent-match 123456789\nquery-recent-match John\nquery-recent-match 123456789 -p\nquery-recent-match John --parse", messages: { not_binded: "By default, it tries to find your information from the bound SteamID players, but it seems you are not bound.\nPlease bind your SteamID in this group. (You can enter [-bind -h] for help)\nOr follow the command with the SteamID or nickname of the player you want to query.", steam_id_invalid: "Invalid SteamID and the player was not found in this group.", querying_match: "Searching for match details, please wait...", query_failed: "Failed to get the player's recent match.", not_in_group: "Command failed.\nCurrently not in a group chat, you must provide the specified player's SteamID.", is_anonymous: "Your player data is not public, and recent match data cannot be obtained.\nIf you need to make data public, please set it to public in the DOTA2 game settings." } }, "query-player": { description: "Query the player's personal information, optionally specify a hero.", options: { hero: "Query the player's usage of the specified hero (same as querying a hero, can use nickname or ID)" }, usage: "Query the personal information of the specified player and generate a picture, optionally specify a hero.\nThe parameter can be the player's SteamID or the nickname of a player bound in this group. If no parameter is provided, it will try to query the SteamID of the command caller.", examples: "query-player\nquery-player 123456789\nquery-player John\nquery-player John --hero Anti-Mage\nquery-player John -o Anti-Mage", messages: { not_binded: "By default, it tries to find your information from the bound SteamID players, but it seems you are not bound.\nPlease bind your SteamID in this group. (You can enter [bind -h] for help)\nOr follow the command with the SteamID or nickname of the player you want to query.", steam_id_invalid: "Invalid SteamID and the player was not found in this group.", querying_player: "Retrieving player data, please wait...", query_failed: "Failed to get player information.", not_in_group: "Command failed.\nCurrently not in a group chat, you must provide the specified player's SteamID." } }, "query-hero": { description: "Query hero skills/stats information.", options: { random: "Randomly select a hero." }, usage: "Query the hero's skill descriptions and various stats, generate a picture.\nThe parameter can be the hero's ID, name, or common nickname.", examples: "query-hero 15\nquery-hero Razor\nquery-hero -r", messages: { not_found: "Hero not found, please confirm and re-enter.", querying_hero: "Retrieving hero data, please wait...", query_failed: "Failed to get hero data.", empty_input: "Please enter a parameter." } } } } };
586
586
  }
587
587
  });
588
588
 
589
589
  // src/locales/en-US.template.yml
590
590
  var require_en_US_template = __commonJS({
591
591
  "src/locales/en-US.template.yml"(exports2, module2) {
592
- module2.exports = { dota2tracker: { template: { radiant: "Radiant", dire: "Dire", won: "Won", lost: "Lost", match_id_: "Match ID: {0}", game_mode_: "Mode: {0}", start_time_: "Start Time: {0}", end_time_: "End Time: {0}", pick_order: "#{0}", random: "R", hero_damage_: "Damage: {0}", building_damage_: "Building: {0}", damage_received_: "Received: {0}", lasthit_: "LastHit: {0}", deny_: "Deny: {0}", "lh/dn_": "LH/DN: {0}", GPM: "GPM", XPM: "XPM", heal_: "Heal: {0}", crowd_control_duration_: "CCD: {0}", "GPM/XPM_": "GPM/XPM: {0}", lane: "Lane", lane_: "Lane: ", lane_advantage: "Lane +", lane_disadvantage: "Lane -", lane_jungle: "Jungle", lane_stomp: "Lane+++", lane_stomped: "Lane---", lane_tie: "Lane ==", analysis_successful: "Analysis successful", analysis_incomplete: "Analysis incomplete", kill: "Kill", kill_contribution_: "KC: {0}", position: "Position", position_: "Position: ", position_1: "Carry", position_2: "Mid", position_3: "OffLane", position_4: "Softsup", position_5: "Hardsup", dire_won: "Dire Won", radiant_won: "Radiant Won", total_damage: "Damage", total_experience: "Exp.", total_gold: "Gold", region_: "Region: {0}", duration_: "Duration: {0}", position_undefined: "?", top10_: "Top 10 Heroes by Matches: ", match_count_: "Matches: ", last25matches_: "Last 25 Matches: ", winrate_: "Winrate: ", imp_: "IMP: ", lane_advantage_rate_: "Lane Advantage Rate: ", hero: "Hero", all_matches_: "All Matches: ", match_count: "Matches", winrate: "Winrate", imp: "IMP", win_count: "Wins", lose_count: "Losses", recently_heroes: "Heroes used more than once recently: ", recently_positions: "Performance in the last 25 matches across all positions: ", winning_streak: "Winning Streak", losing_streak: "Losing Streak", id: "ID", mode: "Mode", kda_kc: "KDA(KC)", time: "Time", duration: "Duration", rank: "Rank", un_parsed: "(Unparsed)", combined_win_loss_summary: "Combined Win/Loss Summary: ", yesterdays_summary: "Yesterday's Summary", last_weeks_summary: "Last Week's Summary", report_won: "W", report_lost: "L", report_winrate: "WR" } } };
592
+ module2.exports = { dota2tracker: { template: { radiant: "Radiant", dire: "Dire", won: "Won", lost: "Lost", match_id_: "Match ID: {0}", game_mode_: "Mode: {0}", start_time_: "Start Time: {0}", end_time_: "End Time: {0}", pick_order: "#{0}", random: "R", hero_damage_: "Damage: {0}", building_damage_: "Building: {0}", damage_received_: "Received: {0}", lasthit_: "LastHit: {0}", deny_: "Deny: {0}", "lh/dn_": "LH/DN: {0}", GPM: "GPM", XPM: "XPM", heal_: "Heal: {0}", crowd_control_duration_: "CCD: {0}", "GPM/XPM_": "GPM/XPM: {0}", lane: "Lane", lane_: "Lane: ", lane_advantage: "Lane +", lane_disadvantage: "Lane -", lane_jungle: "Jungle", lane_stomp: "Lane+++", lane_stomped: "Lane---", lane_tie: "Lane ==", analysis_successful: "Analysis successful", analysis_incomplete: "Analysis incomplete", kill: "Kill", kill_contribution_: "KC: {0}", position: "Position", position_: "Position: ", position_1: "Carry", position_2: "Mid", position_3: "OffLane", position_4: "Softsup", position_5: "Hardsup", dire_won: "Dire Won", radiant_won: "Radiant Won", total_damage: "Damage", total_experience: "Exp.", total_gold: "Gold", region_: "Region: {0}", duration_: "Duration: {0}", position_undefined: "?", top10_: "Top 10 Heroes by Matches: ", match_count_: "Matches: ", last25matches_: "Last 25 Matches: ", winrate_: "Winrate: ", imp_: "IMP: ", lane_advantage_rate_: "Lane Advantage Rate: ", hero: "Hero", all_matches_: "All Matches: ", match_count: "Matches", winrate: "Winrate", imp: "IMP", win_count: "Wins", lose_count: "Losses", recently_heroes: "Heroes used more than once recently: ", recently_positions: "Performance in the last 25 matches across all positions: ", winning_streak: "Winning Streak", losing_streak: "Losing Streak", id: "ID", mode: "Mode", kda_kc: "KDA(KC)", time: "Time", duration: "Duration", rank: "Rank", un_parsed: "(Unparsed)", combined_win_loss_summary: "Combined Win/Loss Summary: ", yesterdays_summary: "Yesterday's Summary", last_weeks_summary: "Last Week's Summary", report_won: "W", report_lost: "L", report_winrate: "WR", anonymous_player_1: "This profile is private.", anonymous_player_2: "Background is for display purposes only. It is not {player}’s data." } } };
593
593
  }
594
594
  });
595
595
 
@@ -603,14 +603,14 @@ var require_en_US = __commonJS({
603
603
  // src/locales/zh-CN.command.yml
604
604
  var require_zh_CN_command = __commonJS({
605
605
  "src/locales/zh-CN.command.yml"(exports2, module2) {
606
- module2.exports = { commands: { dota2tracker: { subscribe: { description: "订阅后还需玩家在本群绑定SteamID", usage: "订阅后还需玩家在本群绑定SteamID,BOT将订阅本群中已绑定玩家的新比赛数据,在STRATZ比赛解析完成后将比赛数据生成为图片战报发布至本群中。", messages: { subscribed: "本群已订阅,无需重复订阅。", subscribe_success: "订阅成功。" } }, unsubscribe: { description: "取消订阅本群。", messages: { unsubscribe_success: "取消订阅成功。", not_subscribed: "本群尚未订阅,无需取消订阅。" } }, bind: { description: "绑定SteamID,并起一个别名(也可以不起)", usage: "将你的SteamID与你的账号绑定,若本群已订阅将会实时获取你的新比赛数据发布至群中。", examples: '绑定 123456789\n绑定 123456789 张三\n绑定 123456789 "张 三"', messages: { steam_id_invalid: "SteamID无效。", bind_success: "绑定成功,\nID:{userId}\n别名:{nickName}\nSteamID:{steamId}", bind_failed: "绑定失败,{0}", reason_without_match: "SteamID无效或无任何场次。", reason_fetch_failed: "网络状况不佳或其他原因无法验证SteamID,请稍后重试。", already_binded: "你已绑定,无需重复绑定。\n以下是你的个人信息:\nID:{userId}\n别名:{nickName}\nSteamID:{steamId}", nick_name_too_long: "别名过长,请限制在20个字符以内。(也可以留空)" } }, unbind: { description: "取消绑定你的个人信息", messages: { unbind_success: "取消绑定成功。", not_binded: "尚未绑定,无需取消绑定。" } }, rename: { description: "修改绑定时设定的别名", examples: '改名 李四\n改名 "李 四"', messages: { rename_success: "改名成功,现在你叫{nick_name}了。", empty_input: "请输入你的别名。", not_binded: "请先绑定,绑定时即可设定别名。", nick_name_too_long: "别名过长,请限制在20个字符以内。" } }, "query-members": { description: "查询本群已绑定的玩家", messages: { no_members: "本群尚无绑定玩家。", query_failed: "查询群友失败。" } }, "query-match": { description: "查询指定比赛ID的比赛数据,生成图片发布", options: { parse: "是否等待解析比赛数据" }, examples: "查询比赛 1234567890\n查询比赛 1234567890 -p\n查询比赛 1234567890 --parse", messages: { empty_input: "请输入比赛ID。", match_id_invalid: "比赛ID无效。", querying_match: "正在搜索对局详情,请稍后……", query_failed: "获取比赛数据失败。", waiting_for_parse: "比赛数据尚未解析,已发送解析请求到服务器,战报将在解析完成或超时后发送。" } }, "query-recent-match": { description: "查询最近的比赛数据,生成图片发布", options: { parse: "是否等待解析比赛数据" }, usage: "查询指定玩家的最近一场比赛的比赛数据,生成图片发布。\n参数可输入该玩家的SteamID或已在本群绑定玩家的别名,无参数时尝试查询调用指令玩家的SteamID", examples: "查询最近比赛\n查询最近比赛 123456789\n查询最近比赛 张三\n查询最近比赛 123456789 -p\n查询最近比赛 张三 --parse", messages: { not_binded: "无参数时默认从已绑定SteamID玩家中寻找你的信息,但你似乎并没有绑定。\n请在本群绑定SteamID。(可输入【绑定 -h】获取帮助)\n或在指令后跟上希望查询的SteamID或已绑定玩家的别名。", steam_id_invalid: "SteamID无效并且未在本群找到此玩家。", querying_match: "正在搜索对局详情,请稍后……", query_failed: "获取玩家最近比赛失败。", not_in_group: "指令调用失败。\n当前不属于群聊状态,必须提供指定玩家的SteamID。" } }, "query-player": { description: "查询玩家的个人信息,可指定英雄", options: { hero: "查询玩家指定英雄使用情况(同查询英雄,可用别名或ID)" }, usage: "查询指定玩家的个人信息,生成图片发布,可指定英雄。\n参数可输入该玩家的SteamID或已在本群绑定玩家的别名,无参数时尝试查询调用指令玩家的SteamID", examples: "查询玩家\n查询玩家 123456789\n查询玩家 张三\n查询玩家 张三 --hero 敌法师\n查询玩家 张三 -o 15", messages: { not_binded: "无参数时默认从已绑定SteamID玩家中寻找你的信息,但你似乎并没有绑定。\n请在本群绑定SteamID。(可输入【绑定 -h】获取帮助)\n或在指令后跟上希望查询的SteamID或已绑定玩家的别名。", steam_id_invalid: "SteamID无效并且未在本群找到此玩家。", querying_player: "正在获取玩家数据,请稍后……", query_failed: "获取玩家信息失败。", not_in_group: "指令调用失败。\n当前不属于群聊状态,必须提供指定玩家的SteamID。" } }, "query-hero": { description: "查询英雄技能/面板信息", options: { random: "随机选择英雄" }, usage: "查询英雄的技能说明与各项数据,生成图片发布。\n参数可输入英雄ID、英雄名、英雄常用别名。", examples: "查询英雄 15\n查询英雄 雷泽\n查询英雄 电魂", messages: { not_found: "未找到输入的英雄,请确认后重新输入。", querying_hero: "正在获取英雄数据,请稍后……", query_failed: "获取英雄数据失败。", empty_input: "请输入参数。" } } } } };
606
+ module2.exports = { commands: { dota2tracker: { subscribe: { description: "订阅后还需玩家在本群绑定SteamID", usage: "订阅后还需玩家在本群绑定SteamID,BOT将订阅本群中已绑定玩家的新比赛数据,在STRATZ比赛解析完成后将比赛数据生成为图片战报发布至本群中。", messages: { subscribed: "本群已订阅,无需重复订阅。", subscribe_success: "订阅成功。" } }, unsubscribe: { description: "取消订阅本群。", messages: { unsubscribe_success: "取消订阅成功。", not_subscribed: "本群尚未订阅,无需取消订阅。" } }, bind: { description: "绑定SteamID,并起一个别名(也可以不起)", usage: "将你的SteamID与你的账号绑定,若本群已订阅将会实时获取你的新比赛数据发布至群中。", examples: '绑定 123456789\n绑定 123456789 张三\n绑定 123456789 "张 三"', messages: { steam_id_invalid: "SteamID无效。", bind_success: "绑定成功,\nID:{userId}\n别名:{nickName}\nSteamID:{steamId}", bind_failed: "绑定失败,{0}", reason_without_match: "SteamID无效或无任何场次。", reason_fetch_failed: "网络状况不佳或其他原因无法验证SteamID,请稍后重试。", already_binded: "你已绑定,无需重复绑定。\n以下是你的个人信息:\nID:{userId}\n别名:{nickName}\nSteamID:{steamId}", nick_name_too_long: "别名过长,请限制在20个字符以内。(也可以留空)", is_anonymous: "请注意:你的Steam玩家数据并未公开,将无法使用BOT的主要功能,如“战报追踪”、“查询最近指令”等。\n如需公开数据,请在DOTA2游戏内设置中公开。" } }, unbind: { description: "取消绑定你的个人信息", messages: { unbind_success: "取消绑定成功。", not_binded: "尚未绑定,无需取消绑定。" } }, rename: { description: "修改绑定时设定的别名", examples: '改名 李四\n改名 "李 四"', messages: { rename_success: "改名成功,现在你叫{nick_name}了。", empty_input: "请输入你的别名。", not_binded: "请先绑定,绑定时即可设定别名。", nick_name_too_long: "别名过长,请限制在20个字符以内。" } }, "query-members": { description: "查询本群已绑定的玩家", messages: { no_members: "本群尚无绑定玩家。", query_failed: "查询群友失败。" } }, "query-match": { description: "查询指定比赛ID的比赛数据,生成图片发布", options: { parse: "是否等待解析比赛数据" }, examples: "查询比赛 1234567890\n查询比赛 1234567890 -p\n查询比赛 1234567890 --parse", messages: { empty_input: "请输入比赛ID。", match_id_invalid: "比赛ID无效。", querying_match: "正在搜索对局详情,请稍后……", query_failed: "获取比赛数据失败。", waiting_for_parse: "比赛数据尚未解析,已发送解析请求到服务器,战报将在解析完成或超时后发送。" } }, "query-recent-match": { description: "查询最近的比赛数据,生成图片发布", options: { parse: "是否等待解析比赛数据" }, usage: "查询指定玩家的最近一场比赛的比赛数据,生成图片发布。\n参数可输入该玩家的SteamID或已在本群绑定玩家的别名,无参数时尝试查询调用指令玩家的SteamID", examples: "查询最近比赛\n查询最近比赛 123456789\n查询最近比赛 张三\n查询最近比赛 123456789 -p\n查询最近比赛 张三 --parse", messages: { not_binded: "无参数时默认从已绑定SteamID玩家中寻找你的信息,但你似乎并没有绑定。\n请在本群绑定SteamID。(可输入【绑定 -h】获取帮助)\n或在指令后跟上希望查询的SteamID或已绑定玩家的别名。", steam_id_invalid: "SteamID无效并且未在本群找到此玩家。", querying_match: "正在搜索对局详情,请稍后……", query_failed: "获取玩家最近比赛失败。", not_in_group: "指令调用失败。\n当前不属于群聊状态,必须提供指定玩家的SteamID。", is_anonymous: "你的比赛数据未公开,无法获取最近比赛数据。\n如需公开数据,请在DOTA2游戏内设置中公开。" } }, "query-player": { description: "查询玩家的个人信息,可指定英雄", options: { hero: "查询玩家指定英雄使用情况(同查询英雄,可用别名或ID)" }, usage: "查询指定玩家的个人信息,生成图片发布,可指定英雄。\n参数可输入该玩家的SteamID或已在本群绑定玩家的别名,无参数时尝试查询调用指令玩家的SteamID", examples: "查询玩家\n查询玩家 123456789\n查询玩家 张三\n查询玩家 张三 --hero 敌法师\n查询玩家 张三 -o 15", messages: { not_binded: "无参数时默认从已绑定SteamID玩家中寻找你的信息,但你似乎并没有绑定。\n请在本群绑定SteamID。(可输入【绑定 -h】获取帮助)\n或在指令后跟上希望查询的SteamID或已绑定玩家的别名。", steam_id_invalid: "SteamID无效并且未在本群找到此玩家。", querying_player: "正在获取玩家数据,请稍后……", query_failed: "获取玩家信息失败。", not_in_group: "指令调用失败。\n当前不属于群聊状态,必须提供指定玩家的SteamID。" } }, "query-hero": { description: "查询英雄技能/面板信息", options: { random: "随机选择英雄" }, usage: "查询英雄的技能说明与各项数据,生成图片发布。\n参数可输入英雄ID、英雄名、英雄常用别名。", examples: "查询英雄 15\n查询英雄 雷泽\n查询英雄 电魂", messages: { not_found: "未找到输入的英雄,请确认后重新输入。", querying_hero: "正在获取英雄数据,请稍后……", query_failed: "获取英雄数据失败。", empty_input: "请输入参数。" } } } } };
607
607
  }
608
608
  });
609
609
 
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: "胜率" } } };
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}的数据。" } } };
614
614
  }
615
615
  });
616
616
 
@@ -632,7 +632,7 @@ __export(src_exports, {
632
632
  usage: () => usage
633
633
  });
634
634
  module.exports = __toCommonJS(src_exports);
635
- var import_koishi = require("koishi");
635
+ var import_koishi2 = require("koishi");
636
636
 
637
637
  // src/utils.ts
638
638
  var utils_exports = {};
@@ -641,6 +641,7 @@ __export(utils_exports, {
641
641
  HeroDescType: () => HeroDescType,
642
642
  ImageFormat: () => ImageFormat,
643
643
  ImageType: () => ImageType,
644
+ enhancedSimpleHashToSeed: () => enhancedSimpleHashToSeed,
644
645
  formatHeroDesc: () => formatHeroDesc,
645
646
  formatNumber: () => formatNumber,
646
647
  getFormattedHeroData: () => getFormattedHeroData,
@@ -659,6 +660,8 @@ __export(utils_exports, {
659
660
  var import_fs = __toESM(require("fs"));
660
661
  var dotaconstants = __toESM(require("dotaconstants"));
661
662
  var import_path = __toESM(require("path"));
663
+ var import_koishi = require("koishi");
664
+ var pluginDir = import_path.default.join(__dirname, "..");
662
665
  var CONFIGS = { STRATZ_API: { URL: "https://api.stratz.com/graphql", TOKEN: "" } };
663
666
  var http = null;
664
667
  var setTimeout;
@@ -704,8 +707,7 @@ async function query(queryName, variables) {
704
707
  }
705
708
  __name(query, "query");
706
709
  function loadGraphqlFile(queryName) {
707
- const filepath = `./node_modules/@sjtdev/koishi-plugin-dota2tracker/queries/${queryName}.graphql`;
708
- return import_fs.default.readFileSync(filepath, { encoding: "utf-8" }).replace(/[\r\n]+/g, " ");
710
+ return import_fs.default.readFileSync(import_path.default.join(pluginDir, "queries", `${queryName}.graphql`), { encoding: "utf-8" }).replace(/[\r\n]+/g, " ");
709
711
  }
710
712
  __name(loadGraphqlFile, "loadGraphqlFile");
711
713
  async function queryHeroFromValve(heroId, languageTag = "zh-CN") {
@@ -743,8 +745,8 @@ var ImageFormat = /* @__PURE__ */ ((ImageFormat2) => {
743
745
  function getImageUrl(image, type = "local" /* Local */, format = "png" /* png */) {
744
746
  if (type === "local" /* Local */) {
745
747
  try {
746
- if (format === "svg" /* svg */) return import_fs.default.readFileSync(`./node_modules/@sjtdev/koishi-plugin-dota2tracker/template/images/${image}.svg`);
747
- const imageData = import_fs.default.readFileSync(`./node_modules/@sjtdev/koishi-plugin-dota2tracker/template/images/${image}.${format}`);
748
+ if (format === "svg" /* svg */) return import_fs.default.readFileSync(import_path.default.join(pluginDir, "template", "images", `${image}.svg`));
749
+ const imageData = import_fs.default.readFileSync(import_path.default.join(pluginDir, "template", "images", `${image}.${format}`));
748
750
  const base64Data = imageData.toString("base64");
749
751
  return `data:image/png;base64,${base64Data}`;
750
752
  } catch (error) {
@@ -1034,11 +1036,37 @@ function getFormattedMatchData(matchQuery, constantsQuery) {
1034
1036
  return match;
1035
1037
  }
1036
1038
  __name(getFormattedMatchData, "getFormattedMatchData");
1037
- function getFormattedPlayerData(playerQuery, playerExtraQuery, genHero) {
1039
+ function getFormattedPlayerData(param) {
1040
+ const { playerQuery, playerExtraQuery, genHero, estimateRank } = param;
1038
1041
  const player = playerQuery.player;
1039
- const playerExtra = playerExtraQuery.player;
1042
+ const playerExtra = playerExtraQuery?.player;
1043
+ if (player.steamAccount.isAnonymous) {
1044
+ for (let index = 0; index < 25; index++) {
1045
+ const random2 = new import_koishi.Random(() => enhancedSimpleHashToSeed(`${player.steamAccount.id}-${index}`));
1046
+ player.matches.push({
1047
+ id: 1e9 + index,
1048
+ gameMode: "UNKNOWN",
1049
+ lobbyType: "UNRANKED",
1050
+ didRadiantWin: random2.bool(0.5),
1051
+ rank: random2.int(0, 8) * 10,
1052
+ radiantKills: [random2.int(0, 30)],
1053
+ direKills: [random2.int(0, 30)],
1054
+ parsedDateTime: 1,
1055
+ players: [
1056
+ {
1057
+ steamAccount: { id: player.steamAccount.id },
1058
+ isRadiant: true,
1059
+ kills: random2.int(0, 20),
1060
+ deaths: random2.int(0, 20),
1061
+ assists: random2.int(0, 20),
1062
+ hero: { id: random2.pick(Object.keys(dotaconstants.heroes)), shortName: dotaconstants.heroes[random2.pick(Object.keys(dotaconstants.heroes))].name.match(/^npc_dota_hero_(.+)$/)[1] }
1063
+ }
1064
+ ]
1065
+ });
1066
+ }
1067
+ }
1040
1068
  let filteredDotaPlus = {};
1041
- playerExtra.dotaPlus.forEach((item) => {
1069
+ playerExtra?.dotaPlus?.forEach((item) => {
1042
1070
  if (!filteredDotaPlus[item.heroId] || filteredDotaPlus[item.heroId].level < item.level) {
1043
1071
  filteredDotaPlus[item.heroId] = {
1044
1072
  heroId: item.heroId,
@@ -1046,13 +1074,51 @@ function getFormattedPlayerData(playerQuery, playerExtraQuery, genHero) {
1046
1074
  };
1047
1075
  }
1048
1076
  });
1049
- playerExtra.heroesPerformance.forEach((hero) => {
1077
+ playerExtra?.heroesPerformance?.forEach((hero) => {
1050
1078
  if (filteredDotaPlus[hero.hero.id]) {
1051
1079
  filteredDotaPlus[hero.hero.id].shortName = hero.hero.shortName;
1052
1080
  filteredDotaPlus[hero.hero.id].winCount = hero.winCount;
1053
1081
  filteredDotaPlus[hero.hero.id].matchCount = hero.matchCount;
1054
1082
  }
1055
1083
  });
1084
+ if (!player.steamAccount.seasonRank && estimateRank && !player.steamAccount.isAnonymous) {
1085
+ let estimateWeightedRank = function(player2) {
1086
+ const ranks = player2.matches.map((match) => match.rank);
1087
+ const validRanks = ranks.filter(validateRank);
1088
+ if (validRanks.length === 0) {
1089
+ return "Unknown";
1090
+ }
1091
+ const totalWeight = validRanks.reduce((sum, _, index) => sum + calculateWeight(index, validRanks.length), 0);
1092
+ const weightedAverage = validRanks.reduce((sum, rank, index) => {
1093
+ const value = rankToValue(rank);
1094
+ const weight = calculateWeight(index, validRanks.length);
1095
+ return sum + value * weight;
1096
+ }, 0) / totalWeight;
1097
+ const tier = Math.floor(weightedAverage / 6);
1098
+ const stars = Math.round(weightedAverage % 6);
1099
+ return tier * 10 + stars;
1100
+ function rankToValue(rank) {
1101
+ const tier2 = Math.floor(rank / 10);
1102
+ const stars2 = rank % 10;
1103
+ return tier2 * 6 + stars2;
1104
+ }
1105
+ __name(rankToValue, "rankToValue");
1106
+ function calculateWeight(index, total) {
1107
+ return 1 - 0.5 / (total - 1) * index;
1108
+ }
1109
+ __name(calculateWeight, "calculateWeight");
1110
+ function validateRank(rank) {
1111
+ if (rank === 80) return true;
1112
+ const tier2 = Math.floor(rank / 10);
1113
+ const stars2 = rank % 10;
1114
+ return tier2 >= 1 && tier2 <= 8 && stars2 >= 0 && stars2 <= 5;
1115
+ }
1116
+ __name(validateRank, "validateRank");
1117
+ };
1118
+ __name(estimateWeightedRank, "estimateWeightedRank");
1119
+ player.isEstimatedRank = true;
1120
+ player.steamAccount.seasonRank = estimateWeightedRank(player);
1121
+ }
1056
1122
  player.rank = {
1057
1123
  medal: parseInt(player.steamAccount.seasonRank?.toString().split("")[0] ?? 0),
1058
1124
  star: parseInt(player.steamAccount.seasonRank?.toString().split("")[1] ?? 0),
@@ -1060,13 +1126,13 @@ function getFormattedPlayerData(playerQuery, playerExtraQuery, genHero) {
1060
1126
  inTop100: player.steamAccount.seasonLeaderboardRank ? player.steamAccount.seasonLeaderboardRank <= 10 ? "8c" : player.steamAccount.seasonLeaderboardRank <= 100 ? "8b" : void 0 : void 0
1061
1127
  };
1062
1128
  player.dotaPlus = Object.values(filteredDotaPlus);
1063
- player.dotaPlus.sort((a, b) => {
1129
+ player.dotaPlus?.sort((a, b) => {
1064
1130
  if (b.level !== a.level) {
1065
1131
  return b.level - a.level;
1066
1132
  }
1067
1133
  return a.heroId - b.heroId;
1068
1134
  });
1069
- player.heroesPerformanceTop10 = playerExtra.heroesPerformance.slice(0, 10);
1135
+ player.heroesPerformanceTop10 = playerExtra?.heroesPerformance.slice(0, 10);
1070
1136
  if (genHero) {
1071
1137
  const { matchCount, winCount, imp } = player.heroesPerformanceTop10[0];
1072
1138
  player.matchCount = matchCount;
@@ -1179,7 +1245,7 @@ __name(formatNumber, "formatNumber");
1179
1245
  function readDirectoryFilesSync(directoryPath) {
1180
1246
  try {
1181
1247
  const files = import_fs.default.readdirSync(directoryPath);
1182
- const fileNames = files.map((file) => import_path.default.basename(file, import_path.default.extname(file)));
1248
+ const fileNames = files.filter((file) => import_path.default.extname(file).toLowerCase() === ".ejs").map((file) => import_path.default.basename(file, ".ejs"));
1183
1249
  return fileNames;
1184
1250
  } catch (error) {
1185
1251
  console.error("Error reading directory:", error);
@@ -1210,7 +1276,7 @@ async function playerisValid(input) {
1210
1276
  try {
1211
1277
  const steamAccountId = parseInt(input);
1212
1278
  let queryRes = await query("VerifyingPlayer", { steamAccountId });
1213
- if (queryRes.player.matchCount != null) return { isValid: true };
1279
+ if (queryRes.player.matchCount != null) return { isValid: true, isAnonymous: queryRes.player.steamAccount.isAnonymous };
1214
1280
  else return { isValid: false, reason: ".reason_without_match" };
1215
1281
  } catch (error) {
1216
1282
  console.error(error);
@@ -1257,13 +1323,26 @@ function formatHeroDesc(template, special_values, type = "normal" /* Normal */)
1257
1323
  });
1258
1324
  }
1259
1325
  __name(formatHeroDesc, "formatHeroDesc");
1326
+ function enhancedSimpleHashToSeed(inputString) {
1327
+ const encoded = btoa(inputString);
1328
+ let total = 0;
1329
+ let complexFactor = 1;
1330
+ for (let i = 0; i < encoded.length; i++) {
1331
+ total += encoded.charCodeAt(i) * complexFactor;
1332
+ complexFactor++;
1333
+ total %= 9973;
1334
+ }
1335
+ total = total % 9973 * (total % 9973) % 9973;
1336
+ return total % 1e3 / 1e3;
1337
+ }
1338
+ __name(enhancedSimpleHashToSeed, "enhancedSimpleHashToSeed");
1260
1339
 
1261
1340
  // src/index.ts
1262
1341
  var import_fs2 = __toESM(require("fs"));
1263
1342
  var import_path2 = __toESM(require("path"));
1264
1343
  var import_moment = __toESM(require("moment"));
1265
1344
  var dotaconstants2 = __toESM(require("dotaconstants"));
1266
- var import_koishi2 = require("koishi");
1345
+ var import_koishi3 = require("koishi");
1267
1346
  var ejs = __toESM(require("ejs"));
1268
1347
 
1269
1348
  // require("./locales/**/*.schema.yml") in src/index.ts
@@ -1306,58 +1385,60 @@ var globRequire_locales_template_yml = __glob({
1306
1385
  var name = "dota2tracker";
1307
1386
  var usage = "";
1308
1387
  var inject = ["http", "database", "cron", "puppeteer", "cache"];
1309
- var Config = import_koishi.Schema.intersect([
1310
- import_koishi.Schema.object({
1311
- STRATZ_API_TOKEN: import_koishi.Schema.string().required().role("secret"),
1312
- dataParsingTimeoutMinutes: import_koishi.Schema.number().default(60).min(0).max(1440),
1313
- urlInMessageType: import_koishi.Schema.array(import_koishi.Schema.union([import_koishi.Schema.const("match"), import_koishi.Schema.const("player"), import_koishi.Schema.const("hero")])).role("checkbox")
1388
+ var pluginDir2 = import_path2.default.resolve(__dirname, "..");
1389
+ var Config = import_koishi2.Schema.intersect([
1390
+ import_koishi2.Schema.object({
1391
+ STRATZ_API_TOKEN: import_koishi2.Schema.string().required().role("secret"),
1392
+ dataParsingTimeoutMinutes: import_koishi2.Schema.number().default(60).min(0).max(1440),
1393
+ urlInMessageType: import_koishi2.Schema.array(import_koishi2.Schema.union([import_koishi2.Schema.const("match"), import_koishi2.Schema.const("player"), import_koishi2.Schema.const("hero")])).role("checkbox")
1314
1394
  }).i18n(["zh-CN", "en-US"].reduce((acc, cur) => (acc[cur] = globRequire_locales_schema_yml(`./locales/${cur}.schema.yml`)._config.base, acc), {})),
1315
- import_koishi.Schema.intersect([
1316
- import_koishi.Schema.object({
1317
- rankBroadSwitch: import_koishi.Schema.boolean().default(false)
1395
+ import_koishi2.Schema.intersect([
1396
+ import_koishi2.Schema.object({
1397
+ rankBroadSwitch: import_koishi2.Schema.boolean().default(false)
1318
1398
  }),
1319
- import_koishi.Schema.union([
1320
- import_koishi.Schema.object({
1321
- rankBroadSwitch: import_koishi.Schema.const(true).required(),
1322
- rankBroadStar: import_koishi.Schema.boolean().default(true),
1323
- rankBroadLeader: import_koishi.Schema.boolean().default(true),
1324
- rankBroadFun: import_koishi.Schema.boolean().default(false)
1399
+ import_koishi2.Schema.union([
1400
+ import_koishi2.Schema.object({
1401
+ rankBroadSwitch: import_koishi2.Schema.const(true).required(),
1402
+ rankBroadStar: import_koishi2.Schema.boolean().default(true),
1403
+ rankBroadLeader: import_koishi2.Schema.boolean().default(true),
1404
+ rankBroadFun: import_koishi2.Schema.boolean().default(false)
1325
1405
  }),
1326
- import_koishi.Schema.object({})
1406
+ import_koishi2.Schema.object({})
1327
1407
  ])
1328
1408
  ]).i18n(["zh-CN", "en-US"].reduce((acc, cur) => (acc[cur] = globRequire_locales_schema_yml(`./locales/${cur}.schema.yml`)._config.rank, acc), {})),
1329
- import_koishi.Schema.intersect([
1330
- import_koishi.Schema.object({
1331
- dailyReportSwitch: import_koishi.Schema.boolean().default(false)
1409
+ import_koishi2.Schema.intersect([
1410
+ import_koishi2.Schema.object({
1411
+ dailyReportSwitch: import_koishi2.Schema.boolean().default(false)
1332
1412
  }),
1333
- import_koishi.Schema.union([
1334
- import_koishi.Schema.object({
1335
- dailyReportSwitch: import_koishi.Schema.const(true).required(),
1336
- dailyReportHours: import_koishi.Schema.number().min(0).max(23).default(6),
1337
- dailyReportShowCombi: import_koishi.Schema.boolean().default(true)
1413
+ import_koishi2.Schema.union([
1414
+ import_koishi2.Schema.object({
1415
+ dailyReportSwitch: import_koishi2.Schema.const(true).required(),
1416
+ dailyReportHours: import_koishi2.Schema.number().min(0).max(23).default(6),
1417
+ dailyReportShowCombi: import_koishi2.Schema.boolean().default(true)
1338
1418
  }),
1339
- import_koishi.Schema.object({})
1419
+ import_koishi2.Schema.object({})
1340
1420
  ]),
1341
- import_koishi.Schema.object({
1342
- weeklyReportSwitch: import_koishi.Schema.boolean().default(false)
1421
+ import_koishi2.Schema.object({
1422
+ weeklyReportSwitch: import_koishi2.Schema.boolean().default(false)
1343
1423
  }),
1344
- import_koishi.Schema.union([
1345
- import_koishi.Schema.object({
1346
- weeklyReportSwitch: import_koishi.Schema.const(true).required(),
1347
- weeklyReportDayHours: import_koishi.Schema.tuple([import_koishi.Schema.number().min(1).max(7), import_koishi.Schema.number().min(0).max(23)]).default([1, 10]),
1348
- weeklyReportShowCombi: import_koishi.Schema.boolean().default(true)
1424
+ import_koishi2.Schema.union([
1425
+ import_koishi2.Schema.object({
1426
+ weeklyReportSwitch: import_koishi2.Schema.const(true).required(),
1427
+ weeklyReportDayHours: import_koishi2.Schema.tuple([import_koishi2.Schema.number().min(1).max(7), import_koishi2.Schema.number().min(0).max(23)]).default([1, 10]),
1428
+ weeklyReportShowCombi: import_koishi2.Schema.boolean().default(true)
1349
1429
  }),
1350
- import_koishi.Schema.object({})
1430
+ import_koishi2.Schema.object({})
1351
1431
  ])
1352
1432
  ]).i18n(["zh-CN", "en-US"].reduce((acc, cur) => (acc[cur] = globRequire_locales_schema_yml(`./locales/${cur}.schema.yml`)._config.report, acc), {})),
1353
- import_koishi.Schema.object({
1354
- template_match: import_koishi.Schema.union([...readDirectoryFilesSync(`./node_modules/@sjtdev/koishi-plugin-${name}/template/match`)]).default("match_1"),
1355
- template_player: import_koishi.Schema.union([...readDirectoryFilesSync(`./node_modules/@sjtdev/koishi-plugin-${name}/template/player`)]).default("player_1"),
1356
- template_hero: import_koishi.Schema.union([...readDirectoryFilesSync(`./node_modules/@sjtdev/koishi-plugin-${name}/template/hero`)]).default("hero_1")
1433
+ import_koishi2.Schema.object({
1434
+ template_match: import_koishi2.Schema.union([...readDirectoryFilesSync(import_path2.default.join(pluginDir2, "template", "match"))]).default("match_1"),
1435
+ template_player: import_koishi2.Schema.union([...readDirectoryFilesSync(import_path2.default.join(pluginDir2, "template", "player"))]).default("player_1"),
1436
+ template_hero: import_koishi2.Schema.union([...readDirectoryFilesSync(import_path2.default.join(pluginDir2, "template", "hero"))]).default("hero_1"),
1437
+ playerRankEstimate: import_koishi2.Schema.boolean().default(true)
1357
1438
  }).i18n(["zh-CN", "en-US"].reduce((acc, cur) => (acc[cur] = globRequire_locales_schema_yml(`./locales/${cur}.schema.yml`)._config.template, acc), {}))
1358
1439
  ]);
1359
1440
  var pendingMatches = [];
1360
- var random = new import_koishi2.Random(() => Math.random());
1441
+ var random = new import_koishi3.Random(() => Math.random());
1361
1442
  var days_30 = 2592e6;
1362
1443
  var GraphqlLanguageEnum = /* @__PURE__ */ ((GraphqlLanguageEnum2) => {
1363
1444
  GraphqlLanguageEnum2["en-US"] = "ENGLISH";
@@ -1436,7 +1517,7 @@ async function apply(ctx, config) {
1436
1517
  session.send(session.text(".nick_name_too_long"));
1437
1518
  return;
1438
1519
  }
1439
- session.send(session.text(".bind_success", { userId: session.event.user.id, nickName: nick_name || "", steamId: steam_id }));
1520
+ session.send(session.text(".bind_success", { userId: session.event.user.id, nickName: nick_name || "", steamId: steam_id }) + (verifyRes.isAnonymous ? "\n" + session.text(".is_anonymous") : ""));
1440
1521
  ctx.database.create("dt_subscribed_players", {
1441
1522
  userId: session.event.user.id,
1442
1523
  guildId: session.event.channel.id,
@@ -1570,7 +1651,7 @@ async function apply(ctx, config) {
1570
1651
  let idsToFind = guild.players.map((player) => player.steamId);
1571
1652
  let broadPlayers = match.players.filter((item) => idsToFind.includes(item.steamAccountId));
1572
1653
  for (let player of broadPlayers) {
1573
- const random2 = new import_koishi2.Random(() => enhancedSimpleHashToSeed(`${match.id}-${player.steamAccountId}-${player.playerSlot}`));
1654
+ const random2 = new import_koishi3.Random(() => enhancedSimpleHashToSeed(`${match.id}-${player.steamAccountId}-${player.playerSlot}`));
1574
1655
  let comment;
1575
1656
  if (player.isRadiant == match.didRadiantWin) {
1576
1657
  if (player.deathContribution < 0.2 || player.killContribution > 0.75 || player.heroDamage / player.networth > 1.5 || player.towerDamage > 1e4 || player.imp > 0)
@@ -1661,9 +1742,14 @@ async function apply(ctx, config) {
1661
1742
  let lastMatchId = 0;
1662
1743
  try {
1663
1744
  await session.send(session.text(".querying_match"));
1664
- lastMatchId = (await query("PlayersLastmatchRankinfo", {
1745
+ const lastMatchQuery = await query("PlayersLastmatchRankinfo", {
1665
1746
  steamAccountIds: [parseInt(flagBindedPlayer?.steamId ?? input_data)]
1666
- })).players[0].matches[0].id;
1747
+ });
1748
+ if (lastMatchQuery.players[0].steamAccount.isAnonymous) {
1749
+ await session.send(session.text(".is_anonymous"));
1750
+ return;
1751
+ }
1752
+ lastMatchId = lastMatchQuery.players[0].matches[0]?.id;
1667
1753
  } catch (error) {
1668
1754
  session.send(session.text(".query_failed"));
1669
1755
  ctx.logger.error(error);
@@ -1700,19 +1786,29 @@ async function apply(ctx, config) {
1700
1786
  }
1701
1787
  session.send(session.text(".querying_player"));
1702
1788
  let heroId = findingHero(options.hero);
1703
- let steamId = flagBindedPlayer?.steamId ?? input_data;
1704
1789
  try {
1790
+ let steamId = Number(flagBindedPlayer?.steamId ?? input_data);
1705
1791
  const playerQuery = await query("PlayerInfoWith25Matches", {
1706
1792
  steamAccountId: steamId,
1707
1793
  heroIds: heroId
1708
1794
  });
1709
- const playerExtraQuery = await query("PlayerExtraInfo", {
1795
+ const playerExtraQuery = !playerQuery.player.steamAccount.isAnonymous ? await query("PlayerExtraInfo", {
1710
1796
  steamAccountId: steamId,
1711
1797
  matchCount: playerQuery.player.matchCount,
1712
1798
  totalHeroCount: Object.keys(dotaconstants2.heroes).length,
1713
1799
  heroIds: heroId
1800
+ }) : {
1801
+ player: {
1802
+ heroesPerformance: [],
1803
+ dotaPlus: null
1804
+ }
1805
+ };
1806
+ const player = getFormattedPlayerData({
1807
+ playerQuery,
1808
+ playerExtraQuery,
1809
+ genHero: heroId ? { heroId, name: constantLocales[languageTag].dota2tracker.template.hero_names[heroId] } : null,
1810
+ estimateRank: config.playerRankEstimate
1714
1811
  });
1715
- const player = getFormattedPlayerData(playerQuery, playerExtraQuery, heroId ? { heroId, name: constantLocales[languageTag].dota2tracker.template.hero_names[heroId] } : null);
1716
1812
  session.send(
1717
1813
  (ctx.config.urlInMessageType.some((type) => type == "player") ? "https://stratz.com/players/" + player.steamAccount.id : "") + await ctx.puppeteer.render(await genImageHTML(player, config.template_player, "player" /* Player */, ctx, languageTag))
1718
1814
  );
@@ -2079,7 +2175,7 @@ async function apply(ctx, config) {
2079
2175
  }
2080
2176
  __name($t, "$t");
2081
2177
  async function genImageHTML(data, template, type, ctx2, languageTag) {
2082
- const templatePath = import_path2.default.join(`./node_modules/@sjtdev/koishi-plugin-${name}/template/${type}`, template + ".ejs");
2178
+ const templatePath = import_path2.default.join(pluginDir2, "template", type, `${template}.ejs`);
2083
2179
  const templateData = {
2084
2180
  data,
2085
2181
  utils: utils_exports,
@@ -2089,7 +2185,8 @@ async function apply(ctx, config) {
2089
2185
  moment: import_moment.default,
2090
2186
  eh: escapeHTML,
2091
2187
  $t: templateI18nHelper,
2092
- languageTag
2188
+ languageTag,
2189
+ Random: import_koishi3.Random
2093
2190
  };
2094
2191
  function templateI18nHelper(key, param) {
2095
2192
  return $t(languageTag, key, param);
@@ -2099,7 +2196,7 @@ async function apply(ctx, config) {
2099
2196
  const html = await ejs.renderFile(templatePath, templateData, {
2100
2197
  strict: false
2101
2198
  });
2102
- if (process.env.NODE_ENV === "development") import_fs2.default.writeFileSync("./node_modules/@sjtdev/koishi-plugin-dota2tracker/temp.html", html);
2199
+ if (process.env.NODE_ENV === "development") import_fs2.default.writeFileSync(import_path2.default.join(pluginDir2, "temp.html"), html);
2103
2200
  return html;
2104
2201
  } catch (error) {
2105
2202
  ctx2.logger.error(error);
@@ -2109,19 +2206,6 @@ async function apply(ctx, config) {
2109
2206
  __name(genImageHTML, "genImageHTML");
2110
2207
  }
2111
2208
  __name(apply, "apply");
2112
- function enhancedSimpleHashToSeed(inputString) {
2113
- const encoded = btoa(inputString);
2114
- let total = 0;
2115
- let complexFactor = 1;
2116
- for (let i = 0; i < encoded.length; i++) {
2117
- total += encoded.charCodeAt(i) * complexFactor;
2118
- complexFactor++;
2119
- total %= 9973;
2120
- }
2121
- total = total % 9973 * (total % 9973) % 9973;
2122
- return total % 1e3 / 1e3;
2123
- }
2124
- __name(enhancedSimpleHashToSeed, "enhancedSimpleHashToSeed");
2125
2209
  function escapeHTML(str) {
2126
2210
  if (str == null) return "";
2127
2211
  return str.replace(/[&<>"']/g, function(match) {
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.3.1-beta.1",
4
+ "version": "1.4.0",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
@@ -27,7 +27,6 @@
27
27
  "dota2"
28
28
  ],
29
29
  "dependencies": {
30
- "date-fns": "^4.1.0",
31
30
  "dotaconstants": "^9.3.0",
32
31
  "ejs": "^3.1.10",
33
32
  "moment": "^2.30.1"
@@ -6,6 +6,7 @@ query PlayerInfoWith25Matches($steamAccountId: Long!, $heroIds: [Short]) {
6
6
  seasonRank
7
7
  seasonLeaderboardRank
8
8
  id
9
+ isAnonymous
9
10
  }
10
11
  guildMember {
11
12
  guild {
@@ -6,6 +6,7 @@ query PlayersLastmatchRankinfo($steamAccountIds: [Long]!) {
6
6
  avatar
7
7
  seasonRank
8
8
  seasonLeaderboardRank
9
+ isAnonymous
9
10
  }
10
11
  matches(request: { take: 1 }) {
11
12
  id
@@ -1,5 +1,8 @@
1
1
  query VerifyingPlayer($steamAccountId: Long!) {
2
2
  player(steamAccountId: $steamAccountId) {
3
3
  matchCount
4
+ steamAccount {
5
+ isAnonymous
6
+ }
4
7
  }
5
8
  }
@@ -127,7 +127,7 @@
127
127
  </head>
128
128
  <body>
129
129
  <% const users = data;
130
- users.forEach(player=>{
130
+ users.forEach(player=>{
131
131
  player.rank = {
132
132
  medal: parseInt(player.steamAccount.seasonRank?.toString().split("")[0] ?? 0),
133
133
  star: parseInt(player.steamAccount.seasonRank?.toString().split("")[1] ?? 0),
@@ -163,7 +163,7 @@
163
163
  <span>${(playerInMatch.imp > 0 ? "+" : "") + playerInMatch.imp}</span>
164
164
  </div>`}).join("")}
165
165
  </div>`:0}
166
- <div class="last_match_date">最近游戏: ${moment(new Date(user.matches[0].startDateTime * 1000)).format("YYYY-MM-DD HH:mm:ss")}</div>
166
+ <div class="last_match_date">最近游戏: ${moment(new Date((user.matches[0]?.startDateTime ?? 0) * 1000)).format("YYYY-MM-DD HH:mm:ss")}</div>
167
167
  </div>`).join("")
168
168
  %>
169
169
  </body>