@sjtdev/koishi-plugin-dota2tracker 2.1.1 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js
CHANGED
|
@@ -773,21 +773,21 @@ var require_en_US_command = __commonJS({
|
|
|
773
773
|
// src/locales/en-US.schema.yml
|
|
774
774
|
var require_en_US_schema = __commonJS({
|
|
775
775
|
"src/locales/en-US.schema.yml"(exports2, module2) {
|
|
776
|
-
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.", proxyAddress: "Proxy address. Leave blank to disable the proxy.", suppressStratzNetworkErrors: "When enabled, downgrades Stratz network errors (e.g. timeouts) to lower-priority output. Debug-level logs are hidden by default. To enable debug logging, see [📖 Documentation: Configuration](http://sjtdev.github.io/koishi-plugin-dota2tracker/en-US/configs.html#suppressstratznetworkerrors-boolean)" }, message: { $desc: "Message Settings", useHeroNicknames: "When disabled, only the official hero names will be used.", 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"] }, rankBroadSwitch: "Rank change broadcast", rankBroadStar: "Star change broadcast", rankBroadLeader: "Leaderboard rank change broadcast", rankBroadFun: "Fun broadcast template", maxSendItemCount: "Maximum number of item images to send<br/>When exceeded, the following option determines whether to send the item list", showItemListAtTooMuchItems: "Send item list when exceeding max count<br/>Controls whether to send the item list image when search results exceed maxSendItemCount", customItemAlias: { $desc: "Custom item aliases<br/>\nAdd additional aliases when built-in list is insufficient. \nFor widely-used missing aliases, please submit issues/pull requests to the source repository.<br/>\n(Example **Keyword**: Blink Dagger,**Alias**: Blink)", keyword: "Keyword", alias: "Alias" } }, 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 [📖 Template Display](https://sjtdev.github.io/koishi-plugin-dota2tracker/template-match.html) 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", templateFonts: 'Font names used in the template. Requires font files installed on the koishi host machine. \nMultiple fonts can be added; the system will use the first available font from top to bottom. \nIf all fonts are unavailable, falls back to system defaults. \nImportant formatting rules: \n- Enclose font names in quotes (" ") if they contain spaces or special characters (recommended for all font names)\n- Do NOT enclose generic font family names (e.g. sans-serif, monospace) in quotes\nExamples:\n```\n"Microsoft YaHei"\nsans-serif\n```\nFor details on font-family syntax, see:\n[📖 MDN: font-family](https://developer.mozilla.org/en-US/docs/Web/CSS/font-family)' } } };
|
|
776
|
+
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.", proxyAddress: "Proxy address. Leave blank to disable the proxy.", suppressStratzNetworkErrors: "When enabled, downgrades Stratz network errors (e.g. timeouts) to lower-priority output. Debug-level logs are hidden by default. To enable debug logging, see [📖 Documentation: Configuration](http://sjtdev.github.io/koishi-plugin-dota2tracker/en-US/configs.html#suppressstratznetworkerrors-boolean)", enableOpenDotaFallback: "Enable OpenDota as a fallback data source for match tracking and query-match features.", OPENDOTA_API_KEY: "Your paid subscription API key for OpenDota.\nSee https://www.opendota.com/api-keys for details.\nFree users should leave this blank." }, message: { $desc: "Message Settings", useHeroNicknames: "When disabled, only the official hero names will be used.", 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"] }, rankBroadSwitch: "Rank change broadcast", rankBroadStar: "Star change broadcast", rankBroadLeader: "Leaderboard rank change broadcast", rankBroadFun: "Fun broadcast template", maxSendItemCount: "Maximum number of item images to send<br/>When exceeded, the following option determines whether to send the item list", showItemListAtTooMuchItems: "Send item list when exceeding max count<br/>Controls whether to send the item list image when search results exceed maxSendItemCount", customItemAlias: { $desc: "Custom item aliases<br/>\nAdd additional aliases when built-in list is insufficient. \nFor widely-used missing aliases, please submit issues/pull requests to the source repository.<br/>\n(Example **Keyword**: Blink Dagger,**Alias**: Blink)", keyword: "Keyword", alias: "Alias" } }, 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 [📖 Template Display](https://sjtdev.github.io/koishi-plugin-dota2tracker/template-match.html) 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", templateFonts: 'Font names used in the template. Requires font files installed on the koishi host machine. \nMultiple fonts can be added; the system will use the first available font from top to bottom. \nIf all fonts are unavailable, falls back to system defaults. \nImportant formatting rules: \n- Enclose font names in quotes (" ") if they contain spaces or special characters (recommended for all font names)\n- Do NOT enclose generic font family names (e.g. sans-serif, monospace) in quotes\nExamples:\n```\n"Microsoft YaHei"\nsans-serif\n```\nFor details on font-family syntax, see:\n[📖 MDN: font-family](https://developer.mozilla.org/en-US/docs/Web/CSS/font-family)' } } };
|
|
777
777
|
}
|
|
778
778
|
});
|
|
779
779
|
|
|
780
780
|
// src/locales/en-US.template.yml
|
|
781
781
|
var require_en_US_template = __commonJS({
|
|
782
782
|
"src/locales/en-US.template.yml"(exports2, module2) {
|
|
783
|
-
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.", rank_fun_down_message: "
|
|
783
|
+
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", analysis_by_opendota: "Analysis by OpenDota", 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.", rank_fun_down_message: "AVATAR_PLACEHOLDER<br/>Sad", rank_fun_up_message: "Hip hip hooray! Our awesome member AVATAR_PLACEHOLDER{name} has leveled up from PREV_PLACEHOLDER to CURR_PLACEHOLDER!", titles: { MVP: "MVP-#FFA500", Soul: "Soul-#66CCFF", Rich: "R-#FFD700", Wise: "W-#8888FF", Controller: "C-#FF00FF", Nuker: "N-#CC0088", Breaker: "B-#DD0000", Ghost: "G-#CCCCCC", Utility: "U-#20B2AA", Assister: "A-#006400", Demolisher: "D-#FEDCBA", Healer: "H-#00FF00", Tank: "T-#84A1C7", Idle: "I-#DDDDDD" }, situation: "Situation", networth: "Net Worth", experience: "Experience", OUTCOME_MAP: { RADIANT_VICTORY: "RADIANT VICTORY", RADIANT_STOMP: "RADIANT STOMP", DIRE_VICTORY: "DIRE VICTORY", DIRE_STOMP: "DIRE STOMP", TIE: "TIE" }, lane_top: "Top", lane_mid: "Mid", lane_bottom: "Bottom", empty_extra_info: "No extra info" } } };
|
|
784
784
|
}
|
|
785
785
|
});
|
|
786
786
|
|
|
787
787
|
// src/locales/en-US.yml
|
|
788
788
|
var require_en_US = __commonJS({
|
|
789
789
|
"src/locales/en-US.yml"(exports2, module2) {
|
|
790
|
-
module2.exports = { dota2tracker: { heroes_nicknames: { "0": 'Please strictly follow the format of "", "" when filling out, if the format is incorrect, only the default name of the hero will be used. The default name of the hero may be omitted.', "1": '"Anti-Mage"', "2": '"Axe"', "3": '"Bane"', "4": '"Bloodseeker"', "5": '"Crystal Maiden"', "6": '"Drow Ranger"', "7": '"Earthshaker"', "8": '"Juggernaut"', "9": '"Mirana"', "10": '"Morphling"', "11": '"Shadow Fiend"', "12": '"Phantom Lancer"', "13": '"Puck"', "14": '"Pudge"', "15": '"Razor"', "16": '"Sand King"', "17": '"Storm Spirit"', "18": '"Sven"', "19": '"Tiny"', "20": '"Vengeful Spirit"', "21": '"Windranger"', "22": '"Zeus"', "23": '"Kunkka"', "25": '"Lina"', "26": '"Lion"', "27": '"Shadow Shaman"', "28": '"Slardar"', "29": '"Tidehunter"', "30": '"Witch Doctor"', "31": '"Lich"', "32": '"Riki"', "33": '"Enigma"', "34": '"Tinker"', "35": '"Sniper"', "36": '"Necrophos"', "37": '"Warlock"', "38": '"Beastmaster"', "39": '"Queen of Pain"', "40": '"Venomancer"', "41": '"Faceless Void"', "42": '"Wraith King"', "43": '"Death Prophet"', "44": '"Phantom Assassin"', "45": '"Pugna"', "46": '"Templar Assassin"', "47": '"Viper"', "48": '"Luna"', "49": '"Dragon Knight"', "50": '"Dazzle"', "51": '"Clockwerk"', "52": '"Leshrac"', "53": `"Nature's Prophet"`, "54": '"Lifestealer"', "55": '"Dark Seer"', "56": '"Clinkz"', "57": '"Omniknight"', "58": '"Enchantress"', "59": '"Huskar"', "60": '"Night Stalker"', "61": '"Broodmother"', "62": '"Bounty Hunter"', "63": '"Weaver"', "64": '"Jakiro"', "65": '"Batrider"', "66": '"Chen"', "67": '"Spectre"', "68": '"Ancient Apparition"', "69": '"Doom"', "70": '"Ursa"', "71": '"Spirit Breaker"', "72": '"Gyrocopter"', "73": '"Alchemist"', "74": '"Invoker"', "75": '"Silencer"', "76": '"Outworld Devourer"', "77": '"Lycan"', "78": '"Brewmaster"', "79": '"Shadow Demon"', "80": '"Lone Druid"', "81": '"Chaos Knight"', "82": '"Meepo"', "83": '"Treant Protector"', "84": '"Ogre Magi"', "85": '"Undying"', "86": '"Rubick"', "87": '"Disruptor"', "88": '"Nyx Assassin"', "89": '"Naga Siren"', "90": '"Keeper of the Light"', "91": '"Io"', "92": '"Visage"', "93": '"Slark"', "94": '"Medusa"', "95": '"Troll Warlord"', "96": '"Centaur Warrunner"', "97": '"Magnus"', "98": '"Timbersaw"', "99": '"Bristleback"', "100": '"Tusk"', "101": '"Skywrath Mage"', "102": '"Abaddon"', "103": '"Elder Titan"', "104": '"Legion Commander"', "105": '"Techies"', "106": '"Ember Spirit"', "107": '"Earth Spirit"', "108": '"Underlord"', "109": '"Terrorblade"', "110": '"Phoenix"', "111": '"Oracle"', "112": '"Winter Wyvern"', "113": '"Arc Warden"', "114": '"Monkey King"', "119": '"Dark Willow"', "120": '"Pangolier"', "121": '"Grimstroke"', "123": '"Hoodwink"', "126": '"Void Spirit"', "128": '"Snapfire"', "129": '"Mars"', "131": '"Ring Master"', "135": '"Dawnbreaker"', "136": '"Marci"', "137": '"Primal Beast"', "138": '"Muerta"', "145": '"Kez"' }, broadcast: { WIN_NEGATIVE: `"Won the match by sheer luck", "Won the match by a stroke of bad luck", "Coasted to victory", "Didn't even show up for the team fight, but my teammates won 4v5"`, WIN_POSITIVE: '"Led the team to victory", "Dominated the opponents and secured the win", "Carried the game to victory", "Treated the opponents like pigs and won", "Won again; this game is just so monotonous and dull", "Simply achieved a win in the match"', LOSE_NEGATIVE: '"Got crushed and lost the match", "Lost the match miserably", "Got my head knocked sideways and lost the match with a blown mindset", "Went fishing but got eaten by the fish, lost the match", "Played terribly", "Simply suffered a loss in the match"', LOSE_POSITIVE: `"Lost the match with no way to turn it around", "Gave it my all, but still lost the match", "Though we lost, we still have honor", "Couldn't carry my teammates, lost the match", "Lost again, it hurts; I'd rather it be me losing"`, message: "{name}'s {hero_name} {comment}.\nKDA: {kda}, GPM/XPM: {gpm_xpm}, Last Hits/Denies: {lh_dn}, Damage/Tower Damage: {damage}, Kill/Death Contribution Rate: {kc_dc}", rank_changed: "Player {name} rank changed: {prev.medal} {prev.star} → {curr.medal} {curr.star}" }, logger: { fetch_guilds_failed: "Failed to fetch guild information.", match_tracked: "Tracked
|
|
790
|
+
module2.exports = { dota2tracker: { heroes_nicknames: { "0": 'Please strictly follow the format of "", "" when filling out, if the format is incorrect, only the default name of the hero will be used. The default name of the hero may be omitted.', "1": '"Anti-Mage"', "2": '"Axe"', "3": '"Bane"', "4": '"Bloodseeker"', "5": '"Crystal Maiden"', "6": '"Drow Ranger"', "7": '"Earthshaker"', "8": '"Juggernaut"', "9": '"Mirana"', "10": '"Morphling"', "11": '"Shadow Fiend"', "12": '"Phantom Lancer"', "13": '"Puck"', "14": '"Pudge"', "15": '"Razor"', "16": '"Sand King"', "17": '"Storm Spirit"', "18": '"Sven"', "19": '"Tiny"', "20": '"Vengeful Spirit"', "21": '"Windranger"', "22": '"Zeus"', "23": '"Kunkka"', "25": '"Lina"', "26": '"Lion"', "27": '"Shadow Shaman"', "28": '"Slardar"', "29": '"Tidehunter"', "30": '"Witch Doctor"', "31": '"Lich"', "32": '"Riki"', "33": '"Enigma"', "34": '"Tinker"', "35": '"Sniper"', "36": '"Necrophos"', "37": '"Warlock"', "38": '"Beastmaster"', "39": '"Queen of Pain"', "40": '"Venomancer"', "41": '"Faceless Void"', "42": '"Wraith King"', "43": '"Death Prophet"', "44": '"Phantom Assassin"', "45": '"Pugna"', "46": '"Templar Assassin"', "47": '"Viper"', "48": '"Luna"', "49": '"Dragon Knight"', "50": '"Dazzle"', "51": '"Clockwerk"', "52": '"Leshrac"', "53": `"Nature's Prophet"`, "54": '"Lifestealer"', "55": '"Dark Seer"', "56": '"Clinkz"', "57": '"Omniknight"', "58": '"Enchantress"', "59": '"Huskar"', "60": '"Night Stalker"', "61": '"Broodmother"', "62": '"Bounty Hunter"', "63": '"Weaver"', "64": '"Jakiro"', "65": '"Batrider"', "66": '"Chen"', "67": '"Spectre"', "68": '"Ancient Apparition"', "69": '"Doom"', "70": '"Ursa"', "71": '"Spirit Breaker"', "72": '"Gyrocopter"', "73": '"Alchemist"', "74": '"Invoker"', "75": '"Silencer"', "76": '"Outworld Devourer"', "77": '"Lycan"', "78": '"Brewmaster"', "79": '"Shadow Demon"', "80": '"Lone Druid"', "81": '"Chaos Knight"', "82": '"Meepo"', "83": '"Treant Protector"', "84": '"Ogre Magi"', "85": '"Undying"', "86": '"Rubick"', "87": '"Disruptor"', "88": '"Nyx Assassin"', "89": '"Naga Siren"', "90": '"Keeper of the Light"', "91": '"Io"', "92": '"Visage"', "93": '"Slark"', "94": '"Medusa"', "95": '"Troll Warlord"', "96": '"Centaur Warrunner"', "97": '"Magnus"', "98": '"Timbersaw"', "99": '"Bristleback"', "100": '"Tusk"', "101": '"Skywrath Mage"', "102": '"Abaddon"', "103": '"Elder Titan"', "104": '"Legion Commander"', "105": '"Techies"', "106": '"Ember Spirit"', "107": '"Earth Spirit"', "108": '"Underlord"', "109": '"Terrorblade"', "110": '"Phoenix"', "111": '"Oracle"', "112": '"Winter Wyvern"', "113": '"Arc Warden"', "114": '"Monkey King"', "119": '"Dark Willow"', "120": '"Pangolier"', "121": '"Grimstroke"', "123": '"Hoodwink"', "126": '"Void Spirit"', "128": '"Snapfire"', "129": '"Mars"', "131": '"Ring Master"', "135": '"Dawnbreaker"', "136": '"Marci"', "137": '"Primal Beast"', "138": '"Muerta"', "145": '"Kez"' }, broadcast: { WIN_NEGATIVE: `"Won the match by sheer luck", "Won the match by a stroke of bad luck", "Coasted to victory", "Didn't even show up for the team fight, but my teammates won 4v5"`, WIN_POSITIVE: '"Led the team to victory", "Dominated the opponents and secured the win", "Carried the game to victory", "Treated the opponents like pigs and won", "Won again; this game is just so monotonous and dull", "Simply achieved a win in the match"', LOSE_NEGATIVE: '"Got crushed and lost the match", "Lost the match miserably", "Got my head knocked sideways and lost the match with a blown mindset", "Went fishing but got eaten by the fish, lost the match", "Played terribly", "Simply suffered a loss in the match"', LOSE_POSITIVE: `"Lost the match with no way to turn it around", "Gave it my all, but still lost the match", "Though we lost, we still have honor", "Couldn't carry my teammates, lost the match", "Lost again, it hurts; I'd rather it be me losing"`, message: "{name}'s {hero_name} {comment}.\nKDA: {kda}, GPM/XPM: {gpm_xpm}, Last Hits/Denies: {lh_dn}, Damage/Tower Damage: {damage}, Kill/Death Contribution Rate: {kc_dc}", rank_changed: "Player {name} rank changed: {prev.medal} {prev.star} → {curr.medal} {curr.star}" }, logger: { fetch_guilds_failed: "Failed to fetch guild information.", match_tracked: "Tracked new match {match.id} from {#each messageToLogger as item}users in guild {item.platform}:{item.guildId} [{#each item.players as player}{player.nickname}({player.steamId}){#if player !== item.players[item.players.length - 1]}, {/if}{/each}]{#if item !== messageToLogger[messageToLogger.length - 1]}, {/if}{/each}.", parse_request_sent: "The parsing request for match {matchId} has been successfully sent to the STRATZ server.", parse_request_failed: "The parsing request for match {matchId} failed to send.", match_parsed: "Match {matchId} has been parsed{#if odParsed} by OpenDota{/if}, an image was generated and published to {#each guilds as guild}{guild.platform}:{guild.guildId}{#if guild !== guilds[guilds.length - 1]}, {/if}{/each}.", match_unparsed: "Match {matchId} exceeded the waiting time [{timeout}] and remains unparsed, an image was generated and published to {#each guilds as guild}{guild.platform}:{guild.guildId}{#if guild !== guilds[guilds.length - 1]}, {/if}{/each}.", waiting_for_parse: "Match {matchId} has not been parsed yet, has been waiting for {time} minutes.", report_sent: "Posted {title} on {platform}:{guildId}.", rank_sent: "Posted rank change information of {player.nickName} ({player.steamId}) on {platform}:{guildId}.", ejs_error: "Error rendering EJS template: {error}", cron_not_enabled: "Cron service is not enabled; match report tracking cannot run.", stratz_token_banned: "Stratz API request denied (403). This usually means your token or IP has been temporarily restricted due to unusual activity. For details, see the documentation: http://sjtdev.github.io/koishi-plugin-dota2tracker/en-US/api-403.html", stratz_api_query_error: "Stratz API returned partial data with errors: {cause}", opendota_parse_request_on_later: "OpenDota fallback is enabled. A parse request will be sent to OpenDota every {timeout} minutes, and the status will be checked every minute.", opendota_parse_request_sent: "Parse request for match {matchId} has been sent to OpenDota servers successfully.", opendota_parse_request_failed: "Failed to send parse request for match {matchId} to OpenDota servers." }, time: { years_months_ago: "{years} years and {months} months ago", years_ago: "{years} years ago" } } };
|
|
791
791
|
}
|
|
792
792
|
});
|
|
793
793
|
|
|
@@ -801,21 +801,21 @@ var require_zh_CN_command = __commonJS({
|
|
|
801
801
|
// src/locales/zh-CN.schema.yml
|
|
802
802
|
var require_zh_CN_schema = __commonJS({
|
|
803
803
|
"src/locales/zh-CN.schema.yml"(exports2, module2) {
|
|
804
|
-
module2.exports = { _config: { base: { $desc: "基础设置", STRATZ_API_TOKEN: "※必须。stratz.com的API TOKEN,可在 https://stratz.com/api 获取。", dataParsingTimeoutMinutes: "等待比赛数据解析的时间(单位:分钟)。如果数据解析时间超过等待时间,将直接生成战报而不再等待解析完成。", proxyAddress: "代理地址,留空时不使用代理", suppressStratzNetworkErrors: "开启后将stratz网络错误日志(如超时、网络不通等,但403 Forbidden除外)使用debug级别输出,默认不显示debug级日志。 \n若需要开启debug日志显示,请见 [📖 配置项#suppressstratznetworkerrors](http://sjtdev.github.io/koishi-plugin-dota2tracker/configs.html#suppressstratznetworkerrors-boolean)" }, message: { $desc: "消息设置", useHeroNicknames: "是否使用英雄别名。关闭后仅使用英雄正式名称。", urlInMessageType: { $desc: "在消息中附带链接,<br/>请选择消息类型:", $inner: ["在查询比赛与战报消息中附带stratz比赛页面链接", "在查询玩家信息消息中附带stratz玩家页面链接", "在查询英雄数据消息中附带刀塔百科对应英雄页面链接"] }, rankBroadSwitch: "段位变动播报", rankBroadStar: "星级变动播报", rankBroadLeader: "冠绝名次变动播报", rankBroadFun: "整活播报模板", maxSendItemCount: "最大发送物品图片数量,<br/> 当超过指定数量时将由下方选项决定是否发送查询结果的物品列表图片", showItemListAtTooMuchItems: "在查询结果的物品数量超过指定数量时,是否发送查询结果的物品列表图片", customItemAlias: { $desc: "额外物品别名设置<br/>当插件内置的[物品别名列表](https://github.com/sjtdev/koishi-plugin-dota2tracker/blob/master/src/locales/zh-CN.constants.json#L304-L407)中没有想要的物品别名可在此处追加,如果是插件疏漏的广为人知的物品别名推荐到源码仓库提交issue或pull request完善列表。<br/>(例如 **关键词**: 闪烁匕首,**别名**: 跳刀)", keyword: "关键词", alias: "别名" } }, report: { $desc: "总结设置", dailyReportSwitch: "日报功能", dailyReportHours: "日报时间小时", dailyReportShowCombi: "日报是否显示组合", weeklyReportSwitch: "周报功能", weeklyReportDayHours: "周报发布于周(几)的(几)点", weeklyReportShowCombi: "周报是否显示组合" }, template: { $desc: "模板设置", template_match: "生成比赛信息图片使用的模板,显示效果见 [📖 模板展示页](https://sjtdev.github.io/koishi-plugin-dota2tracker/template-match.html)。", template_player: "生成玩家信息图片使用的模板。(目前仅有一张模板)", template_hero: "生成英雄信息图片使用的模板。(目前仅有一张模板)", playerRankEstimate: "在player模板中对没有段位的玩家进行段位估算 <br>估算的段位将以灰色图片显示", templateFonts: '模板所使用的字体名。需要 koishi 所在设备安装字体文件。 \n可添加多个字体名,将从上到下回退到第一个可用字体;若所有字体都不可用,则使用系统默认字体。 \n其中字体名若包含空格或特殊字符需要在名称首尾添加引号(此处建议尽量强制使用引号); \n若使用字体族名则必须**不使用引号**,如:\n```\n"Microsoft YaHei"\nsans-serif\n```\n有关font-family的更多信息,请查阅 [📖 MDN: font-family](https://developer.mozilla.org/zh-CN/docs/Web/CSS/font-family) ' } } };
|
|
804
|
+
module2.exports = { _config: { base: { $desc: "基础设置", STRATZ_API_TOKEN: "※必须。stratz.com的API TOKEN,可在 https://stratz.com/api 获取。", dataParsingTimeoutMinutes: "等待比赛数据解析的时间(单位:分钟)。如果数据解析时间超过等待时间,将直接生成战报而不再等待解析完成。", proxyAddress: "代理地址,留空时不使用代理", suppressStratzNetworkErrors: "开启后将stratz网络错误日志(如超时、网络不通等,但403 Forbidden除外)使用debug级别输出,默认不显示debug级日志。 \n若需要开启debug日志显示,请见 [📖 配置项#suppressstratznetworkerrors](http://sjtdev.github.io/koishi-plugin-dota2tracker/configs.html#suppressstratznetworkerrors-boolean)", enableOpenDotaFallback: "启用 OpenDota 作为战报追踪与查询比赛功能的备用数据源。", OPENDOTA_API_KEY: "OpenDota 的订阅付费APIKEY, \n可在 https://www.opendota.com/api-keys 查看详情。 \nOpenDota 的免费用户此处请留空。" }, message: { $desc: "消息设置", useHeroNicknames: "是否使用英雄别名。关闭后仅使用英雄正式名称。", urlInMessageType: { $desc: "在消息中附带链接,<br/>请选择消息类型:", $inner: ["在查询比赛与战报消息中附带stratz比赛页面链接", "在查询玩家信息消息中附带stratz玩家页面链接", "在查询英雄数据消息中附带刀塔百科对应英雄页面链接"] }, rankBroadSwitch: "段位变动播报", rankBroadStar: "星级变动播报", rankBroadLeader: "冠绝名次变动播报", rankBroadFun: "整活播报模板", maxSendItemCount: "最大发送物品图片数量,<br/> 当超过指定数量时将由下方选项决定是否发送查询结果的物品列表图片", showItemListAtTooMuchItems: "在查询结果的物品数量超过指定数量时,是否发送查询结果的物品列表图片", customItemAlias: { $desc: "额外物品别名设置<br/>当插件内置的[物品别名列表](https://github.com/sjtdev/koishi-plugin-dota2tracker/blob/master/src/locales/zh-CN.constants.json#L304-L407)中没有想要的物品别名可在此处追加,如果是插件疏漏的广为人知的物品别名推荐到源码仓库提交issue或pull request完善列表。<br/>(例如 **关键词**: 闪烁匕首,**别名**: 跳刀)", keyword: "关键词", alias: "别名" } }, report: { $desc: "总结设置", dailyReportSwitch: "日报功能", dailyReportHours: "日报时间小时", dailyReportShowCombi: "日报是否显示组合", weeklyReportSwitch: "周报功能", weeklyReportDayHours: "周报发布于周(几)的(几)点", weeklyReportShowCombi: "周报是否显示组合" }, template: { $desc: "模板设置", template_match: "生成比赛信息图片使用的模板,显示效果见 [📖 模板展示页](https://sjtdev.github.io/koishi-plugin-dota2tracker/template-match.html)。", template_player: "生成玩家信息图片使用的模板。(目前仅有一张模板)", template_hero: "生成英雄信息图片使用的模板。(目前仅有一张模板)", playerRankEstimate: "在player模板中对没有段位的玩家进行段位估算 <br>估算的段位将以灰色图片显示", templateFonts: '模板所使用的字体名。需要 koishi 所在设备安装字体文件。 \n可添加多个字体名,将从上到下回退到第一个可用字体;若所有字体都不可用,则使用系统默认字体。 \n其中字体名若包含空格或特殊字符需要在名称首尾添加引号(此处建议尽量强制使用引号); \n若使用字体族名则必须**不使用引号**,如:\n```\n"Microsoft YaHei"\nsans-serif\n```\n有关font-family的更多信息,请查阅 [📖 MDN: font-family](https://developer.mozilla.org/zh-CN/docs/Web/CSS/font-family) ' } } };
|
|
805
805
|
}
|
|
806
806
|
});
|
|
807
807
|
|
|
808
808
|
// src/locales/zh-CN.template.yml
|
|
809
809
|
var require_zh_CN_template = __commonJS({
|
|
810
810
|
"src/locales/zh-CN.template.yml"(exports2, module2) {
|
|
811
|
-
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: "热烈祝贺群友 {
|
|
811
|
+
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: "分析结果不完整", analysis_by_opendota: "数据解析自 OpenDota", 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_PLACEHOLDER{name} 在天梯中再获进步,<br/>由 PREV_PLACEHOLDER 升为 CURR_PLACEHOLDER,再接再厉,再创辉煌!", rank_fun_down_message: "AVATAR_PLACEHOLDER<br/>寄", titles: { MVP: "MVP-#FFA500", Soul: "魂-#66CCFF", Rich: "富-#FFD700", Wise: "睿-#8888FF", Controller: "控-#FF00FF", Nuker: "爆-#CC0088", Breaker: "破-#DD0000", Ghost: "鬼-#CCCCCC", Utility: "辅-#20B2AA", 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: "下路", empty_extra_info: "比赛未解析或信息缺失,无法展示更多数据。" } } };
|
|
812
812
|
}
|
|
813
813
|
});
|
|
814
814
|
|
|
815
815
|
// src/locales/zh-CN.yml
|
|
816
816
|
var require_zh_CN = __commonJS({
|
|
817
817
|
"src/locales/zh-CN.yml"(exports2, module2) {
|
|
818
|
-
module2.exports = { dota2tracker: { heroes_nicknames: { "0": '请严格遵循 "", "" 格式填写(如下方默认数据,注意是英文半角符号),如果格式有误将仅使用英雄默认名称。可以不包含英雄默认名称。', "1": '"敌法师", "敌法", "AM"', "2": '"斧王"', "3": '"祸乱之源", "祸乱", "水桶腰"', "4": '"血魔"', "5": '"水晶室女", "冰女", "CM"', "6": '"卓尔游侠", "小黑"', "7": '"撼地者", "小牛", "牛头"', "8": '"主宰", "剑圣", "jugg", "奶棒人"', "9": '"米拉娜", "白虎", "pom"', "10": '"变体精灵", "水人"', "11": '"影魔", "影魔王", "SF", "影儿魔儿"', "12": '"幻影长矛手", "PL"', "13": '"帕克"', "14": '"帕吉", "屠夫", "扒鸡", "啪唧"', "15": '"雷泽", "电魂", "电棍"', "16": '"沙王", "SK"', "17": '"风暴之灵", "蓝猫"', "18": '"斯温", "流浪剑客", "流浪"', "19": '"小小"', "20": '"复仇之魂", "复仇", "VS"', "21": '"风行者", "风行", "WR"', "22": '"宙斯"', "23": '"昆卡", "船长"', "25": '"莉娜", "火女"', "26": '"莱恩", "恶魔巫师", "Lion"', "27": '"暗影萨满", "小Y", "小歪"', "28": '"斯拉达", "大鱼", "大鱼人"', "29": '"潮汐猎人", "潮汐", "西瓜皮"', "30": '"巫医"', "31": '"巫妖"', "32": '"力丸", "隐形刺客", "隐刺"', "33": '"谜团"', "34": '"修补匠", "TK", "Tinker"', "35": '"狙击手", "矮人火枪手", "火枪", "传说哥"', "36": '"瘟疫法师", "死灵法", "NEC"', "37": '"术士", "Warlock"', "38": '"兽王"', "39": '"痛苦女王", "女王", "QOP"', "40": '"剧毒术士", "剧毒"', "41": '"虚空假面", "虚空", "JB脸"', "42": '"冥魂大帝", "骷髅王"', "43": '"死亡先知", "DP"', "44": '"幻影刺客", "幻刺", "PA"', "45": '"帕格纳", "骨法", "湮灭法师"', "46": '"圣堂刺客", "圣堂", "TA"', "47": '"冥界亚龙", "毒龙", "Viper"', "48": '"露娜", "月骑", "Luna"', "49": '"龙骑士", "龙骑"', "50": '"戴泽", "暗影牧师", "暗牧"', "51": '"发条技师", "发条"', "52": '"拉席克", "老鹿"', "53": '"先知"', "54": '"噬魂鬼", "小狗"', "55": '"黑暗贤者", "黑贤"', "56": '"克林克兹", "小骷髅"', "57": '"全能骑士", "全能"', "58": '"魅惑魔女", "小鹿"', "59": '"哈斯卡", "神灵", "神灵武士"', "60": '"暗夜魔王", "夜魔"', "61": '"育母蜘蛛", "蜘蛛"', "62": '"赏金猎人", "赏金"', "63": '"编织者", "蚂蚁"', "64": '"杰奇洛", "双头龙"', "65": '"蝙蝠骑士", "蝙蝠"', "66": '"陈", "老陈"', "67": '"幽鬼", "SPE", "UG"', "68": '"远古冰魄", "冰魂"', "69": '"末日使者", "末日", "Doom"', "70": '"熊战士", "拍拍", "拍拍熊"', "71": '"裂魂人", "白牛", "sb"', "72": '"矮人直升机", "飞机"', "73": '"炼金术士", "炼金"', "74": '"祈求者", "卡尔"', "75": '"沉默术士", "沉默"', "76": '"殁境神蚀者", "黑鸟"', "77": '"狼人"', "78": '"酒仙", "熊猫", "熊猫酒仙"', "79": '"暗影恶魔", "毒狗"', "80": '"德鲁伊", "熊德"', "81": '"混沌骑士", "混沌", "CK"', "82": '"米波"', "83": '"树精卫士", "大树", "树精"', "84": '"食人魔魔法师", "蓝胖"', "85": '"不朽尸王", "尸王"', "86": '"拉比克"', "87": '"干扰者", "萨尔"', "88": '"司夜刺客", "小强"', "89": '"娜迦海妖", "小娜迦"', "90": '"光之守卫", "光法"', "91": '"艾欧", "小精灵", "精灵", "IO"', "92": '"维萨吉", "死灵龙", "死灵飞龙"', "93": '"斯拉克", "小鱼", "小鱼人"', "94": '"美杜莎", "一姐", "美杜莎"', "95": '"巨魔战将", "巨魔", "巨馍蘸酱"', "96": '"半人马战行者", "人马", "半人马"', "97": '"马格纳斯", "猛犸"', "98": '"伐木机", "花母鸡"', "99": '"钢背兽", "钢背"', "100": '"巨牙海民", "海民"', "101": '"天怒法师", "天怒"', "102": '"亚巴顿"', "103": '"上古巨神", "大牛"', "104": '"军团指挥官", "军团"', "105": '"工程师", "炸弹", "炸弹人"', "106": '"灰烬之灵", "火猫"', "107": '"大地之灵", "土猫"', "108": '"孽主", "大屁股"', "109": '"恐怖利刃", "TB"', "110": '"凤凰", "烧鸡"', "111": '"神谕者", "神谕"', "112": '"寒冬飞龙", "冰龙"', "113": '"天穹守望者", "电狗"', "114": '"齐天大圣", "大圣"', "119": '"邪影芳灵", "小仙女", "花仙子"', "120": '"石鳞剑士", "滚滚"', "121": '"天涯墨客", "墨客"', "123": '"森海飞霞", "松鼠", "小松鼠", "小松许"', "126": '"虚无之灵", "紫猫"', "128": '"电炎绝手", "老奶奶"', "129": '"玛尔斯"', "131": '"百戏大王"', "135": '"破晓辰星", "大锤"', "136": '"玛西"', "137": '"獸", "畜"', "138": '"琼英碧灵", "奶绿", "绿奶奶"', "145": '"凯", "鸟人"' }, broadcast: { WIN_NEGATIVE: '"侥幸赢得了比赛", "走狗屎运赢得了比赛", "躺赢了比赛", "打团都没来, 队友4V5赢得了比赛"', WIN_POSITIVE: '"带领团队走向了胜利", "暴打对面后赢得了胜利", " CARRY全场赢得了胜利", "把对面当猪宰了, 赢得了胜利", "又赢了, 这游戏就是这么枯燥, 且乏味", "直接进行一个比赛的赢"', LOSE_NEGATIVE: '"被人按在地上摩擦, 输掉了这场比赛", "悲惨地输掉了比赛", "头都被打歪了, 心态爆炸地输掉了比赛", "捕鱼被鱼吃了, 输掉了比赛", "打的是个几把", "直接进行一个比赛的输"', LOSE_POSITIVE: '"无力回天输掉了比赛", "尽力了, 但还是输了比赛", "背靠世界树, 虽败犹荣", "带不动队友, 输了比赛", "又输了, 很难受, 宁愿输的是我"', message: "{name}的{hero_name}{comment}。\nKDA:{kda},GPM/XPM:{gpm_xpm},补刀/反补:{lh_dn},伤害/塔伤:{damage},参战/参葬率:{kc_dc}", rank_changed: "群友 {name} 段位变动:{prev.medal}{prev.star} → {curr.medal}{curr.star}" }, logger: { fetch_guilds_failed: "获取群组信息失败,将继续后续步骤。", match_tracked: "
|
|
818
|
+
module2.exports = { dota2tracker: { heroes_nicknames: { "0": '请严格遵循 "", "" 格式填写(如下方默认数据,注意是英文半角符号),如果格式有误将仅使用英雄默认名称。可以不包含英雄默认名称。', "1": '"敌法师", "敌法", "AM"', "2": '"斧王"', "3": '"祸乱之源", "祸乱", "水桶腰"', "4": '"血魔"', "5": '"水晶室女", "冰女", "CM"', "6": '"卓尔游侠", "小黑"', "7": '"撼地者", "小牛", "牛头"', "8": '"主宰", "剑圣", "jugg", "奶棒人"', "9": '"米拉娜", "白虎", "pom"', "10": '"变体精灵", "水人"', "11": '"影魔", "影魔王", "SF", "影儿魔儿"', "12": '"幻影长矛手", "PL"', "13": '"帕克"', "14": '"帕吉", "屠夫", "扒鸡", "啪唧"', "15": '"雷泽", "电魂", "电棍"', "16": '"沙王", "SK"', "17": '"风暴之灵", "蓝猫"', "18": '"斯温", "流浪剑客", "流浪"', "19": '"小小"', "20": '"复仇之魂", "复仇", "VS"', "21": '"风行者", "风行", "WR"', "22": '"宙斯"', "23": '"昆卡", "船长"', "25": '"莉娜", "火女"', "26": '"莱恩", "恶魔巫师", "Lion"', "27": '"暗影萨满", "小Y", "小歪"', "28": '"斯拉达", "大鱼", "大鱼人"', "29": '"潮汐猎人", "潮汐", "西瓜皮"', "30": '"巫医"', "31": '"巫妖"', "32": '"力丸", "隐形刺客", "隐刺"', "33": '"谜团"', "34": '"修补匠", "TK", "Tinker"', "35": '"狙击手", "矮人火枪手", "火枪", "传说哥"', "36": '"瘟疫法师", "死灵法", "NEC"', "37": '"术士", "Warlock"', "38": '"兽王"', "39": '"痛苦女王", "女王", "QOP"', "40": '"剧毒术士", "剧毒"', "41": '"虚空假面", "虚空", "JB脸"', "42": '"冥魂大帝", "骷髅王"', "43": '"死亡先知", "DP"', "44": '"幻影刺客", "幻刺", "PA"', "45": '"帕格纳", "骨法", "湮灭法师"', "46": '"圣堂刺客", "圣堂", "TA"', "47": '"冥界亚龙", "毒龙", "Viper"', "48": '"露娜", "月骑", "Luna"', "49": '"龙骑士", "龙骑"', "50": '"戴泽", "暗影牧师", "暗牧"', "51": '"发条技师", "发条"', "52": '"拉席克", "老鹿"', "53": '"先知"', "54": '"噬魂鬼", "小狗"', "55": '"黑暗贤者", "黑贤"', "56": '"克林克兹", "小骷髅"', "57": '"全能骑士", "全能"', "58": '"魅惑魔女", "小鹿"', "59": '"哈斯卡", "神灵", "神灵武士"', "60": '"暗夜魔王", "夜魔"', "61": '"育母蜘蛛", "蜘蛛"', "62": '"赏金猎人", "赏金"', "63": '"编织者", "蚂蚁"', "64": '"杰奇洛", "双头龙"', "65": '"蝙蝠骑士", "蝙蝠"', "66": '"陈", "老陈"', "67": '"幽鬼", "SPE", "UG"', "68": '"远古冰魄", "冰魂"', "69": '"末日使者", "末日", "Doom"', "70": '"熊战士", "拍拍", "拍拍熊"', "71": '"裂魂人", "白牛", "sb"', "72": '"矮人直升机", "飞机"', "73": '"炼金术士", "炼金"', "74": '"祈求者", "卡尔"', "75": '"沉默术士", "沉默"', "76": '"殁境神蚀者", "黑鸟"', "77": '"狼人"', "78": '"酒仙", "熊猫", "熊猫酒仙"', "79": '"暗影恶魔", "毒狗"', "80": '"德鲁伊", "熊德"', "81": '"混沌骑士", "混沌", "CK"', "82": '"米波"', "83": '"树精卫士", "大树", "树精"', "84": '"食人魔魔法师", "蓝胖"', "85": '"不朽尸王", "尸王"', "86": '"拉比克"', "87": '"干扰者", "萨尔"', "88": '"司夜刺客", "小强"', "89": '"娜迦海妖", "小娜迦"', "90": '"光之守卫", "光法"', "91": '"艾欧", "小精灵", "精灵", "IO"', "92": '"维萨吉", "死灵龙", "死灵飞龙"', "93": '"斯拉克", "小鱼", "小鱼人"', "94": '"美杜莎", "一姐", "美杜莎"', "95": '"巨魔战将", "巨魔", "巨馍蘸酱"', "96": '"半人马战行者", "人马", "半人马"', "97": '"马格纳斯", "猛犸"', "98": '"伐木机", "花母鸡"', "99": '"钢背兽", "钢背"', "100": '"巨牙海民", "海民"', "101": '"天怒法师", "天怒"', "102": '"亚巴顿"', "103": '"上古巨神", "大牛"', "104": '"军团指挥官", "军团"', "105": '"工程师", "炸弹", "炸弹人"', "106": '"灰烬之灵", "火猫"', "107": '"大地之灵", "土猫"', "108": '"孽主", "大屁股"', "109": '"恐怖利刃", "TB"', "110": '"凤凰", "烧鸡"', "111": '"神谕者", "神谕"', "112": '"寒冬飞龙", "冰龙"', "113": '"天穹守望者", "电狗"', "114": '"齐天大圣", "大圣"', "119": '"邪影芳灵", "小仙女", "花仙子"', "120": '"石鳞剑士", "滚滚"', "121": '"天涯墨客", "墨客"', "123": '"森海飞霞", "松鼠", "小松鼠", "小松许"', "126": '"虚无之灵", "紫猫"', "128": '"电炎绝手", "老奶奶"', "129": '"玛尔斯"', "131": '"百戏大王"', "135": '"破晓辰星", "大锤"', "136": '"玛西"', "137": '"獸", "畜"', "138": '"琼英碧灵", "奶绿", "绿奶奶"', "145": '"凯", "鸟人"' }, broadcast: { WIN_NEGATIVE: '"侥幸赢得了比赛", "走狗屎运赢得了比赛", "躺赢了比赛", "打团都没来, 队友4V5赢得了比赛"', WIN_POSITIVE: '"带领团队走向了胜利", "暴打对面后赢得了胜利", " CARRY全场赢得了胜利", "把对面当猪宰了, 赢得了胜利", "又赢了, 这游戏就是这么枯燥, 且乏味", "直接进行一个比赛的赢"', LOSE_NEGATIVE: '"被人按在地上摩擦, 输掉了这场比赛", "悲惨地输掉了比赛", "头都被打歪了, 心态爆炸地输掉了比赛", "捕鱼被鱼吃了, 输掉了比赛", "打的是个几把", "直接进行一个比赛的输"', LOSE_POSITIVE: '"无力回天输掉了比赛", "尽力了, 但还是输了比赛", "背靠世界树, 虽败犹荣", "带不动队友, 输了比赛", "又输了, 很难受, 宁愿输的是我"', message: "{name}的{hero_name}{comment}。\nKDA:{kda},GPM/XPM:{gpm_xpm},补刀/反补:{lh_dn},伤害/塔伤:{damage},参战/参葬率:{kc_dc}", rank_changed: "群友 {name} 段位变动:{prev.medal}{prev.star} → {curr.medal}{curr.star}" }, logger: { fetch_guilds_failed: "获取群组信息失败,将继续后续步骤。", match_tracked: "追踪到最新比赛 {match.id} 来自{#each messageToLogger as item}群组 {item.platform}:{item.guildId} 的玩家 [{#each item.players as player}{player.nickname}({player.steamId}){#if player !== item.players[item.players.length - 1]}、{/if}{/each}]{#if item !== messageToLogger[messageToLogger.length - 1]}、{/if}{/each}。", parse_request_sent: "比赛 {matchId} 解析请求已成功发送至STRATZ服务器。", parse_request_failed: "比赛 {matchId} 解析请求发送失败。", match_parsed: "比赛 {matchId} 已{#if odParsed}由 OpenDota {/if}解析,生成图片并发布于{#each guilds as guild}{guild.platform}:{guild.guildId}{#if guild !== guilds[guilds.length - 1]}、{/if}{/each}。", match_unparsed: "比赛 {matchId} 超过等待时间[{timeout}分钟]仍未解析,生成图片并发布于{#each guilds as guild}{guild.platform}:{guild.guildId}{#if guild !== guilds[guilds.length - 1]}、{/if}{/each}。", waiting_for_parse: "比赛 {matchId} 尚未解析完成,已等待{time}分钟。", report_sent: "发布{title}于{platform}:{guildId}。", rank_sent: "向{platform}:{guildId}发布{player.nickName}({player.steamId})的段位变动信息。", ejs_error: "EJS模板渲染错误:{error}", cron_not_enabled: "未启用cron服务,无法运行战报追踪等定时任务。", stratz_token_banned: "Stratz API 请求被拒绝(403),若频繁发生很有可能意味着您的Token或IP因异常使用被临时限制访问。有关此报错请见文档:http://sjtdev.github.io/koishi-plugin-dota2tracker/api-403.html", stratz_api_query_error: "Stratz API 返回了有效数据,但报错: {cause}", opendota_parse_request_on_later: "已启用 OpenDota 备用查询,将每{timeout}分钟发送一次 OpenDota 解析请求,并每分钟检测比赛是否已被 OpenDota 解析。", opendota_parse_request_sent: "比赛 {matchId} 解析请求已成功发送至 OpenDota 服务器。", opendota_parse_request_failed: "比赛 {matchId} 解析请求向 OpenDota 服务器发送失败。" }, time: { years_months_ago: "{years}年{months}个月前", years_ago: "{years}年前" } } };
|
|
819
819
|
}
|
|
820
820
|
});
|
|
821
821
|
|
|
@@ -1134,10 +1134,7 @@ var HeroService = class _HeroService extends import_koishi2.Service {
|
|
|
1134
1134
|
hero.winRate = hero.matchCount > 0 ? hero.winCount / hero.matchCount : 0;
|
|
1135
1135
|
});
|
|
1136
1136
|
}
|
|
1137
|
-
|
|
1138
|
-
const endOfDay = now.endOf("day");
|
|
1139
|
-
const ttl = endOfDay.diff(now).toMillis();
|
|
1140
|
-
this.ctx.dota2tracker.cache.setWweeklyMetaCache(cacheKey, weeklyHeroMeta, ttl);
|
|
1137
|
+
this.ctx.dota2tracker.cache.setWweeklyMetaCache(cacheKey, weeklyHeroMeta);
|
|
1141
1138
|
return weeklyHeroMeta;
|
|
1142
1139
|
}
|
|
1143
1140
|
async getHeroDetails(input, languageTag, isRandom = false) {
|
|
@@ -1301,7 +1298,7 @@ var ItemService = class _ItemService extends import_koishi3.Service {
|
|
|
1301
1298
|
}
|
|
1302
1299
|
static getFormattedItemListData(rawItems) {
|
|
1303
1300
|
const processItemName = /* @__PURE__ */ __name((name2) => name2.replace(/^item_/i, "").replace(/^recipe_/i, "recipe_"), "processItemName");
|
|
1304
|
-
const [recipes,
|
|
1301
|
+
const [recipes, items3] = rawItems.reduce(
|
|
1305
1302
|
(acc, item) => {
|
|
1306
1303
|
const processed = {
|
|
1307
1304
|
...item,
|
|
@@ -1315,14 +1312,14 @@ var ItemService = class _ItemService extends import_koishi3.Service {
|
|
|
1315
1312
|
[[], []]
|
|
1316
1313
|
);
|
|
1317
1314
|
const itemMap = /* @__PURE__ */ new Map();
|
|
1318
|
-
|
|
1315
|
+
items3.concat(recipes).forEach(
|
|
1319
1316
|
(item) => itemMap.set(item.id, {
|
|
1320
1317
|
id: item.id,
|
|
1321
1318
|
name: item.name,
|
|
1322
1319
|
name_loc: item.name_loc
|
|
1323
1320
|
})
|
|
1324
1321
|
);
|
|
1325
|
-
const processedItems =
|
|
1322
|
+
const processedItems = items3.map((baseItem) => {
|
|
1326
1323
|
const recipe = recipes.find((r) => r.name === `recipe_${baseItem.name.replace("item_", "")}`);
|
|
1327
1324
|
return {
|
|
1328
1325
|
...baseItem,
|
|
@@ -1355,19 +1352,19 @@ var ItemService = class _ItemService extends import_koishi3.Service {
|
|
|
1355
1352
|
}))
|
|
1356
1353
|
}));
|
|
1357
1354
|
}
|
|
1358
|
-
searchItems(
|
|
1355
|
+
searchItems(items3, keyword, languageTag, config) {
|
|
1359
1356
|
if (!keyword) return [];
|
|
1360
1357
|
const alias = this.ctx.dota2tracker.i18n.getConstantLocale(languageTag).dota2tracker.items_alias?.[keyword] ?? config.customItemAlias.filter((cia) => cia.alias == keyword).map((cia) => cia.keyword);
|
|
1361
|
-
const exactMatch =
|
|
1358
|
+
const exactMatch = items3.filter(
|
|
1362
1359
|
(item) => alias?.some((a) => item.name_loc.trim().toLowerCase() == a.toLowerCase()) || item.name_loc.trim().toLowerCase() === keyword.trim().toLowerCase() || Number.isInteger(Number(keyword)) && item.id === Number(keyword)
|
|
1363
1360
|
);
|
|
1364
1361
|
if (exactMatch.length) return exactMatch;
|
|
1365
|
-
return this.fuzzySearchItems(alias.length ? alias : [keyword],
|
|
1362
|
+
return this.fuzzySearchItems(alias.length ? alias : [keyword], items3);
|
|
1366
1363
|
}
|
|
1367
|
-
fuzzySearchItems(keywords,
|
|
1364
|
+
fuzzySearchItems(keywords, items3) {
|
|
1368
1365
|
const resultMap = /* @__PURE__ */ new Map();
|
|
1369
1366
|
if (!keywords.length) return [];
|
|
1370
|
-
for (const item of
|
|
1367
|
+
for (const item of items3) {
|
|
1371
1368
|
const cleanName = item.name_loc.toLowerCase().replace(/[^\p{L}\p{N}]/gu, "").trim();
|
|
1372
1369
|
let matchAllKeywords = true;
|
|
1373
1370
|
for (const keyword of keywords) {
|
|
@@ -1403,10 +1400,23 @@ var MatchService = class _MatchService extends import_koishi4.Service {
|
|
|
1403
1400
|
static {
|
|
1404
1401
|
__name(this, "MatchService");
|
|
1405
1402
|
}
|
|
1406
|
-
async getMatchResult({
|
|
1403
|
+
async getMatchResult({
|
|
1404
|
+
matchId,
|
|
1405
|
+
requestParse,
|
|
1406
|
+
requsetOpenDota
|
|
1407
|
+
}) {
|
|
1407
1408
|
const matchQuery = await this.getMatchData(matchId);
|
|
1408
1409
|
if (matchQuery) {
|
|
1409
1410
|
if (!_MatchService.isMatchParsed(matchQuery) && requestParse && this.ctx.cron) {
|
|
1411
|
+
if (requsetOpenDota) {
|
|
1412
|
+
const odMatchQuery = await this.getOpenDotaMatchData(matchId);
|
|
1413
|
+
if (odMatchQuery) {
|
|
1414
|
+
return {
|
|
1415
|
+
status: "READY",
|
|
1416
|
+
matchData: odMatchQuery
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1410
1420
|
return {
|
|
1411
1421
|
status: "PENDING",
|
|
1412
1422
|
matchId
|
|
@@ -1443,14 +1453,29 @@ var MatchService = class _MatchService extends import_koishi4.Service {
|
|
|
1443
1453
|
throw error;
|
|
1444
1454
|
}
|
|
1445
1455
|
}
|
|
1456
|
+
async getOpenDotaMatchData(matchId) {
|
|
1457
|
+
const odMatch = await this.ctx.dota2tracker.opendotaAPI.queryMatchInfo(matchId);
|
|
1458
|
+
if (odMatch?.od_data?.has_parsed) {
|
|
1459
|
+
const odMatchQuery = { match: this.ctx.dota2tracker.opendotaAdapter.transform(odMatch) };
|
|
1460
|
+
this.ctx.dota2tracker.cache.setMatchCache(matchId, odMatchQuery, this.pluginVersion);
|
|
1461
|
+
return odMatchQuery;
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1446
1464
|
async formatMatchData(matchQuery, languageTag) {
|
|
1447
1465
|
try {
|
|
1448
1466
|
let constantsQuery = await this.ctx.dota2tracker.cache.getFacetConstantsCache(languageTag);
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
!constantsQuery
|
|
1452
|
-
|
|
1467
|
+
const isFromOpenDota = matchQuery.match?.odParsed === true;
|
|
1468
|
+
let needsRefetch = false;
|
|
1469
|
+
if (!constantsQuery) {
|
|
1470
|
+
needsRefetch = true;
|
|
1471
|
+
} else if (!isFromOpenDota) {
|
|
1472
|
+
if (!matchQuery.constants?.gameVersions?.[0]?.id || !constantsQuery.constants?.gameVersions?.[0]?.id || matchQuery.constants.gameVersions[0].id !== constantsQuery.constants.gameVersions[0].id) {
|
|
1473
|
+
needsRefetch = true;
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
if (needsRefetch) {
|
|
1453
1477
|
constantsQuery = await this.ctx.dota2tracker.stratzAPI.queryConstants(languageTag);
|
|
1478
|
+
}
|
|
1454
1479
|
const facetData = await _MatchService.constantsInjectFacetData(constantsQuery, matchQuery, languageTag, this.ctx.dota2tracker.hero);
|
|
1455
1480
|
this.ctx.dota2tracker.cache.setFacetConstantsCache(languageTag, constantsQuery);
|
|
1456
1481
|
const match = _MatchService.extendMatchData(matchQuery, facetData);
|
|
@@ -1464,8 +1489,8 @@ var MatchService = class _MatchService extends import_koishi4.Service {
|
|
|
1464
1489
|
const facetData = {};
|
|
1465
1490
|
for (let player of matchQuery.match.players) {
|
|
1466
1491
|
if (player.variant != null) {
|
|
1467
|
-
const constantsFacet = constantsQuery.constants.facets.find((facet) => facet.id
|
|
1468
|
-
let displayName = constantsFacet
|
|
1492
|
+
const constantsFacet = constantsQuery.constants.facets.find((facet) => facet.id === player.hero.facets[player.variant - 1]?.facetId || facet.name === player.hero.facets[player.variant - 1]?.name);
|
|
1493
|
+
let displayName = constantsFacet?.language?.displayName;
|
|
1469
1494
|
if (!displayName && heroService) {
|
|
1470
1495
|
const valveFacet = (await heroService.getHeroDetails(player.hero.id, languageTag)).facets.find((facet) => facet.index === player.variant - 1);
|
|
1471
1496
|
constantsFacet.language.displayName = valveFacet.title_loc;
|
|
@@ -1715,8 +1740,8 @@ var MatchService = class _MatchService extends import_koishi4.Service {
|
|
|
1715
1740
|
match.durationTime = sec2time(match.durationSeconds);
|
|
1716
1741
|
return match;
|
|
1717
1742
|
}
|
|
1718
|
-
static isMatchParsed(
|
|
1719
|
-
return match
|
|
1743
|
+
static isMatchParsed(matchQuery) {
|
|
1744
|
+
return matchQuery?.match?.parsedDateTime && matchQuery?.match?.players.filter((player) => player?.stats?.heroDamageReport?.dealtTotal).length > 0;
|
|
1720
1745
|
}
|
|
1721
1746
|
};
|
|
1722
1747
|
function createItemObject(itemId, purchaseTimesMap, purchaseTimeIndices) {
|
|
@@ -2040,6 +2065,7 @@ __name(validateRank, "validateRank");
|
|
|
2040
2065
|
|
|
2041
2066
|
// src/app/data/cache.ts
|
|
2042
2067
|
var import_koishi6 = require("koishi");
|
|
2068
|
+
var import_luxon4 = require("luxon");
|
|
2043
2069
|
var CacheService = class extends import_koishi6.Service {
|
|
2044
2070
|
static {
|
|
2045
2071
|
__name(this, "CacheService");
|
|
@@ -2047,8 +2073,24 @@ var CacheService = class extends import_koishi6.Service {
|
|
|
2047
2073
|
constructor(ctx) {
|
|
2048
2074
|
super(ctx, "dota2tracker.cache", true);
|
|
2049
2075
|
}
|
|
2050
|
-
|
|
2051
|
-
|
|
2076
|
+
get msUntilUTCEndOfDay() {
|
|
2077
|
+
const now = import_luxon4.DateTime.utc();
|
|
2078
|
+
const endOfDay = now.endOf("day");
|
|
2079
|
+
const ttl = endOfDay.diff(now).toMillis();
|
|
2080
|
+
return ttl;
|
|
2081
|
+
}
|
|
2082
|
+
addOpendotaAPIRequestLog(request, count = 1) {
|
|
2083
|
+
this.ctx.cache.set("dt_opendota_api_request_log", String(Date.now()), { count, request }, this.msUntilUTCEndOfDay);
|
|
2084
|
+
}
|
|
2085
|
+
async getTodayOpendotaAPIRequestCount() {
|
|
2086
|
+
let count = 0;
|
|
2087
|
+
for await (const value of this.ctx.cache.values("dt_opendota_api_request_log")) {
|
|
2088
|
+
count += value.count;
|
|
2089
|
+
}
|
|
2090
|
+
return count;
|
|
2091
|
+
}
|
|
2092
|
+
setWweeklyMetaCache(key, value) {
|
|
2093
|
+
this.ctx.cache.set("dt_weekly_metadata", key, value, this.msUntilUTCEndOfDay);
|
|
2052
2094
|
}
|
|
2053
2095
|
async getWeeklyMetaCache(key) {
|
|
2054
2096
|
return this.ctx.cache.get("dt_weekly_metadata", key);
|
|
@@ -2476,7 +2518,7 @@ var ImageFormat = /* @__PURE__ */ ((ImageFormat2) => {
|
|
|
2476
2518
|
})(ImageFormat || {});
|
|
2477
2519
|
|
|
2478
2520
|
// src/app/presentation/image.renderer.ts
|
|
2479
|
-
var
|
|
2521
|
+
var import_luxon5 = require("luxon");
|
|
2480
2522
|
var ImageRenderer = class extends import_koishi10.Service {
|
|
2481
2523
|
constructor(ctx, pluginDir3) {
|
|
2482
2524
|
super(ctx, "dota2tracker.image", true);
|
|
@@ -2503,7 +2545,7 @@ var ImageRenderer = class extends import_koishi10.Service {
|
|
|
2503
2545
|
ImageType,
|
|
2504
2546
|
ImageFormat,
|
|
2505
2547
|
dotaconstants: dotaconstants5,
|
|
2506
|
-
DateTime:
|
|
2548
|
+
DateTime: import_luxon5.DateTime,
|
|
2507
2549
|
$t: /* @__PURE__ */ __name((key, params) => this.ctx.dota2tracker.i18n.$t(languageTag, key, params), "$t"),
|
|
2508
2550
|
languageTag,
|
|
2509
2551
|
Random: import_koishi10.Random,
|
|
@@ -2547,7 +2589,7 @@ var ImageRenderer = class extends import_koishi10.Service {
|
|
|
2547
2589
|
|
|
2548
2590
|
// src/app/presentation/message.builder.ts
|
|
2549
2591
|
var import_koishi11 = require("koishi");
|
|
2550
|
-
var
|
|
2592
|
+
var import_luxon6 = require("luxon");
|
|
2551
2593
|
var MessageBuilder = class extends import_koishi11.Service {
|
|
2552
2594
|
static {
|
|
2553
2595
|
__name(this, "MessageBuilder");
|
|
@@ -2765,7 +2807,7 @@ var MessageBuilder = class extends import_koishi11.Service {
|
|
|
2765
2807
|
`;
|
|
2766
2808
|
for (const member of members) {
|
|
2767
2809
|
const winRate = typeof member.winRate === "number" && !isNaN(member.winRate) ? `${roundToDecimalPlaces(member.winRate * 100).toFixed(1)}%` : "-----";
|
|
2768
|
-
const lastMatch = member.lastMatchTime ? formatCustomRelativeTime(
|
|
2810
|
+
const lastMatch = member.lastMatchTime ? formatCustomRelativeTime(import_luxon6.DateTime.fromSeconds(member.lastMatchTime), this.ctx.dota2tracker.i18n, languageTag) : "----------";
|
|
2769
2811
|
ejs2 += `
|
|
2770
2812
|
<tr>
|
|
2771
2813
|
<td>${member.name}</td>
|
|
@@ -2835,7 +2877,7 @@ __name(customConvertArrayOfString, "customConvertArrayOfString");
|
|
|
2835
2877
|
|
|
2836
2878
|
// src/app/tasks/match-watcher.task.ts
|
|
2837
2879
|
var import_koishi12 = require("koishi");
|
|
2838
|
-
var
|
|
2880
|
+
var import_luxon7 = require("luxon");
|
|
2839
2881
|
var MatchWatcherTask = class extends import_koishi12.Service {
|
|
2840
2882
|
static {
|
|
2841
2883
|
__name(this, "MatchWatcherTask");
|
|
@@ -2857,7 +2899,7 @@ var MatchWatcherTask = class extends import_koishi12.Service {
|
|
|
2857
2899
|
}
|
|
2858
2900
|
async discoverNewMatches(playersData, activePlayers) {
|
|
2859
2901
|
const sendedMatchIds = await this.ctx.dota2tracker.cache.getSendedMatchIds();
|
|
2860
|
-
const lastMatches = playersData.map((player) => player.matches[0]).filter((match) => match && match.id).filter((item, index, self) => index === self.findIndex((t) => t.id === item.id)).filter((match) =>
|
|
2902
|
+
const lastMatches = playersData.map((player) => player.matches[0]).filter((match) => match && match.id).filter((item, index, self) => index === self.findIndex((t) => t.id === item.id)).filter((match) => import_luxon7.DateTime.fromSeconds(match.startDateTime) > import_luxon7.DateTime.now().minus({ days: 1 })).filter((match) => !this.ctx.dota2tracker.parsePolling.isPending(match.id)).filter((match) => !sendedMatchIds.has(match.id));
|
|
2861
2903
|
for (const match of lastMatches) {
|
|
2862
2904
|
const steamIdsInMatch = new Set(match.players.map((p) => p.steamAccount.id));
|
|
2863
2905
|
const relevantActivePlayers = activePlayers.filter((p) => steamIdsInMatch.has(p.steamId));
|
|
@@ -2936,7 +2978,7 @@ var MatchWatcherTask = class extends import_koishi12.Service {
|
|
|
2936
2978
|
isRising: rankMap.get(subPlayer.steamId).rank > subPlayer.rank.rank || rankMap.get(subPlayer.steamId).rank == subPlayer.rank.rank && rankMap.get(subPlayer.steamId).leader < subPlayer.rank.leader || rankMap.get(subPlayer.steamId).leader > 0 && subPlayer.rank.leader == null,
|
|
2937
2979
|
prevRank,
|
|
2938
2980
|
currRank,
|
|
2939
|
-
date:
|
|
2981
|
+
date: import_luxon7.DateTime.now().toFormat(languageTag === "zh-CN" ? "yyyy/MM/dd HH时mm分" : "yyyy/MM/dd HH:mm")
|
|
2940
2982
|
},
|
|
2941
2983
|
"rank_fun",
|
|
2942
2984
|
"rank" /* Rank */,
|
|
@@ -2960,7 +3002,7 @@ var MatchWatcherTask = class extends import_koishi12.Service {
|
|
|
2960
3002
|
|
|
2961
3003
|
// src/app/tasks/parse-polling.task.ts
|
|
2962
3004
|
var import_koishi13 = require("koishi");
|
|
2963
|
-
var
|
|
3005
|
+
var import_luxon8 = require("luxon");
|
|
2964
3006
|
var ParsePollingTask = class extends import_koishi13.Service {
|
|
2965
3007
|
static {
|
|
2966
3008
|
__name(this, "ParsePollingTask");
|
|
@@ -3002,7 +3044,12 @@ var ParsePollingTask = class extends import_koishi13.Service {
|
|
|
3002
3044
|
entry.subscribers.push(...subscribers);
|
|
3003
3045
|
this.pendingMatches.set(matchId, entry);
|
|
3004
3046
|
if (isNewEntry) {
|
|
3005
|
-
this.ctx.dota2tracker.stratzAPI.requestParseMatch(matchId).then((value) =>
|
|
3047
|
+
this.ctx.dota2tracker.stratzAPI.requestParseMatch(matchId).then((value) => {
|
|
3048
|
+
this.logger.info(this.ctx.dota2tracker.i18n.gt(`dota2tracker.logger.parse_request_${value ? "sent" : "failed"}`, { matchId }));
|
|
3049
|
+
if (this.config.enableOpenDotaFallback) {
|
|
3050
|
+
this.logger.info(this.ctx.dota2tracker.i18n.gt("dota2tracker.logger.opendota_parse_request_on_later", { timeout: 5 }));
|
|
3051
|
+
}
|
|
3052
|
+
});
|
|
3006
3053
|
}
|
|
3007
3054
|
}
|
|
3008
3055
|
async polling() {
|
|
@@ -3012,10 +3059,18 @@ var ParsePollingTask = class extends import_koishi13.Service {
|
|
|
3012
3059
|
if (this.pollingIndex >= matches.length) this.pollingIndex = 0;
|
|
3013
3060
|
const pendingMatch = matches[this.pollingIndex];
|
|
3014
3061
|
this.pollingIndex++;
|
|
3015
|
-
const
|
|
3016
|
-
const
|
|
3017
|
-
const
|
|
3062
|
+
const requestTime = import_luxon8.DateTime.fromJSDate(pendingMatch.requestTime);
|
|
3063
|
+
const timeout = requestTime.plus({ minutes: this.config.dataParsingTimeoutMinutes });
|
|
3064
|
+
const needToWait = import_luxon8.DateTime.now() < timeout;
|
|
3065
|
+
const result = await this.ctx.dota2tracker.match.getMatchResult({ matchId: pendingMatch.matchId, requestParse: needToWait, requsetOpenDota: this.config.enableOpenDotaFallback });
|
|
3018
3066
|
if (result.status === "PENDING") {
|
|
3067
|
+
const waitingTime = import_luxon8.DateTime.now().diff(requestTime, "minutes");
|
|
3068
|
+
const waitingTimeMinutes = Math.floor(waitingTime.minutes);
|
|
3069
|
+
if (waitingTimeMinutes > 0 && waitingTimeMinutes % 5 === 0) {
|
|
3070
|
+
this.logger.info(this.ctx.dota2tracker.i18n.gt("dota2tracker.logger.waiting_for_parse", { matchId: pendingMatch.matchId, time: waitingTimeMinutes }));
|
|
3071
|
+
if (this.config.enableOpenDotaFallback)
|
|
3072
|
+
this.ctx.dota2tracker.opendotaAPI.requestParseMatch(pendingMatch.matchId).then((value) => this.logger.info(this.ctx.dota2tracker.i18n.gt(`dota2tracker.logger.opendota_parse_request_${value ? "sent" : "failed"}`, { matchId: pendingMatch.matchId })));
|
|
3073
|
+
}
|
|
3019
3074
|
return;
|
|
3020
3075
|
}
|
|
3021
3076
|
if (result.status === "READY") {
|
|
@@ -3048,7 +3103,8 @@ var ParsePollingTask = class extends import_koishi13.Service {
|
|
|
3048
3103
|
this.ctx.dota2tracker.i18n.gt(`dota2tracker.logger.match_${result.matchData.match.parsedDateTime ? "parsed" : "unparsed"}`, {
|
|
3049
3104
|
matchId: result.matchData.match.id,
|
|
3050
3105
|
timeout: this.config.dataParsingTimeoutMinutes,
|
|
3051
|
-
guilds: guildsToLogger
|
|
3106
|
+
guilds: guildsToLogger,
|
|
3107
|
+
odParsed: result.matchData.match["odParsed"]
|
|
3052
3108
|
})
|
|
3053
3109
|
);
|
|
3054
3110
|
this.ctx.dota2tracker.cache.markMatchAsSended(pendingMatch.matchId);
|
|
@@ -3080,7 +3136,7 @@ var ParsePollingTask = class extends import_koishi13.Service {
|
|
|
3080
3136
|
|
|
3081
3137
|
// src/app/tasks/report.task.ts
|
|
3082
3138
|
var import_koishi14 = require("koishi");
|
|
3083
|
-
var
|
|
3139
|
+
var import_luxon9 = require("luxon");
|
|
3084
3140
|
var ReportTask = class extends import_koishi14.Service {
|
|
3085
3141
|
static {
|
|
3086
3142
|
__name(this, "ReportTask");
|
|
@@ -3094,7 +3150,7 @@ var ReportTask = class extends import_koishi14.Service {
|
|
|
3094
3150
|
if (this.config.dailyReportSwitch) {
|
|
3095
3151
|
ctx.cron(`0 ${this.config.dailyReportHours} * * *`, async () => {
|
|
3096
3152
|
try {
|
|
3097
|
-
const oneDayAgo =
|
|
3153
|
+
const oneDayAgo = Math.floor(import_luxon9.DateTime.now().minus({ days: 1 }).toSeconds());
|
|
3098
3154
|
await this.report(oneDayAgo, "dota2tracker.template.yesterdays_summary", this.config.dailyReportShowCombi);
|
|
3099
3155
|
} catch (error) {
|
|
3100
3156
|
handleError(error, this.logger, this.ctx.dota2tracker.i18n, this.config);
|
|
@@ -3104,7 +3160,7 @@ var ReportTask = class extends import_koishi14.Service {
|
|
|
3104
3160
|
if (this.config.weeklyReportSwitch) {
|
|
3105
3161
|
ctx.cron(`0 ${this.config.weeklyReportDayHours[1]} * * ${this.config.weeklyReportDayHours[0]}`, async () => {
|
|
3106
3162
|
try {
|
|
3107
|
-
const oneWeekAgo =
|
|
3163
|
+
const oneWeekAgo = Math.floor(import_luxon9.DateTime.now().minus({ weeks: 1 }).toSeconds());
|
|
3108
3164
|
await this.report(oneWeekAgo, "dota2tracker.template.last_weeks_summary", this.config.weeklyReportShowCombi);
|
|
3109
3165
|
} catch (error) {
|
|
3110
3166
|
handleError(error, this.logger, this.ctx.dota2tracker.i18n, this.config);
|
|
@@ -3248,7 +3304,7 @@ async function resolvePlayerAndHandleErrors(ctx, session, input) {
|
|
|
3248
3304
|
__name(resolvePlayerAndHandleErrors, "resolvePlayerAndHandleErrors");
|
|
3249
3305
|
|
|
3250
3306
|
// src/app/commands/hero-of-the-day.command.ts
|
|
3251
|
-
var
|
|
3307
|
+
var import_luxon10 = require("luxon");
|
|
3252
3308
|
function registerHeroOfTheDayCommand(ctx) {
|
|
3253
3309
|
ctx.command("dota2tracker.hero-of-the-day <input_data>").alias("今日英雄").option("days", "-d <value:number>").action(async ({ session, options }, input_data) => {
|
|
3254
3310
|
const steamId = await resolvePlayerAndHandleErrors(ctx, session, input_data);
|
|
@@ -3256,7 +3312,7 @@ function registerHeroOfTheDayCommand(ctx) {
|
|
|
3256
3312
|
const days = clamp(options.days, 1, 180, 30);
|
|
3257
3313
|
const result = await ctx.dota2tracker.stratzAPI.queryPlayerPerformanceForHeroRecommendation({
|
|
3258
3314
|
steamAccountId: steamId,
|
|
3259
|
-
recentDateTime:
|
|
3315
|
+
recentDateTime: import_luxon10.DateTime.now().minus({ days }).toUnixInteger()
|
|
3260
3316
|
});
|
|
3261
3317
|
const recommendationPromise = ctx.dota2tracker.player.getHeroRecommendation(steamId, result.player);
|
|
3262
3318
|
const metaPromise = ctx.dota2tracker.hero.getWeeklyHeroMeta(PlayerService.estimateWeightedRank(result.player));
|
|
@@ -3360,7 +3416,7 @@ async function handleQueryMatchCommand(ctx, config, session, options, matchId) {
|
|
|
3360
3416
|
if (result.status === "PENDING") {
|
|
3361
3417
|
const subscriber = ctx.dota2tracker.parsePolling.createSubscriberByCommand(session, languageTag, { templateName: options?.template });
|
|
3362
3418
|
ctx.dota2tracker.parsePolling.add(result.matchId, [subscriber]);
|
|
3363
|
-
return session.text(".waiting_for_parse");
|
|
3419
|
+
return session.text("commands.dota2tracker.query-match.messages.waiting_for_parse");
|
|
3364
3420
|
} else if (result.status === "NOT_FOUND") {
|
|
3365
3421
|
return session.text(".query_failed");
|
|
3366
3422
|
} else {
|
|
@@ -3468,8 +3524,412 @@ function registerUserCommand(ctx) {
|
|
|
3468
3524
|
}
|
|
3469
3525
|
__name(registerUserCommand, "registerUserCommand");
|
|
3470
3526
|
|
|
3471
|
-
// src/
|
|
3527
|
+
// src/app/data/opendota.api.ts
|
|
3472
3528
|
var import_koishi15 = require("koishi");
|
|
3529
|
+
var OpenDotaAPI = class extends import_koishi15.Service {
|
|
3530
|
+
static {
|
|
3531
|
+
__name(this, "OpenDotaAPI");
|
|
3532
|
+
}
|
|
3533
|
+
BASE_URL = "https://api.opendota.com/api";
|
|
3534
|
+
constructor(ctx) {
|
|
3535
|
+
super(ctx, "opendota-api", true);
|
|
3536
|
+
this.config = ctx.config;
|
|
3537
|
+
}
|
|
3538
|
+
async queryMatchInfo(matchId) {
|
|
3539
|
+
const path5 = `${this.BASE_URL}/matches/${matchId}`;
|
|
3540
|
+
const data = await this.fetchData("GET", path5);
|
|
3541
|
+
this.ctx.dota2tracker.cache.addOpendotaAPIRequestLog(path5, 1);
|
|
3542
|
+
return data;
|
|
3543
|
+
}
|
|
3544
|
+
async requestParseMatch(matchId) {
|
|
3545
|
+
const path5 = `${this.BASE_URL}/request/${matchId}`;
|
|
3546
|
+
const job = await this.fetchData("POST", path5);
|
|
3547
|
+
this.ctx.dota2tracker.cache.addOpendotaAPIRequestLog(path5, 10);
|
|
3548
|
+
return job;
|
|
3549
|
+
}
|
|
3550
|
+
async fetchData(type, path5, data) {
|
|
3551
|
+
const config = {
|
|
3552
|
+
responseType: "json",
|
|
3553
|
+
proxyAgent: this.config.proxyAddress || void 0
|
|
3554
|
+
};
|
|
3555
|
+
if (this.config.OPENDOTA_API_KEY) {
|
|
3556
|
+
config.headers = {
|
|
3557
|
+
...config.headers,
|
|
3558
|
+
Authorization: `Bearer ${this.config.OPENDOTA_API_KEY}`
|
|
3559
|
+
};
|
|
3560
|
+
}
|
|
3561
|
+
switch (type) {
|
|
3562
|
+
case "GET":
|
|
3563
|
+
return await this.ctx.http.get(path5, config);
|
|
3564
|
+
case "POST":
|
|
3565
|
+
return await this.ctx.http.post(path5, data, config);
|
|
3566
|
+
// POST需要空数据占位
|
|
3567
|
+
default:
|
|
3568
|
+
throw new Error(`Unsupported HTTP method: ${type}`);
|
|
3569
|
+
}
|
|
3570
|
+
}
|
|
3571
|
+
};
|
|
3572
|
+
|
|
3573
|
+
// src/app/core/opendota.adapter.ts
|
|
3574
|
+
var import_koishi16 = require("koishi");
|
|
3575
|
+
var dotaconstants6 = __toESM(require("dotaconstants"));
|
|
3576
|
+
var OpenDotaAdapter = class extends import_koishi16.Service {
|
|
3577
|
+
static {
|
|
3578
|
+
__name(this, "OpenDotaAdapter");
|
|
3579
|
+
}
|
|
3580
|
+
constructor(ctx) {
|
|
3581
|
+
super(ctx, "opendota-adapter", true);
|
|
3582
|
+
}
|
|
3583
|
+
transform(_match) {
|
|
3584
|
+
determinePlayerPositions(_match);
|
|
3585
|
+
const players = [];
|
|
3586
|
+
for (const _player of _match.players) {
|
|
3587
|
+
const player = {
|
|
3588
|
+
steamAccountId: _player.account_id,
|
|
3589
|
+
level: _player.level,
|
|
3590
|
+
variant: _player.hero_variant,
|
|
3591
|
+
leaverStatus: convertLeaverStatus(_player.leaver_status),
|
|
3592
|
+
partyId: _player.party_id,
|
|
3593
|
+
position: convertPosition(_player.calculatedPosition),
|
|
3594
|
+
playerSlot: _player.player_slot,
|
|
3595
|
+
lane: convertLane(_player.lane),
|
|
3596
|
+
imp: determineIMP(_player),
|
|
3597
|
+
kills: _player.kills,
|
|
3598
|
+
deaths: _player.deaths,
|
|
3599
|
+
assists: _player.assists,
|
|
3600
|
+
isRadiant: _player.isRadiant,
|
|
3601
|
+
networth: _player.net_worth,
|
|
3602
|
+
// 因万恶的TypeScript严格类型检查,此处优雅的reduce赋值物品算法无法使用!
|
|
3603
|
+
// ...[0, 1, 2, 3, 4, 5].reduce((acc, i) => {
|
|
3604
|
+
// acc[`item${i}Id`] = _player[`item_${i}`];
|
|
3605
|
+
// if (i < 3) acc[`backpack${i}Id`] = _player[`backpack_${i}`];
|
|
3606
|
+
// return acc;
|
|
3607
|
+
// }, {}),
|
|
3608
|
+
item0Id: _player.item_0,
|
|
3609
|
+
item1Id: _player.item_1,
|
|
3610
|
+
item2Id: _player.item_2,
|
|
3611
|
+
item3Id: _player.item_3,
|
|
3612
|
+
item4Id: _player.item_4,
|
|
3613
|
+
item5Id: _player.item_5,
|
|
3614
|
+
backpack0Id: _player.backpack_0,
|
|
3615
|
+
backpack1Id: _player.backpack_1,
|
|
3616
|
+
backpack2Id: _player.backpack_2,
|
|
3617
|
+
neutral0Id: _player.item_neutral,
|
|
3618
|
+
// _player.item_neutral2 是一个因stratz不再更新而缺失的字段,而opendota更新了的功能:中立物品的附魔。在此记录以留待未来考量决定是否使用。
|
|
3619
|
+
heroDamage: _player.hero_damage,
|
|
3620
|
+
towerDamage: _player.tower_damage,
|
|
3621
|
+
numLastHits: _player.last_hits,
|
|
3622
|
+
numDenies: _player.denies,
|
|
3623
|
+
goldPerMinute: _player.gold_per_min,
|
|
3624
|
+
experiencePerMinute: _player.xp_per_min,
|
|
3625
|
+
heroHealing: _player.hero_healing,
|
|
3626
|
+
isRandom: _player.randomed,
|
|
3627
|
+
steamAccount: { name: _player.personaname, seasonRank: _player.rank_tier, seasonLeaderboardRank: null },
|
|
3628
|
+
hero: {
|
|
3629
|
+
id: _player.hero_id,
|
|
3630
|
+
name: dotaconstants6.heroes[_player.hero_id].name,
|
|
3631
|
+
shortName: dotaconstants6.heroes[_player.hero_id].name.match(/^npc_dota_hero_(.+)$/)[1],
|
|
3632
|
+
facets: [...dotaconstants6.hero_abilities[dotaconstants6.heroes[_player.hero_id].name].facets.map((f) => ({ id: -1, name: f.name }))]
|
|
3633
|
+
},
|
|
3634
|
+
dotaPlus: null,
|
|
3635
|
+
stats: {
|
|
3636
|
+
networthPerMinute: _player.gold_t,
|
|
3637
|
+
experiencePerMinute: _player.xp_t,
|
|
3638
|
+
campStack: [_player.camps_stacked],
|
|
3639
|
+
matchPlayerBuffEvent: [],
|
|
3640
|
+
killEvents: [],
|
|
3641
|
+
deathEvents: [],
|
|
3642
|
+
assistEvents: [],
|
|
3643
|
+
heroDamageReport: {
|
|
3644
|
+
receivedTotal: {
|
|
3645
|
+
// 由于opendota获取不到伤害类型,此处只能粗暴地归一化。
|
|
3646
|
+
physicalDamage: Object.entries(_player.damage_taken).filter(([source, damage]) => source.startsWith("npc_dota_hero_")).reduce((total, [source, damage]) => total + damage, 0),
|
|
3647
|
+
magicalDamage: 0,
|
|
3648
|
+
pureDamage: 0
|
|
3649
|
+
},
|
|
3650
|
+
dealtTotal: {
|
|
3651
|
+
// 同理,opendota只能获取到眩晕相关数据。
|
|
3652
|
+
// 据测stratz的控制时间单位为10毫秒,此处乘以100来模拟。
|
|
3653
|
+
stunDuration: _player.stuns * 100,
|
|
3654
|
+
stunCount: _player.stuns > 0 ? 1 : 0,
|
|
3655
|
+
slowDuration: 0,
|
|
3656
|
+
slowCount: 0,
|
|
3657
|
+
disableDuration: 0,
|
|
3658
|
+
disableCount: 0
|
|
3659
|
+
}
|
|
3660
|
+
},
|
|
3661
|
+
itemPurchases: _player.purchase_log.map((p) => ({ time: p.time, itemId: dotaconstants6.items[p.key].id }))
|
|
3662
|
+
},
|
|
3663
|
+
additionalUnit: null
|
|
3664
|
+
};
|
|
3665
|
+
if (_player.additional_units) {
|
|
3666
|
+
const additionalUnit = _player.additional_units[0];
|
|
3667
|
+
player.additionalUnit = {
|
|
3668
|
+
// ...[0, 1, 2, 3, 4, 5].reduce((acc, i) => {
|
|
3669
|
+
// acc[`item${i}Id`] = additionalUnit[`item_${i}`];
|
|
3670
|
+
// if (i < 3) acc[`backpack${i}Id`] = additionalUnit[`backpack_${i}`];
|
|
3671
|
+
// return acc;
|
|
3672
|
+
// }, {}),
|
|
3673
|
+
item0Id: additionalUnit.item_0,
|
|
3674
|
+
item1Id: additionalUnit.item_1,
|
|
3675
|
+
item2Id: additionalUnit.item_2,
|
|
3676
|
+
item3Id: additionalUnit.item_3,
|
|
3677
|
+
item4Id: additionalUnit.item_4,
|
|
3678
|
+
item5Id: additionalUnit.item_5,
|
|
3679
|
+
backpack0Id: additionalUnit.backpack_0,
|
|
3680
|
+
backpack1Id: additionalUnit.backpack_1,
|
|
3681
|
+
backpack2Id: additionalUnit.backpack_2,
|
|
3682
|
+
neutral0Id: additionalUnit.item_neutral
|
|
3683
|
+
};
|
|
3684
|
+
}
|
|
3685
|
+
players.push(player);
|
|
3686
|
+
}
|
|
3687
|
+
const match = {
|
|
3688
|
+
id: _match.match_id,
|
|
3689
|
+
didRadiantWin: _match.radiant_win,
|
|
3690
|
+
lobbyType: convertLobbyType(_match.lobby_type),
|
|
3691
|
+
gameMode: convertGameMode(_match.game_mode),
|
|
3692
|
+
regionId: _match.region,
|
|
3693
|
+
parsedDateTime: _match.start_time + _match.duration,
|
|
3694
|
+
startDateTime: _match.start_time,
|
|
3695
|
+
endDateTime: _match.start_time + _match.duration,
|
|
3696
|
+
rank: (({ sum, count }) => count ? sum / count : 0)(_match.players.reduce((acc, player) => player.rank_tier != null ? { sum: acc.sum + player.rank_tier, count: acc.count + 1 } : acc, { sum: 0, count: 0 })),
|
|
3697
|
+
actualRank: 0,
|
|
3698
|
+
averageRank: 0,
|
|
3699
|
+
durationSeconds: _match.duration,
|
|
3700
|
+
topLaneOutcome: null,
|
|
3701
|
+
midLaneOutcome: null,
|
|
3702
|
+
bottomLaneOutcome: null,
|
|
3703
|
+
...determineLaneOutcome(_match),
|
|
3704
|
+
radiantKills: [_match.radiant_score],
|
|
3705
|
+
direKills: [_match.dire_score],
|
|
3706
|
+
radiantNetworthLeads: _match.radiant_gold_adv,
|
|
3707
|
+
radiantExperienceLeads: _match.radiant_xp_adv,
|
|
3708
|
+
winRates: null,
|
|
3709
|
+
players,
|
|
3710
|
+
pickBans: _match.picks_bans.map((pb) => ({ isPick: pb.is_pick, ...pb.is_pick ? { heroId: pb.hero_id, bannedHeroId: null } : { bannedHeroId: pb.hero_id, heroId: null }, order: pb.order })),
|
|
3711
|
+
odParsed: true
|
|
3712
|
+
};
|
|
3713
|
+
return match;
|
|
3714
|
+
}
|
|
3715
|
+
};
|
|
3716
|
+
function convertLeaverStatus(openDotaStatus) {
|
|
3717
|
+
switch (openDotaStatus) {
|
|
3718
|
+
case 0:
|
|
3719
|
+
return "NONE" /* None */;
|
|
3720
|
+
case 1:
|
|
3721
|
+
return "DISCONNECTED_TOO_LONG" /* DisconnectedTooLong */;
|
|
3722
|
+
case 2:
|
|
3723
|
+
return "ABANDONED" /* Abandoned */;
|
|
3724
|
+
case 3:
|
|
3725
|
+
return "AFK" /* Afk */;
|
|
3726
|
+
case 4:
|
|
3727
|
+
return "NEVER_CONNECTED" /* NeverConnected */;
|
|
3728
|
+
case 5:
|
|
3729
|
+
return "NEVER_CONNECTED_TOO_LONG" /* NeverConnectedTooLong */;
|
|
3730
|
+
case 6:
|
|
3731
|
+
return "FAILED_TO_READY_UP" /* FailedToReadyUp */;
|
|
3732
|
+
default:
|
|
3733
|
+
return "NONE" /* None */;
|
|
3734
|
+
}
|
|
3735
|
+
}
|
|
3736
|
+
__name(convertLeaverStatus, "convertLeaverStatus");
|
|
3737
|
+
function convertLane(openDotaLane) {
|
|
3738
|
+
switch (openDotaLane) {
|
|
3739
|
+
case 1:
|
|
3740
|
+
return "SAFE_LANE" /* SafeLane */;
|
|
3741
|
+
case 2:
|
|
3742
|
+
return "MID_LANE" /* MidLane */;
|
|
3743
|
+
case 3:
|
|
3744
|
+
return "OFF_LANE" /* OffLane */;
|
|
3745
|
+
case 4:
|
|
3746
|
+
return "JUNGLE" /* Jungle */;
|
|
3747
|
+
case 5:
|
|
3748
|
+
return "ROAMING" /* Roaming */;
|
|
3749
|
+
default:
|
|
3750
|
+
return "UNKNOWN" /* Unknown */;
|
|
3751
|
+
}
|
|
3752
|
+
}
|
|
3753
|
+
__name(convertLane, "convertLane");
|
|
3754
|
+
function convertPosition(openDotaPosition) {
|
|
3755
|
+
switch (openDotaPosition) {
|
|
3756
|
+
case 1:
|
|
3757
|
+
return "POSITION_1" /* Position_1 */;
|
|
3758
|
+
case 2:
|
|
3759
|
+
return "POSITION_2" /* Position_2 */;
|
|
3760
|
+
case 3:
|
|
3761
|
+
return "POSITION_3" /* Position_3 */;
|
|
3762
|
+
case 4:
|
|
3763
|
+
return "POSITION_4" /* Position_4 */;
|
|
3764
|
+
case 5:
|
|
3765
|
+
return "POSITION_5" /* Position_5 */;
|
|
3766
|
+
default:
|
|
3767
|
+
return "UNKNOWN" /* Unknown */;
|
|
3768
|
+
}
|
|
3769
|
+
}
|
|
3770
|
+
__name(convertPosition, "convertPosition");
|
|
3771
|
+
function convertLobbyType(openDotaLobbyType) {
|
|
3772
|
+
const map = {
|
|
3773
|
+
lobby_type_normal: "UNRANKED" /* Unranked */,
|
|
3774
|
+
lobby_type_practice: "PRACTICE" /* Practice */,
|
|
3775
|
+
lobby_type_tournament: "TOURNAMENT" /* Tournament */,
|
|
3776
|
+
lobby_type_tutorial: "TUTORIAL" /* Tutorial */,
|
|
3777
|
+
lobby_type_coop_bots: "COOP_VS_BOTS" /* CoopVsBots */,
|
|
3778
|
+
lobby_type_ranked_team_mm: "TEAM_MATCH" /* TeamMatch */,
|
|
3779
|
+
lobby_type_ranked_solo_mm: "SOLO_QUEUE" /* SoloQueue */,
|
|
3780
|
+
lobby_type_ranked: "RANKED" /* Ranked */,
|
|
3781
|
+
lobby_type_1v1_mid: "SOLO_MID" /* SoloMid */,
|
|
3782
|
+
lobby_type_battle_cup: "BATTLE_CUP" /* BattleCup */,
|
|
3783
|
+
lobby_type_event: "EVENT" /* Event */,
|
|
3784
|
+
lobby_type_gauntlet: "EVENT" /* Event */,
|
|
3785
|
+
lobby_type_new_player: "COOP_VS_BOTS" /* CoopVsBots */,
|
|
3786
|
+
lobby_type_featured: "EVENT" /* Event */
|
|
3787
|
+
};
|
|
3788
|
+
return map[dotaconstants6.lobby_type[openDotaLobbyType].name] || "EVENT" /* Event */;
|
|
3789
|
+
}
|
|
3790
|
+
__name(convertLobbyType, "convertLobbyType");
|
|
3791
|
+
function convertGameMode(openDotaGameModeId) {
|
|
3792
|
+
const gameModeName = dotaconstants6.game_mode[openDotaGameModeId]?.name;
|
|
3793
|
+
switch (gameModeName) {
|
|
3794
|
+
case "game_mode_all_pick":
|
|
3795
|
+
return "ALL_PICK" /* AllPick */;
|
|
3796
|
+
case "game_mode_captains_mode":
|
|
3797
|
+
return "CAPTAINS_MODE" /* CaptainsMode */;
|
|
3798
|
+
case "game_mode_random_draft":
|
|
3799
|
+
return "RANDOM_DRAFT" /* RandomDraft */;
|
|
3800
|
+
case "game_mode_single_draft":
|
|
3801
|
+
return "SINGLE_DRAFT" /* SingleDraft */;
|
|
3802
|
+
case "game_mode_all_random":
|
|
3803
|
+
return "ALL_RANDOM" /* AllRandom */;
|
|
3804
|
+
case "game_mode_intro":
|
|
3805
|
+
return "INTRO" /* Intro */;
|
|
3806
|
+
case "game_mode_diretide":
|
|
3807
|
+
return "THE_DIRETIDE" /* TheDiretide */;
|
|
3808
|
+
case "game_mode_reverse_captains_mode":
|
|
3809
|
+
return "REVERSE_CAPTAINS_MODE" /* ReverseCaptainsMode */;
|
|
3810
|
+
case "game_mode_greeviling":
|
|
3811
|
+
return "THE_GREEVILING" /* TheGreeviling */;
|
|
3812
|
+
case "game_mode_tutorial":
|
|
3813
|
+
return "TUTORIAL" /* Tutorial */;
|
|
3814
|
+
case "game_mode_mid_only":
|
|
3815
|
+
return "MID_ONLY" /* MidOnly */;
|
|
3816
|
+
case "game_mode_least_played":
|
|
3817
|
+
return "LEAST_PLAYED" /* LeastPlayed */;
|
|
3818
|
+
case "game_mode_compendium_matchmaking":
|
|
3819
|
+
return "COMPENDIUM_MATCHMAKING" /* CompendiumMatchmaking */;
|
|
3820
|
+
case "game_mode_custom":
|
|
3821
|
+
return "CUSTOM" /* Custom */;
|
|
3822
|
+
case "game_mode_captains_draft":
|
|
3823
|
+
return "CAPTAINS_DRAFT" /* CaptainsDraft */;
|
|
3824
|
+
case "game_mode_balanced_draft":
|
|
3825
|
+
return "BALANCED_DRAFT" /* BalancedDraft */;
|
|
3826
|
+
case "game_mode_ability_draft":
|
|
3827
|
+
return "ABILITY_DRAFT" /* AbilityDraft */;
|
|
3828
|
+
case "game_mode_event":
|
|
3829
|
+
return "EVENT" /* Event */;
|
|
3830
|
+
case "game_mode_all_random_death_match":
|
|
3831
|
+
return "ALL_RANDOM_DEATH_MATCH" /* AllRandomDeathMatch */;
|
|
3832
|
+
case "game_mode_1v1_mid":
|
|
3833
|
+
return "SOLO_MID" /* SoloMid */;
|
|
3834
|
+
case "game_mode_all_draft":
|
|
3835
|
+
return "ALL_PICK_RANKED" /* AllPickRanked */;
|
|
3836
|
+
// 天梯 AP
|
|
3837
|
+
case "game_mode_turbo":
|
|
3838
|
+
return "TURBO" /* Turbo */;
|
|
3839
|
+
case "game_mode_mutation":
|
|
3840
|
+
return "MUTATION" /* Mutation */;
|
|
3841
|
+
default:
|
|
3842
|
+
return "UNKNOWN" /* Unknown */;
|
|
3843
|
+
}
|
|
3844
|
+
}
|
|
3845
|
+
__name(convertGameMode, "convertGameMode");
|
|
3846
|
+
function determinePlayerPositions(match) {
|
|
3847
|
+
const players = match.players;
|
|
3848
|
+
players.filter((p) => p.lane === 2).forEach((p) => {
|
|
3849
|
+
p.calculatedPosition = 2;
|
|
3850
|
+
});
|
|
3851
|
+
const sideLanePlayers = players.filter((p) => p.lane !== 2);
|
|
3852
|
+
const radiant = sideLanePlayers.filter((p) => p.isRadiant).sort((a, b) => b.last_hits - a.last_hits);
|
|
3853
|
+
const dire = sideLanePlayers.filter((p) => !p.isRadiant).sort((a, b) => b.last_hits - a.last_hits);
|
|
3854
|
+
for (const team of [radiant, dire]) {
|
|
3855
|
+
team[0].calculatedPosition = 1;
|
|
3856
|
+
team[1].calculatedPosition = 3;
|
|
3857
|
+
const supA = team[2];
|
|
3858
|
+
const supB = team[3];
|
|
3859
|
+
const pos1 = team[0];
|
|
3860
|
+
let pos4 = supA;
|
|
3861
|
+
let pos5 = supB;
|
|
3862
|
+
if (supA.lane === pos1.lane) {
|
|
3863
|
+
pos5 = supA;
|
|
3864
|
+
pos4 = supB;
|
|
3865
|
+
}
|
|
3866
|
+
pos4.calculatedPosition = 4;
|
|
3867
|
+
pos5.calculatedPosition = 5;
|
|
3868
|
+
}
|
|
3869
|
+
}
|
|
3870
|
+
__name(determinePlayerPositions, "determinePlayerPositions");
|
|
3871
|
+
function determineLaneOutcome(match) {
|
|
3872
|
+
const laneGold = {
|
|
3873
|
+
radiant: { top: 0, mid: 0, bottom: 0 },
|
|
3874
|
+
dire: { top: 0, mid: 0, bottom: 0 }
|
|
3875
|
+
};
|
|
3876
|
+
for (const player of match.players) {
|
|
3877
|
+
const team = player.isRadiant ? "radiant" : "dire";
|
|
3878
|
+
const goldAt10 = player.gold_t?.[10] || 0;
|
|
3879
|
+
switch (player.calculatedPosition) {
|
|
3880
|
+
case 1:
|
|
3881
|
+
// 核心C位,在优势路
|
|
3882
|
+
case 5:
|
|
3883
|
+
const safeLane = player.isRadiant ? "bottom" : "top";
|
|
3884
|
+
laneGold[team][safeLane] += goldAt10;
|
|
3885
|
+
break;
|
|
3886
|
+
case 2:
|
|
3887
|
+
laneGold[team]["mid"] += goldAt10;
|
|
3888
|
+
break;
|
|
3889
|
+
case 3:
|
|
3890
|
+
// 劣势路核心
|
|
3891
|
+
case 4:
|
|
3892
|
+
const offLane = player.isRadiant ? "top" : "bottom";
|
|
3893
|
+
laneGold[team][offLane] += goldAt10;
|
|
3894
|
+
break;
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
const topDiff = laneGold.radiant.top - laneGold.dire.top;
|
|
3898
|
+
const midDiff = laneGold.radiant.mid - laneGold.dire.mid;
|
|
3899
|
+
const bottomDiff = laneGold.radiant.bottom - laneGold.dire.bottom;
|
|
3900
|
+
return {
|
|
3901
|
+
topLaneOutcome: judge(topDiff),
|
|
3902
|
+
midLaneOutcome: judge(midDiff),
|
|
3903
|
+
bottomLaneOutcome: judge(bottomDiff)
|
|
3904
|
+
};
|
|
3905
|
+
function judge(goldAdv) {
|
|
3906
|
+
const STOMP_THRESHOLD = 2500;
|
|
3907
|
+
const VICTORY_THRESHOLD = 800;
|
|
3908
|
+
if (goldAdv > STOMP_THRESHOLD) {
|
|
3909
|
+
return "RADIANT_STOMP" /* RadiantStomp */;
|
|
3910
|
+
} else if (goldAdv > VICTORY_THRESHOLD) {
|
|
3911
|
+
return "RADIANT_VICTORY" /* RadiantVictory */;
|
|
3912
|
+
} else if (goldAdv < -STOMP_THRESHOLD) {
|
|
3913
|
+
return "DIRE_STOMP" /* DireStomp */;
|
|
3914
|
+
} else if (goldAdv < -VICTORY_THRESHOLD) {
|
|
3915
|
+
return "DIRE_VICTORY" /* DireVictory */;
|
|
3916
|
+
} else {
|
|
3917
|
+
return "TIE" /* Tie */;
|
|
3918
|
+
}
|
|
3919
|
+
}
|
|
3920
|
+
__name(judge, "judge");
|
|
3921
|
+
}
|
|
3922
|
+
__name(determineLaneOutcome, "determineLaneOutcome");
|
|
3923
|
+
function determineIMP(player) {
|
|
3924
|
+
const values = Object.values(player.benchmarks).filter((value) => value.raw > 0);
|
|
3925
|
+
if (values.length === 0) return 0;
|
|
3926
|
+
const totalScore = values.reduce((acc, cur) => acc + cur.pct, 0);
|
|
3927
|
+
return Math.round(totalScore / values.length * 100 - 50);
|
|
3928
|
+
}
|
|
3929
|
+
__name(determineIMP, "determineIMP");
|
|
3930
|
+
|
|
3931
|
+
// src/config.ts
|
|
3932
|
+
var import_koishi17 = require("koishi");
|
|
3473
3933
|
var import_fs3 = __toESM(require("fs"));
|
|
3474
3934
|
var import_path3 = __toESM(require("path"));
|
|
3475
3935
|
|
|
@@ -3481,67 +3941,77 @@ var globRequire_locales_schema_yml = __glob({
|
|
|
3481
3941
|
|
|
3482
3942
|
// src/config.ts
|
|
3483
3943
|
var pluginDir = import_path3.default.resolve(__dirname, "..");
|
|
3484
|
-
var Config =
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3944
|
+
var Config = import_koishi17.Schema.intersect([
|
|
3945
|
+
import_koishi17.Schema.intersect([
|
|
3946
|
+
import_koishi17.Schema.object({
|
|
3947
|
+
STRATZ_API_TOKEN: import_koishi17.Schema.string().required().role("secret"),
|
|
3948
|
+
dataParsingTimeoutMinutes: import_koishi17.Schema.number().default(60).min(0).max(1440),
|
|
3949
|
+
proxyAddress: import_koishi17.Schema.string(),
|
|
3950
|
+
suppressStratzNetworkErrors: import_koishi17.Schema.boolean().default(false),
|
|
3951
|
+
enableOpenDotaFallback: import_koishi17.Schema.boolean().default(false)
|
|
3952
|
+
}).i18n(Object.keys(LanguageTags).reduce((acc, cur) => (acc[cur] = globRequire_locales_schema_yml(`./locales/${cur}.schema.yml`)._config.base, acc), {})),
|
|
3953
|
+
import_koishi17.Schema.union([
|
|
3954
|
+
import_koishi17.Schema.object({
|
|
3955
|
+
enableOpenDotaFallback: import_koishi17.Schema.const(true).required(),
|
|
3956
|
+
OPENDOTA_API_KEY: import_koishi17.Schema.string().role("secret")
|
|
3957
|
+
}),
|
|
3958
|
+
import_koishi17.Schema.object({})
|
|
3959
|
+
]).i18n(Object.keys(LanguageTags).reduce((acc, cur) => (acc[cur] = globRequire_locales_schema_yml(`./locales/${cur}.schema.yml`)._config.base, acc), {}))
|
|
3960
|
+
]),
|
|
3961
|
+
import_koishi17.Schema.intersect([
|
|
3962
|
+
import_koishi17.Schema.object({
|
|
3963
|
+
useHeroNicknames: import_koishi17.Schema.boolean().default(true),
|
|
3964
|
+
urlInMessageType: import_koishi17.Schema.array(import_koishi17.Schema.union([import_koishi17.Schema.const("match"), import_koishi17.Schema.const("player"), import_koishi17.Schema.const("hero")])).role("checkbox"),
|
|
3965
|
+
maxSendItemCount: import_koishi17.Schema.number().default(5).min(1).max(10),
|
|
3966
|
+
showItemListAtTooMuchItems: import_koishi17.Schema.boolean().default(true),
|
|
3967
|
+
customItemAlias: import_koishi17.Schema.array(
|
|
3968
|
+
import_koishi17.Schema.object({
|
|
3969
|
+
keyword: import_koishi17.Schema.string().required(),
|
|
3970
|
+
alias: import_koishi17.Schema.string().required()
|
|
3501
3971
|
})
|
|
3502
3972
|
).default([]).role("table"),
|
|
3503
|
-
rankBroadSwitch:
|
|
3973
|
+
rankBroadSwitch: import_koishi17.Schema.boolean().default(false)
|
|
3504
3974
|
}).i18n(Object.keys(LanguageTags).reduce((acc, cur) => (acc[cur] = globRequire_locales_schema_yml(`./locales/${cur}.schema.yml`)._config.message, acc), {})),
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
rankBroadSwitch:
|
|
3508
|
-
rankBroadStar:
|
|
3509
|
-
rankBroadLeader:
|
|
3510
|
-
rankBroadFun:
|
|
3975
|
+
import_koishi17.Schema.union([
|
|
3976
|
+
import_koishi17.Schema.object({
|
|
3977
|
+
rankBroadSwitch: import_koishi17.Schema.const(true).required(),
|
|
3978
|
+
rankBroadStar: import_koishi17.Schema.boolean().default(true),
|
|
3979
|
+
rankBroadLeader: import_koishi17.Schema.boolean().default(true),
|
|
3980
|
+
rankBroadFun: import_koishi17.Schema.boolean().default(false)
|
|
3511
3981
|
}),
|
|
3512
|
-
|
|
3982
|
+
import_koishi17.Schema.object({})
|
|
3513
3983
|
]).i18n(Object.keys(LanguageTags).reduce((acc, cur) => (acc[cur] = globRequire_locales_schema_yml(`./locales/${cur}.schema.yml`)._config.message, acc), {}))
|
|
3514
3984
|
]),
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
dailyReportSwitch:
|
|
3985
|
+
import_koishi17.Schema.intersect([
|
|
3986
|
+
import_koishi17.Schema.object({
|
|
3987
|
+
dailyReportSwitch: import_koishi17.Schema.boolean().default(false)
|
|
3518
3988
|
}).i18n(Object.keys(LanguageTags).reduce((acc, cur) => (acc[cur] = globRequire_locales_schema_yml(`./locales/${cur}.schema.yml`)._config.report, acc), {})),
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
dailyReportSwitch:
|
|
3522
|
-
dailyReportHours:
|
|
3523
|
-
dailyReportShowCombi:
|
|
3989
|
+
import_koishi17.Schema.union([
|
|
3990
|
+
import_koishi17.Schema.object({
|
|
3991
|
+
dailyReportSwitch: import_koishi17.Schema.const(true).required(),
|
|
3992
|
+
dailyReportHours: import_koishi17.Schema.number().min(0).max(23).default(6),
|
|
3993
|
+
dailyReportShowCombi: import_koishi17.Schema.boolean().default(true)
|
|
3524
3994
|
}),
|
|
3525
|
-
|
|
3995
|
+
import_koishi17.Schema.object({})
|
|
3526
3996
|
]).i18n(Object.keys(LanguageTags).reduce((acc, cur) => (acc[cur] = globRequire_locales_schema_yml(`./locales/${cur}.schema.yml`)._config.report, acc), {})),
|
|
3527
|
-
|
|
3528
|
-
weeklyReportSwitch:
|
|
3997
|
+
import_koishi17.Schema.object({
|
|
3998
|
+
weeklyReportSwitch: import_koishi17.Schema.boolean().default(false)
|
|
3529
3999
|
}).i18n(Object.keys(LanguageTags).reduce((acc, cur) => (acc[cur] = globRequire_locales_schema_yml(`./locales/${cur}.schema.yml`)._config.report, acc), {})).description(void 0),
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
weeklyReportSwitch:
|
|
3533
|
-
weeklyReportDayHours:
|
|
3534
|
-
weeklyReportShowCombi:
|
|
4000
|
+
import_koishi17.Schema.union([
|
|
4001
|
+
import_koishi17.Schema.object({
|
|
4002
|
+
weeklyReportSwitch: import_koishi17.Schema.const(true).required(),
|
|
4003
|
+
weeklyReportDayHours: import_koishi17.Schema.tuple([import_koishi17.Schema.number().min(1).max(7), import_koishi17.Schema.number().min(0).max(23)]).default([1, 10]),
|
|
4004
|
+
weeklyReportShowCombi: import_koishi17.Schema.boolean().default(true)
|
|
3535
4005
|
}),
|
|
3536
|
-
|
|
4006
|
+
import_koishi17.Schema.object({})
|
|
3537
4007
|
]).i18n(Object.keys(LanguageTags).reduce((acc, cur) => (acc[cur] = globRequire_locales_schema_yml(`./locales/${cur}.schema.yml`)._config.report, acc), {}))
|
|
3538
4008
|
]),
|
|
3539
|
-
|
|
3540
|
-
template_match:
|
|
3541
|
-
template_player:
|
|
3542
|
-
template_hero:
|
|
3543
|
-
playerRankEstimate:
|
|
3544
|
-
templateFonts:
|
|
4009
|
+
import_koishi17.Schema.object({
|
|
4010
|
+
template_match: import_koishi17.Schema.union([...readDirectoryFilesSync(import_path3.default.join(pluginDir, "template", "match"))]).default("match_1"),
|
|
4011
|
+
template_player: import_koishi17.Schema.union([...readDirectoryFilesSync(import_path3.default.join(pluginDir, "template", "player"))]).default("player_1"),
|
|
4012
|
+
template_hero: import_koishi17.Schema.union([...readDirectoryFilesSync(import_path3.default.join(pluginDir, "template", "hero"))]).default("hero_1"),
|
|
4013
|
+
playerRankEstimate: import_koishi17.Schema.boolean().default(true),
|
|
4014
|
+
templateFonts: import_koishi17.Schema.array(String).default([]).role("table")
|
|
3545
4015
|
}).i18n(Object.keys(LanguageTags).reduce((acc, cur) => (acc[cur] = globRequire_locales_schema_yml(`./locales/${cur}.schema.yml`)._config.template, acc), {}))
|
|
3546
4016
|
]);
|
|
3547
4017
|
function readDirectoryFilesSync(directoryPath) {
|
|
@@ -3581,7 +4051,7 @@ async function apply(ctx, config) {
|
|
|
3581
4051
|
await ctx.dota2tracker.parsePolling.polling();
|
|
3582
4052
|
});
|
|
3583
4053
|
} else {
|
|
3584
|
-
ctx.logger.info(ctx.dota2tracker.i18n.gt("dota2tracker.logger.cron_not_enabled"));
|
|
4054
|
+
ctx.logger("dota2tracker").info(ctx.dota2tracker.i18n.gt("dota2tracker.logger.cron_not_enabled"));
|
|
3585
4055
|
}
|
|
3586
4056
|
ctx.dota2tracker.hero = new HeroService(ctx);
|
|
3587
4057
|
ctx.dota2tracker.item = new ItemService(ctx);
|
|
@@ -3589,6 +4059,10 @@ async function apply(ctx, config) {
|
|
|
3589
4059
|
ctx.dota2tracker.database = new DatabaseService(ctx);
|
|
3590
4060
|
ctx.dota2tracker.valveAPI = new ValveAPI(ctx);
|
|
3591
4061
|
ctx.dota2tracker.stratzAPI = new StratzAPI(ctx, pluginDir2);
|
|
4062
|
+
if (config.enableOpenDotaFallback) {
|
|
4063
|
+
ctx.dota2tracker.opendotaAPI = new OpenDotaAPI(ctx);
|
|
4064
|
+
ctx.dota2tracker.opendotaAdapter = new OpenDotaAdapter(ctx);
|
|
4065
|
+
}
|
|
3592
4066
|
ctx.dota2tracker = ctx.dota2tracker;
|
|
3593
4067
|
usage = await ctx.dota2tracker.i18n.generateUsage();
|
|
3594
4068
|
registerHelpCommand(ctx);
|
package/package.json
CHANGED
|
@@ -15,8 +15,10 @@
|
|
|
15
15
|
<span class="time text-sm"><%= match.durationTime %></span>
|
|
16
16
|
<span class="score dire text-3xl"><%= match.dire.killsCount %></span>
|
|
17
17
|
</p>
|
|
18
|
-
<
|
|
19
|
-
|
|
18
|
+
<div class="rank absolute w-[64px] h-[64px] bottom-[10px] left-1/2 -translate-x-1/2<%- match.odParsed && match.lobbyType !== "RANKED" ? ` grayscale` : "" %>">
|
|
19
|
+
<img class="star absolute w-full" src="<%= getImageUrl('star_' + (match.rank ? match.rank.toString().split('')[1] : '')) %>">
|
|
20
|
+
<img class="medal absolute w-full" src="<%= getImageUrl('medal_' + (match.rank ? match.rank.toString().split('')[0] : '')) %>">
|
|
21
|
+
</div>
|
|
20
22
|
</div>
|
|
21
23
|
<div class="flag w-[100px] h-full flex justify-center items-end bg-cover <%= match.didRadiantWin ? 'grayscale' : "" %>" style="background-image: url('<%= getImageUrl("flag_dire") %>')"><%= !match.didRadiantWin ? $t("dota2tracker.template.won") : "" %></div>
|
|
22
24
|
</div>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! tailwindcss v4.1.
|
|
1
|
+
/*! tailwindcss v4.1.13 | MIT License | https://tailwindcss.com */
|
|
2
2
|
@layer properties;
|
|
3
3
|
@layer theme, base, components, utilities;
|
|
4
4
|
@layer theme {
|
|
@@ -153,6 +153,9 @@
|
|
|
153
153
|
::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {
|
|
154
154
|
padding-block: 0;
|
|
155
155
|
}
|
|
156
|
+
::-webkit-calendar-picker-indicator {
|
|
157
|
+
line-height: 1;
|
|
158
|
+
}
|
|
156
159
|
:-moz-ui-invalid {
|
|
157
160
|
box-shadow: none;
|
|
158
161
|
}
|
|
@@ -282,6 +285,9 @@
|
|
|
282
285
|
.h-\[44px\] {
|
|
283
286
|
height: 44px;
|
|
284
287
|
}
|
|
288
|
+
.h-\[64px\] {
|
|
289
|
+
height: 64px;
|
|
290
|
+
}
|
|
285
291
|
.h-\[100px\] {
|
|
286
292
|
height: 100px;
|
|
287
293
|
}
|
|
@@ -41,6 +41,10 @@ nav > div.match_id > p.fail::after {
|
|
|
41
41
|
content: "※<%= $t('dota2tracker.template.analysis_incomplete') %>";
|
|
42
42
|
color: #ffb400;
|
|
43
43
|
}
|
|
44
|
+
nav > div.match_id > p.opendota::after {
|
|
45
|
+
content: "※<%= $t('dota2tracker.template.analysis_by_opendota') %>";
|
|
46
|
+
color: #34a39a;
|
|
47
|
+
}
|
|
44
48
|
|
|
45
49
|
nav > .rank {
|
|
46
50
|
width: 48px;
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
<nav>
|
|
31
31
|
<div class="match_id">
|
|
32
32
|
<p><%= $t("dota2tracker.template.match_id_").slice(0, -1) %> <%-match.id%></p>
|
|
33
|
-
<p class="<%-match.parsedDateTime?'success':'fail'%>"><!--伪类赋值:类名输入success时此处为※录像分析成功,fail为※分析结果不完整,同时自动应用字体颜色样式。--></p>
|
|
33
|
+
<p class="<%-match.odParsed?"opendota":(match.parsedDateTime?'success':'fail')%>"><!--伪类赋值:类名输入success时此处为※录像分析成功,fail为※分析结果不完整,同时自动应用字体颜色样式。--></p>
|
|
34
34
|
</div>
|
|
35
35
|
<div class="start_time">
|
|
36
36
|
<p><%= $t("dota2tracker.template.start_time_").slice(0, -1) %></p>
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
<p><%= $t("dota2tracker.template.game_mode_").slice(0, -1) %></p>
|
|
49
49
|
<p><%-$t("dota2tracker.template.lobby_types."+match.lobbyType) || match.lobbyType%>/<%-$t("dota2tracker.template.game_modes."+match.gameMode) || match.gameMode%></p>
|
|
50
50
|
</div>
|
|
51
|
-
<div class="rank"
|
|
51
|
+
<div class="rank"<%- match.odParsed && match.lobbyType !== "RANKED" ? ` style="filter:grayscale(1)"` : "" %>>
|
|
52
52
|
<img src="<%-getImageUrl('medal_' + (match.rank?.toString().split('')[0] ?? '0'))%>" alt="" />
|
|
53
53
|
<img style="z-index: 1;" src="<%-getImageUrl('star_' + (match.rank?.toString().split('')[1] ?? '0'))%>" alt="" />
|
|
54
54
|
</div>
|
|
@@ -90,30 +90,38 @@
|
|
|
90
90
|
</head>
|
|
91
91
|
<% const {name, avatar, isRising, prevRank, currRank, date} = data; %>
|
|
92
92
|
<% const kind = isRising ? "xi" : "bei"; %>
|
|
93
|
-
<%
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
93
|
+
<%
|
|
94
|
+
// 1. 准备好要注入的 HTML
|
|
95
|
+
const avatarHtml = `<img src="${avatar}" />`;
|
|
96
|
+
const prevHtml = `<span class="rank prev">${$t("dota2tracker.template.ranks."+prevRank.medal)}${prevRank.leader ?? prevRank.star}</span>`;
|
|
97
|
+
const currHtml = `<span class="rank curr">${$t("dota2tracker.template.ranks."+currRank.medal)}${currRank.leader ?? currRank.star}</span>`;
|
|
98
|
+
|
|
99
|
+
// 2. 先处理文本插值
|
|
100
|
+
const messageTemplate = isRising
|
|
101
|
+
? $t("dota2tracker.template.rank_fun_up_message", { name: name })
|
|
102
|
+
: $t("dota2tracker.template.rank_fun_down_message", { name: name });
|
|
103
|
+
|
|
104
|
+
// 3. 手动将占位符替换为 HTML
|
|
105
|
+
const finalMessage = messageTemplate
|
|
106
|
+
.replace('AVATAR_PLACEHOLDER', avatarHtml)
|
|
107
|
+
.replace('PREV_PLACEHOLDER', prevHtml)
|
|
108
|
+
.replace('CURR_PLACEHOLDER', currHtml);
|
|
109
|
+
%>
|
|
99
110
|
<body class="<%= kind %>" <%- `style="background-image: url( ${getImageUrl(kind, undefined, "jpg")} )"`%>>
|
|
100
111
|
<div class="wrapper">
|
|
101
112
|
<p class="<%= kind %>">
|
|
102
|
-
|
|
103
|
-
$t("dota2tracker.template.rank_fun_up_message", rankInfo) :
|
|
104
|
-
$t("dota2tracker.template.rank_fun_down_message", rankInfo)
|
|
105
|
-
%>
|
|
113
|
+
<%- finalMessage %>
|
|
106
114
|
</p>
|
|
107
115
|
<p></p>
|
|
108
116
|
<div class="ranks">
|
|
109
117
|
<div class="rank prev">
|
|
110
|
-
<img src="
|
|
111
|
-
<img src="
|
|
118
|
+
<img src="<%- getImageUrl('medal_' +(prevRank.inTop100 ?? prevRank.medal)) %>" alt="" />
|
|
119
|
+
<img src="<%- getImageUrl('star_' + prevRank.star) %>" alt="" />
|
|
112
120
|
<p><%= prevRank.leader ?? "" %></p>
|
|
113
121
|
</div>
|
|
114
122
|
<div class="rank curr">
|
|
115
|
-
<img src="
|
|
116
|
-
<img src="
|
|
123
|
+
<img src="<%- getImageUrl('medal_' +(currRank.inTop100 ?? currRank.medal)) %>" alt="" />
|
|
124
|
+
<img src="<%- getImageUrl('star_' + currRank.star) %>" alt="" />
|
|
117
125
|
<p><%= currRank.leader ?? "" %></p>
|
|
118
126
|
</div>
|
|
119
127
|
</div>
|