diff --git a/develop_requirements.txt b/develop_requirements.txt index 9230364..68460c3 100644 --- a/develop_requirements.txt +++ b/develop_requirements.txt @@ -7,6 +7,7 @@ attrs==23.1.0 backcall==0.2.0 beautifulsoup4==4.12.2 blurhash==1.1.4 +boto3==1.34.143 bs4==0.0.1 certifi==2022.12.7 charset-normalizer==3.0.1 diff --git a/discord_bot.py b/discord_bot.py index df1f9f1..ff4e742 100644 --- a/discord_bot.py +++ b/discord_bot.py @@ -33,7 +33,8 @@ channel_id = 1152167937852055552 @tasks.loop(minutes=1) async def send_message(channel): if utils.is_game_time(): - send_message.change_interval(minutes=3) + # send_message.change_interval(minutes=3) + pass else: send_message.change_interval(minutes=15) diff --git a/dota.py b/dota.py index bbffc6b..499ff3b 100644 --- a/dota.py +++ b/dota.py @@ -2,15 +2,16 @@ import peewee import opendota import datetime from loguru import logger - +import json import players import utils - +from image_generator import ImageGenerator db = peewee.SqliteDatabase('dota.db') hero_client = opendota.HeroesApi() player_client = opendota.PlayersApi() match_client = opendota.MatchesApi() +image_generator = ImageGenerator() class BaseModel(peewee.Model): @@ -48,27 +49,38 @@ class Match(BaseModel): duration = peewee.IntegerField() radiant_win = peewee.BooleanField() party_size = peewee.IntegerField(null=True) + opendota_response = peewee.TextField(null=True) def serialize_match(self): - try: - match_ = match_client.get_matches_by_match_id(self.match_id) - except Exception as e: - logger.error('fail to get match %s' % self.match_id) - raise e + if not self.opendota_response: + try: + match_ = match_client.get_matches_by_match_id(self.match_id) + m_dict = match_.to_dict() + for player in m_dict['players']: + if player['last_login']: + # datatime obj to timestamp + player['last_login'] = int(player['last_login'].timestamp()) + self.opendota_response = json.dumps(m_dict) + self.save() + except Exception as e: + logger.error('fail to get match %s' % self.match_id) + raise e + + md = json.loads(self.opendota_response) match_data = { - 'players': [players.serialize_player(player) for player in match_.players], - 'dire_score': match_.dire_score, - 'radiant_score': match_.radiant_score, + 'players': [players.serialize_player(player) for player in md['players']], + 'dire_score': md['dire_score'], + 'radiant_score': md['radiant_score'], # isoformat utc+8 - 'start_time': datetime.datetime.fromtimestamp(match_.start_time).strftime('%Y-%m-%dT%H:%M:%S.000+08:00'), - 'end_time': datetime.datetime.fromtimestamp(match_.start_time + match_.duration).strftime('%Y-%m-%dT%H:%M:%S.000+08:00'), - 'duration': '%d:%02d:%02d' % utils.convert_seconds_to_hms(match_.duration), - 'radiant_win': match_.radiant_win, + 'start_time': datetime.datetime.fromtimestamp(md['start_time']).strftime('%Y-%m-%dT%H:%M:%S.000+08:00'), + 'end_time': datetime.datetime.fromtimestamp(md['start_time'] + md['duration']).strftime('%Y-%m-%dT%H:%M:%S.000+08:00'), + 'duration': '%d:%02d:%02d' % utils.convert_seconds_to_hms(md['duration']), + 'radiant_win': md['radiant_win'], 'party_size': self.party_size, 'match_id': self.match_id, } if not self.party_size: - player_account_ids = [player.account_id for player in match_.players if player.account_id] + player_account_ids = [player['account_id'] for player in md['players'] if player['account_id']] match_data['party_size'] = Friend.select().where(Friend.steam_id.in_(player_account_ids)).count() return match_data @@ -289,6 +301,13 @@ def serialize_match_for_discord(match_): else: dire_indicator = ' 🌟' + # 生成比赛报告图片 + try: + image_url = image_generator.generate_match_report(match_) + except Exception as e: + logger.error(f"生成比赛报告图片失败: {str(e)}") + image_url = None + data = { "content": content, "tts": False, @@ -319,4 +338,11 @@ def serialize_match_for_discord(match_): } ], } + + # 如果成功生成了图片,添加到embeds中 + if image_url: + data["embeds"][0]["image"] = { + "url": image_url + } + return data diff --git a/env.ini b/env.ini new file mode 100644 index 0000000..edada41 --- /dev/null +++ b/env.ini @@ -0,0 +1,10 @@ +[cloudflare] +account_id = your_account_id +access_key_id = your_access_key_id +secret_access_key = your_secret_access_key +bucket_name = dotabot-images +region = auto +custom_domain = + +[bot] +# 其他机器人配置可以放在这里 \ No newline at end of file diff --git a/heroes.json b/heroes.json new file mode 100644 index 0000000..cb2cd79 --- /dev/null +++ b/heroes.json @@ -0,0 +1,5026 @@ +{ + "1": { + "id": 1, + "name": "npc_dota_hero_antimage", + "primary_attr": "agi", + "attack_type": "Melee", + "roles": [ + "Carry", + "Escape", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/antimage.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/antimage.png?", + "base_health": 120, + "base_health_regen": 1, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 29, + "base_attack_max": 33, + "base_str": 21, + "base_agi": 24, + "base_int": 12, + "str_gain": 1.6, + "agi_gain": 2.8, + "int_gain": 1.8, + "attack_range": 150, + "projectile_speed": 0, + "attack_rate": 1.4, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 310, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Anti-Mage" + }, + "2": { + "id": 2, + "name": "npc_dota_hero_axe", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Initiator", + "Durable", + "Disabler", + "Carry" + ], + "img": "/apps/dota2/images/dota_react/heroes/axe.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/axe.png?", + "base_health": 120, + "base_health_regen": 2.5, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 31, + "base_attack_max": 35, + "base_str": 25, + "base_agi": 20, + "base_int": 18, + "str_gain": 2.8, + "agi_gain": 1.7, + "int_gain": 1.6, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.4, + "move_speed": 315, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Axe" + }, + "3": { + "id": 3, + "name": "npc_dota_hero_bane", + "primary_attr": "all", + "attack_type": "Ranged", + "roles": [ + "Support", + "Disabler", + "Nuker", + "Durable" + ], + "img": "/apps/dota2/images/dota_react/heroes/bane.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/bane.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 24, + "base_attack_max": 30, + "base_str": 23, + "base_agi": 23, + "base_int": 23, + "str_gain": 2.7, + "agi_gain": 2.7, + "int_gain": 2.7, + "attack_range": 400, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 305, + "turn_rate": null, + "cm_enabled": true, + "legs": 4, + "day_vision": 1800, + "night_vision": 1200, + "localized_name": "Bane" + }, + "4": { + "id": 4, + "name": "npc_dota_hero_bloodseeker", + "primary_attr": "agi", + "attack_type": "Melee", + "roles": [ + "Carry", + "Disabler", + "Nuker", + "Initiator" + ], + "img": "/apps/dota2/images/dota_react/heroes/bloodseeker.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/bloodseeker.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 2, + "base_mr": 25, + "base_attack_min": 35, + "base_attack_max": 41, + "base_str": 24, + "base_agi": 24, + "base_int": 17, + "str_gain": 2.7, + "agi_gain": 3.1, + "int_gain": 2, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.43, + "move_speed": 285, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Bloodseeker" + }, + "5": { + "id": 5, + "name": "npc_dota_hero_crystal_maiden", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Disabler", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/crystal_maiden.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/crystal_maiden.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 30, + "base_attack_max": 36, + "base_str": 17, + "base_agi": 16, + "base_int": 18, + "str_gain": 2.2, + "agi_gain": 1.8, + "int_gain": 3.3, + "attack_range": 600, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.45, + "move_speed": 280, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Crystal Maiden" + }, + "6": { + "id": 6, + "name": "npc_dota_hero_drow_ranger", + "primary_attr": "agi", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Disabler", + "Pusher" + ], + "img": "/apps/dota2/images/dota_react/heroes/drow_ranger.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/drow_ranger.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 29, + "base_attack_max": 36, + "base_str": 16, + "base_agi": 22, + "base_int": 15, + "str_gain": 1.9, + "agi_gain": 2.9, + "int_gain": 1.4, + "attack_range": 625, + "projectile_speed": 1250, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.5, + "move_speed": 310, + "turn_rate": 0.7, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Drow Ranger" + }, + "7": { + "id": 7, + "name": "npc_dota_hero_earthshaker", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Support", + "Initiator", + "Disabler", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/earthshaker.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/earthshaker.png?", + "base_health": 120, + "base_health_regen": 1, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 2, + "base_mr": 25, + "base_attack_min": 27, + "base_attack_max": 37, + "base_str": 22, + "base_agi": 12, + "base_int": 18, + "str_gain": 3.7, + "agi_gain": 1.4, + "int_gain": 2.1, + "attack_range": 150, + "projectile_speed": 0, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.467, + "move_speed": 315, + "turn_rate": 0.9, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Earthshaker" + }, + "8": { + "id": 8, + "name": "npc_dota_hero_juggernaut", + "primary_attr": "agi", + "attack_type": "Melee", + "roles": [ + "Carry", + "Pusher", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/juggernaut.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/juggernaut.png?", + "base_health": 120, + "base_health_regen": 0.5, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 22, + "base_attack_max": 24, + "base_str": 20, + "base_agi": 34, + "base_int": 14, + "str_gain": 2, + "agi_gain": 2.8, + "int_gain": 1.4, + "attack_range": 150, + "projectile_speed": 0, + "attack_rate": 1.4, + "base_attack_time": 110, + "attack_point": 0.33, + "move_speed": 300, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Juggernaut" + }, + "9": { + "id": 9, + "name": "npc_dota_hero_mirana", + "primary_attr": "agi", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Support", + "Escape", + "Nuker", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/mirana.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/mirana.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.4, + "base_armor": -2, + "base_mr": 25, + "base_attack_min": 22, + "base_attack_max": 28, + "base_str": 20, + "base_agi": 26, + "base_int": 22, + "str_gain": 2.2, + "agi_gain": 3.1, + "int_gain": 1.9, + "attack_range": 630, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.35, + "move_speed": 285, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Mirana" + }, + "10": { + "id": 10, + "name": "npc_dota_hero_morphling", + "primary_attr": "agi", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Escape", + "Durable", + "Nuker", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/morphling.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/morphling.png?", + "base_health": 120, + "base_health_regen": 0.5, + "base_mana": 75, + "base_mana_regen": 0.5, + "base_armor": -2, + "base_mr": 25, + "base_attack_min": 12, + "base_attack_max": 21, + "base_str": 23, + "base_agi": 24, + "base_int": 19, + "str_gain": 3.2, + "agi_gain": 3.9, + "int_gain": 1.8, + "attack_range": 350, + "projectile_speed": 1300, + "attack_rate": 1.5, + "base_attack_time": 100, + "attack_point": 0.5, + "move_speed": 285, + "turn_rate": 0.7, + "cm_enabled": true, + "legs": 0, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Morphling" + }, + "11": { + "id": 11, + "name": "npc_dota_hero_nevermore", + "primary_attr": "agi", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/nevermore.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/nevermore.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.3, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 16, + "base_attack_max": 22, + "base_str": 19, + "base_agi": 25, + "base_int": 18, + "str_gain": 2.7, + "agi_gain": 3.5, + "int_gain": 2.2, + "attack_range": 525, + "projectile_speed": 1200, + "attack_rate": 1.6, + "base_attack_time": 100, + "attack_point": 0.5, + "move_speed": 305, + "turn_rate": 0.9, + "cm_enabled": true, + "legs": 0, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Shadow Fiend" + }, + "12": { + "id": 12, + "name": "npc_dota_hero_phantom_lancer", + "primary_attr": "agi", + "attack_type": "Melee", + "roles": [ + "Carry", + "Escape", + "Pusher", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/phantom_lancer.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/phantom_lancer.png?", + "base_health": 120, + "base_health_regen": 1.5, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 26, + "base_attack_max": 36, + "base_str": 21, + "base_agi": 26, + "base_int": 21, + "str_gain": 2.2, + "agi_gain": 2.8, + "int_gain": 1.8, + "attack_range": 225, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 110, + "attack_point": 0.5, + "move_speed": 285, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Phantom Lancer" + }, + "13": { + "id": 13, + "name": "npc_dota_hero_puck", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Initiator", + "Disabler", + "Escape", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/puck.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/puck.png?", + "base_health": 120, + "base_health_regen": 0.5, + "base_mana": 75, + "base_mana_regen": 0.5, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 22, + "base_attack_max": 30, + "base_str": 17, + "base_agi": 14, + "base_int": 23, + "str_gain": 2.4, + "agi_gain": 2.5, + "int_gain": 3.8, + "attack_range": 550, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.5, + "move_speed": 290, + "turn_rate": 0.9, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Puck" + }, + "14": { + "id": 14, + "name": "npc_dota_hero_pudge", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Disabler", + "Initiator", + "Durable", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/pudge.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/pudge.png?", + "base_health": 120, + "base_health_regen": 2.5, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 45, + "base_attack_max": 51, + "base_str": 25, + "base_agi": 11, + "base_int": 16, + "str_gain": 3, + "agi_gain": 1.4, + "int_gain": 1.8, + "attack_range": 175, + "projectile_speed": 0, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.5, + "move_speed": 280, + "turn_rate": 0.7, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Pudge" + }, + "15": { + "id": 15, + "name": "npc_dota_hero_razor", + "primary_attr": "agi", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Durable", + "Nuker", + "Pusher" + ], + "img": "/apps/dota2/images/dota_react/heroes/razor.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/razor.png?", + "base_health": 120, + "base_health_regen": 1, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 23, + "base_attack_max": 25, + "base_str": 22, + "base_agi": 24, + "base_int": 21, + "str_gain": 2.8, + "agi_gain": 2.8, + "int_gain": 2.2, + "attack_range": 475, + "projectile_speed": 2000, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 280, + "turn_rate": null, + "cm_enabled": true, + "legs": 0, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Razor" + }, + "16": { + "id": 16, + "name": "npc_dota_hero_sand_king", + "primary_attr": "all", + "attack_type": "Melee", + "roles": [ + "Initiator", + "Disabler", + "Support", + "Nuker", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/sand_king.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/sand_king.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 19, + "base_attack_max": 29, + "base_str": 22, + "base_agi": 19, + "base_int": 17, + "str_gain": 2.3, + "agi_gain": 2, + "int_gain": 2, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 110, + "attack_point": 0.53, + "move_speed": 290, + "turn_rate": null, + "cm_enabled": true, + "legs": 6, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Sand King" + }, + "17": { + "id": 17, + "name": "npc_dota_hero_storm_spirit", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Escape", + "Nuker", + "Initiator", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/storm_spirit.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/storm_spirit.png?", + "base_health": 120, + "base_health_regen": 0.5, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 24, + "base_attack_max": 34, + "base_str": 21, + "base_agi": 22, + "base_int": 23, + "str_gain": 2, + "agi_gain": 2.6, + "int_gain": 3.9, + "attack_range": 480, + "projectile_speed": 1100, + "attack_rate": 1.6, + "base_attack_time": 115, + "attack_point": 0.5, + "move_speed": 285, + "turn_rate": 0.8, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Storm Spirit" + }, + "18": { + "id": 18, + "name": "npc_dota_hero_sven", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Carry", + "Disabler", + "Initiator", + "Durable", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/sven.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/sven.png?", + "base_health": 120, + "base_health_regen": 0.75, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 37, + "base_attack_max": 39, + "base_str": 23, + "base_agi": 18, + "base_int": 16, + "str_gain": 3.5, + "agi_gain": 2.2, + "int_gain": 1.5, + "attack_range": 150, + "projectile_speed": 0, + "attack_rate": 1.9, + "base_attack_time": 110, + "attack_point": 0.4, + "move_speed": 325, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Sven" + }, + "19": { + "id": 19, + "name": "npc_dota_hero_tiny", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Carry", + "Nuker", + "Pusher", + "Initiator", + "Durable", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/tiny.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/tiny.png?", + "base_health": 120, + "base_health_regen": 2, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 31, + "base_attack_max": 37, + "base_str": 30, + "base_agi": 0, + "base_int": 17, + "str_gain": 4, + "agi_gain": 0, + "int_gain": 2.2, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 90, + "attack_point": 0.4, + "move_speed": 275, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Tiny" + }, + "20": { + "id": 20, + "name": "npc_dota_hero_vengefulspirit", + "primary_attr": "agi", + "attack_type": "Ranged", + "roles": [ + "Support", + "Initiator", + "Disabler", + "Nuker", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/vengefulspirit.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/vengefulspirit.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 26, + "base_attack_max": 32, + "base_str": 20, + "base_agi": 21, + "base_int": 19, + "str_gain": 2.6, + "agi_gain": 3.2, + "int_gain": 1.5, + "attack_range": 400, + "projectile_speed": 1500, + "attack_rate": 1.7, + "base_attack_time": 110, + "attack_point": 0.33, + "move_speed": 295, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Vengeful Spirit" + }, + "21": { + "id": 21, + "name": "npc_dota_hero_windrunner", + "primary_attr": "all", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Support", + "Disabler", + "Escape", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/windrunner.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/windrunner.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.25, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 21, + "base_attack_max": 33, + "base_str": 18, + "base_agi": 17, + "base_int": 18, + "str_gain": 2.6, + "agi_gain": 1.7, + "int_gain": 3.2, + "attack_range": 600, + "projectile_speed": 1250, + "attack_rate": 1.5, + "base_attack_time": 90, + "attack_point": 0.4, + "move_speed": 290, + "turn_rate": 0.8, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Windranger" + }, + "22": { + "id": 22, + "name": "npc_dota_hero_zuus", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Nuker", + "Carry" + ], + "img": "/apps/dota2/images/dota_react/heroes/zuus.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/zuus.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 2, + "base_mr": 25, + "base_attack_min": 33, + "base_attack_max": 41, + "base_str": 19, + "base_agi": 11, + "base_int": 22, + "str_gain": 2.1, + "agi_gain": 1.2, + "int_gain": 3.3, + "attack_range": 380, + "projectile_speed": 1100, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.35, + "move_speed": 315, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Zeus" + }, + "23": { + "id": 23, + "name": "npc_dota_hero_kunkka", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Carry", + "Support", + "Disabler", + "Initiator", + "Durable", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/kunkka.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/kunkka.png?", + "base_health": 120, + "base_health_regen": 0.75, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 2, + "base_mr": 25, + "base_attack_min": 30, + "base_attack_max": 36, + "base_str": 24, + "base_agi": 14, + "base_int": 18, + "str_gain": 3.6, + "agi_gain": 1.6, + "int_gain": 1.8, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.4, + "move_speed": 315, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Kunkka" + }, + "25": { + "id": 25, + "name": "npc_dota_hero_lina", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Carry", + "Nuker", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/lina.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/lina.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 21, + "base_attack_max": 29, + "base_str": 20, + "base_agi": 23, + "base_int": 30, + "str_gain": 2.4, + "agi_gain": 2.4, + "int_gain": 3.8, + "attack_range": 670, + "projectile_speed": 1000, + "attack_rate": 1.6, + "base_attack_time": 100, + "attack_point": 0.65, + "move_speed": 290, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Lina" + }, + "26": { + "id": 26, + "name": "npc_dota_hero_lion", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Disabler", + "Nuker", + "Initiator" + ], + "img": "/apps/dota2/images/dota_react/heroes/lion.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/lion.png?", + "base_health": 120, + "base_health_regen": 0.5, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 29, + "base_attack_max": 35, + "base_str": 18, + "base_agi": 15, + "base_int": 19, + "str_gain": 2.4, + "agi_gain": 1.7, + "int_gain": 3.5, + "attack_range": 600, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 115, + "attack_point": 0.43, + "move_speed": 290, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Lion" + }, + "27": { + "id": 27, + "name": "npc_dota_hero_shadow_shaman", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Pusher", + "Disabler", + "Nuker", + "Initiator" + ], + "img": "/apps/dota2/images/dota_react/heroes/shadow_shaman.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/shadow_shaman.png?", + "base_health": 120, + "base_health_regen": 1, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 3, + "base_mr": 25, + "base_attack_min": 40, + "base_attack_max": 47, + "base_str": 20, + "base_agi": 16, + "base_int": 25, + "str_gain": 2.3, + "agi_gain": 1.6, + "int_gain": 3.5, + "attack_range": 400, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 285, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Shadow Shaman" + }, + "28": { + "id": 28, + "name": "npc_dota_hero_slardar", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Carry", + "Durable", + "Initiator", + "Disabler", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/slardar.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/slardar.png?", + "base_health": 120, + "base_health_regen": 0.5, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 3, + "base_mr": 25, + "base_attack_min": 30, + "base_attack_max": 38, + "base_str": 21, + "base_agi": 17, + "base_int": 15, + "str_gain": 3.6, + "agi_gain": 2.1, + "int_gain": 1.5, + "attack_range": 150, + "projectile_speed": 0, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.36, + "move_speed": 300, + "turn_rate": null, + "cm_enabled": true, + "legs": 0, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Slardar" + }, + "29": { + "id": 29, + "name": "npc_dota_hero_tidehunter", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Initiator", + "Durable", + "Disabler", + "Nuker", + "Carry" + ], + "img": "/apps/dota2/images/dota_react/heroes/tidehunter.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/tidehunter.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.5, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 25, + "base_attack_max": 31, + "base_str": 27, + "base_agi": 15, + "base_int": 20, + "str_gain": 3.6, + "agi_gain": 1.5, + "int_gain": 1.7, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.6, + "move_speed": 300, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Tidehunter" + }, + "30": { + "id": 30, + "name": "npc_dota_hero_witch_doctor", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Nuker", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/witch_doctor.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/witch_doctor.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 26, + "base_attack_max": 36, + "base_str": 20, + "base_agi": 13, + "base_int": 22, + "str_gain": 2.1, + "agi_gain": 1.4, + "int_gain": 3.1, + "attack_range": 550, + "projectile_speed": 1200, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.4, + "move_speed": 300, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Witch Doctor" + }, + "31": { + "id": 31, + "name": "npc_dota_hero_lich", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/lich.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/lich.png?", + "base_health": 120, + "base_health_regen": 0.5, + "base_mana": 75, + "base_mana_regen": 0.75, + "base_armor": -1, + "base_mr": 25, + "base_attack_min": 24, + "base_attack_max": 33, + "base_str": 20, + "base_agi": 17, + "base_int": 26, + "str_gain": 2.1, + "agi_gain": 2, + "int_gain": 3.8, + "attack_range": 550, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.46, + "move_speed": 290, + "turn_rate": 0.7, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Lich" + }, + "32": { + "id": 32, + "name": "npc_dota_hero_riki", + "primary_attr": "agi", + "attack_type": "Melee", + "roles": [ + "Carry", + "Escape", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/riki.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/riki.png?", + "base_health": 120, + "base_health_regen": 2.5, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 22, + "base_attack_max": 26, + "base_str": 18, + "base_agi": 30, + "base_int": 14, + "str_gain": 2, + "agi_gain": 2.4, + "int_gain": 1.3, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 90, + "attack_point": 0.3, + "move_speed": 315, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Riki" + }, + "33": { + "id": 33, + "name": "npc_dota_hero_enigma", + "primary_attr": "all", + "attack_type": "Ranged", + "roles": [ + "Disabler", + "Initiator", + "Pusher" + ], + "img": "/apps/dota2/images/dota_react/heroes/enigma.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/enigma.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.5, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 19, + "base_attack_max": 25, + "base_str": 21, + "base_agi": 14, + "base_int": 19, + "str_gain": 2.5, + "agi_gain": 1, + "int_gain": 3.6, + "attack_range": 500, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.4, + "move_speed": 280, + "turn_rate": null, + "cm_enabled": true, + "legs": 0, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Enigma" + }, + "34": { + "id": 34, + "name": "npc_dota_hero_tinker", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Nuker", + "Pusher" + ], + "img": "/apps/dota2/images/dota_react/heroes/tinker.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/tinker.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.5, + "base_armor": 4, + "base_mr": 25, + "base_attack_min": 22, + "base_attack_max": 28, + "base_str": 19, + "base_agi": 17, + "base_int": 30, + "str_gain": 2.3, + "agi_gain": 1.4, + "int_gain": 3.3, + "attack_range": 500, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.35, + "move_speed": 310, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Tinker" + }, + "35": { + "id": 35, + "name": "npc_dota_hero_sniper", + "primary_attr": "agi", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/sniper.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/sniper.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": -1, + "base_mr": 25, + "base_attack_min": 13, + "base_attack_max": 19, + "base_str": 19, + "base_agi": 27, + "base_int": 15, + "str_gain": 2, + "agi_gain": 3.2, + "int_gain": 2.6, + "attack_range": 550, + "projectile_speed": 3000, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.17, + "move_speed": 285, + "turn_rate": 0.7, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 1400, + "localized_name": "Sniper" + }, + "36": { + "id": 36, + "name": "npc_dota_hero_necrolyte", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Nuker", + "Durable", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/necrolyte.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/necrolyte.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 2, + "base_mr": 25, + "base_attack_min": 26, + "base_attack_max": 30, + "base_str": 18, + "base_agi": 12, + "base_int": 23, + "str_gain": 2.3, + "agi_gain": 1.3, + "int_gain": 2.7, + "attack_range": 500, + "projectile_speed": 1200, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 280, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Necrophos" + }, + "37": { + "id": 37, + "name": "npc_dota_hero_warlock", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Initiator", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/warlock.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/warlock.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 27, + "base_attack_max": 37, + "base_str": 22, + "base_agi": 10, + "base_int": 25, + "str_gain": 2.4, + "agi_gain": 1, + "int_gain": 3.1, + "attack_range": 600, + "projectile_speed": 1200, + "attack_rate": 1.7, + "base_attack_time": 90, + "attack_point": 0.3, + "move_speed": 300, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Warlock" + }, + "38": { + "id": 38, + "name": "npc_dota_hero_beastmaster", + "primary_attr": "all", + "attack_type": "Melee", + "roles": [ + "Initiator", + "Disabler", + "Durable", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/beastmaster.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/beastmaster.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": -1, + "base_mr": 25, + "base_attack_min": 28, + "base_attack_max": 32, + "base_str": 25, + "base_agi": 22, + "base_int": 16, + "str_gain": 2.9, + "agi_gain": 1.9, + "int_gain": 1.9, + "attack_range": 150, + "projectile_speed": 0, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 305, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Beastmaster" + }, + "39": { + "id": 39, + "name": "npc_dota_hero_queenofpain", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Nuker", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/queenofpain.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/queenofpain.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 26, + "base_attack_max": 32, + "base_str": 20, + "base_agi": 22, + "base_int": 25, + "str_gain": 2.4, + "agi_gain": 2.5, + "int_gain": 3.4, + "attack_range": 550, + "projectile_speed": 1500, + "attack_rate": 1.5, + "base_attack_time": 100, + "attack_point": 0.56, + "move_speed": 290, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Queen of Pain" + }, + "40": { + "id": 40, + "name": "npc_dota_hero_venomancer", + "primary_attr": "all", + "attack_type": "Ranged", + "roles": [ + "Support", + "Nuker", + "Initiator", + "Pusher", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/venomancer.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/venomancer.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 18, + "base_attack_max": 21, + "base_str": 19, + "base_agi": 24, + "base_int": 19, + "str_gain": 2.1, + "agi_gain": 2.8, + "int_gain": 1.8, + "attack_range": 450, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 115, + "attack_point": 0.3, + "move_speed": 280, + "turn_rate": null, + "cm_enabled": true, + "legs": 0, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Venomancer" + }, + "41": { + "id": 41, + "name": "npc_dota_hero_faceless_void", + "primary_attr": "agi", + "attack_type": "Melee", + "roles": [ + "Carry", + "Initiator", + "Disabler", + "Escape", + "Durable" + ], + "img": "/apps/dota2/images/dota_react/heroes/faceless_void.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/faceless_void.png?", + "base_health": 120, + "base_health_regen": 1, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 37, + "base_attack_max": 43, + "base_str": 20, + "base_agi": 21, + "base_int": 15, + "str_gain": 2.6, + "agi_gain": 3.3, + "int_gain": 1.5, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.5, + "move_speed": 300, + "turn_rate": 0.9, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Faceless Void" + }, + "42": { + "id": 42, + "name": "npc_dota_hero_skeleton_king", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Carry", + "Support", + "Durable", + "Disabler", + "Initiator" + ], + "img": "/apps/dota2/images/dota_react/heroes/skeleton_king.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/skeleton_king.png?", + "base_health": 120, + "base_health_regen": 0.5, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 38, + "base_attack_max": 40, + "base_str": 22, + "base_agi": 16, + "base_int": 18, + "str_gain": 2.8, + "agi_gain": 1.7, + "int_gain": 1.6, + "attack_range": 150, + "projectile_speed": 0, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.56, + "move_speed": 315, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Wraith King" + }, + "43": { + "id": 43, + "name": "npc_dota_hero_death_prophet", + "primary_attr": "all", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Pusher", + "Nuker", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/death_prophet.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/death_prophet.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.5, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 22, + "base_attack_max": 32, + "base_str": 21, + "base_agi": 17, + "base_int": 24, + "str_gain": 2.9, + "agi_gain": 2, + "int_gain": 3, + "attack_range": 600, + "projectile_speed": 1000, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.5, + "move_speed": 290, + "turn_rate": 0.8, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Death Prophet" + }, + "44": { + "id": 44, + "name": "npc_dota_hero_phantom_assassin", + "primary_attr": "agi", + "attack_type": "Melee", + "roles": [ + "Carry", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/phantom_assassin.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/phantom_assassin.png?", + "base_health": 120, + "base_health_regen": 1, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 35, + "base_attack_max": 37, + "base_str": 19, + "base_agi": 21, + "base_int": 15, + "str_gain": 2, + "agi_gain": 3.4, + "int_gain": 1.7, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 110, + "attack_point": 0.3, + "move_speed": 310, + "turn_rate": 0.8, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Phantom Assassin" + }, + "45": { + "id": 45, + "name": "npc_dota_hero_pugna", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Nuker", + "Pusher" + ], + "img": "/apps/dota2/images/dota_react/heroes/pugna.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/pugna.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.5, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 21, + "base_attack_max": 28, + "base_str": 19, + "base_agi": 21, + "base_int": 26, + "str_gain": 2, + "agi_gain": 1.3, + "int_gain": 5.2, + "attack_range": 630, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.5, + "move_speed": 330, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Pugna" + }, + "46": { + "id": 46, + "name": "npc_dota_hero_templar_assassin", + "primary_attr": "agi", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/templar_assassin.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/templar_assassin.png?", + "base_health": 120, + "base_health_regen": 1, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 30, + "base_attack_max": 36, + "base_str": 23, + "base_agi": 23, + "base_int": 20, + "str_gain": 2.4, + "agi_gain": 3.4, + "int_gain": 2, + "attack_range": 250, + "projectile_speed": 1000, + "attack_rate": 1.6, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 310, + "turn_rate": 0.7, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Templar Assassin" + }, + "47": { + "id": 47, + "name": "npc_dota_hero_viper", + "primary_attr": "agi", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Durable", + "Initiator", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/viper.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/viper.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": -2, + "base_mr": 25, + "base_attack_min": 27, + "base_attack_max": 29, + "base_str": 21, + "base_agi": 21, + "base_int": 17, + "str_gain": 2.4, + "agi_gain": 2.7, + "int_gain": 2, + "attack_range": 575, + "projectile_speed": 1200, + "attack_rate": 1.7, + "base_attack_time": 120, + "attack_point": 0.33, + "move_speed": 275, + "turn_rate": null, + "cm_enabled": true, + "legs": 0, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Viper" + }, + "48": { + "id": 48, + "name": "npc_dota_hero_luna", + "primary_attr": "agi", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Nuker", + "Pusher" + ], + "img": "/apps/dota2/images/dota_react/heroes/luna.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/luna.png?", + "base_health": 120, + "base_health_regen": 0.5, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 2, + "base_mr": 25, + "base_attack_min": 26, + "base_attack_max": 32, + "base_str": 21, + "base_agi": 24, + "base_int": 23, + "str_gain": 2.2, + "agi_gain": 3.4, + "int_gain": 1.9, + "attack_range": 330, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.35, + "move_speed": 325, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Luna" + }, + "49": { + "id": 49, + "name": "npc_dota_hero_dragon_knight", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Carry", + "Pusher", + "Durable", + "Disabler", + "Initiator", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/dragon_knight.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/dragon_knight.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 34, + "base_attack_max": 40, + "base_str": 21, + "base_agi": 16, + "base_int": 18, + "str_gain": 3.6, + "agi_gain": 2, + "int_gain": 1.7, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.6, + "base_attack_time": 100, + "attack_point": 0.5, + "move_speed": 315, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Dragon Knight" + }, + "50": { + "id": 50, + "name": "npc_dota_hero_dazzle", + "primary_attr": "all", + "attack_type": "Ranged", + "roles": [ + "Support", + "Nuker", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/dazzle.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/dazzle.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.3, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 19, + "base_attack_max": 25, + "base_str": 18, + "base_agi": 20, + "base_int": 25, + "str_gain": 2.3, + "agi_gain": 1.7, + "int_gain": 3.7, + "attack_range": 575, + "projectile_speed": 1200, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 305, + "turn_rate": 0.7, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Dazzle" + }, + "51": { + "id": 51, + "name": "npc_dota_hero_rattletrap", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Initiator", + "Disabler", + "Durable", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/rattletrap.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/rattletrap.png?", + "base_health": 120, + "base_health_regen": 0.5, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 24, + "base_attack_max": 26, + "base_str": 26, + "base_agi": 13, + "base_int": 18, + "str_gain": 3.2, + "agi_gain": 2.3, + "int_gain": 1.7, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.33, + "move_speed": 310, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Clockwerk" + }, + "52": { + "id": 52, + "name": "npc_dota_hero_leshrac", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Support", + "Nuker", + "Pusher", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/leshrac.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/leshrac.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 27, + "base_attack_max": 31, + "base_str": 20, + "base_agi": 17, + "base_int": 22, + "str_gain": 2.8, + "agi_gain": 2.5, + "int_gain": 3.5, + "attack_range": 575, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.4, + "move_speed": 325, + "turn_rate": null, + "cm_enabled": true, + "legs": 4, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Leshrac" + }, + "53": { + "id": 53, + "name": "npc_dota_hero_furion", + "primary_attr": "all", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Pusher", + "Escape", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/furion.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/furion.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 16, + "base_attack_max": 26, + "base_str": 19, + "base_agi": 14, + "base_int": 22, + "str_gain": 2.6, + "agi_gain": 3.2, + "int_gain": 3.5, + "attack_range": 620, + "projectile_speed": 1125, + "attack_rate": 1.5, + "base_attack_time": 100, + "attack_point": 0.4, + "move_speed": 295, + "turn_rate": 0.9, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Nature's Prophet" + }, + "54": { + "id": 54, + "name": "npc_dota_hero_life_stealer", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Carry", + "Durable", + "Escape", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/life_stealer.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/life_stealer.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 16, + "base_attack_max": 22, + "base_str": 25, + "base_agi": 19, + "base_int": 15, + "str_gain": 2.4, + "agi_gain": 1.9, + "int_gain": 1.8, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.39, + "move_speed": 315, + "turn_rate": 0.9, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Lifestealer" + }, + "55": { + "id": 55, + "name": "npc_dota_hero_dark_seer", + "primary_attr": "int", + "attack_type": "Melee", + "roles": [ + "Initiator", + "Escape", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/dark_seer.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/dark_seer.png?", + "base_health": 120, + "base_health_regen": 1, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 2, + "base_mr": 25, + "base_attack_min": 33, + "base_attack_max": 39, + "base_str": 20, + "base_agi": 19, + "base_int": 21, + "str_gain": 2.6, + "agi_gain": 1.8, + "int_gain": 2.7, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.59, + "move_speed": 295, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Dark Seer" + }, + "56": { + "id": 56, + "name": "npc_dota_hero_clinkz", + "primary_attr": "agi", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Escape", + "Pusher" + ], + "img": "/apps/dota2/images/dota_react/heroes/clinkz.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/clinkz.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 19, + "base_attack_max": 25, + "base_str": 17, + "base_agi": 25, + "base_int": 21, + "str_gain": 2, + "agi_gain": 2.7, + "int_gain": 2.2, + "attack_range": 600, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.4, + "move_speed": 290, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Clinkz" + }, + "57": { + "id": 57, + "name": "npc_dota_hero_omniknight", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Support", + "Durable", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/omniknight.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/omniknight.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 2, + "base_mr": 25, + "base_attack_min": 31, + "base_attack_max": 41, + "base_str": 22, + "base_agi": 15, + "base_int": 16, + "str_gain": 3.1, + "agi_gain": 2, + "int_gain": 2.1, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.433, + "move_speed": 310, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Omniknight" + }, + "58": { + "id": 58, + "name": "npc_dota_hero_enchantress", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Pusher", + "Durable", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/enchantress.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/enchantress.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.5, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 21, + "base_attack_max": 31, + "base_str": 17, + "base_agi": 19, + "base_int": 22, + "str_gain": 2, + "agi_gain": 2.1, + "int_gain": 3.8, + "attack_range": 575, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 320, + "turn_rate": null, + "cm_enabled": true, + "legs": 4, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Enchantress" + }, + "59": { + "id": 59, + "name": "npc_dota_hero_huskar", + "primary_attr": "str", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Durable", + "Initiator" + ], + "img": "/apps/dota2/images/dota_react/heroes/huskar.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/huskar.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 0, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 21, + "base_attack_max": 26, + "base_str": 23, + "base_agi": 10, + "base_int": 18, + "str_gain": 3.3, + "agi_gain": 1.5, + "int_gain": 1.5, + "attack_range": 400, + "projectile_speed": 1400, + "attack_rate": 1.6, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 295, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Huskar" + }, + "60": { + "id": 60, + "name": "npc_dota_hero_night_stalker", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Carry", + "Initiator", + "Durable", + "Disabler", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/night_stalker.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/night_stalker.png?", + "base_health": 120, + "base_health_regen": 1.5, + "base_mana": 75, + "base_mana_regen": 0.25, + "base_armor": 2, + "base_mr": 25, + "base_attack_min": 40, + "base_attack_max": 44, + "base_str": 23, + "base_agi": 18, + "base_int": 15, + "str_gain": 3, + "agi_gain": 2.2, + "int_gain": 1.6, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.55, + "move_speed": 295, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 800, + "night_vision": 1800, + "localized_name": "Night Stalker" + }, + "61": { + "id": 61, + "name": "npc_dota_hero_broodmother", + "primary_attr": "agi", + "attack_type": "Melee", + "roles": [ + "Carry", + "Pusher", + "Escape", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/broodmother.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/broodmother.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": -1, + "base_mr": 25, + "base_attack_min": 29, + "base_attack_max": 35, + "base_str": 18, + "base_agi": 18, + "base_int": 18, + "str_gain": 2.9, + "agi_gain": 3.4, + "int_gain": 2, + "attack_range": 175, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 125, + "attack_point": 0.4, + "move_speed": 285, + "turn_rate": null, + "cm_enabled": true, + "legs": 8, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Broodmother" + }, + "62": { + "id": 62, + "name": "npc_dota_hero_bounty_hunter", + "primary_attr": "agi", + "attack_type": "Melee", + "roles": [ + "Escape", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/bounty_hunter.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/bounty_hunter.png?", + "base_health": 120, + "base_health_regen": 0.75, + "base_mana": 75, + "base_mana_regen": 0.5, + "base_armor": 5, + "base_mr": 25, + "base_attack_min": 33, + "base_attack_max": 41, + "base_str": 19, + "base_agi": 21, + "base_int": 20, + "str_gain": 2.5, + "agi_gain": 2.6, + "int_gain": 1.9, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.5, + "base_attack_time": 100, + "attack_point": 0.59, + "move_speed": 325, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 1000, + "localized_name": "Bounty Hunter" + }, + "63": { + "id": 63, + "name": "npc_dota_hero_weaver", + "primary_attr": "agi", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/weaver.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/weaver.png?", + "base_health": 120, + "base_health_regen": 1.25, + "base_mana": 75, + "base_mana_regen": 0.5, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 37, + "base_attack_max": 43, + "base_str": 16, + "base_agi": 17, + "base_int": 20, + "str_gain": 2, + "agi_gain": 3.3, + "int_gain": 2, + "attack_range": 425, + "projectile_speed": 900, + "attack_rate": 1.8, + "base_attack_time": 120, + "attack_point": 0.55, + "move_speed": 275, + "turn_rate": null, + "cm_enabled": true, + "legs": 4, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Weaver" + }, + "64": { + "id": 64, + "name": "npc_dota_hero_jakiro", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Nuker", + "Pusher", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/jakiro.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/jakiro.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 27, + "base_attack_max": 35, + "base_str": 25, + "base_agi": 15, + "base_int": 26, + "str_gain": 2.5, + "agi_gain": 1.2, + "int_gain": 3, + "attack_range": 400, + "projectile_speed": 1100, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.4, + "move_speed": 290, + "turn_rate": 0.8, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Jakiro" + }, + "65": { + "id": 65, + "name": "npc_dota_hero_batrider", + "primary_attr": "all", + "attack_type": "Ranged", + "roles": [ + "Initiator", + "Disabler", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/batrider.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/batrider.png?", + "base_health": 120, + "base_health_regen": 1.75, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 2, + "base_mr": 25, + "base_attack_min": 12, + "base_attack_max": 16, + "base_str": 23, + "base_agi": 15, + "base_int": 22, + "str_gain": 2.9, + "agi_gain": 1.8, + "int_gain": 2.9, + "attack_range": 400, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 330, + "turn_rate": 0.9, + "cm_enabled": true, + "legs": 2, + "day_vision": 1600, + "night_vision": 800, + "localized_name": "Batrider" + }, + "66": { + "id": 66, + "name": "npc_dota_hero_chen", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Pusher" + ], + "img": "/apps/dota2/images/dota_react/heroes/chen.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/chen.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.5, + "base_armor": -1, + "base_mr": 25, + "base_attack_min": 27, + "base_attack_max": 37, + "base_str": 23, + "base_agi": 15, + "base_int": 19, + "str_gain": 2, + "agi_gain": 2.1, + "int_gain": 3.2, + "attack_range": 650, + "projectile_speed": 1100, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.5, + "move_speed": 305, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Chen" + }, + "67": { + "id": 67, + "name": "npc_dota_hero_spectre", + "primary_attr": "all", + "attack_type": "Melee", + "roles": [ + "Carry", + "Durable", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/spectre.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/spectre.png?", + "base_health": 120, + "base_health_regen": 1, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 21, + "base_attack_max": 25, + "base_str": 21, + "base_agi": 25, + "base_int": 16, + "str_gain": 2.5, + "agi_gain": 2.1, + "int_gain": 1.7, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 90, + "attack_point": 0.3, + "move_speed": 290, + "turn_rate": null, + "cm_enabled": true, + "legs": 0, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Spectre" + }, + "68": { + "id": 68, + "name": "npc_dota_hero_ancient_apparition", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Disabler", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/ancient_apparition.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/ancient_apparition.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": -1, + "base_mr": 25, + "base_attack_min": 21, + "base_attack_max": 31, + "base_str": 20, + "base_agi": 20, + "base_int": 23, + "str_gain": 1.9, + "agi_gain": 2.2, + "int_gain": 3.1, + "attack_range": 675, + "projectile_speed": 1250, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.45, + "move_speed": 285, + "turn_rate": null, + "cm_enabled": true, + "legs": 0, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Ancient Apparition" + }, + "69": { + "id": 69, + "name": "npc_dota_hero_doom_bringer", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Carry", + "Disabler", + "Initiator", + "Durable", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/doom_bringer.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/doom_bringer.png?", + "base_health": 120, + "base_health_regen": 1.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 2, + "base_mr": 25, + "base_attack_min": 32, + "base_attack_max": 42, + "base_str": 24, + "base_agi": 15, + "base_int": 15, + "str_gain": 3.8, + "agi_gain": 1.5, + "int_gain": 1.7, + "attack_range": 200, + "projectile_speed": 900, + "attack_rate": 1.9, + "base_attack_time": 100, + "attack_point": 0.5, + "move_speed": 290, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Doom" + }, + "70": { + "id": 70, + "name": "npc_dota_hero_ursa", + "primary_attr": "agi", + "attack_type": "Melee", + "roles": [ + "Carry", + "Durable", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/ursa.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/ursa.png?", + "base_health": 120, + "base_health_regen": 1, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 24, + "base_attack_max": 28, + "base_str": 25, + "base_agi": 18, + "base_int": 16, + "str_gain": 2.4, + "agi_gain": 2.8, + "int_gain": 1.5, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 310, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Ursa" + }, + "71": { + "id": 71, + "name": "npc_dota_hero_spirit_breaker", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Carry", + "Initiator", + "Disabler", + "Durable", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/spirit_breaker.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/spirit_breaker.png?", + "base_health": 120, + "base_health_regen": 1.25, + "base_mana": 75, + "base_mana_regen": 0.5, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 31, + "base_attack_max": 41, + "base_str": 28, + "base_agi": 17, + "base_int": 14, + "str_gain": 3.3, + "agi_gain": 1.7, + "int_gain": 1.8, + "attack_range": 170, + "projectile_speed": 900, + "attack_rate": 1.9, + "base_attack_time": 100, + "attack_point": 0.6, + "move_speed": 295, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Spirit Breaker" + }, + "72": { + "id": 72, + "name": "npc_dota_hero_gyrocopter", + "primary_attr": "agi", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Nuker", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/gyrocopter.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/gyrocopter.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 2, + "base_mr": 25, + "base_attack_min": 18, + "base_attack_max": 24, + "base_str": 22, + "base_agi": 25, + "base_int": 21, + "str_gain": 2.5, + "agi_gain": 3.2, + "int_gain": 2.4, + "attack_range": 365, + "projectile_speed": 3000, + "attack_rate": 1.7, + "base_attack_time": 125, + "attack_point": 0.2, + "move_speed": 320, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Gyrocopter" + }, + "73": { + "id": 73, + "name": "npc_dota_hero_alchemist", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Carry", + "Support", + "Durable", + "Disabler", + "Initiator", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/alchemist.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/alchemist.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 27, + "base_attack_max": 33, + "base_str": 23, + "base_agi": 22, + "base_int": 25, + "str_gain": 2.7, + "agi_gain": 1.5, + "int_gain": 1.8, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 110, + "attack_point": 0.35, + "move_speed": 295, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Alchemist" + }, + "74": { + "id": 74, + "name": "npc_dota_hero_invoker", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Nuker", + "Disabler", + "Escape", + "Pusher" + ], + "img": "/apps/dota2/images/dota_react/heroes/invoker.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/invoker.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": -1, + "base_mr": 25, + "base_attack_min": 15, + "base_attack_max": 21, + "base_str": 19, + "base_agi": 14, + "base_int": 20, + "str_gain": 2.5, + "agi_gain": 2, + "int_gain": 4.7, + "attack_range": 600, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.4, + "move_speed": 285, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Invoker" + }, + "75": { + "id": 75, + "name": "npc_dota_hero_silencer", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Support", + "Disabler", + "Initiator", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/silencer.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/silencer.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 18, + "base_attack_max": 32, + "base_str": 19, + "base_agi": 22, + "base_int": 25, + "str_gain": 2.2, + "agi_gain": 2.2, + "int_gain": 2.9, + "attack_range": 600, + "projectile_speed": 900, + "attack_rate": 1.6, + "base_attack_time": 110, + "attack_point": 0.5, + "move_speed": 290, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Silencer" + }, + "76": { + "id": 76, + "name": "npc_dota_hero_obsidian_destroyer", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Nuker", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/obsidian_destroyer.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/obsidian_destroyer.png?", + "base_health": 120, + "base_health_regen": 1, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 19, + "base_attack_max": 31, + "base_str": 21, + "base_agi": 22, + "base_int": 30, + "str_gain": 2.9, + "agi_gain": 1.6, + "int_gain": 4.2, + "attack_range": 450, + "projectile_speed": 900, + "attack_rate": 1.9, + "base_attack_time": 100, + "attack_point": 0.46, + "move_speed": 315, + "turn_rate": null, + "cm_enabled": true, + "legs": 4, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Outworld Devourer" + }, + "77": { + "id": 77, + "name": "npc_dota_hero_lycan", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Carry", + "Pusher", + "Durable", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/lycan.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/lycan.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 24, + "base_attack_max": 29, + "base_str": 26, + "base_agi": 18, + "base_int": 23, + "str_gain": 3.4, + "agi_gain": 1.7, + "int_gain": 1.7, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 110, + "attack_point": 0.55, + "move_speed": 305, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Lycan" + }, + "78": { + "id": 78, + "name": "npc_dota_hero_brewmaster", + "primary_attr": "all", + "attack_type": "Melee", + "roles": [ + "Carry", + "Initiator", + "Durable", + "Disabler", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/brewmaster.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/brewmaster.png?", + "base_health": 120, + "base_health_regen": 0.5, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 26, + "base_attack_max": 33, + "base_str": 23, + "base_agi": 19, + "base_int": 17, + "str_gain": 3.7, + "agi_gain": 2, + "int_gain": 1.6, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.35, + "move_speed": 310, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Brewmaster" + }, + "79": { + "id": 79, + "name": "npc_dota_hero_shadow_demon", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Disabler", + "Initiator", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/shadow_demon.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/shadow_demon.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 29, + "base_attack_max": 33, + "base_str": 23, + "base_agi": 18, + "base_int": 22, + "str_gain": 2.6, + "agi_gain": 2.2, + "int_gain": 3.6, + "attack_range": 500, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.35, + "move_speed": 290, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Shadow Demon" + }, + "80": { + "id": 80, + "name": "npc_dota_hero_lone_druid", + "primary_attr": "agi", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Pusher", + "Durable" + ], + "img": "/apps/dota2/images/dota_react/heroes/lone_druid.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/lone_druid.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 18, + "base_attack_max": 22, + "base_str": 18, + "base_agi": 20, + "base_int": 13, + "str_gain": 2.5, + "agi_gain": 2.8, + "int_gain": 1.4, + "attack_range": 550, + "projectile_speed": 900, + "attack_rate": 1.6, + "base_attack_time": 100, + "attack_point": 0.33, + "move_speed": 325, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Lone Druid" + }, + "81": { + "id": 81, + "name": "npc_dota_hero_chaos_knight", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Carry", + "Disabler", + "Durable", + "Pusher", + "Initiator" + ], + "img": "/apps/dota2/images/dota_react/heroes/chaos_knight.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/chaos_knight.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 2, + "base_mr": 25, + "base_attack_min": 24, + "base_attack_max": 54, + "base_str": 24, + "base_agi": 18, + "base_int": 18, + "str_gain": 3.1, + "agi_gain": 1.8, + "int_gain": 1.2, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.5, + "move_speed": 325, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Chaos Knight" + }, + "82": { + "id": 82, + "name": "npc_dota_hero_meepo", + "primary_attr": "agi", + "attack_type": "Melee", + "roles": [ + "Carry", + "Escape", + "Nuker", + "Disabler", + "Initiator", + "Pusher" + ], + "img": "/apps/dota2/images/dota_react/heroes/meepo.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/meepo.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.25, + "base_armor": 4, + "base_mr": 25, + "base_attack_min": 29, + "base_attack_max": 35, + "base_str": 24, + "base_agi": 17, + "base_int": 18, + "str_gain": 2.2, + "agi_gain": 2.1, + "int_gain": 1.9, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.38, + "move_speed": 320, + "turn_rate": 0.9, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Meepo" + }, + "83": { + "id": 83, + "name": "npc_dota_hero_treant", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Support", + "Initiator", + "Durable", + "Disabler", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/treant.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/treant.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.25, + "base_armor": -1, + "base_mr": 25, + "base_attack_min": 60, + "base_attack_max": 68, + "base_str": 25, + "base_agi": 15, + "base_int": 20, + "str_gain": 3.4, + "agi_gain": 1.8, + "int_gain": 1.8, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.9, + "base_attack_time": 100, + "attack_point": 0.6, + "move_speed": 280, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Treant Protector" + }, + "84": { + "id": 84, + "name": "npc_dota_hero_ogre_magi", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Support", + "Nuker", + "Disabler", + "Durable", + "Initiator" + ], + "img": "/apps/dota2/images/dota_react/heroes/ogre_magi.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/ogre_magi.png?", + "base_health": 120, + "base_health_regen": 1.5, + "base_mana": 120, + "base_mana_regen": 0, + "base_armor": 4, + "base_mr": 25, + "base_attack_min": 44, + "base_attack_max": 50, + "base_str": 23, + "base_agi": 14, + "base_int": 0, + "str_gain": 4.2, + "agi_gain": 1.5, + "int_gain": 0, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 290, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Ogre Magi" + }, + "85": { + "id": 85, + "name": "npc_dota_hero_undying", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Support", + "Durable", + "Disabler", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/undying.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/undying.png?", + "base_health": 120, + "base_health_regen": null, + "base_mana": 75, + "base_mana_regen": 0.25, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 31, + "base_attack_max": 39, + "base_str": 22, + "base_agi": 10, + "base_int": 27, + "str_gain": 2.4, + "agi_gain": 1.2, + "int_gain": 2.8, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 300, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Undying" + }, + "86": { + "id": 86, + "name": "npc_dota_hero_rubick", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Disabler", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/rubick.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/rubick.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.5, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 25, + "base_attack_max": 31, + "base_str": 21, + "base_agi": 23, + "base_int": 24, + "str_gain": 2, + "agi_gain": 2.5, + "int_gain": 3.7, + "attack_range": 550, + "projectile_speed": 1125, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.4, + "move_speed": 290, + "turn_rate": 0.7, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Rubick" + }, + "87": { + "id": 87, + "name": "npc_dota_hero_disruptor", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Disabler", + "Nuker", + "Initiator" + ], + "img": "/apps/dota2/images/dota_react/heroes/disruptor.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/disruptor.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 27, + "base_attack_max": 31, + "base_str": 21, + "base_agi": 15, + "base_int": 20, + "str_gain": 2.4, + "agi_gain": 1.4, + "int_gain": 2.9, + "attack_range": 625, + "projectile_speed": 1200, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.4, + "move_speed": 295, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Disruptor" + }, + "88": { + "id": 88, + "name": "npc_dota_hero_nyx_assassin", + "primary_attr": "all", + "attack_type": "Melee", + "roles": [ + "Disabler", + "Nuker", + "Initiator", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/nyx_assassin.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/nyx_assassin.png?", + "base_health": 120, + "base_health_regen": 2, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 26, + "base_attack_max": 30, + "base_str": 17, + "base_agi": 18, + "base_int": 21, + "str_gain": 2.7, + "agi_gain": 2.9, + "int_gain": 2.6, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.46, + "move_speed": 310, + "turn_rate": null, + "cm_enabled": true, + "legs": 6, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Nyx Assassin" + }, + "89": { + "id": 89, + "name": "npc_dota_hero_naga_siren", + "primary_attr": "agi", + "attack_type": "Melee", + "roles": [ + "Carry", + "Support", + "Pusher", + "Disabler", + "Initiator", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/naga_siren.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/naga_siren.png?", + "base_health": 120, + "base_health_regen": 1, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 23, + "base_attack_max": 25, + "base_str": 21, + "base_agi": 21, + "base_int": 21, + "str_gain": 2.4, + "agi_gain": 3.3, + "int_gain": 2, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 110, + "attack_point": 0.5, + "move_speed": 330, + "turn_rate": null, + "cm_enabled": true, + "legs": 0, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Naga Siren" + }, + "90": { + "id": 90, + "name": "npc_dota_hero_keeper_of_the_light", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Nuker", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/keeper_of_the_light.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/keeper_of_the_light.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 20, + "base_attack_max": 27, + "base_str": 18, + "base_agi": 15, + "base_int": 23, + "str_gain": 2.3, + "agi_gain": 1.6, + "int_gain": 3.3, + "attack_range": 600, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 320, + "turn_rate": 0.7, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Keeper of the Light" + }, + "91": { + "id": 91, + "name": "npc_dota_hero_wisp", + "primary_attr": "all", + "attack_type": "Ranged", + "roles": [ + "Support", + "Escape", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/wisp.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/wisp.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 21, + "base_attack_max": 27, + "base_str": 17, + "base_agi": 14, + "base_int": 23, + "str_gain": 3, + "agi_gain": 1.6, + "int_gain": 1.7, + "attack_range": 500, + "projectile_speed": 1200, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 320, + "turn_rate": 0.7, + "cm_enabled": true, + "legs": 0, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Io" + }, + "92": { + "id": 92, + "name": "npc_dota_hero_visage", + "primary_attr": "all", + "attack_type": "Ranged", + "roles": [ + "Support", + "Nuker", + "Durable", + "Disabler", + "Pusher" + ], + "img": "/apps/dota2/images/dota_react/heroes/visage.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/visage.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 21, + "base_attack_max": 31, + "base_str": 22, + "base_agi": 11, + "base_int": 22, + "str_gain": 2.8, + "agi_gain": 1.3, + "int_gain": 2.9, + "attack_range": 600, + "projectile_speed": 1400, + "attack_rate": 1.7, + "base_attack_time": 110, + "attack_point": 0.4, + "move_speed": 285, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Visage" + }, + "93": { + "id": 93, + "name": "npc_dota_hero_slark", + "primary_attr": "agi", + "attack_type": "Melee", + "roles": [ + "Carry", + "Escape", + "Disabler", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/slark.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/slark.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 37, + "base_attack_max": 43, + "base_str": 20, + "base_agi": 21, + "base_int": 16, + "str_gain": 2.1, + "agi_gain": 1.5, + "int_gain": 1.9, + "attack_range": 150, + "projectile_speed": 0, + "attack_rate": 1.7, + "base_attack_time": 120, + "attack_point": 0.5, + "move_speed": 300, + "turn_rate": 0.7, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 1800, + "localized_name": "Slark" + }, + "94": { + "id": 94, + "name": "npc_dota_hero_medusa", + "primary_attr": "agi", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Disabler", + "Durable" + ], + "img": "/apps/dota2/images/dota_react/heroes/medusa.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/medusa.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": -1, + "base_mr": 25, + "base_attack_min": 26, + "base_attack_max": 32, + "base_str": 0, + "base_agi": 22, + "base_int": 25, + "str_gain": 0, + "agi_gain": 3.6, + "int_gain": 3.6, + "attack_range": 600, + "projectile_speed": 1200, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.5, + "move_speed": 275, + "turn_rate": null, + "cm_enabled": true, + "legs": 0, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Medusa" + }, + "95": { + "id": 95, + "name": "npc_dota_hero_troll_warlord", + "primary_attr": "agi", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Pusher", + "Disabler", + "Durable" + ], + "img": "/apps/dota2/images/dota_react/heroes/troll_warlord.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/troll_warlord.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": -1, + "base_mr": 25, + "base_attack_min": 27, + "base_attack_max": 35, + "base_str": 21, + "base_agi": 23, + "base_int": 13, + "str_gain": 2.5, + "agi_gain": 3.3, + "int_gain": 1, + "attack_range": 500, + "projectile_speed": 1200, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 290, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Troll Warlord" + }, + "96": { + "id": 96, + "name": "npc_dota_hero_centaur", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Durable", + "Initiator", + "Disabler", + "Nuker", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/centaur.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/centaur.png?", + "base_health": 120, + "base_health_regen": 3, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": -2, + "base_mr": 25, + "base_attack_min": 36, + "base_attack_max": 38, + "base_str": 27, + "base_agi": 15, + "base_int": 15, + "str_gain": 4, + "agi_gain": 1, + "int_gain": 1.6, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 90, + "attack_point": 0.3, + "move_speed": 305, + "turn_rate": null, + "cm_enabled": true, + "legs": 4, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Centaur Warrunner" + }, + "97": { + "id": 97, + "name": "npc_dota_hero_magnataur", + "primary_attr": "all", + "attack_type": "Melee", + "roles": [ + "Initiator", + "Disabler", + "Nuker", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/magnataur.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/magnataur.png?", + "base_health": 120, + "base_health_regen": 0.5, + "base_mana": 75, + "base_mana_regen": 0.5, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 30, + "base_attack_max": 38, + "base_str": 25, + "base_agi": 12, + "base_int": 20, + "str_gain": 3.1, + "agi_gain": 2, + "int_gain": 2.1, + "attack_range": 200, + "projectile_speed": 900, + "attack_rate": 1.8, + "base_attack_time": 100, + "attack_point": 0.5, + "move_speed": 305, + "turn_rate": 0.8, + "cm_enabled": true, + "legs": 4, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Magnus" + }, + "98": { + "id": 98, + "name": "npc_dota_hero_shredder", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Nuker", + "Durable", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/shredder.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/shredder.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.5, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 23, + "base_attack_max": 27, + "base_str": 26, + "base_agi": 16, + "base_int": 23, + "str_gain": 3.5, + "agi_gain": 1.6, + "int_gain": 2.7, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.36, + "move_speed": 285, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Timbersaw" + }, + "99": { + "id": 99, + "name": "npc_dota_hero_bristleback", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Carry", + "Durable", + "Initiator", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/bristleback.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/bristleback.png?", + "base_health": 120, + "base_health_regen": 1, + "base_mana": 75, + "base_mana_regen": 0.4, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 31, + "base_attack_max": 37, + "base_str": 22, + "base_agi": 17, + "base_int": 14, + "str_gain": 2.7, + "agi_gain": 1.8, + "int_gain": 2.8, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.8, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 295, + "turn_rate": 0.9, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Bristleback" + }, + "100": { + "id": 100, + "name": "npc_dota_hero_tusk", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Initiator", + "Disabler", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/tusk.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/tusk.png?", + "base_health": 120, + "base_health_regen": 1, + "base_mana": 75, + "base_mana_regen": 0.5, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 27, + "base_attack_max": 31, + "base_str": 23, + "base_agi": 23, + "base_int": 18, + "str_gain": 3.9, + "agi_gain": 2.1, + "int_gain": 1.7, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.36, + "move_speed": 310, + "turn_rate": 0.7, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Tusk" + }, + "101": { + "id": 101, + "name": "npc_dota_hero_skywrath_mage", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Nuker", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/skywrath_mage.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/skywrath_mage.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.25, + "base_armor": -2, + "base_mr": 25, + "base_attack_min": 16, + "base_attack_max": 26, + "base_str": 21, + "base_agi": 13, + "base_int": 23, + "str_gain": 2, + "agi_gain": 0.8, + "int_gain": 4.1, + "attack_range": 625, + "projectile_speed": 1000, + "attack_rate": 1.7, + "base_attack_time": 90, + "attack_point": 0.4, + "move_speed": 325, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Skywrath Mage" + }, + "102": { + "id": 102, + "name": "npc_dota_hero_abaddon", + "primary_attr": "all", + "attack_type": "Melee", + "roles": [ + "Support", + "Carry", + "Durable" + ], + "img": "/apps/dota2/images/dota_react/heroes/abaddon.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/abaddon.png?", + "base_health": 120, + "base_health_regen": 1, + "base_mana": 75, + "base_mana_regen": 0.25, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 22, + "base_attack_max": 32, + "base_str": 22, + "base_agi": 23, + "base_int": 18, + "str_gain": 2.6, + "agi_gain": 1.5, + "int_gain": 2, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.5, + "base_attack_time": 95, + "attack_point": 0.56, + "move_speed": 325, + "turn_rate": 0.6, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Abaddon" + }, + "103": { + "id": 103, + "name": "npc_dota_hero_elder_titan", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Initiator", + "Disabler", + "Nuker", + "Durable" + ], + "img": "/apps/dota2/images/dota_react/heroes/elder_titan.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/elder_titan.png?", + "base_health": 120, + "base_health_regen": 0.75, + "base_mana": 75, + "base_mana_regen": 0.25, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 23, + "base_attack_max": 33, + "base_str": 26, + "base_agi": 16, + "base_int": 23, + "str_gain": 3, + "agi_gain": 1.8, + "int_gain": 1.6, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.35, + "move_speed": 310, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Elder Titan" + }, + "104": { + "id": 104, + "name": "npc_dota_hero_legion_commander", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Carry", + "Disabler", + "Initiator", + "Durable", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/legion_commander.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/legion_commander.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 33, + "base_attack_max": 37, + "base_str": 24, + "base_agi": 18, + "base_int": 20, + "str_gain": 3.1, + "agi_gain": 1.7, + "int_gain": 2.2, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.46, + "move_speed": 325, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Legion Commander" + }, + "105": { + "id": 105, + "name": "npc_dota_hero_techies", + "primary_attr": "all", + "attack_type": "Ranged", + "roles": [ + "Nuker", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/techies.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/techies.png?", + "base_health": 120, + "base_health_regen": 1, + "base_mana": 75, + "base_mana_regen": 1, + "base_armor": 2, + "base_mr": 25, + "base_attack_min": 20, + "base_attack_max": 22, + "base_str": 19, + "base_agi": 14, + "base_int": 25, + "str_gain": 2.6, + "agi_gain": 1.8, + "int_gain": 2.8, + "attack_range": 600, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 90, + "attack_point": 0.5, + "move_speed": 300, + "turn_rate": null, + "cm_enabled": true, + "legs": 6, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Techies" + }, + "106": { + "id": 106, + "name": "npc_dota_hero_ember_spirit", + "primary_attr": "agi", + "attack_type": "Melee", + "roles": [ + "Carry", + "Escape", + "Nuker", + "Disabler", + "Initiator" + ], + "img": "/apps/dota2/images/dota_react/heroes/ember_spirit.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/ember_spirit.png?", + "base_health": 120, + "base_health_regen": 0.5, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 33, + "base_attack_max": 37, + "base_str": 22, + "base_agi": 22, + "base_int": 20, + "str_gain": 2.6, + "agi_gain": 3.2, + "int_gain": 2.2, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.4, + "move_speed": 300, + "turn_rate": 0.8, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Ember Spirit" + }, + "107": { + "id": 107, + "name": "npc_dota_hero_earth_spirit", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Nuker", + "Escape", + "Disabler", + "Initiator", + "Durable" + ], + "img": "/apps/dota2/images/dota_react/heroes/earth_spirit.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/earth_spirit.png?", + "base_health": 120, + "base_health_regen": 1, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 25, + "base_attack_max": 35, + "base_str": 22, + "base_agi": 17, + "base_int": 18, + "str_gain": 3.8, + "agi_gain": 2.4, + "int_gain": 2.1, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.35, + "move_speed": 290, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Earth Spirit" + }, + "108": { + "id": 108, + "name": "npc_dota_hero_abyssal_underlord", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Support", + "Nuker", + "Disabler", + "Durable", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/abyssal_underlord.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/abyssal_underlord.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 3, + "base_mr": 25, + "base_attack_min": 37, + "base_attack_max": 43, + "base_str": 25, + "base_agi": 12, + "base_int": 17, + "str_gain": 3, + "agi_gain": 1.6, + "int_gain": 2.3, + "attack_range": 200, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.45, + "move_speed": 290, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Underlord" + }, + "109": { + "id": 109, + "name": "npc_dota_hero_terrorblade", + "primary_attr": "agi", + "attack_type": "Melee", + "roles": [ + "Carry", + "Pusher", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/terrorblade.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/terrorblade.png?", + "base_health": 120, + "base_health_regen": 1.5, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 5, + "base_mr": 25, + "base_attack_min": 28, + "base_attack_max": 34, + "base_str": 18, + "base_agi": 22, + "base_int": 19, + "str_gain": 2, + "agi_gain": 4, + "int_gain": 1.6, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.5, + "base_attack_time": 110, + "attack_point": 0.3, + "move_speed": 315, + "turn_rate": 0.8, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Terrorblade" + }, + "110": { + "id": 110, + "name": "npc_dota_hero_phoenix", + "primary_attr": "str", + "attack_type": "Ranged", + "roles": [ + "Support", + "Nuker", + "Initiator", + "Escape", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/phoenix.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/phoenix.png?", + "base_health": 120, + "base_health_regen": 2, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": -1, + "base_mr": 25, + "base_attack_min": 21, + "base_attack_max": 31, + "base_str": 23, + "base_agi": 12, + "base_int": 16, + "str_gain": 3.3, + "agi_gain": 1.5, + "int_gain": 1.8, + "attack_range": 525, + "projectile_speed": 1100, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.35, + "move_speed": 280, + "turn_rate": 0.9, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Phoenix" + }, + "111": { + "id": 111, + "name": "npc_dota_hero_oracle", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Nuker", + "Disabler", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/oracle.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/oracle.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.5, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 13, + "base_attack_max": 19, + "base_str": 20, + "base_agi": 15, + "base_int": 26, + "str_gain": 2.4, + "agi_gain": 1.7, + "int_gain": 3.6, + "attack_range": 620, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 295, + "turn_rate": 0.7, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Oracle" + }, + "112": { + "id": 112, + "name": "npc_dota_hero_winter_wyvern", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Disabler", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/winter_wyvern.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/winter_wyvern.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.5, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 14, + "base_attack_max": 21, + "base_str": 22, + "base_agi": 16, + "base_int": 26, + "str_gain": 2.5, + "agi_gain": 1.7, + "int_gain": 3.6, + "attack_range": 425, + "projectile_speed": 700, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.25, + "move_speed": 285, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 1200, + "localized_name": "Winter Wyvern" + }, + "113": { + "id": 113, + "name": "npc_dota_hero_arc_warden", + "primary_attr": "all", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Escape", + "Nuker" + ], + "img": "/apps/dota2/images/dota_react/heroes/arc_warden.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/arc_warden.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 23, + "base_attack_max": 29, + "base_str": 20, + "base_agi": 20, + "base_int": 24, + "str_gain": 2.4, + "agi_gain": 3, + "int_gain": 2.6, + "attack_range": 625, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 285, + "turn_rate": 0.7, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Arc Warden" + }, + "114": { + "id": 114, + "name": "npc_dota_hero_monkey_king", + "primary_attr": "agi", + "attack_type": "Melee", + "roles": [ + "Carry", + "Escape", + "Disabler", + "Initiator" + ], + "img": "/apps/dota2/images/dota_react/heroes/monkey_king.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/monkey_king.png?", + "base_health": 120, + "base_health_regen": 1, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 2, + "base_mr": 25, + "base_attack_min": 29, + "base_attack_max": 33, + "base_str": 19, + "base_agi": 24, + "base_int": 20, + "str_gain": 2.8, + "agi_gain": 3.7, + "int_gain": 1.8, + "attack_range": 300, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.45, + "move_speed": 300, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Monkey King" + }, + "119": { + "id": 119, + "name": "npc_dota_hero_dark_willow", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Nuker", + "Disabler", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/dark_willow.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/dark_willow.png?", + "base_health": 120, + "base_health_regen": 0.75, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 27, + "base_attack_max": 35, + "base_str": 20, + "base_agi": 18, + "base_int": 21, + "str_gain": 2, + "agi_gain": 1.6, + "int_gain": 3.5, + "attack_range": 475, + "projectile_speed": 1200, + "attack_rate": 1.5, + "base_attack_time": 115, + "attack_point": 0.3, + "move_speed": 290, + "turn_rate": 0.7, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Dark Willow" + }, + "120": { + "id": 120, + "name": "npc_dota_hero_pangolier", + "primary_attr": "all", + "attack_type": "Melee", + "roles": [ + "Carry", + "Nuker", + "Disabler", + "Durable", + "Escape", + "Initiator" + ], + "img": "/apps/dota2/images/dota_react/heroes/pangolier.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/pangolier.png?", + "base_health": 120, + "base_health_regen": 1.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 2, + "base_mr": 25, + "base_attack_min": 28, + "base_attack_max": 34, + "base_str": 19, + "base_agi": 18, + "base_int": 16, + "str_gain": 2.7, + "agi_gain": 3.2, + "int_gain": 2.2, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.33, + "move_speed": 295, + "turn_rate": 0.9, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Pangolier" + }, + "121": { + "id": 121, + "name": "npc_dota_hero_grimstroke", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Nuker", + "Disabler", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/grimstroke.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/grimstroke.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 21, + "base_attack_max": 25, + "base_str": 21, + "base_agi": 18, + "base_int": 25, + "str_gain": 2.4, + "agi_gain": 1.9, + "int_gain": 3.8, + "attack_range": 600, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.35, + "move_speed": 290, + "turn_rate": null, + "cm_enabled": true, + "legs": 0, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Grimstroke" + }, + "123": { + "id": 123, + "name": "npc_dota_hero_hoodwink", + "primary_attr": "agi", + "attack_type": "Ranged", + "roles": [ + "Support", + "Nuker", + "Escape", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/hoodwink.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/hoodwink.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 22, + "base_attack_max": 29, + "base_str": 17, + "base_agi": 25, + "base_int": 21, + "str_gain": 2, + "agi_gain": 3.6, + "int_gain": 2.9, + "attack_range": 575, + "projectile_speed": 1800, + "attack_rate": 2, + "base_attack_time": 100, + "attack_point": 0.4, + "move_speed": 310, + "turn_rate": 0.9, + "cm_enabled": true, + "legs": 4, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Hoodwink" + }, + "126": { + "id": 126, + "name": "npc_dota_hero_void_spirit", + "primary_attr": "all", + "attack_type": "Melee", + "roles": [ + "Carry", + "Escape", + "Nuker", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/void_spirit.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/void_spirit.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.6, + "base_armor": -1, + "base_mr": 25, + "base_attack_min": 22, + "base_attack_max": 26, + "base_str": 22, + "base_agi": 19, + "base_int": 24, + "str_gain": 2.6, + "agi_gain": 2.2, + "int_gain": 3.1, + "attack_range": 200, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.35, + "move_speed": 290, + "turn_rate": 0.7, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Void Spirit" + }, + "128": { + "id": 128, + "name": "npc_dota_hero_snapfire", + "primary_attr": "all", + "attack_type": "Ranged", + "roles": [ + "Support", + "Nuker", + "Disabler", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/snapfire.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/snapfire.png?", + "base_health": 120, + "base_health_regen": 0.75, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 25, + "base_attack_max": 31, + "base_str": 21, + "base_agi": 16, + "base_int": 21, + "str_gain": 3.5, + "agi_gain": 1.9, + "int_gain": 2.2, + "attack_range": 400, + "projectile_speed": 1800, + "attack_rate": 1.8, + "base_attack_time": 100, + "attack_point": 0.35, + "move_speed": 300, + "turn_rate": null, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Snapfire" + }, + "129": { + "id": 129, + "name": "npc_dota_hero_mars", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Carry", + "Initiator", + "Disabler", + "Durable" + ], + "img": "/apps/dota2/images/dota_react/heroes/mars.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/mars.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0.5, + "base_armor": -1, + "base_mr": 25, + "base_attack_min": 26, + "base_attack_max": 34, + "base_str": 23, + "base_agi": 20, + "base_int": 21, + "str_gain": 3.7, + "agi_gain": 1.7, + "int_gain": 2.2, + "attack_range": 250, + "projectile_speed": 900, + "attack_rate": 1.8, + "base_attack_time": 100, + "attack_point": 0.4, + "move_speed": 310, + "turn_rate": 0.8, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Mars" + }, + "131": { + "id": 131, + "name": "npc_dota_hero_ringmaster", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Support", + "Nuker", + "Escape", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/ringmaster.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/ringmaster.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 23, + "base_attack_max": 33, + "base_str": 21, + "base_agi": 13, + "base_int": 25, + "str_gain": 2.1, + "agi_gain": 1.4, + "int_gain": 3, + "attack_range": 575, + "projectile_speed": 1200, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.5, + "move_speed": 310, + "turn_rate": 0.6, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Ring Master" + }, + "135": { + "id": 135, + "name": "npc_dota_hero_dawnbreaker", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Carry", + "Durable" + ], + "img": "/apps/dota2/images/dota_react/heroes/dawnbreaker.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/dawnbreaker.png?", + "base_health": 120, + "base_health_regen": 0.5, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 2, + "base_mr": 25, + "base_attack_min": 29, + "base_attack_max": 33, + "base_str": 25, + "base_agi": 14, + "base_int": 20, + "str_gain": 3.4, + "agi_gain": 1.7, + "int_gain": 2, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.46, + "move_speed": 300, + "turn_rate": 0.6, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Dawnbreaker" + }, + "136": { + "id": 136, + "name": "npc_dota_hero_marci", + "primary_attr": "all", + "attack_type": "Melee", + "roles": [ + "Support", + "Carry", + "Initiator", + "Disabler", + "Escape" + ], + "img": "/apps/dota2/images/dota_react/heroes/marci.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/marci.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 29, + "base_attack_max": 33, + "base_str": 23, + "base_agi": 20, + "base_int": 19, + "str_gain": 3, + "agi_gain": 1.8, + "int_gain": 1.9, + "attack_range": 150, + "projectile_speed": 0, + "attack_rate": 1.7, + "base_attack_time": 100, + "attack_point": 0.3, + "move_speed": 315, + "turn_rate": 0.9, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Marci" + }, + "137": { + "id": 137, + "name": "npc_dota_hero_primal_beast", + "primary_attr": "str", + "attack_type": "Melee", + "roles": [ + "Initiator", + "Durable", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/primal_beast.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/primal_beast.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 2, + "base_mr": 25, + "base_attack_min": 36, + "base_attack_max": 38, + "base_str": 26, + "base_agi": 15, + "base_int": 16, + "str_gain": 4.3, + "agi_gain": 1, + "int_gain": 1.7, + "attack_range": 150, + "projectile_speed": 900, + "attack_rate": 1.8, + "base_attack_time": 100, + "attack_point": 0.6, + "move_speed": 305, + "turn_rate": 0.6, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Primal Beast" + }, + "138": { + "id": 138, + "name": "npc_dota_hero_muerta", + "primary_attr": "int", + "attack_type": "Ranged", + "roles": [ + "Carry", + "Nuker", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/muerta.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/muerta.png?", + "base_health": 120, + "base_health_regen": 0.25, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 0, + "base_mr": 25, + "base_attack_min": 24, + "base_attack_max": 32, + "base_str": 19, + "base_agi": 20, + "base_int": 23, + "str_gain": 2, + "agi_gain": 2.7, + "int_gain": 3.4, + "attack_range": 575, + "projectile_speed": 3000, + "attack_rate": 1.7, + "base_attack_time": 115, + "attack_point": 0.35, + "move_speed": 295, + "turn_rate": 0.7, + "cm_enabled": true, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Muerta" + }, + "145": { + "id": 145, + "name": "npc_dota_hero_kez", + "primary_attr": "agi", + "attack_type": "Melee", + "roles": [ + "Carry", + "Escape", + "Disabler" + ], + "img": "/apps/dota2/images/dota_react/heroes/kez.png?", + "icon": "/apps/dota2/images/dota_react/heroes/icons/kez.png?", + "base_health": 120, + "base_health_regen": 1, + "base_mana": 75, + "base_mana_regen": 0, + "base_armor": 1, + "base_mr": 25, + "base_attack_min": 23, + "base_attack_max": 29, + "base_str": 19, + "base_agi": 27, + "base_int": 18, + "str_gain": 2.6, + "agi_gain": 3.5, + "int_gain": 1.7, + "attack_range": 225, + "projectile_speed": 900, + "attack_rate": 2, + "base_attack_time": 100, + "attack_point": 0.35, + "move_speed": 315, + "turn_rate": 1, + "cm_enabled": false, + "legs": 2, + "day_vision": 1800, + "night_vision": 800, + "localized_name": "Kez" + } +} \ No newline at end of file diff --git a/image_generator.py b/image_generator.py new file mode 100644 index 0000000..08751dc --- /dev/null +++ b/image_generator.py @@ -0,0 +1,135 @@ +from playwright.sync_api import sync_playwright +from jinja2 import Environment, FileSystemLoader +import utils +import json +import os + + +class ImageGenerator: + def __init__(self): + self.env = Environment(loader=FileSystemLoader('templates')) + # 加载英雄数据 + with open('heroes.json', 'r', encoding='utf-8') as f: + self.heroes_data = json.load(f) + + def get_hero_image_url(self, hero_id): + """根据英雄id获取图片URL""" + hero_info = self.heroes_data.get(str(hero_id)) + if hero_info: + return f"https://cdn.cloudflare.steamstatic.com{hero_info['img']}" + return None + + def generate_match_report(self, match_data): + """ + 根据match_data生成比赛报告图片 + match_data应该是dota.py中serialize_match方法返回的格式 + """ + # 处理数据,标记最高经济和最高伤害的玩家 + radiant_players = [] + dire_players = [] + + # 分离天辉和夜魇玩家 + for player in match_data['players']: + player_data = { + 'name': player['personaname'] if player['personaname'] else '-', + 'is_friend': bool(player.get('nickname', '')), + 'hero': player['hero'], + 'level': player['level'], + 'kills': player['kills'], + 'deaths': player['deaths'], + 'assists': player['assists'], + 'total_gold': player['total_gold'], + 'hero_damage': player['hero_damage'], + 'hero_img': self.get_hero_image_url(player['hero_id']), + 'highest_gold': False, + 'highest_damage': False, + 'rank': player.get('rank'), + 'rank_tier': player.get('rank_tier'), + } + + # 如果是好友,使用昵称 + if player.get('nickname'): + player_data['name'] = player['nickname'] + + # 转换英雄名称为中文 + player_data['hero'] = utils.get_hero_chinese_name(player['hero']) + + if player['is_radiant']: + radiant_players.append(player_data) + else: + dire_players.append(player_data) + + # 找出天辉最高经济和伤害 + if radiant_players: + max_gold_player = max(radiant_players, key=lambda p: int(p['total_gold'])) + max_damage_player = max(radiant_players, key=lambda p: int(p['hero_damage'])) + + for player in radiant_players: + if player == max_gold_player: + player['highest_gold'] = True + if player == max_damage_player: + player['highest_damage'] = True + + # 找出夜魇最高经济和伤害 + if dire_players: + max_gold_player = max(dire_players, key=lambda p: int(p['total_gold'])) + max_damage_player = max(dire_players, key=lambda p: int(p['hero_damage'])) + + for player in dire_players: + if player == max_gold_player: + player['highest_gold'] = True + if player == max_damage_player: + player['highest_damage'] = True + + + # 短数字 + for player in radiant_players: + player['total_gold'] = utils.shorten_digits(player['total_gold']) + player['hero_damage'] = utils.shorten_digits(player['hero_damage']) + for player in dire_players: + player['total_gold'] = utils.shorten_digits(player['total_gold']) + player['hero_damage'] = utils.shorten_digits(player['hero_damage']) + + end_time = match_data.get('end_time', '') + # '2025-03-05T01:04:30.000+08:00' get 01:04:30 + end_time = end_time.split('T')[1].split('.')[0] + + # 准备模板数据 + template_data = { + 'radiant_players': radiant_players, + 'dire_players': dire_players, + 'radiant_score': match_data.get('radiant_score', 0), + 'dire_score': match_data.get('dire_score', 0), + 'duration': match_data.get('duration', 0), + 'radiant_win': match_data.get('radiant_win', False), + 'start_time': match_data.get('start_time', ''), + 'end_time': end_time, + } + + # 渲染模板 + template = self.env.get_template('match_report.html') + html_content = template.render(**template_data) + + # 使用Playwright生成图片 + with sync_playwright() as playwright: + browser = playwright.chromium.launch() + page = browser.new_page() + page.set_content(html_content) + page.set_viewport_size({"width": 800, "height": 800}) + + # 等待内容完全加载 + page.wait_for_timeout(1000) + + # 调整截图高度以适应内容 + body_height = page.evaluate('document.body.scrollHeight') + body_width = page.evaluate('document.body.offsetWidth') + page.set_viewport_size({"width": body_width, "height": body_height}) + + # 截图 + image_path = "match_report_%s.png" % match_data.get('match_id') + page.screenshot(path=image_path, full_page=True) + browser.close() + image_url = utils.upload_image(image_path, image_path) + os.remove(image_path) + return image_url + return None \ No newline at end of file diff --git a/players.py b/players.py index 93082b0..83def44 100644 --- a/players.py +++ b/players.py @@ -1,21 +1,25 @@ import dota +import utils def serialize_player(player): - friend = dota.Friend.get_or_none(steam_id=player.account_id) + friend = dota.Friend.get_or_none(steam_id=player['account_id']) player_data = { - 'personaname': player.personaname if player.personaname else '', + 'personaname': player['personaname'] if player['personaname'] else '', 'nickname': friend.name if friend else '', - 'kills': player.kills, - 'deaths': player.deaths, - 'assists': player.assists, - 'total_gold': player.total_gold, - 'last_hits': player.last_hits, - 'denies': player.denies, - 'hero_damage': player.hero_damage, - 'party_id': player.party_id, - 'win': player.win, - 'level': player.level, - 'is_radiant': player.is_radiant, - 'hero': dota.Hero.get(hero_id=player.hero_id).localized_name, + 'kills': player['kills'], + 'deaths': player['deaths'], + 'assists': player['assists'], + 'total_gold': player['total_gold'], + 'last_hits': player['last_hits'], + 'denies': player['denies'], + 'hero_damage': player['hero_damage'], + 'party_id': player['party_id'], + 'win': player['win'], + 'level': player['level'], + 'is_radiant': player['is_radiant'], + 'hero': dota.Hero.get(hero_id=player['hero_id']).localized_name, + 'hero_id': player['hero_id'], + 'rank_tier': player['rank_tier'], + 'rank': utils.get_ranking(player['rank_tier']), } return player_data diff --git a/templates/match_report.html b/templates/match_report.html new file mode 100644 index 0000000..f5038c2 --- /dev/null +++ b/templates/match_report.html @@ -0,0 +1,339 @@ + + + + + + + + +
+
+
比赛时长: {{ duration }}
+
结束时间: {{ end_time }}
+
+
+ {% if radiant_win %}天辉胜利{% else %}夜魇胜利{% endif %} +
+
+
天辉: {{ radiant_score }}
+
夜魇: {{ dire_score }}
+
+
+ +
+
+
+

