def parse_magnet(self, row, row_tag='<tr'): magnet_links = [] def build_magnet(matches): if len(matches) == 2: return [self.magnet_template % (matches[0], matches[1])] return [] if row_tag == '<tr': magnet_links = re.findall(r'(magnet:\?.*?&dn=.*?)[&"]', row) if len(magnet_links) == 0: # lime matches = safe_list_get( re.findall(r'\/([0-9a-zA-Z]*).torrent\?title=(.*?)"', row), 0, []) magnet_links = build_magnet(matches) elif row_tag == '<dl': # torrentz2 matches = safe_list_get( re.findall(r'href=\/([0-9a-zA-Z]*)>(.*?)<', row), 0, []) magnet_links = build_magnet(matches) if len(magnet_links) > 0: return safe_list_get(magnet_links, 0) return None
def parse_seeds(self, row): seeds = safe_list_get(re.findall(r'Seeders:?\s*?(\d+)', row), 0) if seeds == '': seeds = safe_list_get(re.findall(r'Seed:?\s*?(\d+)', row), 0) if seeds == '': seeds = self._parse_number(row, -2) if seeds == 'N/A': seeds = '0' return seeds
def parse_size(self, row): size = safe_list_get(re.findall(r'(\d+\.?\d*\s*[GM]i?B)', row), 0) \ .replace('GiB', 'GB') \ .replace('MiB', 'MB') if size == '': # bitlord size = self._parse_number(row, -3) if size is not '': size += ' MB' return size
def _parse_torrent(self, row, row_tag): magnet_link = self.parse_magnet(row, row_tag) if magnet_link is not None: torrent = lambda: None torrent.magnet = magnet_link torrent.title = safe_list_get(torrent.magnet.split('dn='), 1) torrent.size = self.parse_size(row) torrent.seeds = self.parse_seeds(row) return torrent return None
def _parse_number(self, row, idx): number = safe_list_get(re.findall(r'>\s*?(\d+)\s*?<', row)[idx:], 0) return number
def __init__(self, *, player, raw_profile_data, load_all): self.player = player self.profile_data = raw_profile_data['members'][player.uuid] self.id = raw_profile_data['profile_id'] self.name = raw_profile_data['cute_name'].lower() self.last_save = datetime.fromtimestamp(self.profile_data.get('last_save', 0) / 1000.0) self.join_date = datetime.fromtimestamp(self.profile_data.get('first_join', 0) / 1000.0) self.purse = float(f'{self.profile_data.get("coin_purse", 0.00):.2f}') # Get coop members uuid and coop crafted minions if there is self.coop_members = [] if len(raw_profile_data['members']) > 1: minions = [] for member in raw_profile_data['members'].keys(): minions += raw_profile_data['members'][member].get('crafted_generators', []) if member != player.uuid: self.coop_members.append(member) self.minions = self._parse_collection(minions) else: self.minions = self._parse_collection(self.profile_data.get('crafted_generators', [])) self.enabled_api = {'skills': False, 'collection': False, 'inventory': False, 'banking': False} self.current_armor = {'helmet': None, 'chestplate': None, 'leggings': None, 'boots': None} self.armor = {'helmet': None, 'chestplate': None, 'leggings': None, 'boots': None} self.weapon = None self.pet = None # Dungeon skill level, will be set if there is a dungeon item self.dungeon_skill = 0 # Load profile's bank data if banking api is enabled self.bank_balance = 0.00 self.bank_transactions = None if 'banking' in raw_profile_data: self.enabled_api['banking'] = True self.bank_balance = float(f'{raw_profile_data["banking"].get("balance", 0.00):.2f}') self.bank_transactions = raw_profile_data['banking'].get('transactions', []) # Loads profile's skill skills api is enabled self.skills_xp = {} self.skills = {} for skill in SKILL_NAMES: if f'experience_skill_{skill}' in self.profile_data: self.enabled_api['skills'] = True xp = int(self.profile_data.get(f'experience_skill_{skill}', 0)) self.skills_xp[skill] = xp self.skills[skill] = level_from_xp_table( xp, RUNECRAFTING_LEVEL_REQUIREMENT if skill == 'runecrafting' else SKILL_LEVEL_REQUIREMENT ) self.skill_average = sum( skill_level for skill, skill_level in self.skills.items() if skill not in COSMETIC_SKILL_NAMES) / ( len(SKILL_NAMES) - len(COSMETIC_SKILL_NAMES)) # Load profile's slayer data self.slayers_xp = {} self.slayers = {} for slayer_name in SLAYER_NAMES: xp = self.profile_data.get('slayer_bosses', {}).get(slayer_name, {}).get('xp', 0) self.slayers_xp[slayer_name] = xp self.slayers[slayer_name] = level_from_xp_table(xp, SLAYER_LEVEL_REQUIREMENT[slayer_name]) self.total_slayer_xp = sum(self.slayers_xp.values()) # Loads profile's kills and deaths self.kills = int(self.profile_data.get('stats', {'kills': 0}).get('kills', 0)) self.specifc_kills = {name.replace('kills_', '').replace('_', ' '): int(amount) for name, amount in self.profile_data.get('stats', {}).items() if re.match('kills_', name)} self.deaths = int(self.profile_data.get('stats', {'deaths': 0}).get('deaths', 0)) self.specifc_deaths = {name.replace('deaths_', '').replace('_', ' '): int(amount) for name, amount in self.profile_data.get('stats', {}).items() if re.match('deaths_', name)} # Loads profile's collections if collection api is enabled try: # TODO: revise to get exactly collection name instead of collection's icon name self.collections = {collection.lower().replace('_', ' '): amount for collection, amount in self.profile_data['collection'].items()} self.enabled_api['collection'] = True except KeyError: self.collections = {} # Load profile's minion slots self.unlocked_collections = self._parse_collection(self.profile_data.get('unlocked_coll_tiers', [])) self.unique_minions = sum(self.minions.values()) self.minion_slots = level_from_xp_table(self.unique_minions, MINION_SLOT_REQUIREMENT) # Load profile stats self.stats = ProfileStats(BASE_PLAYER_STATS.copy(), profile=self) self.stats.combat_bonus = self.skills.get('combat', 0) * 4 self.fairy_souls = self.profile_data.get('fairy_souls_collected', 0) self.stats.add_stat('health', FAIRY_SOUL_HP_BONUS[self.fairy_souls // 5]) self.stats.add_stat('defense', self.fairy_souls // 5 + self.fairy_souls // 25) self.stats.add_stat('strength', self.fairy_souls // 5 + self.fairy_souls // 25) self.stats.add_stat('speed', self.fairy_souls // 50) for slayer_name, slayer_level in self.slayers.items(): self.stats += ProfileStats(SLAYER_REWARDS[slayer_name][slayer_level]) for skill_name, skill_level in self.skills.items(): self.stats += ProfileStats(SKILL_REWARDS[skill_name][skill_level]) # Load profile's current equipped armor for armor in self._parse_inventory(self.profile_data, ['inv_armor', 'data']): # check for special type if armor.type == 'hatccessory': self.current_armor['helmet'] = armor else: self.current_armor[armor.type] = armor # Load profile's inventories self.inventory = self._parse_inventory(self.profile_data, ['inv_contents', 'data']) self.echest = self._parse_inventory(self.profile_data, ['ender_chest_contents', 'data']) self.talisman_bag = self._parse_inventory(self.profile_data, ['talisman_bag', 'data']) # Comment out unnecessary inventories for now # self.candy_bag = self._parse_inventory(self.profile_raw_data, ['candy_inventory_contents', 'data']) # self.potion_bag = self._parse_inventory(self.profile_raw_data, ['potion_bag', 'data']) # self.fish_bag = self._parse_inventory(self.profile_raw_data, ['fishing_bag', 'data']) # self.quiver = self._parse_inventory(self.profile_raw_data, ['quiver', 'data']) if self.inventory or self.echest or self.talisman_bag: self.enabled_api['inventory'] = True # Load profile's weapon from inventory and ender chest self.weapons = [item for item in self.inventory + self.echest if item.type in ('sword', 'bow')] # Load profile's wardrobe self.wardrobe = [] wardrobe = self._parse_inventory(self.profile_data, ['wardrobe_contents', 'data'], index_empty=True) for wardrobe_page in range(0, 2 * 36, 36): # 2 for current max 2 pages of wardrobe for i in range(wardrobe_page, wardrobe_page + 9): armor_set = { 'helmet': safe_list_get(wardrobe, i), 'chestplate': safe_list_get(wardrobe, i + 9), 'leggings': safe_list_get(wardrobe, i + 18), 'boots': safe_list_get(wardrobe, i + 27) } if all(item is None for item in armor_set.values()): continue self.wardrobe.append(armor_set) # Check if player has dungeon armor/wep items self.has_dungeon_items = False for item in self.inventory + self.echest + wardrobe: if not item: continue if item.dungeon: self.has_dungeon_items = True break self.loaded_all = load_all if load_all: # Load profile's talismans from inventory + talisman bag and talisman counts self.talismans = [talisman for talisman in self.inventory + self.talisman_bag if talisman.type in ('accessory', 'hatccessory')] self.talisman_counts = {'common': 0, 'uncommon': 0, 'rare': 0, 'epic': 0, 'legendary': 0, 'mythic': 0} talismans_dup = [] for talisman in self.talismans: talisman.active = True # Check for talisman families if talisman.internal_name in TIERED_TALISMANS: for other_fam_member in TIERED_TALISMANS[talisman.internal_name]: if other_fam_member in self.talismans: talisman.active = False break if not talisman.active: continue # if talisman active already set to false due to families # Check for duplicate talismans if self.talismans.count(talisman) > 1: talisman.active = False if talisman not in talismans_dup: talismans_dup.append(talisman) # append the reference to a list to set active true later # Set one of the talisman dup to active true for talisman in talismans_dup: talisman.active = True # Load talisman stats self.stats.childrens.extend([talisman.stats for talisman in self.talismans if talisman.active]) # Load talisman counts for taliman in self.talismans: if taliman.active: # Check for hypixel language because it's most reliable here try: self.talisman_counts[taliman.rarity] += 1 except KeyError: raise HypixelLanguageError from None # Loads profile's pets self.pets = [] if 'pets' in self.profile_data: for pet_data in self.profile_data['pets']: self.pets.append(Pet(pet_data, profile=self))