@sjtdev/koishi-plugin-dota2tracker 2.0.4 → 2.1.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.
Files changed (2) hide show
  1. package/lib/index.js +160 -38
  2. package/package.json +12 -8
package/lib/index.js CHANGED
@@ -766,7 +766,7 @@ var require_zh_CN_constants = __commonJS({
766
766
  // src/locales/en-US.command.yml
767
767
  var require_en_US_command = __commonJS({
768
768
  "src/locales/en-US.command.yml"(exports2, module2) {
769
- module2.exports = { commands: { dota2tracker: { subscribe: { description: "After subscribing, players need to bind their Steam ID to this group.", usage: "After subscribing, players need to bind their Steam ID to this group. BOT will subscribe to the new game data of bound players in this group. After the STRATZ game analysis is completed, the game data will be generated into a picture battle report and published to this group.", messages: { subscribe_success: "Subscription successful.", subscribed: "This Channel has been subscribed, no need to subscribe again." } }, unsubscribe: { description: "Unsubscribe from this group.", messages: { unsubscribe_success: "Unsubscription successful.", not_subscribed: "This Channel has not been subscribed yet, so there is no need to unsubscribe." } }, bind: { description: "Bind your SteamID and optionally set a nickname.", usage: "Bind your SteamID to your account. If the group is subscribed, your new match data will be posted in the group in real-time.", examples: 'bind 123456789\nbind 123456789 John\nbind 123456789 "John Doe"', messages: { steam_id_invalid: "Invalid SteamID.", bind_success: "Binding successful,\nID: {userId}\nNickname: {nickName}\nSteamID: {steamId}", bind_failed: "Binding failed, {0}", reason_without_match: "Invalid SteamID or no matches found.", reason_fetch_failed: "Poor network conditions or other reasons prevented the verification of the SteamID. Please try again later.", already_binded: "You are already bound, no need to bind again.\nHere is your personal information:\nID: {userId}\nNickname: {nickName}\nSteamID: {steamId}", nick_name_too_long: "Nickname is too long, please limit it to 20 characters or less. (It can also be left blank)", is_anonymous: 'Please note: Your Steam player data is not public, and you will not be able to use the main functions of the BOT, such as "battle report tracking," "query-recent-match commands," etc.\nIf you need to make data public, please set it to public in the DOTA2 game settings.' } }, unbind: { description: "Unbind your personal information.", messages: { unbind_success: "Unbinding successful.", not_binded: "Not bound, no need to unbind." } }, rename: { description: "Change the nickname set during binding.", examples: 'rename John\nrename "John Doe"', messages: { rename_success: "Rename successful, now you are called {nick_name}.", empty_input: "Please enter your nickname.", not_binded: "Please bind first, you can set a nickname during binding.", nick_name_too_long: "Nickname is too long, please limit it to 20 characters.", nick_name_same: "The input content is the same as the original nickname and does not need to be renamed." } }, "query-members": { description: "Query the players bound in this group.", messages: { title: "Guild DOTA 2 Roster (Total: {count})", table_headers: { nickname: "Nickname", winrate: "Win Rate (L10)", last_match: "Last Match" }, no_members: "No players bound in this group.", query_failed: "Failed to query group members." } }, "query-match": { description: "Query the match data of the specified match ID and generate a picture.", options: { parse: "Whether to wait for match data parsing" }, examples: "query-match 1234567890\nquery-match 1234567890 -p\nquery-match 1234567890 --parse", messages: { empty_input: "Please enter the match ID.", match_id_invalid: "Invalid match ID.", querying_match: "Searching for match details, please wait...", query_failed: "Failed to get match data.", waiting_for_parse: "Match data has not been parsed yet, a parse request has been sent to the server. The battle report will be sent once parsing is complete or times out." } }, "query-recent-match": { description: "Query the most recent match data and generate a picture.", options: { parse: "Whether to wait for match data parsing" }, usage: "Query the most recent match data of the specified player and generate a picture.\nThe parameter can be the player's SteamID or the nickname of a player bound in this group. If no parameter is provided, it will try to query the SteamID of the command caller.", examples: "query-recent-match\nquery-recent-match 123456789\nquery-recent-match John\nquery-recent-match 123456789 -p\nquery-recent-match John --parse", messages: { querying_match: "Searching for match details, please wait...", query_failed: "Failed to get the player's recent match.", is_anonymous: "Your player data is not public, and recent match data cannot be obtained.\nIf you need to make data public, please set it to public in the DOTA2 game settings." } }, "query-player": { description: "Query the player's personal information, optionally specify a hero.", options: { hero: "Query the player's usage of the specified hero (same as querying a hero, can use nickname or ID)" }, usage: "Query the personal information of the specified player and generate a picture, optionally specify a hero.\nThe parameter can be the player's SteamID or the nickname of a player bound in this group. If no parameter is provided, it will try to query the SteamID of the command caller.", examples: "query-player\nquery-player 123456789\nquery-player John\nquery-player John --hero Anti-Mage\nquery-player John -o Anti-Mage", messages: { querying_player: "Retrieving player data, please wait...", query_failed: "Failed to get player information." } }, "query-hero": { description: "Query hero skills/stats information.", options: { random: "Randomly select a hero." }, usage: "Query the hero's skill descriptions and various stats, generate a picture.\nThe parameter can be the hero's ID, name, or common nickname.", examples: "query-hero 15\nquery-hero Razor\nquery-hero -r", messages: { not_found: "Hero not found, please confirm and re-enter.", querying_hero: "Retrieving hero data, please wait...", query_failed: "Failed to get hero data.", empty_input: "Please enter a parameter." } }, "query-item": { description: "Query item information", usage: "Query item descriptions and attributes, then generate and publish an image report.\nParameters can be item name (supports fuzzy search), item alias, or item ID.\nYou can set the maximum number of items to send per query on the configuration page, as well as whether to send the item list when the limit is exceeded or parameters are not entered.", examples: "query-item Vanguard", messages: { query_list_failed: "Failed to retrieve item list data", query_item_failed: "Failed to retrieve data for item '{0}'", querying_item: "Querying item data, please wait...", cache_building: "Initializing or rebuilding item cache for the current version, please wait...", empty_input: "No keywords provided. \n{#if show}Displaying full item list per current configuration\n{:else}No content available\n{/if}", not_found: "No items found matching the keywords, please verify and retry", too_many_items: "Found {count} items, exceeding maximum display limit ({max} items)\n{#if show}(Displaying item list){/if}", finded_items: "Matching items: \n{#each items as item}\n{item.name_loc}{#if item !== items[items.length - 1]}, {/if}\n{/each}" } }, "hero-of-the-day": { description: "Get hero recommendations for the day.", usage: "Fetches recent and lifetime match history to recommend heroes based on parameters like wins, performance score, and hot streaks.\nThe parameter can be a player's SteamID or the nickname of a player bound in this group. If no parameter is provided, it will try to look up the command caller's SteamID.", options: { days: "-d <number> The range of recent days to consider, default is 30." }, examples: 'hero-of-the-day\nhero-of-the-day -d 60\nhero-of-the-day 1234567890\nhero-of-the-day "John Doe"', messages: { title_recommendation: "Today's Recommendation:", recommendation_intro: "The recommended heroes for you today are:", recommendation_heroes: "{#each heroes as hero}{hero}{#if hero !== heroes[heroes.length - 1]}, {/if}{/each}", recommendation_type_lifetime_only: "Your recent match history is empty. This recommendation is based on your lifetime statistics.", recommendation_type_no_record: "Recommendations cannot be generated due to a lack of recent and lifetime match data.", recommendation_type_anonymous: "Recommendations cannot be generated because your profile data is private.", details: { pool_description: "The recommendation is generated by scoring your recent and lifetime hero performance, sorting by total score, and then randomly selecting from the top 10 heroes weighted by their scores.", table_intro: "Below is the detailed score breakdown for the top 10 heroes.", table_headers: { hero: "Hero", recent_wins: "Recent Wins Score", lifetime_wins: "Lifetime Wins<br>(Logarithmic)", imp_bonus: "IMP Bonus", is_hot_streak: "Hot Streak", total_score: "Total Score" }, scoring_formula: "Current Scoring Formula: [Recent Wins x 1] + [log(Lifetime Wins + 1) x 5] + [Recent IMP x 0.1]", hot_streak_desc: "If a hero was played in the last 3 days, it's considered a 'Hot Streak' hero, receiving a 20% bonus to its total score." }, title_meta: "Meta Trends:", meta_intro: "Top 3 advantage heroes for each position with a <b>pick rate ≥2%</b> within ±1 of your rank bracket ({tiers}) over the last week, sorted by win rate:", meta_table_header: "Hero (Pick% Win%)", meta_position: "Pos {pos}:" } }, common: { messages: { user_not_binded_in_channel: "By default, it tries to find your information from the bound SteamID players, but it seems you are not bound.\nPlease bind your SteamID in this group. (You can enter [bind -h] for help)\nOr follow the command with the SteamID or nickname of the player you want to query.", user_not_in_group: "Command failed.\nCurrently not in a group chat, you must provide the specified player's SteamID.", invalid_input_include_steam_id: "Invalid SteamID and the player was not found in this group by the given input." } }, help: { description: "Get the link to the detailed online documentation for the plugin.", messages: { content: "For a detailed guide on DOTA2Tracker features, command usage, and algorithm explanations, please visit the online documentation:\nhttps://sjtdev.github.io/koishi-plugin-dota2tracker/en-US/" } } } } };
769
+ module2.exports = { commands: { dota2tracker: { description: 'A series of commands for Dota 2 Tracker. Use "dota2tracker -h" to see all available commands.', subscribe: { description: "Subscribes the current channel to Dota 2 match tracking.", usage: "After subscribing, players need to bind their Steam ID in this channel. The bot will then track new matches of bound players and post image-based reports upon completion of parsing by Stratz.", examples: "subscribe", messages: { subscribe_success: "Subscription successful.", subscribed: "This Channel has been subscribed, no need to subscribe again." } }, unsubscribe: { description: "Unsubscribes the current channel from match tracking.", usage: "Unsubscribes the current channel from match tracking.", examples: "unsubscribe", messages: { unsubscribe_success: "Unsubscription successful.", not_subscribed: "This Channel has not been subscribed yet, so there is no need to unsubscribe." } }, bind: { description: "Binds your SteamID to your account in the current channel.", usage: 'Bind your SteamID to your account. If the channel is subscribed, your new match data will be posted automatically. Nicknames containing spaces must be enclosed in double quotes ("").', examples: 'bind 123456789\nbind 123456789 John\nbind 123456789 "John Doe"', messages: { steam_id_invalid: "Invalid SteamID.", bind_success: "Binding successful,\nID: {userId}\nNickname: {nickName}\nSteamID: {steamId}", bind_failed: "Binding failed, {0}", reason_without_match: "Invalid SteamID or no matches found.", reason_fetch_failed: "Poor network conditions or other reasons prevented the verification of the SteamID. Please try again later.", already_binded: "You are already bound, no need to bind again.\nHere is your personal information:\nID: {userId}\nNickname: {nickName}\nSteamID: {steamId}", nick_name_too_long: "Nickname is too long, please limit it to 20 characters or less. (It can also be left blank)", is_anonymous: 'Please note: Your Steam player data is not public, and you will not be able to use the main functions of the BOT, such as "battle report tracking," "query-recent-match commands," etc.\nIf you need to make data public, please set it to public in the DOTA2 game settings.' } }, unbind: { description: "Unbinds your personal information in the current channel.", usage: "Unbind your personal information in the current channel.", examples: "unbind", messages: { unbind_success: "Unbinding successful.", not_binded: "Not bound, no need to unbind." } }, rename: { description: "Changes the nickname set during binding.", usage: 'Change the nickname set during binding. Nicknames containing spaces must be enclosed in double quotes ("").', examples: 'rename John\nrename "John Doe"', messages: { rename_success: "Rename successful, now you are called {nick_name}.", empty_input: "Please enter your nickname.", not_binded: "Please bind first, you can set a nickname during binding.", nick_name_too_long: "Nickname is too long, please limit it to 20 characters.", nick_name_same: "The input content is the same as the original nickname and does not need to be renamed." } }, "query-members": { description: "Queries the players bound in this channel and generates an info image.", usage: "Queries the players bound in this channel and generates an informational image.", examples: "query-members", messages: { title: "Guild DOTA 2 Roster (Total: {count})", table_headers: { nickname: "Nickname", winrate: "Win Rate (L10)", last_match: "Last Match" }, no_members: "No players bound in this group.", query_failed: "Failed to query group members." } }, "query-match": { description: "Query the match data of the specified match ID and generate a picture.", usage: "Query the match data of the specified match ID and generate a picture.", options: { parse: "Whether to wait for match data parsing" }, examples: "query-match 1234567890\nquery-match 1234567890 -p\nquery-match 1234567890 --parse", messages: { empty_input: "Please enter the match ID.", match_id_invalid: "Invalid match ID.", querying_match: "Searching for match details, please wait...", query_failed: "Failed to get match data.", waiting_for_parse: "Match data has not been parsed yet, a parse request has been sent to the server. The battle report will be sent once parsing is complete or times out." } }, "query-recent-match": { description: "Query the most recent match data and generate a picture.", options: { parse: "Whether to wait for match data parsing" }, usage: "Query the most recent match data of the specified player and generate a picture.\nThe parameter can be the player's SteamID or the nickname of a player bound in this group. If no parameter is provided, it will try to query the SteamID of the command caller.", examples: "query-recent-match\nquery-recent-match 123456789\nquery-recent-match John\nquery-recent-match 123456789 -p\nquery-recent-match John --parse", messages: { querying_match: "Searching for match details, please wait...", query_failed: "Failed to get the player's recent match.", is_anonymous: "Your player data is not public, and recent match data cannot be obtained.\nIf you need to make data public, please set it to public in the DOTA2 game settings." } }, "query-player": { description: "Query the player's personal information, optionally specify a hero.", options: { hero: "Query the player's usage of the specified hero (same as querying a hero, can use nickname or ID)" }, usage: "Query the personal information of the specified player and generate a picture, optionally specify a hero.\nThe parameter can be the player's SteamID or the nickname of a player bound in this group. If no parameter is provided, it will try to query the SteamID of the command caller.", examples: "query-player\nquery-player 123456789\nquery-player John\nquery-player John --hero Anti-Mage\nquery-player John -o Anti-Mage", messages: { querying_player: "Retrieving player data, please wait...", query_failed: "Failed to get player information." } }, "query-hero": { description: "Query hero skills/stats information.", options: { random: "Randomly select a hero." }, usage: "Query the hero's skill descriptions and various stats, generate a picture.\nThe parameter can be the hero's ID, name, or common nickname.", examples: "query-hero 15\nquery-hero Razor\nquery-hero -r", messages: { not_found: "Hero not found, please confirm and re-enter.", querying_hero: "Retrieving hero data, please wait...", query_failed: "Failed to get hero data.", empty_input: "Please enter a parameter." } }, "query-item": { description: "Query item information", usage: "Query item descriptions and attributes, then generate and publish an image report.\nParameters can be item name (supports fuzzy search), item alias, or item ID.\nYou can set the maximum number of items to send per query on the configuration page, as well as whether to send the item list when the limit is exceeded or parameters are not entered.", examples: "query-item Vanguard", messages: { query_list_failed: "Failed to retrieve item list data", query_item_failed: "Failed to retrieve data for item '{0}'", querying_item: "Querying item data, please wait...", cache_building: "Initializing or rebuilding item cache for the current version, please wait...", empty_input: "No keywords provided. \n{#if show}Displaying full item list per current configuration\n{:else}No content available\n{/if}", not_found: "No items found matching the keywords, please verify and retry", too_many_items: "Found {count} items, exceeding maximum display limit ({max} items)\n{#if show}(Displaying item list){/if}", finded_items: "Matching items: \n{#each items as item}\n{item.name_loc}{#if item !== items[items.length - 1]}, {/if}\n{/each}" } }, "hero-of-the-day": { description: "Get hero recommendations for the day.", usage: "Fetches recent and lifetime match history to recommend heroes based on parameters like wins, performance score, and hot streaks.\nThe parameter can be a player's SteamID or the nickname of a player bound in this group. If no parameter is provided, it will try to look up the command caller's SteamID.", options: { days: "-d <number> The range of recent days to consider, default is 30." }, examples: 'hero-of-the-day\nhero-of-the-day -d 60\nhero-of-the-day 1234567890\nhero-of-the-day "John Doe"', messages: { title_recommendation: "Today's Recommendation:", recommendation_intro: "The recommended heroes for you today are:", recommendation_heroes: "{#each heroes as hero}{hero}{#if hero !== heroes[heroes.length - 1]}, {/if}{/each}", recommendation_type_lifetime_only: "Your recent match history is empty. This recommendation is based on your lifetime statistics.", recommendation_type_no_record: "Recommendations cannot be generated due to a lack of recent and lifetime match data.", recommendation_type_anonymous: "Recommendations cannot be generated because your profile data is private.", details: { pool_description: "The recommendation is generated by scoring your recent and lifetime hero performance, sorting by total score, and then randomly selecting from the top 10 heroes weighted by their scores.", table_intro: "Below is the detailed score breakdown for the top 10 heroes.", table_headers: { hero: "Hero", recent_wins: "Recent Wins Score", lifetime_wins: "Lifetime Wins<br>(Logarithmic)", imp_bonus: "IMP Bonus", is_hot_streak: "Hot Streak", total_score: "Total Score" }, scoring_formula: "Current Scoring Formula: [Recent Wins x 1] + [log(Lifetime Wins + 1) x 5] + [Recent IMP x 0.1]", hot_streak_desc: "If a hero was played in the last 3 days, it's considered a 'Hot Streak' hero, receiving a 20% bonus to its total score." }, title_meta: "Meta Trends:", meta_intro: "Top 3 advantage heroes for each position with a <b>pick rate ≥2%</b> within ±1 of your rank bracket ({tiers}) over the last week, sorted by win rate:", meta_table_header: "Hero (Pick% Win%)", meta_position: "Pos {pos}:" } }, common: { messages: { user_not_binded_in_channel: "By default, it tries to find your information from the bound SteamID players, but it seems you are not bound.\nPlease bind your SteamID in this group. (You can enter [bind -h] for help)\nOr follow the command with the SteamID or nickname of the player you want to query.", user_not_in_group: "Command failed.\nCurrently not in a group chat, you must provide the specified player's SteamID.", invalid_input_include_steam_id: "Invalid SteamID and the player was not found in this group by the given input." } }, help: { description: "Get detailed information for all commands and the link to the online documentation.", usage: "Get detailed information for all commands and the link to the online documentation.", examples: "help", messages: { header: "Below is the full list of plugin commands.\n※Note: <arg> is a required argument, and [arg] is an optional argument. Please see the 'Examples' column for specific usage.", footer: "For more information on plugin configuration, template showcases, and other details, please visit the online documentation:\nhttps://sjtdev.github.io/koishi-plugin-dota2tracker/en-US/", table_headers: { command: "Command", alias: "Alias", arguments: "Arguments", description: "Description", options: "Options", examples: "Examples" } } } } } };
770
770
  }
771
771
  });
772
772
 
@@ -780,7 +780,7 @@ var require_en_US_schema = __commonJS({
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: "{avatar}<br/>Sad", rank_fun_up_message: "Hip hip hooray! Our awesome member {avatar}{name} has leveled up from {prev} to {curr}!", 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", 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" } } };
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: "{avatar}<br/>Sad", rank_fun_up_message: "Hip hip hooray! Our awesome member {avatar}{name} has leveled up from {prev} to {curr}!", 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
 
@@ -794,7 +794,7 @@ var require_en_US = __commonJS({
794
794
  // src/locales/zh-CN.command.yml
795
795
  var require_zh_CN_command = __commonJS({
796
796
  "src/locales/zh-CN.command.yml"(exports2, module2) {
797
- module2.exports = { commands: { dota2tracker: { description: "dota2tracker的一系列指令,可以使用dota2tracker -h查看所有可用指令。", subscribe: { description: "[订阅本群]", usage: "订阅后还需玩家在本群绑定SteamID,BOT将订阅本群中已绑定玩家的新比赛数据,在STRATZ比赛解析完成后将比赛数据生成为图片战报发布至本群中。", messages: { subscribed: "本群已订阅,无需重复订阅。", subscribe_success: "订阅成功。" } }, unsubscribe: { description: "[取消订阅] 取消订阅本群。", messages: { unsubscribe_success: "取消订阅成功。", not_subscribed: "本群尚未订阅,无需取消订阅。" } }, bind: { description: "[绑定] 绑定SteamID,并起一个别名(也可以不起)。", usage: "将你的SteamID与你的账号绑定,若本群已订阅将会实时获取你的新比赛数据发布至群中。", examples: '绑定 123456789\n绑定 123456789 张三\n绑定 123456789 "张 三"', messages: { steam_id_invalid: "SteamID无效。", bind_success: "绑定成功,\nID:{userId}\n别名:{nickName}\nSteamID:{steamId}", bind_failed: "绑定失败,{0}", reason_without_match: "SteamID无效或无任何场次。", reason_fetch_failed: "网络状况不佳或其他原因无法验证SteamID,请稍后重试。", already_binded: "你已绑定,无需重复绑定。\n以下是你的个人信息:\nID:{userId}\n别名:{nickName}\nSteamID:{steamId}", nick_name_too_long: "别名过长,请限制在20个字符以内。(也可以留空)", is_anonymous: "请注意:你的Steam玩家数据并未公开,将无法使用BOT的主要功能,如“战报追踪”、“查询最近指令”等。\n如需公开数据,请在DOTA2游戏内设置中公开。" } }, unbind: { description: "[取消绑定] 取消绑定你的个人信息。", messages: { unbind_success: "取消绑定成功。", not_binded: "尚未绑定,无需取消绑定。" } }, rename: { description: "[改名] 修改绑定时设定的别名。", examples: '改名 李四\n改名 "李 四"', messages: { rename_success: "改名成功,现在你叫{nick_name}了。", empty_input: "请输入你的别名。", not_binded: "请先绑定,绑定时即可设定别名。", nick_name_too_long: "别名过长,请限制在20个字符以内。", nick_name_same: "目标别名与原始别名相同,无需改名。" } }, "query-members": { description: "[查询群友] 查询本群已绑定的玩家。", messages: { title: "本群 DOTA2 玩家名册 (共 {count} 人)", table_headers: { nickname: "昵称/别名", winrate: "胜率 (近10场)", last_match: "最近比赛" }, no_members: "本群尚无绑定玩家。", query_failed: "查询群友失败。" } }, "query-match": { description: "[查询比赛] 查询指定比赛ID的比赛数据,生成图片发布。", options: { parse: "-p 是否等待解析比赛数据" }, examples: "查询比赛 1234567890\n查询比赛 1234567890 -p\n查询比赛 1234567890 --parse", messages: { empty_input: "请输入比赛ID。", match_id_invalid: "比赛ID无效。", querying_match: "正在搜索对局详情,请稍后……", query_failed: "获取比赛数据失败。", waiting_for_parse: "比赛数据尚未解析,已发送解析请求到服务器,战报将在解析完成或超时后发送。" } }, "query-recent-match": { description: "[查询最近比赛] 查询最近的比赛数据,生成图片发布。", options: { parse: "-p 是否等待解析比赛数据" }, usage: "查询指定玩家的最近一场比赛的比赛数据,生成图片发布。\n参数可输入该玩家的SteamID或已在本群绑定玩家的别名,无参数时尝试查询调用指令玩家的SteamID。", examples: "查询最近比赛\n查询最近比赛 123456789\n查询最近比赛 张三\n查询最近比赛 123456789 -p\n查询最近比赛 张三 --parse", messages: { querying_match: "正在搜索对局详情,请稍后……", query_failed: "获取玩家最近比赛失败。", not_in_group: "指令调用失败。\n当前不属于群聊状态,必须提供指定玩家的SteamID。", is_anonymous: "你的比赛数据未公开,无法获取最近比赛数据。\n如需公开数据,请在DOTA2游戏内设置中公开。" } }, "query-player": { description: "[查询玩家] 查询玩家的个人信息,可指定英雄。", options: { hero: "-o 查询玩家指定英雄使用情况(同查询英雄,可用别名或ID)" }, usage: "查询指定玩家的个人信息,生成图片发布,可指定英雄。\n参数可输入该玩家的SteamID或已在本群绑定玩家的别名,无参数时尝试查询调用指令玩家的SteamID。", examples: "查询玩家\n查询玩家 123456789\n查询玩家 张三\n查询玩家 张三 --hero 敌法师\n查询玩家 张三 -o 15", messages: { querying_player: "正在获取玩家数据,请稍后……", query_failed: "获取玩家信息失败。", not_in_group: "指令调用失败。\n当前不属于群聊状态,必须提供指定玩家的SteamID。" } }, "query-hero": { description: "[查询英雄] 查询英雄技能/面板信息。", options: { random: "-r 随机选择英雄" }, usage: "查询英雄的技能说明与各项数据,生成图片发布。\n参数可输入英雄ID、英雄名、英雄常用别名。", examples: "查询英雄 15\n查询英雄 雷泽\n查询英雄 电魂", messages: { not_found: "未找到输入的英雄,请确认后重新输入。", querying_hero: "正在获取英雄数据,请稍后……", query_failed: "获取英雄数据失败。", empty_input: "请输入参数。" } }, "query-item": { description: "[查询物品] 查询物品信息。", usage: "查询物品的描述与各项数据,生成图片发布。\n参数可输入物品名(可模糊查找)、物品别名、物品ID。\n可在配置页中设置每次查询的最大发送数量、以及是否在超过限制或未输入参数时发送物品列表。", examples: "查询物品 先锋盾", messages: { query_list_failed: "获取物品列表数据失败。", query_item_failed: "获取物品「{0}」数据失败", querying_item: "正在查询物品数据,请稍候…", cache_building: "初次使用或缓存已过期,正在生成当前版本的物品缓存,请稍后……", empty_input: "未输入关键字参数。根据当前配置{#if show},将返回全部物品列表{:else}无内容可发送{/if}。", not_found: "未找到与关键字匹配的物品,请确认后重试。", too_many_items: "找到{count}个物品,超过最大发送限制({max}个){#if show},将发送物品列表{/if}。", finded_items: "找到以下物品:{#each items as item}{item.name_loc}{#if item !== items[items.length - 1]}、{/if}{/each}" } }, "hero-of-the-day": { description: "[今日英雄] 获取今日英雄推荐。", usage: "获取近期比赛记录、生涯比赛记录,根据胜场、表现分、是否手热等参数计算推荐英雄。\n参数可输入该玩家的SteamID或已在本群绑定玩家的别名,无参数时尝试查询调用指令玩家的SteamID。", options: { days: "-d <number> 近期天数范围,默认值为30" }, examples: "今日英雄\n今日英雄 -d 60\n今日英雄 1234567890\n今日英雄 张三", messages: { title_recommendation: "今日推荐:", recommendation_intro: "今日为您推荐的英雄是:", recommendation_heroes: "{#each heroes as hero}{hero}{#if hero !== heroes[heroes.length - 1]}、{/if}{/each}", recommendation_type_lifetime_only: "您的近期数据为空,本次推荐结果基于您的生涯数据。", recommendation_type_no_record: "您的近期与生涯数据为空,无法生成推荐信息。", recommendation_type_anonymous: "您的个人数据未公开,无法生成推荐信息。", details: { pool_description: "推荐结果根据对您的近期与生涯英雄使用记录计分后,按总分排序后对前10位英雄以分数为权重随机取得。", table_intro: "以下是前10位英雄具体得分表。", table_headers: { hero: "英雄名称", recent_wins: "近期胜场分", lifetime_wins: "生涯胜场分<br>(对数)", imp_bonus: "imp奖励分", is_hot_streak: "是否手热", total_score: "总分" }, scoring_formula: "当前计分规则:[近期胜场数 x 1] + [log(生涯胜场数+1) x 5] + [近期imp x 0.1]", hot_streak_desc: "若英雄在3天内使用过,则记为手热英雄,总分提升20%。" }, title_meta: "环境趋势:", meta_intro: "一周内,基于您段位±1 ({tiers}) 范围内各位置<b>选择率≥2%</b>按胜率从高到低前三名优势英雄:", meta_table_header: "英雄名称(选择率% 胜率%)", meta_position: "{pos}号位:" } }, common: { messages: { user_not_binded_in_channel: "无参数时默认从已绑定SteamID玩家中寻找你的信息,但你似乎并没有绑定。\n请在本群绑定SteamID。(可输入【绑定 -h】获取帮助)\n或在指令后跟上希望查询的SteamID或已绑定玩家的别名。", user_not_in_group: "指令调用失败。\n当前不属于群聊状态,必须提供指定玩家的SteamID。", invalid_input_include_steam_id: "SteamID无效并且未在本群根据输入信息找到玩家。" } }, help: { description: "[DOTA2指南] 获取插件的详细在线文档链接。", messages: { content: "DOTA2Tracker 详细功能指南、指令用法、算法说明,请访问在线文档:\nhttps://sjtdev.github.io/koishi-plugin-dota2tracker/" } } } } };
797
+ module2.exports = { commands: { dota2tracker: { description: "dota2tracker的一系列指令,可以使用dota2tracker -h查看所有可用指令。", subscribe: { description: "[订阅本群]", usage: "订阅后还需玩家在本群绑定SteamID,BOT将订阅本群中已绑定玩家的新比赛数据,在STRATZ比赛解析完成后将比赛数据生成为图片战报发布至本群中。", examples: "订阅本群", messages: { subscribed: "本群已订阅,无需重复订阅。", subscribe_success: "订阅成功。" } }, unsubscribe: { description: "[取消订阅] 取消订阅本群。", usage: "取消订阅本群。", examples: "取消订阅", messages: { unsubscribe_success: "取消订阅成功。", not_subscribed: "本群尚未订阅,无需取消订阅。" } }, bind: { description: "[绑定] 绑定SteamID,并起一个别名(也可以不起)。", usage: '将你的SteamID与你的账号绑定,若本群已订阅将会实时获取你的新比赛数据发布至群中。名称中含有空格时需要使用""引号包裹(英文半角引号)。', examples: '绑定 123456789\n绑定 123456789 张三\n绑定 123456789 "张 三"', messages: { steam_id_invalid: "SteamID无效。", bind_success: "绑定成功,\nID:{userId}\n别名:{nickName}\nSteamID:{steamId}", bind_failed: "绑定失败,{0}", reason_without_match: "SteamID无效或无任何场次。", reason_fetch_failed: "网络状况不佳或其他原因无法验证SteamID,请稍后重试。", already_binded: "你已绑定,无需重复绑定。\n以下是你的个人信息:\nID:{userId}\n别名:{nickName}\nSteamID:{steamId}", nick_name_too_long: "别名过长,请限制在20个字符以内。(也可以留空)", is_anonymous: "请注意:你的Steam玩家数据并未公开,将无法使用BOT的主要功能,如“战报追踪”、“查询最近指令”等。\n如需公开数据,请在DOTA2游戏内设置中公开。" } }, unbind: { description: "[取消绑定] 取消绑定你的个人信息。", usage: "取消绑定你的个人信息。", examples: "取消绑定", messages: { unbind_success: "取消绑定成功。", not_binded: "尚未绑定,无需取消绑定。" } }, rename: { description: "[改名] 修改绑定时设定的别名。", usage: '修改绑定时设定的别名。名称中含有空格时需要使用""引号包裹(英文半角引号)。', examples: '改名 李四\n改名 "李 四"', messages: { rename_success: "改名成功,现在你叫{nick_name}了。", empty_input: "请输入你的别名。", not_binded: "请先绑定,绑定时即可设定别名。", nick_name_too_long: "别名过长,请限制在20个字符以内。", nick_name_same: "目标别名与原始别名相同,无需改名。" } }, "query-members": { description: "[查询群友] 查询本群已绑定的玩家。", usage: "查询本群已绑定的玩家,生成简单信息图片发布。", examples: "查询群友", messages: { title: "本群 DOTA2 玩家名册 (共 {count} 人)", table_headers: { nickname: "昵称/别名", winrate: "胜率 (近10场)", last_match: "最近比赛" }, no_members: "本群尚无绑定玩家。", query_failed: "查询群友失败。" } }, "query-match": { description: "[查询比赛] 查询指定比赛ID的比赛数据,生成图片发布。", usage: "查询指定MatchID的比赛数据,生成图片发布。", options: { parse: "-p 是否等待解析比赛数据" }, examples: "查询比赛 1234567890\n查询比赛 1234567890 -p\n查询比赛 1234567890 --parse", messages: { empty_input: "请输入比赛ID。", match_id_invalid: "比赛ID无效。", querying_match: "正在搜索对局详情,请稍后……", query_failed: "获取比赛数据失败。", waiting_for_parse: "比赛数据尚未解析,已发送解析请求到服务器,战报将在解析完成或超时后发送。" } }, "query-recent-match": { description: "[查询最近比赛] 查询最近的比赛数据,生成图片发布。", options: { parse: "-p 是否等待解析比赛数据" }, usage: "查询指定玩家的最近一场比赛的比赛数据,生成图片发布。\n参数可输入该玩家的SteamID或已在本群绑定玩家的别名,无参数时尝试查询调用指令玩家的SteamID。", examples: "查询最近比赛\n查询最近比赛 123456789\n查询最近比赛 张三\n查询最近比赛 123456789 -p\n查询最近比赛 张三 --parse", messages: { querying_match: "正在搜索对局详情,请稍后……", query_failed: "获取玩家最近比赛失败。", not_in_group: "指令调用失败。\n当前不属于群聊状态,必须提供指定玩家的SteamID。", is_anonymous: "你的比赛数据未公开,无法获取最近比赛数据。\n如需公开数据,请在DOTA2游戏内设置中公开。" } }, "query-player": { description: "[查询玩家] 查询玩家的个人信息,可指定英雄。", options: { hero: "-o 查询玩家指定英雄使用情况(同查询英雄,可用别名或ID)" }, usage: "查询指定玩家的个人信息,生成图片发布,可指定英雄。\n参数可输入该玩家的SteamID或已在本群绑定玩家的别名,无参数时尝试查询调用指令玩家的SteamID。", examples: "查询玩家\n查询玩家 123456789\n查询玩家 张三\n查询玩家 张三 --hero 敌法师\n查询玩家 张三 -o 15", messages: { querying_player: "正在获取玩家数据,请稍后……", query_failed: "获取玩家信息失败。", not_in_group: "指令调用失败。\n当前不属于群聊状态,必须提供指定玩家的SteamID。" } }, "query-hero": { description: "[查询英雄] 查询英雄技能/面板信息。", options: { random: "-r 随机选择英雄" }, usage: "查询英雄的技能说明与各项数据,生成图片发布。\n参数可输入英雄ID、英雄名、英雄常用别名。", examples: "查询英雄 15\n查询英雄 雷泽\n查询英雄 电魂\n查询英雄 -r", messages: { not_found: "未找到输入的英雄,请确认后重新输入。", querying_hero: "正在获取英雄数据,请稍后……", query_failed: "获取英雄数据失败。", empty_input: "请输入参数。" } }, "query-item": { description: "[查询物品] 查询物品信息。", usage: "查询物品的描述与各项数据,生成图片发布。\n参数可输入物品名(可模糊查找)、物品别名、物品ID。\n可在配置页中设置每次查询的最大发送数量、以及是否在超过限制或未输入参数时发送物品列表。", examples: "查询物品 先锋盾", messages: { query_list_failed: "获取物品列表数据失败。", query_item_failed: "获取物品「{0}」数据失败", querying_item: "正在查询物品数据,请稍候…", cache_building: "初次使用或缓存已过期,正在生成当前版本的物品缓存,请稍后……", empty_input: "未输入关键字参数。根据当前配置{#if show},将返回全部物品列表{:else}无内容可发送{/if}。", not_found: "未找到与关键字匹配的物品,请确认后重试。", too_many_items: "找到{count}个物品,超过最大发送限制({max}个){#if show},将发送物品列表{/if}。", finded_items: "找到以下物品:{#each items as item}{item.name_loc}{#if item !== items[items.length - 1]}、{/if}{/each}" } }, "hero-of-the-day": { description: "[今日英雄] 获取今日英雄推荐。", usage: "获取近期比赛记录、生涯比赛记录,根据胜场、表现分、是否手热等参数计算推荐英雄。\n参数可输入该玩家的SteamID或已在本群绑定玩家的别名,无参数时尝试查询调用指令玩家的SteamID。", options: { days: "-d <number> 近期数据的获取范围,单位为天数,默认值为30" }, examples: "今日英雄\n今日英雄 -d 60\n今日英雄 1234567890\n今日英雄 张三", messages: { title_recommendation: "今日推荐:", recommendation_intro: "今日为您推荐的英雄是:", recommendation_heroes: "{#each heroes as hero}{hero}{#if hero !== heroes[heroes.length - 1]}、{/if}{/each}", recommendation_type_lifetime_only: "您的近期数据为空,本次推荐结果基于您的生涯数据。", recommendation_type_no_record: "您的近期与生涯数据为空,无法生成推荐信息。", recommendation_type_anonymous: "您的个人数据未公开,无法生成推荐信息。", details: { pool_description: "推荐结果根据对您的近期与生涯英雄使用记录计分后,按总分排序后对前10位英雄以分数为权重随机取得。", table_intro: "以下是前10位英雄具体得分表。", table_headers: { hero: "英雄名称", recent_wins: "近期胜场分", lifetime_wins: "生涯胜场分<br>(对数)", imp_bonus: "imp奖励分", is_hot_streak: "是否手热", total_score: "总分" }, scoring_formula: "当前计分规则:[近期胜场数 x 1] + [log(生涯胜场数+1) x 5] + [近期imp x 0.1]", hot_streak_desc: "若英雄在3天内使用过,则记为手热英雄,总分提升20%。" }, title_meta: "环境趋势:", meta_intro: "一周内,基于您段位±1 ({tiers}) 范围内各位置<b>选择率≥2%</b>按胜率从高到低前三名优势英雄:", meta_table_header: "英雄名称(选择率% 胜率%)", meta_position: "{pos}号位:" } }, common: { messages: { user_not_binded_in_channel: "无参数时默认从已绑定SteamID玩家中寻找你的信息,但你似乎并没有绑定。\n请在本群绑定SteamID。(可输入【绑定 -h】获取帮助)\n或在指令后跟上希望查询的SteamID或已绑定玩家的别名。", user_not_in_group: "指令调用失败。\n当前不属于群聊状态,必须提供指定玩家的SteamID。", invalid_input_include_steam_id: "SteamID无效并且未在本群根据输入信息找到玩家。" } }, help: { description: "[DOTA2指南] 获取插件的全部指令详细信息与在线文档链接。", usage: "获取插件的全部指令详细信息与在线文档链接。", examples: "DOTA2指南\nDOTA2帮助\nDOTA2说明", messages: { header: "以下是插件的全部指令。\n※注意 <arg> 是必须参数,[arg] 是可选参数,具体使用方法请看“用法示例”列。", footer: "插件配置、模板展示等更多信息请访问在线文档:\nhttps://sjtdev.github.io/koishi-plugin-dota2tracker/", table_headers: { command: "指令名", alias: "指令中文", arguments: "参数", description: "说明", options: "选项", examples: "用法示例" } } } } } };
798
798
  }
799
799
  });
800
800
 
@@ -808,7 +808,7 @@ var require_zh_CN_schema = __commonJS({
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: "热烈祝贺群友 {avatar}{name} 在天梯中再获进步,<br/>由 {prev} 升为 {curr},再接再厉,再创辉煌!", rank_fun_down_message: "{avatar}<br/>寄", titles: { MVP: "MVP-#FFA500", Soul: "魂-#66CCFF", Rich: "富-#FFD700", Wise: "睿-#8888FF", Controller: "控-#FF00FF", Nuker: "爆-#CC0088", Breaker: "破-#DD0000", Ghost: "鬼-#CCCCCC", Assister: "助-#006400", Demolisher: "拆-#FEDCBA", Healer: "奶-#00FF00", Tank: "耐-#84A1C7", Idle: "摸-#DDDDDD" }, situation: "局势", networth: "经济", experience: "经验", OUTCOME_MAP: { RADIANT_VICTORY: "天辉优势", RADIANT_STOMP: "天辉碾压", DIRE_VICTORY: "夜魇优势", DIRE_STOMP: "夜魇碾压", TIE: "势均力敌" }, lane_top: "上路", lane_mid: "中路", lane_bottom: "下路", empty_extra_info: "比赛未解析或信息缺失,无法展示更多数据。" } } };
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: "热烈祝贺群友 {avatar}{name} 在天梯中再获进步,<br/>由 {prev} 升为 {curr},再接再厉,再创辉煌!", rank_fun_down_message: "{avatar}<br/>寄", titles: { MVP: "MVP-#FFA500", Soul: "魂-#66CCFF", Rich: "富-#FFD700", Wise: "睿-#8888FF", Controller: "控-#FF00FF", Nuker: "爆-#CC0088", Breaker: "破-#DD0000", Ghost: "鬼-#CCCCCC", 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
 
@@ -960,7 +960,7 @@ var I18NService = class extends import_koishi.Service {
960
960
  render(...args) {
961
961
  return this.i18n.render.apply(this.i18n, args);
962
962
  }
963
- $t(languageTag, key, param) {
963
+ $t(languageTag, key, param, options) {
964
964
  const keys = Array.isArray(key) ? key : key.split(".");
965
965
  const params = Array.isArray(param) ? param : [param];
966
966
  const constantTranslation = keys.reduce((result2, k) => {
@@ -973,11 +973,20 @@ var I18NService = class extends import_koishi.Service {
973
973
  if (typeof params === "object" && params !== null) {
974
974
  return constantTranslation.replace(/\{(\w+)\}/g, (_, key2) => params[key2] || "");
975
975
  }
976
- return constantTranslation;
976
+ let result2 = constantTranslation;
977
+ const targetFormat2 = options?.target || "text";
978
+ if (targetFormat2 === "html") {
979
+ result2 = result2.replace(/\n/g, "<br/>");
980
+ }
981
+ return result2;
977
982
  }
978
983
  const originalKey = Array.isArray(key) ? key : [key];
979
984
  const elements = this.render([languageTag], originalKey, param ?? {});
980
- const result = elements.map((el) => el.toString()).join("");
985
+ let result = elements.map((el) => el.toString()).join("");
986
+ const targetFormat = options?.target || "text";
987
+ if (targetFormat === "html") {
988
+ result = result.replace(/\n/g, "<br/>");
989
+ }
981
990
  if (result == key) return;
982
991
  return result;
983
992
  }
@@ -1292,7 +1301,7 @@ var ItemService = class _ItemService extends import_koishi3.Service {
1292
1301
  }
1293
1302
  static getFormattedItemListData(rawItems) {
1294
1303
  const processItemName = /* @__PURE__ */ __name((name2) => name2.replace(/^item_/i, "").replace(/^recipe_/i, "recipe_"), "processItemName");
1295
- const [recipes, items] = rawItems.reduce(
1304
+ const [recipes, items2] = rawItems.reduce(
1296
1305
  (acc, item) => {
1297
1306
  const processed = {
1298
1307
  ...item,
@@ -1306,14 +1315,14 @@ var ItemService = class _ItemService extends import_koishi3.Service {
1306
1315
  [[], []]
1307
1316
  );
1308
1317
  const itemMap = /* @__PURE__ */ new Map();
1309
- items.concat(recipes).forEach(
1318
+ items2.concat(recipes).forEach(
1310
1319
  (item) => itemMap.set(item.id, {
1311
1320
  id: item.id,
1312
1321
  name: item.name,
1313
1322
  name_loc: item.name_loc
1314
1323
  })
1315
1324
  );
1316
- const processedItems = items.map((baseItem) => {
1325
+ const processedItems = items2.map((baseItem) => {
1317
1326
  const recipe = recipes.find((r) => r.name === `recipe_${baseItem.name.replace("item_", "")}`);
1318
1327
  return {
1319
1328
  ...baseItem,
@@ -1346,19 +1355,19 @@ var ItemService = class _ItemService extends import_koishi3.Service {
1346
1355
  }))
1347
1356
  }));
1348
1357
  }
1349
- searchItems(items, keyword, languageTag, config) {
1358
+ searchItems(items2, keyword, languageTag, config) {
1350
1359
  if (!keyword) return [];
1351
1360
  const alias = this.ctx.dota2tracker.i18n.getConstantLocale(languageTag).dota2tracker.items_alias?.[keyword] ?? config.customItemAlias.filter((cia) => cia.alias == keyword).map((cia) => cia.keyword);
1352
- const exactMatch = items.filter(
1361
+ const exactMatch = items2.filter(
1353
1362
  (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)
1354
1363
  );
1355
1364
  if (exactMatch.length) return exactMatch;
1356
- return this.fuzzySearchItems(alias.length ? alias : [keyword], items);
1365
+ return this.fuzzySearchItems(alias.length ? alias : [keyword], items2);
1357
1366
  }
1358
- fuzzySearchItems(keywords, items) {
1367
+ fuzzySearchItems(keywords, items2) {
1359
1368
  const resultMap = /* @__PURE__ */ new Map();
1360
1369
  if (!keywords.length) return [];
1361
- for (const item of items) {
1370
+ for (const item of items2) {
1362
1371
  const cleanName = item.name_loc.toLowerCase().replace(/[^\p{L}\p{N}]/gu, "").trim();
1363
1372
  let matchAllKeywords = true;
1364
1373
  for (const keyword of keywords) {
@@ -1397,7 +1406,7 @@ var MatchService = class _MatchService extends import_koishi4.Service {
1397
1406
  async getMatchResult({ matchId, requestParse }) {
1398
1407
  const matchQuery = await this.getMatchData(matchId);
1399
1408
  if (matchQuery) {
1400
- if (!this.isMatchParsed(matchQuery) && requestParse && this.ctx.cron) {
1409
+ if (!_MatchService.isMatchParsed(matchQuery) && requestParse && this.ctx.cron) {
1401
1410
  return {
1402
1411
  status: "PENDING",
1403
1412
  matchId
@@ -1424,7 +1433,7 @@ var MatchService = class _MatchService extends import_koishi4.Service {
1424
1433
  this.ctx.dota2tracker.cache.setMatchCache(matchId, matchQuery, this.pluginVersion);
1425
1434
  } else {
1426
1435
  matchQuery = await this.ctx.dota2tracker.stratzAPI.queryMatchInfo(matchId);
1427
- if (this.isMatchParsed(matchQuery)) {
1436
+ if (_MatchService.isMatchParsed(matchQuery)) {
1428
1437
  this.ctx.dota2tracker.cache.setMatchCache(matchId, matchQuery, this.pluginVersion);
1429
1438
  }
1430
1439
  }
@@ -1471,10 +1480,11 @@ var MatchService = class _MatchService extends import_koishi4.Service {
1471
1480
  // 对比赛数据进行补充以供生成模板函数使用
1472
1481
  static extendMatchData(matchQuery, facetData) {
1473
1482
  const match = matchQuery.match;
1483
+ const matchParsed = _MatchService.isMatchParsed(matchQuery);
1474
1484
  ["radiant", "dire"].forEach((team) => {
1475
1485
  match[team] = { killsCount: match?.[team + "Kills"]?.reduce((acc, cva) => acc + cva, 0) ?? 0, damageReceived: 0, heroDamage: 0, networth: 0, experience: 0 };
1476
1486
  });
1477
- if (!match.parsedDateTime) {
1487
+ if (!matchParsed) {
1478
1488
  match.players.reduce((acc, player) => {
1479
1489
  if (player.isRadiant) {
1480
1490
  acc.radiant.killsCount += player.kills;
@@ -1524,9 +1534,6 @@ var MatchService = class _MatchService extends import_koishi4.Service {
1524
1534
  match[player.team].damageReceived = (match[player.team].damageReceived ?? 0) + player.damageReceived;
1525
1535
  match[player.team].networth += player.networth;
1526
1536
  match[player.team].experience += Math.floor(player.experiencePerMinute / 60 * match.durationSeconds);
1527
- player.titles = [];
1528
- player.mvpScore = // 计算MVP分数
1529
- player.kills * 5 + player.assists * 3 + (player.stats.heroDamageReport?.dealtTotal.stunDuration ?? 0) / 100 * 0.1 + (player.stats.heroDamageReport?.dealtTotal.disableDuration ?? 0) / 100 * 0.05 + (player.stats.heroDamageReport?.dealtTotal.slowDuration ?? 0) / 100 * 0.025 + player.heroDamage * 1e-3 + player.towerDamage * 0.01 + player.heroHealing * 2e-3 + player.imp * 0.25;
1530
1537
  player.order = heroOrderList[player.hero.id];
1531
1538
  if (player.partyId != null) {
1532
1539
  if (!match.party[player.partyId]) match.party[player.partyId] = party_mark[party_index++];
@@ -1565,28 +1572,40 @@ var MatchService = class _MatchService extends import_koishi4.Service {
1565
1572
  player.laneResult = laneResult.mid[player.team];
1566
1573
  break;
1567
1574
  }
1568
- const supportItemIds = [30, 40, 42, 43, 188];
1569
- const supportItemsCount = supportItemIds.reduce((obj, key) => {
1575
+ player.utilityScore = (player.stats?.campStack?.at(-1) || 0) * 100;
1576
+ const utilityItemIds = [
1577
+ 30,
1578
+ // 真视宝石
1579
+ 40,
1580
+ // 显影之尘
1581
+ 42,
1582
+ // 侦查守卫 (假眼)
1583
+ 43,
1584
+ // 岗哨守卫 (真眼)
1585
+ 188
1586
+ // 诡计之雾
1587
+ ];
1588
+ const supportItemsCount = utilityItemIds.reduce((obj, key) => {
1570
1589
  obj[key] = 0;
1571
1590
  return obj;
1572
1591
  }, {});
1573
1592
  const purchaseTimesMap = {};
1574
1593
  if (player.stats?.itemPurchases) {
1575
1594
  for (const item of player.stats.itemPurchases) {
1576
- if (!supportItemIds.includes(item.itemId)) {
1595
+ if (!utilityItemIds.includes(item.itemId)) {
1577
1596
  if (!purchaseTimesMap[item.itemId]) {
1578
1597
  purchaseTimesMap[item.itemId] = [];
1579
1598
  }
1580
1599
  purchaseTimesMap[item.itemId].push(item.time);
1581
- }
1582
- switch (item.itemId) {
1583
- case 30:
1584
- case 40:
1585
- case 42:
1586
- case 43:
1587
- case 188:
1588
- supportItemsCount[item.itemId]++;
1589
- break;
1600
+ } else {
1601
+ const itemName = dotaconstants3.item_ids[item.itemId];
1602
+ if (itemName) {
1603
+ const itemDetails = dotaconstants3.items[itemName];
1604
+ if (itemDetails && itemDetails.cost) {
1605
+ player.utilityScore += itemDetails.cost;
1606
+ }
1607
+ }
1608
+ supportItemsCount[item.itemId]++;
1590
1609
  }
1591
1610
  }
1592
1611
  }
@@ -1627,6 +1646,9 @@ var MatchService = class _MatchService extends import_koishi4.Service {
1627
1646
  }
1628
1647
  player.formattedNetworth = formatNumber(player.networth);
1629
1648
  player.facet = facetData[player.steamAccountId];
1649
+ player.titles = [];
1650
+ player.mvpScore = // 计算MVP分数
1651
+ player.kills * 5 + player.assists * 3 + (player.stats.heroDamageReport?.dealtTotal.stunDuration ?? 0) / 100 * 0.1 + (player.stats.heroDamageReport?.dealtTotal.disableDuration ?? 0) / 100 * 0.05 + (player.stats.heroDamageReport?.dealtTotal.slowDuration ?? 0) / 100 * 0.025 + player.heroDamage * 1e-3 + player.towerDamage * 0.01 + player.heroHealing * 2e-3 + player.imp * 0.25 + player.utilityScore * 5e-3;
1630
1652
  });
1631
1653
  let ComparisonMode;
1632
1654
  ((ComparisonMode2) => {
@@ -1659,7 +1681,7 @@ var MatchService = class _MatchService extends import_koishi4.Service {
1659
1681
  )?.titles.push("dota2tracker.template.titles.Soul");
1660
1682
  findMaxByProperty("networth")?.titles.push("dota2tracker.template.titles.Rich");
1661
1683
  findMaxByProperty("experiencePerMinute")?.titles.push("dota2tracker.template.titles.Wise");
1662
- if (match.parsedDateTime && match.players.every((player) => player?.stats?.heroDamageReport?.dealtTotal)) {
1684
+ if (matchParsed) {
1663
1685
  match.players.reduce(
1664
1686
  (max, player) => player.stats.heroDamageReport.dealtTotal.stunDuration + player.stats.heroDamageReport.dealtTotal.disableDuration / 2 + player.stats.heroDamageReport.dealtTotal.slowDuration / 4 > max.stats.heroDamageReport.dealtTotal.stunDuration + max.stats.heroDamageReport.dealtTotal.disableDuration / 2 + max.stats.heroDamageReport.dealtTotal.slowDuration / 4 ? player : max
1665
1687
  ).titles.push("dota2tracker.template.titles.Controller");
@@ -1670,6 +1692,7 @@ var MatchService = class _MatchService extends import_koishi4.Service {
1670
1692
  findMaxByProperty("heroDamage")?.titles.push("dota2tracker.template.titles.Nuker");
1671
1693
  findMaxByProperty("kills", "heroDamage")?.titles.push("dota2tracker.template.titles.Breaker");
1672
1694
  findMaxByProperty("deaths", "networth", void 0, void 0, "min" /* Min */)?.titles.push("dota2tracker.template.titles.Ghost");
1695
+ if (matchParsed) findMaxByProperty("utilityScore", "networth", match.players, "max" /* Max */, "min" /* Min */)?.titles.push("dota2tracker.template.titles.Utility");
1673
1696
  findMaxByProperty("assists", "heroDamage")?.titles.push("dota2tracker.template.titles.Assister");
1674
1697
  findMaxByProperty("towerDamage", "heroDamage")?.titles.push("dota2tracker.template.titles.Demolisher");
1675
1698
  findMaxByProperty("heroHealing")?.titles.push("dota2tracker.template.titles.Healer");
@@ -1692,7 +1715,7 @@ var MatchService = class _MatchService extends import_koishi4.Service {
1692
1715
  match.durationTime = sec2time(match.durationSeconds);
1693
1716
  return match;
1694
1717
  }
1695
- isMatchParsed(match) {
1718
+ static isMatchParsed(match) {
1696
1719
  return match.match?.parsedDateTime && match.match.players.filter((player) => player?.stats?.heroDamageReport?.dealtTotal).length > 0;
1697
1720
  }
1698
1721
  };
@@ -2196,7 +2219,6 @@ function handleError(error, logger, i18n, config) {
2196
2219
  } else if (error instanceof NetworkError) {
2197
2220
  if (config.suppressStratzNetworkErrors) {
2198
2221
  logger.debug(error);
2199
- logger.info(1);
2200
2222
  } else {
2201
2223
  logger.error(error);
2202
2224
  }
@@ -2472,6 +2494,9 @@ var ImageRenderer = class extends import_koishi10.Service {
2472
2494
  const html = await this.generateHTML(data, { source: "CODE", code: ejsCode }, languageTag);
2473
2495
  return this.ctx.puppeteer.render(html);
2474
2496
  }
2497
+ async renderToImageByHTML(html) {
2498
+ return this.ctx.puppeteer.render(html);
2499
+ }
2475
2500
  async generateHTML(data, template, languageTag) {
2476
2501
  const templateData = {
2477
2502
  data,
@@ -2531,6 +2556,67 @@ var MessageBuilder = class extends import_koishi11.Service {
2531
2556
  super(ctx, "dota2tracker.message-builder", true);
2532
2557
  this.config = ctx.config;
2533
2558
  }
2559
+ async buildHelpMessage(languageTag, pluginName) {
2560
+ let message = "";
2561
+ let html = "";
2562
+ const $t_header = /* @__PURE__ */ __name((key) => this.ctx.dota2tracker.i18n.$t(languageTag, `commands.dota2tracker.help.messages.table_headers.${key}`), "$t_header");
2563
+ const header = [
2564
+ { content: $t_header("command") },
2565
+ { content: $t_header("alias") },
2566
+ { content: $t_header("arguments") },
2567
+ { content: $t_header("description") },
2568
+ { content: $t_header("options") },
2569
+ { content: $t_header("examples") }
2570
+ ];
2571
+ const table = [header];
2572
+ const rootCommand = this.ctx.$commander.get(pluginName);
2573
+ const commandList = rootCommand?.children;
2574
+ for (const command of commandList) {
2575
+ const names = Object.keys(command._aliases);
2576
+ const localeKey = "commands." + names.at(0);
2577
+ const optionsFromCommand = command._options;
2578
+ const localeData = this.ctx.i18n._data[languageTag];
2579
+ const formattedOptions = [];
2580
+ if (optionsFromCommand) {
2581
+ for (const optionKey in optionsFromCommand) {
2582
+ const i18nKey = `commands.${command.name}.options.${optionKey}`;
2583
+ let description = localeData[i18nKey];
2584
+ const optionInfo = optionsFromCommand[optionKey];
2585
+ if (optionInfo && description) {
2586
+ const syntax = optionInfo.syntax;
2587
+ const flagsRegex = /-{1,2}[a-zA-Z-]+/g;
2588
+ const flags = syntax.match(flagsRegex);
2589
+ if (flags) {
2590
+ for (const flag of flags) {
2591
+ if (description.startsWith(flag + " ")) {
2592
+ description = description.substring(flag.length + 1);
2593
+ break;
2594
+ }
2595
+ }
2596
+ }
2597
+ formattedOptions.push({
2598
+ // 现在 description 已经是被清理过的干净版本了
2599
+ syntax: `[${syntax}]`,
2600
+ description
2601
+ });
2602
+ }
2603
+ }
2604
+ }
2605
+ const row = [
2606
+ { content: command.name },
2607
+ { content: names.slice(1).join("\n") },
2608
+ { content: command._arguments["stripped"] },
2609
+ { content: this.ctx.dota2tracker.i18n.$t(languageTag, localeKey + ".usage") },
2610
+ { content: formattedOptions.map((option) => `${option.syntax} ${option.description}`).join("\n") },
2611
+ { content: this.ctx.dota2tracker.i18n.$t(languageTag, localeKey + ".examples") }
2612
+ ];
2613
+ table.push(row);
2614
+ }
2615
+ html += this._createTableHTML(table, void 0, this.ctx.i18n._data[languageTag]["commands.dota2tracker.help.messages.header"]);
2616
+ message += await this.ctx.dota2tracker.image.renderToImageByEJSCode(void 0, html, languageTag);
2617
+ message += this.ctx.dota2tracker.i18n.$t(languageTag, "commands.dota2tracker.help.messages.footer");
2618
+ return message;
2619
+ }
2534
2620
  async buildHeroOfTheDayMessage(languageTag, heroRcmd, metaRcmd) {
2535
2621
  const $t = /* @__PURE__ */ __name((key, params) => this.ctx.dota2tracker.i18n.$t(languageTag, `commands.dota2tracker.hero-of-the-day.messages.${key}`, params), "$t");
2536
2622
  let ejs2 = "<html><head><style>body{width:fit-content;height:fit-content;margin:0;padding:12px;font-family:<%-fontFamily%>;}</style></head><body>";
@@ -2703,6 +2789,40 @@ var MessageBuilder = class extends import_koishi11.Service {
2703
2789
  curr: { medal: this.ctx.dota2tracker.i18n.$t(languageTag, "dota2tracker.template.ranks." + currRank.medal), star: currRank.star }
2704
2790
  });
2705
2791
  }
2792
+ _createTableHTML(data, totalAlign = "left", header) {
2793
+ const maxLength = data.reduce((max, row) => Math.max(max, row.length), 0);
2794
+ let html = `<html><head><style>body{width:fit-content;height:fit-content;margin:0;padding:12px;font-family:${this.config.templateFonts};}.table div:not(.row){background-color: #fff;padding: 4px;}</style></head><body>`;
2795
+ if (header) html += `<header>${escapeHtml(header)}</header>`;
2796
+ html += `<div class="table" style="display:grid;grid-template-columns:repeat(${maxLength},auto);background-color:#000;gap:1px;border:#000 solid 1px;text-align:${totalAlign}">`;
2797
+ for (const row of data) {
2798
+ html += `<div class="row" style="display:contents;">`;
2799
+ for (const cell of row) {
2800
+ const style = {
2801
+ ...cell.align ? { "text-align": cell.align } : {},
2802
+ ...cell.colSpan ? { "grid-column": "span " + cell.colSpan } : {},
2803
+ ...cell.rowSpan ? { "grid-row": "span " + cell.rowSpan } : {}
2804
+ };
2805
+ const styleStr = Object.entries(style).map(([key, value]) => `${key}:${value}`).join(";");
2806
+ const styleAttribute = styleStr ? ` style="${styleStr}"` : "";
2807
+ html += `<div${styleAttribute}>${escapeHtml(cell.content)}</div>`;
2808
+ }
2809
+ html += `</div>`;
2810
+ }
2811
+ html += `</div></body></html>`;
2812
+ return html;
2813
+ function escapeHtml(str) {
2814
+ return (str || "").replace(/[&<>"']/g, function(match) {
2815
+ return {
2816
+ "&": "&amp;",
2817
+ "<": "&lt;",
2818
+ ">": "&gt;",
2819
+ '"': "&quot;",
2820
+ "'": "&#39;"
2821
+ }[match];
2822
+ }).replace(/\n/g, "<br/>");
2823
+ }
2824
+ __name(escapeHtml, "escapeHtml");
2825
+ }
2706
2826
  };
2707
2827
  function customConvertArrayOfString(str) {
2708
2828
  try {
@@ -3097,7 +3217,9 @@ __name(registerSubscibeCommand, "registerSubscibeCommand");
3097
3217
  // src/app/commands/help.command.ts
3098
3218
  function registerHelpCommand(ctx) {
3099
3219
  ctx.command("dota2tracker.help").alias("DOTA2指南").alias("DOTA2帮助").alias("DOTA2说明").action(async ({ session }) => {
3100
- return session.text(".content");
3220
+ const languageTag = await ctx.dota2tracker.i18n.getLanguageTag({ session });
3221
+ const pluginName = "dota2tracker";
3222
+ return ctx.dota2tracker.messageBuilder.buildHelpMessage(languageTag, pluginName);
3101
3223
  });
3102
3224
  }
3103
3225
  __name(registerHelpCommand, "registerHelpCommand");
@@ -3164,7 +3286,7 @@ __name(registerQueryHeroCommand, "registerQueryHeroCommand");
3164
3286
 
3165
3287
  // src/app/commands/query-item.command.ts
3166
3288
  function registerQueryItemCommand(ctx) {
3167
- ctx.command("dota2tracker.query-item").alias("查询物品").action(async ({ session }, input_data) => {
3289
+ ctx.command("dota2tracker.query-item <input_data>").alias("查询物品").action(async ({ session }, input_data) => {
3168
3290
  if (!input_data) {
3169
3291
  if (ctx.config.showItemListAtTooMuchItems) {
3170
3292
  await session.send(session.text(".querying_item"));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sjtdev/koishi-plugin-dota2tracker",
3
3
  "description": "koishi插件-追踪群友的DOTA2对局",
4
- "version": "2.0.4",
4
+ "version": "2.1.1",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
@@ -39,13 +39,17 @@
39
39
  "zh": "追踪群友的DOTA2对局",
40
40
  "en": "Track Friends' DOTA2 Matches"
41
41
  },
42
- "required": [
43
- "http",
44
- "database",
45
- "puppeteer",
46
- "cron",
47
- "cache"
48
- ],
42
+ "service": {
43
+ "required": [
44
+ "http",
45
+ "database",
46
+ "puppeteer",
47
+ "cache"
48
+ ],
49
+ "optional": [
50
+ "cron"
51
+ ]
52
+ },
49
53
  "locales": [
50
54
  "zh-CN",
51
55
  "en-US"