天辉

+
+
+
玩家
+
+
KDA
+
经济
+
伤害
+
+
+ {% for player in radiant_players %} +
+
+ {{ player.hero }} +
+
+ {{ player.name }} +
+ {% if player.rank_tier %} +
{{ player.rank }}
+ {% endif %} +
+
+
+
+ {{ player.kills }}/{{ player.deaths }}/{{ player.assists + }} +
+
+ {{ player.total_gold }} +
+
+ {{ player.hero_damage }} +
+
+
+ {% endfor %} +
+ +
+
+

夜魇

+
+
+
玩家
+
+
KDA
+
经济
+
伤害
+
+
+ {% for player in dire_players %} +
+
+ {{ player.hero }} +
+
+ {{ player.name }} +
+ {% if player.rank_tier %} +
{{ player.rank }}
+ {% endif %} +
+
+
+
+ {{ player.kills }}/{{ player.deaths }}/{{ player.assists + }} +
+
+ {{ player.total_gold }} +
+
+ {{ player.hero_damage }} +
+
+
+ {% endfor %} +
+
+ + diff --git a/utils.py b/utils.py index d9e6b4c..8c896e5 100644 --- a/utils.py +++ b/utils.py @@ -2,6 +2,12 @@ import datetime import requests from loguru import logger +import boto3 +import os +from botocore.config import Config +import uuid +import configparser + # logger_file = '/root/develop/log/dotabot.log' logger_file = 'dotabot.log' logger.add(logger_file) @@ -12,14 +18,15 @@ def convert_seconds_to_hms(total_seconds): return hours, minutes, seconds def is_workday(): - return datetime.datetime.today().weekday() < 5 + return datetime.datetime.today().weekday() < 4 def is_game_time(): # game time is workday 21:00 - 1:00, weekend 10:00 - 1:00 if is_workday(): - return datetime.datetime.now().hour >= 21 or datetime.datetime.now().hour < 1 + # return datetime.datetime.now().hour >= 21 or datetime.datetime.now().hour < 1 + return False else: - return datetime.datetime.now().hour >= 10 or datetime.datetime.now().hour < 1 + return datetime.datetime.now().hour >= 12 or datetime.datetime.now().hour < 1 def heartbeat(): @@ -203,4 +210,97 @@ def get_hero_chinese_name(english_name): } # 将英文名转换为小写并去除空格,用作字典键 key = english_name.lower().replace(' ', '_') - return hero_name_map.get(key, english_name) \ No newline at end of file + return hero_name_map.get(key, english_name) + + +def upload_image(image_path, file_name=None): + """ + 将图片上传到 Cloudflare R2 存储桶并返回访问URL + + Args: + image_path (str): 本地图片文件路径 + + Returns: + str: 上传成功后的图片URL,失败则返回None + """ + + try: + # 读取配置文件 + config = configparser.ConfigParser() + config_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'env.ini') + if not os.path.exists(config_path): + logger.error(f"配置文件不存在: {config_path}") + return None + + config.read(config_path) + + # 获取Cloudflare R2配置 + if 'cloudflare' not in config: + logger.error("配置文件中缺少cloudflare部分") + return None + + cf_config = config['cloudflare'] + account_id = cf_config.get('account_id') + access_key_id = cf_config.get('access_key_id') + secret_access_key = cf_config.get('secret_access_key') + bucket_name = cf_config.get('bucket_name', 'dotabot-images') + region = cf_config.get('region', 'auto') + custom_domain = cf_config.get('custom_domain', '') + + # 检查必要的配置 + if not all([account_id, access_key_id, secret_access_key]): + logger.error("缺少Cloudflare R2必要配置") + return None + + # 创建S3客户端连接到Cloudflare R2 + s3_client = boto3.client( + 's3', + endpoint_url=f'https://{account_id}.r2.cloudflarestorage.com', + aws_access_key_id=access_key_id, + aws_secret_access_key=secret_access_key, + region_name=region, + config=Config( + signature_version='s3v4', + retries={ + 'max_attempts': 3, + 'mode': 'standard' + } + ) + ) + + # 生成唯一的文件名 + if not file_name: + timestamp = datetime.now().strftime('%Y%m%d%H%M%S') + random_id = str(uuid.uuid4())[:8] + file_extension = os.path.splitext(image_path)[1] + object_key = f'dotabot/{timestamp}_{random_id}{file_extension}' + else: + object_key = f'dotabot/{file_name}' + + # 设置文件的Content-Type + content_type = 'image/png' if file_extension.lower() == '.png' else 'image/jpeg' + + # 上传文件 + with open(image_path, 'rb') as file_data: + s3_client.upload_fileobj( + file_data, + bucket_name, + object_key, + ExtraArgs={ + 'ContentType': content_type, + 'CacheControl': 'max-age=2592000' # 30天缓存 + } + ) + + # 构建公共访问URL + if custom_domain: + image_url = f'https://{custom_domain}/{object_key}' + else: + image_url = f'https://{bucket_name}.{account_id}.r2.cloudflarestorage.com/{object_key}' + + logger.info(f"图片上传成功: {image_url}") + return image_url + + except Exception as e: + logger.error(f"上传图片失败: {str(e)}") + return None \ No newline at end of file