import os from PIL import Image, ImageDraw, ImageFont, ImageOps import json import user import colorsys import logging import math import urllib.request from datetime import datetime from io import BytesIO from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton from utils import mask_email, mask_account_id, bool_to_emoji, country_to_flag from user import RiftUser from cosmetic import FortniteCosmetic from epic_auth import EpicUser, EpicEndpoints, EpicGenerator, LockerData def escape_markdown(text): """ Escapes special characters for markdown formatting @note: sometimes external connections shows error due to these, so we handle it that way """ escape_chars = ['_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!'] for char in escape_chars: text = text.replace(char, f"\\{char}") return text class FortniteCache: def __init__(self): self.cache = {} self.cache_dir = "cache" if not os.path.exists(self.cache_dir): os.makedirs(self.cache_dir) self.load_cache_from_directory() def load_cache_from_directory(self): for filename in os.listdir(self.cache_dir): if filename.endswith(".png"): id = os.path.splitext(filename)[0] file_path = os.path.join(self.cache_dir, filename) try: image = Image.open(file_path).convert('RGBA') self.cache[id] = image except Exception as e: continue def get_cosmetic_icon_from_cache(self, url, id): if not url: print(f"Error: No URL provided for ID: {id}") return None cache_path = os.path.join(self.cache_dir, f"{id}.png") if id in self.cache: return self.cache[id] if os.path.exists(cache_path): # getting the icon from filesystem try: image = Image.open(cache_path).convert('RGBA') self.cache[id] = image return image except Exception as e: print(f"Error loading {cache_path}: {e}") try: # downloading the icon from url with urllib.request.urlopen(url) as response: image_data = response.read() image = Image.open(BytesIO(image_data)).convert('RGBA') try: image.save(cache_path) except Exception as e: print(f"Error saving {cache_path}: {e}") self.cache[id] = image return image except Exception as e: print(f"Error downloading image from {url}: {e}") return None # global members fortnite_cache = FortniteCache() available_styles = [ {"ID": 0, "name": "Rift", "image": "img/styles/rift.png"}, {"ID": 1, "name": "Easy", "image": "img/styles/easy.png"}, {"ID": 2, "name": "Raika", "image": "img/styles/raika.png"}, {"ID": 3, "name": "kayy", "image": "img/styles/kayy.png"}, {"ID": 4, "name": "Storm", "image": "img/styles/storm.png"} ] avaliable_badges = [ {"name": "Alpha Tester 1", "data": "alpha_tester_1_badge", "data2": "alpha_tester_1_badge_active", "image": "badges/icon/alpha1.png"}, {"name": "Alpha Tester 2", "data": "alpha_tester_2_badge", "data2": "alpha_tester_2_badge_active", "image": "badges/icon/alpha2.png"}, {"name": "Alpha Tester 3", "data": "alpha_tester_3_badge", "data2": "alpha_tester_3_badge_active", "image": "badges/icon/alpha3.png"}, {"name": "Epic Games", "data": "epic_badge", "data2": "epic_badge_active", "image": "badges/icon/epic.png"} ] locker_categories = ['AthenaCharacter', 'AthenaBackpack', 'AthenaPickaxe', 'AthenaDance', 'AthenaGlider', 'AthenaPopular', 'AthenaExclusive'] # global members def draw_gradient_text(gradient_type, draw, position, text, font, fill=(255, 255, 255)): """ Draw text with a rainbow gradient at a given position. :param gradient_type: The gradient we use to render the text as. :param draw: ImageDraw object to draw on. :param position: Tuple (x, y) of the position where the text starts. :param text: Text to draw. :param font: Font object to use for the text. :param fill: the color in RGB in base to draw """ num_colors = len(text) if gradient_type == 0: # white text(no gradient) gradient_colors = [(255, 255, 255)] * num_colors elif gradient_type == 1: # rainbow gradient gradient_colors = [ tuple(int(c * 255) for c in colorsys.hsv_to_rgb(i / num_colors, 1, 1)) for i in range(num_colors) ] elif gradient_type == 2: # golden gradient gradient_colors = [ tuple(int(c * 255) for c in colorsys.hsv_to_rgb(0.13, 0.5 + (i / num_colors) * 0.5, 0.8 + (i / num_colors) * 0.2)) for i in range(num_colors) ] elif gradient_type == 3: # silver gradient gradient_colors = [ tuple(int(c * 255) for c in colorsys.hsv_to_rgb(0, 0 + (i / num_colors) * 0.2, 0.6 + (i / num_colors) * 0.4)) for i in range(num_colors) ] x, y = position for i, char in enumerate(text): color = gradient_colors[i] char_width = font.getbbox(char)[2] draw.text((x, y), char, font=font, fill=color) x += char_width def render_rift_style(header:str, user_data: json, arr: list[str], nametosave:str) -> None: # calculating cosmetics per row cosmetic_per_row = 6 total_cosmetics = len(arr) num_rows = math.ceil(total_cosmetics / cosmetic_per_row) if total_cosmetics > 30: num_rows = int(math.sqrt(total_cosmetics)) cosmetic_per_row = math.ceil(total_cosmetics / num_rows) while cosmetic_per_row * num_rows < total_cosmetics: num_rows += 1 cosmetic_per_row = math.ceil(total_cosmetics / num_rows) # setup for our image, thumbnails padding = 30 thumbnail_width = 128 thumbnail_height = 128 image_width = int(cosmetic_per_row * thumbnail_width) image_height = int(thumbnail_height + 5 + thumbnail_width * num_rows + 180) font_path = 'styles/rift/font.ttf' font_size = 16 font = ImageFont.truetype(font_path, font_size) image = Image.new('RGB', (image_width, image_height), (0, 0, 0)) # custom background custom_background_path = f"users/backgrounds/{user_data['ID']}.png" if os.path.isfile(custom_background_path): custom_background = Image.open(custom_background_path).resize((image_width, image_height), Image.Resampling.BILINEAR) image.paste(custom_background, (0, 0)) current_row = 0 current_column = 0 sortarray = ['mythic', 'legendary', 'dark', 'slurp', 'starwars', 'marvel', 'lava', 'frozen', 'gaminglegends', 'shadow', 'icon', 'dc', 'epic', 'rare', 'uncommon', 'common'] arr.sort(key=lambda x: sortarray.index(x.rarity_value)) # had some issues with exclusives rendering in wrong order, so i'm sorting them try: with open('exclusive.txt', 'r', encoding='utf-8') as f: exclusive_cosmetics = [i.strip() for i in f.readlines()] with open('most_wanted.txt', 'r', encoding='utf-8') as f: popular_cosmetics = [i.strip() for i in f.readlines()] except FileNotFoundError: print("Error: 'exclusive.txt' or 'most_wanted.txt' not found.") exclusive_cosmetics = [] popular_cosmetics = [] mythic_items = [item for item in arr if item.rarity_value == 'mythic'] other_items = [item for item in arr if item.rarity_value != 'mythic'] mythic_items.sort( key=lambda x: exclusive_cosmetics.index(x.cosmetic_id) if x.cosmetic_id in exclusive_cosmetics else float('inf') ) if header == "Popular": other_items.sort( key=lambda x: popular_cosmetics.index(x.cosmetic_id) if x.cosmetic_id in popular_cosmetics else float('inf') ) arr = mythic_items + other_items draw = ImageDraw.Draw(image) # top icon_logo = Image.open(f'cosmetic_icons/{header}.png') icon_logo.thumbnail((thumbnail_width, thumbnail_height)) image.paste(icon_logo, (5, 0), mask=icon_logo) draw.text((thumbnail_width + 12, 14), '{}'.format(len(arr)), font=ImageFont.truetype(font_path, 70), fill=(0, 0, 0)) # shadow draw.text((thumbnail_width + 12, 82), '{}'.format(header), font=ImageFont.truetype(font_path, 40), fill=(0, 0, 0)) # shadow draw.text((thumbnail_width + 8, 10), '{}'.format(len(arr)), font=ImageFont.truetype(font_path, 70), fill=(255, 255, 255)) draw.text((thumbnail_width + 8, 78), '{}'.format(header), font=ImageFont.truetype(font_path, 40), fill=(200, 200, 200)) special_items = { "CID_029_Athena_Commando_F_Halloween": "cache/pink_ghoul.png", "CID_030_Athena_Commando_M_Halloween": "cache/purple_skull.png", "CID_116_Athena_Commando_M_CarbideBlack": "cache/omega_max.png", "CID_694_Athena_Commando_M_CatBurglar": "cache/gold_midas.png", "CID_693_Athena_Commando_M_BuffCat": "cache/gold_cat.png", "CID_691_Athena_Commando_F_TNTina": "cache/gold_tntina.png", "CID_690_Athena_Commando_F_Photographer": "cache/gold_skye.png", "CID_701_Athena_Commando_M_BananaAgent": "cache/gold_peely.png", "CID_315_Athena_Commando_M_TeriyakiFish": "cache/worldcup_fish.png", "CID_971_Athena_Commando_M_Jupiter_S0Z6M": "cache/black_masterchief.png", "CID_028_Athena_Commando_F": "cache/og_rene.png", "CID_017_Athena_Commando_M": "cache/og_aat.png" } for cosmetic in arr: special_icon = False is_banner = cosmetic.is_banner photo = None if cosmetic.rarity_value.lower() == "mythic" and cosmetic.cosmetic_id in special_items: special_icon = True icon_path = special_items[cosmetic.cosmetic_id] if os.path.exists(icon_path): try: photo = Image.open(icon_path) except Exception as e: special_icon = False else: special_icon = False else: photo = fortnite_cache.get_cosmetic_icon_from_cache(cosmetic.small_icon, cosmetic.cosmetic_id) if is_banner: scaled_width = int(photo.width * 1.5) scaled_height = int(photo.height * 1.5) photo = photo.resize((scaled_width, scaled_height), Image.Resampling.LANCZOS) x_offset = 32 y_offset = 10 new_img = Image.open(f'styles/rift/rarity/{cosmetic.rarity_value.lower()}.png').convert('RGBA') new_img.paste(photo, (x_offset, y_offset), mask=photo) photo = new_img photo.thumbnail((thumbnail_width, thumbnail_height)) else: new_img = Image.open(f'styles/rift/rarity/{cosmetic.rarity_value.lower()}.png').convert('RGBA').resize(photo.size) new_img.paste(photo, mask=photo) photo = new_img photo.thumbnail((thumbnail_width, thumbnail_height)) # black box for cosmetic name box = Image.new('RGBA', (128, 28), (0, 0, 0, 100)) photo.paste(box, (0, new_img.size[1] - 28), mask=box) if header != "Exclusives" and cosmetic.cosmetic_id in popular_cosmetics: star_image = Image.open('cosmetic_icons/WantedStar.png').resize((128, 128), Image.BILINEAR) photo.paste(star_image, (0, 0), star_image.convert("RGBA")) x = thumbnail_width * current_column y = thumbnail_width + thumbnail_height * current_row image.paste(photo, (x, y)) name = cosmetic.name.upper() max_text_width = thumbnail_width - 10 max_text_height = 20 # fixed font size font_size = 16 offset = 9 while True: font = ImageFont.truetype(font_path, font_size) bbox = draw.textbbox((0, 0), name, font=font) name_width = bbox[2] - bbox[0] name_height = bbox[3] - bbox[1] if name_width > max_text_width or name_height > max_text_height: font_size -= 1 offset += 0.5 else: break # cosmetic name bbox = draw.textbbox((0, 0), name, font=font) name_width = bbox[2] - bbox[0] draw.text((x + (thumbnail_width - name_width) // 2, y + (thumbnail_height - padding + offset)), name, font=font, fill=(255, 255, 255)) # make the cosmetics show ordered in rows(cosmetic_per_row is hardcoded) current_column += 1 if current_column >= cosmetic_per_row: current_row += 1 current_column = 0 # footer current_date = datetime.now().strftime('%B %d, %Y') # custom logo custom_logo_path = f"users/logos/{user_data['ID']}.png" if os.path.isfile(custom_logo_path): custom_logo = Image.open(custom_logo_path).resize((150, 150), Image.Resampling.BILINEAR) image.paste(custom_logo, (10, image_height - 165), mask=custom_logo) else: # original logo if not found logo = Image.open('img/logo.png').resize((150, 150), Image.Resampling.BILINEAR) image.paste(logo, (10, image_height - 165), mask=logo) draw.text((174, image_height - 40 * 3 - 24), '{}'.format(current_date), font=ImageFont.truetype(font_path, 40), fill=(0, 0, 0)) # shadow draw.text((170, image_height - 40 * 3 - 28), '{}'.format(current_date), font=ImageFont.truetype(font_path, 40), fill=(255, 255, 255)) draw.text((174, image_height - 40 * 2 - 24), '@{}'.format(user_data['username']), font=ImageFont.truetype(font_path, 40), fill=(0, 0, 0)) # shadow draw_gradient_text(user_data['gradient_type'], draw, (170, image_height - 40 * 2 - 28), '@{}'.format(user_data['username']), font=ImageFont.truetype(font_path, 40)) # badges font_size = 40 font = ImageFont.truetype(font_path, font_size) username_width = font.getbbox(f"@{user_data['username']}")[2] offset_badge = 170 + username_width + 8 if user_data['epic_badge_active'] == True and user_data['epic_badge'] == True: # epic games badge(special people only) alpha_badge = Image.open('badges/epic.png').resize((40, 40), Image.BILINEAR) image.paste(alpha_badge, (offset_badge, image_height - 40 * 2 - 28), alpha_badge.convert("RGBA")) offset_badge += 45 if user_data['alpha_tester_3_badge_active'] == True and user_data['alpha_tester_3_badge'] == True: # alpha tester 3 badge alpha_badge = Image.open('badges/alpha3.png').resize((40, 40), Image.BILINEAR) image.paste(alpha_badge, (offset_badge, image_height - 40 * 2 - 28), alpha_badge.convert("RGBA")) offset_badge += 45 if user_data['alpha_tester_2_badge_active'] == True and user_data['alpha_tester_2_badge'] == True: # alpha tester 2 badge alpha_badge = Image.open('badges/alpha2.png').resize((40, 40), Image.BILINEAR) image.paste(alpha_badge, (offset_badge, image_height - 40 * 2 - 28), alpha_badge.convert("RGBA")) offset_badge += 45 if user_data['alpha_tester_1_badge_active'] == True and user_data['alpha_tester_1_badge'] == True: # alpha tester 1 badge alpha_badge = Image.open('badges/alpha1.png').resize((40, 40), Image.BILINEAR) image.paste(alpha_badge, (offset_badge, image_height - 40 * 2 - 28), alpha_badge.convert("RGBA")) offset_badge += 45 draw.text((174, image_height - 61), "t.me/RiftCheckerBot", font=ImageFont.truetype(font_path, 40), fill=(0, 0, 0)) # shadow draw.text((170, image_height - 65), "t.me/RiftCheckerBot", font=ImageFont.truetype(font_path, 40), fill=(255, 255, 255)) image.save(nametosave) def render_easy_style(header:str, user_data: json, arr: list[str], nametosave:str) -> None: # calculating cosmetics per row cosmetic_per_row = 6 total_cosmetics = len(arr) num_rows = math.ceil(total_cosmetics / cosmetic_per_row) if total_cosmetics > 30: num_rows = int(math.sqrt(total_cosmetics)) cosmetic_per_row = math.ceil(total_cosmetics / num_rows) while cosmetic_per_row * num_rows < total_cosmetics: num_rows += 1 cosmetic_per_row = math.ceil(total_cosmetics / num_rows) # setup for our image, thumbnails padding_height = 10 padding_width = 50 thumbnail_width = 128 thumbnail_height = 128 image_width = int(cosmetic_per_row * thumbnail_width + padding_width * 2) image_height = int(padding_width + (padding_height + thumbnail_width) * num_rows + padding_width * 2) font_path = 'styles/easy/font.ttf' font_size = 16 font = ImageFont.truetype(font_path, font_size) image = Image.new('RGB', (image_width, image_height), (58, 58, 58)) current_row = 0 current_column = 0 sortarray = ['mythic', 'legendary', 'dark', 'slurp', 'starwars', 'marvel', 'lava', 'frozen', 'gaminglegends', 'shadow', 'icon', 'dc', 'epic', 'rare', 'uncommon', 'common'] arr.sort(key=lambda x: sortarray.index(x.rarity_value)) # had some issues with exclusives rendering in wrong order, so i'm sorting them try: with open('exclusive.txt', 'r', encoding='utf-8') as f: exclusive_cosmetics = [i.strip() for i in f.readlines()] with open('most_wanted.txt', 'r', encoding='utf-8') as f: popular_cosmetics = [i.strip() for i in f.readlines()] except FileNotFoundError: print("Error: 'exclusive.txt' or 'most_wanted.txt' not found.") exclusive_cosmetics = [] popular_cosmetics = [] mythic_items = [item for item in arr if item.rarity_value == 'mythic'] other_items = [item for item in arr if item.rarity_value != 'mythic'] mythic_items.sort( key=lambda x: exclusive_cosmetics.index(x.cosmetic_id) if x.cosmetic_id in exclusive_cosmetics else float('inf') ) if header == "Popular": other_items.sort( key=lambda x: popular_cosmetics.index(x.cosmetic_id) if x.cosmetic_id in popular_cosmetics else float('inf') ) arr = mythic_items + other_items draw = ImageDraw.Draw(image) # cosmetics count cosmetics = '{} count: {}'.format(header, len(arr)) max_text_width = image_width - 20 max_text_height = 40 min_font_size = 10 font_size = 40 while True: bbox = draw.textbbox((0, 0), cosmetics, font=font) text_width = bbox[2] - bbox[0] text_height = bbox[3] - bbox[1] if text_width > max_text_width or font_size > image_height - 2: font_size -= 1 if font_size < min_font_size: font_size = min_font_size break font = ImageFont.truetype(font_path, font_size) else: break font = ImageFont.truetype(font_path, font_size) bbox = draw.textbbox((0, 0), cosmetics, font=font) text_width = bbox[2] - bbox[0] text_x = (image_width - text_width) // 2 draw.text((text_x, 9), cosmetics, font=font, fill=(255, 255, 255)) special_items = { "CID_029_Athena_Commando_F_Halloween": "cache/pink_ghoul.png", "CID_030_Athena_Commando_M_Halloween": "cache/purple_skull_old.png", "CID_116_Athena_Commando_M_CarbideBlack": "cache/omega_max.png", "CID_694_Athena_Commando_M_CatBurglar": "cache/gold_midas.png", "CID_693_Athena_Commando_M_BuffCat": "cache/gold_cat.png", "CID_691_Athena_Commando_F_TNTina": "cache/gold_tntina.png", "CID_690_Athena_Commando_F_Photographer": "cache/gold_skye.png", "CID_701_Athena_Commando_M_BananaAgent": "cache/gold_peely.png", "CID_315_Athena_Commando_M_TeriyakiFish": "cache/worldcup_fish.png", "CID_971_Athena_Commando_M_Jupiter_S0Z6M": "cache/black_masterchief.png", "CID_028_Athena_Commando_F": "cache/og_rene.png", "CID_017_Athena_Commando_M": "cache/og_aat.png" } for cosmetic in arr: special_icon = False is_banner = cosmetic.is_banner photo = None if cosmetic.rarity_value.lower() == "mythic" and cosmetic.cosmetic_id in special_items: special_icon = True icon_path = special_items[cosmetic.cosmetic_id] if os.path.exists(icon_path): try: photo = Image.open(icon_path) except Exception as e: special_icon = False else: special_icon = False else: photo = fortnite_cache.get_cosmetic_icon_from_cache(cosmetic.small_icon, cosmetic.cosmetic_id) if is_banner: scaled_width = int(photo.width * 1.5) scaled_height = int(photo.height * 1.5) photo = photo.resize((scaled_width, scaled_height), Image.Resampling.LANCZOS) x_offset = 32 y_offset = 10 new_img = Image.open(f'styles/easy/rarity/{cosmetic.rarity_value.lower()}.png').convert('RGBA') new_img.paste(photo, (x_offset, y_offset), mask=photo) photo = new_img photo.thumbnail((thumbnail_width, thumbnail_height)) else: new_img = Image.open(f'styles/easy/rarity/{cosmetic.rarity_value.lower()}.png').convert('RGBA').resize(photo.size) new_img.paste(photo, mask=photo) photo = new_img photo.thumbnail((thumbnail_width, thumbnail_height)) # black box for cosmetic name box = Image.new('RGBA', (128, 20), (0, 0, 0, 75)) photo.paste(box, (0, new_img.size[1] - 40), mask=box) border = Image.open(f'styles/easy/border/{cosmetic.rarity_value.lower()}.png').convert('RGBA').resize(photo.size) photo.paste(border, mask=border) x = padding_width + thumbnail_width * current_column y = padding_width + (thumbnail_height + padding_height) * current_row image.paste(photo, (x, y)) name = cosmetic.name max_text_width = thumbnail_width - 10 max_text_height = 20 offset_y = 38 font_size = 18 while True: font = ImageFont.truetype(font_path, font_size) bbox = draw.textbbox((0, 0), name, font=font) name_width = bbox[2] - bbox[0] name_height = bbox[3] - bbox[1] if name_width > max_text_width or name_height > max_text_height: font_size -= 1 offset_y -= 0.5 else: break # cosmetic name bbox = draw.textbbox((0, 0), name, font=font) name_width = bbox[2] - bbox[0] draw.text((x + (thumbnail_width - name_width) // 2, y + (thumbnail_height - offset_y)), name, font=font, fill=(255, 255, 255)) # make the cosmetics show ordered in rows(cosmetic_per_row is hardcoded) current_column += 1 if current_column >= cosmetic_per_row: current_row += 1 current_column = 0 # footer current_date = datetime.now().strftime('%d/%m/%Y') footer = 'Submitted by @{} on {}'.format(user_data['username'], current_date) max_text_width = image_width - 20 max_text_height = 40 min_font_size = 15 font_size = 40 while True: bbox = draw.textbbox((0, 0), footer, font=font) text_width = bbox[2] - bbox[0] text_height = bbox[3] - bbox[1] if text_width > max_text_width or font_size > image_height - 2: font_size -= 1 if font_size < min_font_size: font_size = min_font_size break font = ImageFont.truetype(font_path, font_size) else: break font = ImageFont.truetype(font_path, font_size) bbox = draw.textbbox((0, 0), footer, font=font) text_width = bbox[2] - bbox[0] text_x = (image_width - text_width) // 2 text_y = image_height - font_size - 52 draw.text((text_x, text_y), footer, font=font, fill=(255, 255, 255)) # bot advertisment footer2 = 't.me/RiftCheckerBot' max_text_width = image_width - 20 max_text_height = 40 min_font_size = 15 font_size = 40 while True: bbox = draw.textbbox((0, 0), footer2, font=font) text_width = bbox[2] - bbox[0] text_height = bbox[3] - bbox[1] if text_width > max_text_width or font_size > image_height - 2: font_size -= 1 if font_size < min_font_size: font_size = min_font_size break font = ImageFont.truetype(font_path, font_size) else: break font = ImageFont.truetype(font_path, font_size) bbox = draw.textbbox((0, 0), footer2, font=font) text_width = bbox[2] - bbox[0] text_x = (image_width - text_width) // 2 text_y = image_height - font_size - 8 draw.text((text_x, text_y), footer2, font=font, fill=(255, 255, 255)) image.save(nametosave) def render_raika_style(header:str, user_data: json, arr: list[str], nametosave:str) -> None: # calculating cosmetics per row cosmetic_per_row = 6 total_cosmetics = len(arr) num_rows = math.ceil(total_cosmetics / cosmetic_per_row) if total_cosmetics > 30: num_rows = int(math.sqrt(total_cosmetics)) cosmetic_per_row = math.ceil(total_cosmetics / num_rows) while cosmetic_per_row * num_rows < total_cosmetics: num_rows += 1 cosmetic_per_row = math.ceil(total_cosmetics / num_rows) # setup for our image, thumbnails padding = 30 thumbnail_width = 128 thumbnail_height = 128 image_width = int(cosmetic_per_row * thumbnail_width) image_height = int(thumbnail_height + 5 + thumbnail_width * num_rows + 180) font_path = 'styles/raika/font.ttf' font_size = 16 font = ImageFont.truetype(font_path, font_size) image = Image.new('RGB', (image_width, image_height), (0, 0, 0)) # custom background custom_background_path = f"users/backgrounds/{user_data['ID']}.png" if os.path.isfile(custom_background_path): custom_background = Image.open(custom_background_path).resize((image_width, image_height), Image.Resampling.BILINEAR) image.paste(custom_background, (0, 0)) current_row = 0 current_column = 0 sortarray = ['mythic', 'legendary', 'dark', 'slurp', 'starwars', 'marvel', 'lava', 'frozen', 'gaminglegends', 'shadow', 'icon', 'dc', 'epic', 'rare', 'uncommon', 'common'] arr.sort(key=lambda x: sortarray.index(x.rarity_value)) # had some issues with exclusives rendering in wrong order, so i'm sorting them try: with open('exclusive.txt', 'r', encoding='utf-8') as f: exclusive_cosmetics = [i.strip() for i in f.readlines()] with open('most_wanted.txt', 'r', encoding='utf-8') as f: popular_cosmetics = [i.strip() for i in f.readlines()] except FileNotFoundError: print("Error: 'exclusive.txt' or 'most_wanted.txt' not found.") exclusive_cosmetics = [] popular_cosmetics = [] mythic_items = [item for item in arr if item.rarity_value == 'mythic'] other_items = [item for item in arr if item.rarity_value != 'mythic'] mythic_items.sort( key=lambda x: exclusive_cosmetics.index(x.cosmetic_id) if x.cosmetic_id in exclusive_cosmetics else float('inf') ) if header == "Popular": other_items.sort( key=lambda x: popular_cosmetics.index(x.cosmetic_id) if x.cosmetic_id in popular_cosmetics else float('inf') ) arr = mythic_items + other_items draw = ImageDraw.Draw(image) # top icon_logo = Image.open(f'cosmetic_icons/{header}.png') icon_logo.thumbnail((thumbnail_width, thumbnail_height)) image.paste(icon_logo, (5, 0), mask=icon_logo) draw.text((thumbnail_width + 12, 14), '{}'.format(len(arr)), font=ImageFont.truetype(font_path, 70), fill=(0, 0, 0)) # shadow draw.text((thumbnail_width + 12, 82), '{}'.format(header), font=ImageFont.truetype(font_path, 40), fill=(0, 0, 0)) # shadow draw.text((thumbnail_width + 8, 10), '{}'.format(len(arr)), font=ImageFont.truetype(font_path, 70), fill=(255, 255, 255)) draw.text((thumbnail_width + 8, 78), '{}'.format(header), font=ImageFont.truetype(font_path, 40), fill=(200, 200, 200)) special_items = { "CID_029_Athena_Commando_F_Halloween": "cache/pink_ghoul.png", "CID_030_Athena_Commando_M_Halloween": "cache/purple_skull_old.png", "CID_116_Athena_Commando_M_CarbideBlack": "cache/omega_max.png", "CID_694_Athena_Commando_M_CatBurglar": "cache/gold_midas.png", "CID_693_Athena_Commando_M_BuffCat": "cache/gold_cat.png", "CID_691_Athena_Commando_F_TNTina": "cache/gold_tntina.png", "CID_690_Athena_Commando_F_Photographer": "cache/gold_skye.png", "CID_701_Athena_Commando_M_BananaAgent": "cache/gold_peely.png", "CID_315_Athena_Commando_M_TeriyakiFish": "cache/worldcup_fish.png", "CID_971_Athena_Commando_M_Jupiter_S0Z6M": "cache/black_masterchief.png", "CID_028_Athena_Commando_F": "cache/og_rene.png", "CID_017_Athena_Commando_M": "cache/og_aat.png" } for cosmetic in arr: special_icon = False is_banner = cosmetic.is_banner photo = None if cosmetic.rarity_value.lower() == "mythic" and cosmetic.cosmetic_id in special_items: special_icon = True icon_path = special_items[cosmetic.cosmetic_id] if os.path.exists(icon_path): try: photo = Image.open(icon_path) except Exception as e: special_icon = False else: special_icon = False else: photo = fortnite_cache.get_cosmetic_icon_from_cache(cosmetic.small_icon, cosmetic.cosmetic_id) if is_banner: scaled_width = int(photo.width * 1.5) scaled_height = int(photo.height * 1.5) photo = photo.resize((scaled_width, scaled_height), Image.Resampling.LANCZOS) x_offset = 32 y_offset = 10 new_img = Image.open(f'styles/raika/rarity/{cosmetic.rarity_value.lower()}.png').convert('RGBA') new_img.paste(photo, (x_offset, y_offset), mask=photo) photo = new_img photo.thumbnail((thumbnail_width, thumbnail_height)) else: new_img = Image.open(f'styles/raika/rarity/{cosmetic.rarity_value.lower()}.png').convert('RGBA').resize(photo.size) new_img.paste(photo, mask=photo) photo = new_img photo.thumbnail((thumbnail_width, thumbnail_height)) # black box for cosmetic name box = Image.new('RGBA', (128, 28), (0, 0, 0, 100)) photo.paste(box, (0, new_img.size[1] - 28), mask=box) if header != "Exclusives" and cosmetic.cosmetic_id in popular_cosmetics: star_image = Image.open('cosmetic_icons/WantedStar.png').resize((128, 128), Image.BILINEAR) photo.paste(star_image, (0, 0), star_image.convert("RGBA")) x = thumbnail_width * current_column y = thumbnail_width + thumbnail_height * current_row image.paste(photo, (x, y)) name = cosmetic.name.upper() max_text_width = thumbnail_width - 10 max_text_height = 20 # fixed font size font_size = 16 offset = 6 while True: font = ImageFont.truetype(font_path, font_size) bbox = draw.textbbox((0, 0), name, font=font) name_width = bbox[2] - bbox[0] name_height = bbox[3] - bbox[1] if name_width > max_text_width or name_height > max_text_height: font_size -= 1 offset += 0.5 else: break # cosmetic name bbox = draw.textbbox((0, 0), name, font=font) name_width = bbox[2] - bbox[0] draw.text((x + (thumbnail_width - name_width) // 2, y + (thumbnail_height - padding + offset)), name, font=font, fill=(255, 255, 255)) # make the cosmetics show ordered in rows(cosmetic_per_row is hardcoded) current_column += 1 if current_column >= cosmetic_per_row: current_row += 1 current_column = 0 # footer current_date = datetime.now().strftime('%B %d, %Y') # custom logo custom_logo_path = f"users/logos/{user_data['ID']}.png" if os.path.isfile(custom_logo_path): custom_logo = Image.open(custom_logo_path).resize((150, 150), Image.Resampling.BILINEAR) image.paste(custom_logo, (10, image_height - 165), mask=custom_logo) else: # original logo if not found logo = Image.open('img/logo.png').resize((150, 150), Image.Resampling.BILINEAR) image.paste(logo, (10, image_height - 165), mask=logo) draw.text((174, image_height - 40 * 3 - 24), '{}'.format(current_date), font=ImageFont.truetype(font_path, 40), fill=(0, 0, 0)) # shadow draw.text((170, image_height - 40 * 3 - 28), '{}'.format(current_date), font=ImageFont.truetype(font_path, 40), fill=(255, 255, 255)) draw.text((174, image_height - 40 * 2 - 24), '@{}'.format(user_data['username']), font=ImageFont.truetype(font_path, 40), fill=(0, 0, 0)) # shadow draw_gradient_text(user_data['gradient_type'], draw, (170, image_height - 40 * 2 - 28), '@{}'.format(user_data['username']), font=ImageFont.truetype(font_path, 40)) # badges font_size = 40 font = ImageFont.truetype(font_path, font_size) username_width = font.getbbox(f"@{user_data['username']}")[2] offset_badge = 170 + username_width + 8 if user_data['epic_badge_active'] == True and user_data['epic_badge'] == True: # epic games badge(special people only) alpha_badge = Image.open('badges/epic.png').resize((40, 40), Image.BILINEAR) image.paste(alpha_badge, (offset_badge, image_height - 40 * 2 - 28), alpha_badge.convert("RGBA")) offset_badge += 45 if user_data['alpha_tester_3_badge_active'] == True and user_data['alpha_tester_3_badge'] == True: # alpha tester 3 badge alpha_badge = Image.open('badges/alpha3.png').resize((40, 40), Image.BILINEAR) image.paste(alpha_badge, (offset_badge, image_height - 40 * 2 - 28), alpha_badge.convert("RGBA")) offset_badge += 45 if user_data['alpha_tester_2_badge_active'] == True and user_data['alpha_tester_2_badge'] == True: # alpha tester 2 badge alpha_badge = Image.open('badges/alpha2.png').resize((40, 40), Image.BILINEAR) image.paste(alpha_badge, (offset_badge, image_height - 40 * 2 - 28), alpha_badge.convert("RGBA")) offset_badge += 45 if user_data['alpha_tester_1_badge_active'] == True and user_data['alpha_tester_1_badge'] == True: # alpha tester 1 badge alpha_badge = Image.open('badges/alpha1.png').resize((40, 40), Image.BILINEAR) image.paste(alpha_badge, (offset_badge, image_height - 40 * 2 - 28), alpha_badge.convert("RGBA")) offset_badge += 45 draw.text((174, image_height - 61), "t.me/RiftCheckerBot", font=ImageFont.truetype(font_path, 40), fill=(0, 0, 0)) # shadow draw.text((170, image_height - 65), "t.me/RiftCheckerBot", font=ImageFont.truetype(font_path, 40), fill=(255, 255, 255)) image.save(nametosave) def render_kayy_style(header:str, user_data: json, arr: list[str], nametosave:str) -> None: # calculating cosmetics per row cosmetic_per_row = 6 total_cosmetics = len(arr) num_rows = math.ceil(total_cosmetics / cosmetic_per_row) if total_cosmetics > 30: num_rows = int(math.sqrt(total_cosmetics)) cosmetic_per_row = math.ceil(total_cosmetics / num_rows) while cosmetic_per_row * num_rows < total_cosmetics: num_rows += 1 cosmetic_per_row = math.ceil(total_cosmetics / num_rows) # setup for our image, thumbnails padding = 30 thumbnail_width = 128 thumbnail_height = 128 image_width = int(cosmetic_per_row * thumbnail_width) image_height = int(thumbnail_width * num_rows + 180) font_path = 'styles/kayy/font.ttf' font_size = 16 font = ImageFont.truetype(font_path, font_size) image = Image.new('RGB', (image_width, image_height), (0, 0, 0)) # custom background custom_background_path = f"users/backgrounds/{user_data['ID']}.png" if os.path.isfile(custom_background_path): custom_background = Image.open(custom_background_path).resize((image_width, image_height), Image.Resampling.BILINEAR) image.paste(custom_background, (0, 0)) current_row = 0 current_column = 0 sortarray = ['mythic', 'legendary', 'dark', 'slurp', 'starwars', 'marvel', 'lava', 'frozen', 'gaminglegends', 'shadow', 'icon', 'dc', 'epic', 'rare', 'uncommon', 'common'] arr.sort(key=lambda x: sortarray.index(x.rarity_value)) # had some issues with exclusives rendering in wrong order, so i'm sorting them try: with open('exclusive.txt', 'r', encoding='utf-8') as f: exclusive_cosmetics = [i.strip() for i in f.readlines()] with open('most_wanted.txt', 'r', encoding='utf-8') as f: popular_cosmetics = [i.strip() for i in f.readlines()] except FileNotFoundError: print("Error: 'exclusive.txt' or 'most_wanted.txt' not found.") exclusive_cosmetics = [] popular_cosmetics = [] mythic_items = [item for item in arr if item.rarity_value == 'mythic'] other_items = [item for item in arr if item.rarity_value != 'mythic'] mythic_items.sort( key=lambda x: exclusive_cosmetics.index(x.cosmetic_id) if x.cosmetic_id in exclusive_cosmetics else float('inf') ) if header == "Popular": other_items.sort( key=lambda x: popular_cosmetics.index(x.cosmetic_id) if x.cosmetic_id in popular_cosmetics else float('inf') ) arr = mythic_items + other_items draw = ImageDraw.Draw(image) special_items = { "CID_029_Athena_Commando_F_Halloween": "cache/pink_ghoul.png", "CID_030_Athena_Commando_M_Halloween": "cache/purple_skull_old.png", "CID_116_Athena_Commando_M_CarbideBlack": "cache/omega_max.png", "CID_694_Athena_Commando_M_CatBurglar": "cache/gold_midas.png", "CID_693_Athena_Commando_M_BuffCat": "cache/gold_cat.png", "CID_691_Athena_Commando_F_TNTina": "cache/gold_tntina.png", "CID_690_Athena_Commando_F_Photographer": "cache/gold_skye.png", "CID_701_Athena_Commando_M_BananaAgent": "cache/gold_peely.png", "CID_315_Athena_Commando_M_TeriyakiFish": "cache/worldcup_fish.png", "CID_971_Athena_Commando_M_Jupiter_S0Z6M": "cache/black_masterchief.png", "CID_028_Athena_Commando_F": "cache/og_rene.png", "CID_017_Athena_Commando_M": "cache/og_aat.png" } for cosmetic in arr: special_icon = False is_banner = cosmetic.is_banner photo = None if cosmetic.rarity_value.lower() == "mythic" and cosmetic.cosmetic_id in special_items: special_icon = True icon_path = special_items[cosmetic.cosmetic_id] if os.path.exists(icon_path): try: photo = Image.open(icon_path) except Exception as e: special_icon = False else: special_icon = False else: photo = fortnite_cache.get_cosmetic_icon_from_cache(cosmetic.small_icon, cosmetic.cosmetic_id) if is_banner: scaled_width = int(photo.width * 1.5) scaled_height = int(photo.height * 1.5) photo = photo.resize((scaled_width, scaled_height), Image.Resampling.LANCZOS) x_offset = 32 y_offset = 10 new_img = Image.open(f'styles/kayy/rarity/{cosmetic.rarity_value.lower()}.png').convert('RGBA') new_img.paste(photo, (x_offset, y_offset), mask=photo) photo = new_img photo.thumbnail((thumbnail_width, thumbnail_height)) else: new_img = Image.open(f'styles/kayy/rarity/{cosmetic.rarity_value.lower()}.png').convert('RGBA').resize(photo.size) new_img.paste(photo, mask=photo) photo = new_img photo.thumbnail((thumbnail_width, thumbnail_height)) # black box for cosmetic name box = Image.new('RGBA', (128, 28), (0, 0, 0, 100)) photo.paste(box, (0, new_img.size[1] - 28), mask=box) if header != "Exclusives" and cosmetic.cosmetic_id in popular_cosmetics: star_image = Image.open('cosmetic_icons/WantedStar.png').resize((128, 128), Image.BILINEAR) photo.paste(star_image, (0, 0), star_image.convert("RGBA")) x = thumbnail_width * current_column y = thumbnail_height * current_row image.paste(photo, (x, y)) name = cosmetic.name.upper() max_text_width = thumbnail_width - 10 max_text_height = 20 # fixed font size font_size = 16 offset = 9 while True: font = ImageFont.truetype(font_path, font_size) bbox = draw.textbbox((0, 0), name, font=font) name_width = bbox[2] - bbox[0] name_height = bbox[3] - bbox[1] if name_width > max_text_width or name_height > max_text_height: font_size -= 1 offset += 0.5 else: break # cosmetic name bbox = draw.textbbox((0, 0), name, font=font) name_width = bbox[2] - bbox[0] draw.text((x + (thumbnail_width - name_width) // 2, y + (thumbnail_height - padding + offset)), name, font=font, fill=(255, 255, 255)) # make the cosmetics show ordered in rows(cosmetic_per_row is hardcoded) current_column += 1 if current_column >= cosmetic_per_row: current_row += 1 current_column = 0 # footer current_date = datetime.now().strftime('%d/%m/%Y') # custom logo custom_logo_path = f"users/logos/{user_data['ID']}.png" if os.path.isfile(custom_logo_path): custom_logo = Image.open(custom_logo_path).resize((150, 150), Image.Resampling.BILINEAR) image.paste(custom_logo, (10, image_height - 165), mask=custom_logo) else: # original logo if not found logo = Image.open('img/logo.png').resize((150, 150), Image.Resampling.BILINEAR) image.paste(logo, (10, image_height - 165), mask=logo) draw.text((174, image_height - 40 * 3 - 24), 'Objects Total: {}'.format(len(arr)), font=ImageFont.truetype(font_path, 40), fill=(255, 255, 255)) # shadow draw.text((170, image_height - 40 * 3 - 28), 'Objects Total: {}'.format(len(arr)), font=ImageFont.truetype(font_path, 40), fill=(255, 255, 255)) draw.text((174, image_height - 40 * 2 - 24), '@{}'.format(user_data['username']), font=ImageFont.truetype(font_path, 40), fill=(255, 255, 255)) # shadow draw_gradient_text(user_data['gradient_type'], draw, (170, image_height - 40 * 2 - 28), '@{}'.format(user_data['username']), font=ImageFont.truetype(font_path, 40)) # badges font_size = 40 font = ImageFont.truetype(font_path, font_size) username_width = font.getbbox(f"@{user_data['username']}")[2] offset_badge = 170 + username_width + 8 if user_data['epic_badge_active'] == True and user_data['epic_badge'] == True: # epic games badge(special people only) alpha_badge = Image.open('badges/epic.png').resize((40, 40), Image.BILINEAR) image.paste(alpha_badge, (offset_badge, image_height - 40 * 2 - 28), alpha_badge.convert("RGBA")) offset_badge += 45 if user_data['alpha_tester_3_badge_active'] == True and user_data['alpha_tester_3_badge'] == True: # alpha tester 3 badge alpha_badge = Image.open('badges/alpha3.png').resize((40, 40), Image.BILINEAR) image.paste(alpha_badge, (offset_badge, image_height - 40 * 2 - 28), alpha_badge.convert("RGBA")) offset_badge += 45 if user_data['alpha_tester_2_badge_active'] == True and user_data['alpha_tester_2_badge'] == True: # alpha tester 2 badge alpha_badge = Image.open('badges/alpha2.png').resize((40, 40), Image.BILINEAR) image.paste(alpha_badge, (offset_badge, image_height - 40 * 2 - 28), alpha_badge.convert("RGBA")) offset_badge += 45 if user_data['alpha_tester_1_badge_active'] == True and user_data['alpha_tester_1_badge'] == True: # alpha tester 1 badge alpha_badge = Image.open('badges/alpha1.png').resize((40, 40), Image.BILINEAR) image.paste(alpha_badge, (offset_badge, image_height - 40 * 2 - 28), alpha_badge.convert("RGBA")) offset_badge += 45 draw.text((offset_badge + 10, image_height - 40 * 2 - 26), '| {}'.format(current_date), font=ImageFont.truetype(font_path, 40), fill=(0, 0, 0)) # shadow draw.text((offset_badge + 8, image_height - 40 * 2 - 28), '| {}'.format(current_date), font=ImageFont.truetype(font_path, 40), fill=(255, 255, 255)) draw.text((174, image_height - 61), "t.me/RiftCheckerBot", font=ImageFont.truetype(font_path, 40), fill=(0, 0, 0)) # shadow draw.text((170, image_height - 65), "t.me/RiftCheckerBot", font=ImageFont.truetype(font_path, 40), fill=(255, 255, 255)) image.save(nametosave) def render_storm_style(header:str, user_data: json, arr: list[str], nametosave:str) -> None: # calculating cosmetics per row cosmetic_per_row = 6 total_cosmetics = len(arr) num_rows = math.ceil(total_cosmetics / cosmetic_per_row) if total_cosmetics > 30: num_rows = int(math.sqrt(total_cosmetics)) cosmetic_per_row = math.ceil(total_cosmetics / num_rows) while cosmetic_per_row * num_rows < total_cosmetics: num_rows += 1 cosmetic_per_row = math.ceil(total_cosmetics / num_rows) # setup for our image, thumbnails padding = 30 thumbnail_width = 128 thumbnail_height = 128 image_width = int(cosmetic_per_row * thumbnail_width) image_height = int(thumbnail_width * num_rows + 180) font_path = 'styles/storm/font.ttf' font_size = 16 font = ImageFont.truetype(font_path, font_size) image = Image.new('RGB', (image_width, image_height), (0, 0, 0)) # custom background custom_background_path = f"users/backgrounds/{user_data['ID']}.png" if os.path.isfile(custom_background_path): custom_background = Image.open(custom_background_path).resize((image_width, image_height), Image.Resampling.BILINEAR) image.paste(custom_background, (0, 0)) current_row = 0 current_column = 0 sortarray = ['mythic', 'legendary', 'dark', 'slurp', 'starwars', 'marvel', 'lava', 'frozen', 'gaminglegends', 'shadow', 'icon', 'dc', 'epic', 'rare', 'uncommon', 'common'] arr.sort(key=lambda x: sortarray.index(x.rarity_value)) # had some issues with exclusives rendering in wrong order, so i'm sorting them try: with open('exclusive.txt', 'r', encoding='utf-8') as f: exclusive_cosmetics = [i.strip() for i in f.readlines()] with open('most_wanted.txt', 'r', encoding='utf-8') as f: popular_cosmetics = [i.strip() for i in f.readlines()] except FileNotFoundError: print("Error: 'exclusive.txt' or 'most_wanted.txt' not found.") exclusive_cosmetics = [] popular_cosmetics = [] mythic_items = [item for item in arr if item.rarity_value == 'mythic'] other_items = [item for item in arr if item.rarity_value != 'mythic'] mythic_items.sort( key=lambda x: exclusive_cosmetics.index(x.cosmetic_id) if x.cosmetic_id in exclusive_cosmetics else float('inf') ) if header == "Popular": other_items.sort( key=lambda x: popular_cosmetics.index(x.cosmetic_id) if x.cosmetic_id in popular_cosmetics else float('inf') ) arr = mythic_items + other_items draw = ImageDraw.Draw(image) special_items = { "CID_029_Athena_Commando_F_Halloween": "cache/pink_ghoul.png", "CID_030_Athena_Commando_M_Halloween": "cache/purple_skull_old.png", "CID_116_Athena_Commando_M_CarbideBlack": "cache/omega_max.png", "CID_694_Athena_Commando_M_CatBurglar": "cache/gold_midas.png", "CID_693_Athena_Commando_M_BuffCat": "cache/gold_cat.png", "CID_691_Athena_Commando_F_TNTina": "cache/gold_tntina.png", "CID_690_Athena_Commando_F_Photographer": "cache/gold_skye.png", "CID_701_Athena_Commando_M_BananaAgent": "cache/gold_peely.png", "CID_315_Athena_Commando_M_TeriyakiFish": "cache/worldcup_fish.png", "CID_971_Athena_Commando_M_Jupiter_S0Z6M": "cache/black_masterchief.png", "CID_028_Athena_Commando_F": "cache/og_rene.png", "CID_017_Athena_Commando_M": "cache/og_aat.png" } for cosmetic in arr: special_icon = False is_banner = cosmetic.is_banner photo = None if cosmetic.rarity_value.lower() == "mythic" and cosmetic.cosmetic_id in special_items: special_icon = True icon_path = special_items[cosmetic.cosmetic_id] if os.path.exists(icon_path): try: photo = Image.open(icon_path) except Exception as e: special_icon = False else: special_icon = False else: photo = fortnite_cache.get_cosmetic_icon_from_cache(cosmetic.small_icon, cosmetic.cosmetic_id) if is_banner: scaled_width = int(photo.width * 1.5) scaled_height = int(photo.height * 1.5) photo = photo.resize((scaled_width, scaled_height), Image.Resampling.LANCZOS) x_offset = 32 y_offset = 10 new_img = Image.open(f'styles/storm/rarity/{cosmetic.rarity_value.lower()}.png').convert('RGBA') new_img.paste(photo, (x_offset, y_offset), mask=photo) photo = new_img photo.thumbnail((thumbnail_width, thumbnail_height)) else: new_img = Image.open(f'styles/storm/rarity/{cosmetic.rarity_value.lower()}.png').convert('RGBA').resize(photo.size) new_img.paste(photo, mask=photo) photo = new_img photo.thumbnail((thumbnail_width, thumbnail_height)) # black box for cosmetic name box = Image.new('RGBA', (128, 28), (0, 0, 0, 100)) photo.paste(box, (0, new_img.size[1] - 28), mask=box) if header != "Exclusives" and cosmetic.cosmetic_id in popular_cosmetics: star_image = Image.open('cosmetic_icons/WantedStar.png').resize((128, 128), Image.BILINEAR) photo.paste(star_image, (0, 0), star_image.convert("RGBA")) x = thumbnail_width * current_column y = thumbnail_height * current_row image.paste(photo, (x, y)) name = cosmetic.name.upper() max_text_width = thumbnail_width - 10 max_text_height = 20 # fixed font size font_size = 16 offset = 9 while True: font = ImageFont.truetype(font_path, font_size) bbox = draw.textbbox((0, 0), name, font=font) name_width = bbox[2] - bbox[0] name_height = bbox[3] - bbox[1] if name_width > max_text_width or name_height > max_text_height: font_size -= 1 offset += 0.5 else: break # cosmetic name bbox = draw.textbbox((0, 0), name, font=font) name_width = bbox[2] - bbox[0] draw.text((x + (thumbnail_width - name_width) // 2, y + (thumbnail_height - padding + offset)), name, font=font, fill=(255, 255, 255)) # make the cosmetics show ordered in rows(cosmetic_per_row is hardcoded) current_column += 1 if current_column >= cosmetic_per_row: current_row += 1 current_column = 0 # footer current_date = datetime.now().strftime('%d %B %Y') # custom logo custom_logo_path = f"users/logos/{user_data['ID']}.png" if os.path.isfile(custom_logo_path): custom_logo = Image.open(custom_logo_path).resize((150, 150), Image.Resampling.BILINEAR) image.paste(custom_logo, (10, image_height - 165), mask=custom_logo) else: # original logo if not found logo = Image.open('img/logo.png').resize((150, 150), Image.Resampling.BILINEAR) image.paste(logo, (10, image_height - 165), mask=logo) draw.text((174, image_height - 40 * 3 - 24), '{}'.format(current_date), font=ImageFont.truetype(font_path, 40), fill=(0, 0, 0)) # shadow draw.text((170, image_height - 40 * 3 - 28), '{}'.format(current_date), font=ImageFont.truetype(font_path, 40), fill=(255, 255, 255)) draw.text((174, image_height - 40 * 2 - 24), 'Submitted by ', font=ImageFont.truetype(font_path, 40), fill=(0, 0, 0)) # shadow draw_gradient_text(0, draw, (170, image_height - 40 * 2 - 28), 'Submitted by ', font=ImageFont.truetype(font_path, 40)) # badges font_size = 40 font = ImageFont.truetype(font_path, font_size) submit_width = font.getbbox(f"Submitted by")[2] offset_submit = 170 + submit_width + 8 username_width = font.getbbox(f"Submitted by @{user_data['username']}")[2] offset_badge = 170 + username_width + 8 if user_data['epic_badge_active'] == True and user_data['epic_badge'] == True: # epic games badge(special people only) alpha_badge = Image.open('badges/epic.png').resize((40, 40), Image.BILINEAR) image.paste(alpha_badge, (offset_badge, image_height - 40 * 2 - 28), alpha_badge.convert("RGBA")) offset_badge += 45 if user_data['alpha_tester_3_badge_active'] == True and user_data['alpha_tester_3_badge'] == True: # alpha tester 3 badge alpha_badge = Image.open('badges/alpha3.png').resize((40, 40), Image.BILINEAR) image.paste(alpha_badge, (offset_badge, image_height - 40 * 2 - 28), alpha_badge.convert("RGBA")) offset_badge += 45 if user_data['alpha_tester_2_badge_active'] == True and user_data['alpha_tester_2_badge'] == True: # alpha tester 2 badge alpha_badge = Image.open('badges/alpha2.png').resize((40, 40), Image.BILINEAR) image.paste(alpha_badge, (offset_badge, image_height - 40 * 2 - 28), alpha_badge.convert("RGBA")) offset_badge += 45 if user_data['alpha_tester_1_badge_active'] == True and user_data['alpha_tester_1_badge'] == True: # alpha tester 1 badge alpha_badge = Image.open('badges/alpha1.png').resize((40, 40), Image.BILINEAR) image.paste(alpha_badge, (offset_badge, image_height - 40 * 2 - 28), alpha_badge.convert("RGBA")) offset_badge += 45 draw.text((offset_submit + 2, image_height - 40 * 2 - 26), '@{}'.format(user_data['username']), font=ImageFont.truetype(font_path, 40), fill=(0, 0, 0)) # shadow draw_gradient_text(user_data['gradient_type'], draw, (offset_submit, image_height - 40 * 2 - 28), '@{}'.format(user_data['username']), font=ImageFont.truetype(font_path, 40)) draw.text((174, image_height - 61), "t.me/RiftCheckerBot", font=ImageFont.truetype(font_path, 40), fill=(0, 0, 0)) # shadow draw.text((170, image_height - 65), "t.me/RiftCheckerBot", font=ImageFont.truetype(font_path, 40), fill=(255, 255, 255)) image.save(nametosave) def render_aqua_style(header:str, user_data: json, arr: list[str], nametosave:str) -> None: # calculating cosmetics per row cosmetic_per_row = 6 total_cosmetics = len(arr) num_rows = math.ceil(total_cosmetics / cosmetic_per_row) if total_cosmetics > 30: num_rows = int(math.sqrt(total_cosmetics)) cosmetic_per_row = math.ceil(total_cosmetics / num_rows) while cosmetic_per_row * num_rows < total_cosmetics: num_rows += 1 cosmetic_per_row = math.ceil(total_cosmetics / num_rows) # setup for our image, thumbnails padding = 30 padding_height = 2 thumbnail_width = 128 thumbnail_height = 128 image_width = int(cosmetic_per_row * thumbnail_width) image_height = int(thumbnail_height + 5 + (num_rows * (padding_height + thumbnail_height)) + 140) font_path = 'styles/aqua/font.ttf' font_size = 16 font = ImageFont.truetype(font_path, font_size) image = Image.new('RGB', (image_width, image_height), (0, 0, 0)) current_row = 0 current_column = 0 sortarray = ['mythic', 'legendary', 'dark', 'slurp', 'starwars', 'marvel', 'lava', 'frozen', 'gaminglegends', 'shadow', 'icon', 'dc', 'epic', 'rare', 'uncommon', 'common'] arr.sort(key=lambda x: sortarray.index(x.rarity_value)) # had some issues with exclusives rendering in wrong order, so i'm sorting them try: with open('exclusive.txt', 'r', encoding='utf-8') as f: exclusive_cosmetics = [i.strip() for i in f.readlines()] with open('most_wanted.txt', 'r', encoding='utf-8') as f: popular_cosmetics = [i.strip() for i in f.readlines()] except FileNotFoundError: print("Error: 'exclusive.txt' or 'most_wanted.txt' not found.") exclusive_cosmetics = [] popular_cosmetics = [] mythic_items = [item for item in arr if item.rarity_value == 'mythic'] other_items = [item for item in arr if item.rarity_value != 'mythic'] mythic_items.sort( key=lambda x: exclusive_cosmetics.index(x.cosmetic_id) if x.cosmetic_id in exclusive_cosmetics else float('inf') ) if header == "Popular": other_items.sort( key=lambda x: popular_cosmetics.index(x.cosmetic_id) if x.cosmetic_id in popular_cosmetics else float('inf') ) arr = mythic_items + other_items draw = ImageDraw.Draw(image) # top icon_logo = Image.open(f'cosmetic_icons/{header}.png').resize((128, 128), Image.Resampling.BILINEAR) icon_logo.thumbnail((thumbnail_width, thumbnail_height)) image.paste(icon_logo, (10, 5), mask=icon_logo) draw.text((162, 30), '{}'.format(len(arr)), font=ImageFont.truetype(font_path, 70), fill=(0, 0, 0)) # shadow draw.text((166, 87), '{}'.format(header), font=ImageFont.truetype(font_path, 40), fill=(0, 0, 0)) # shadow draw.text((162, 26), '{}'.format(len(arr)), font=ImageFont.truetype(font_path, 70), fill=(255, 255, 255)) draw.text((166, 83), '{}'.format(header), font=ImageFont.truetype(font_path, 40), fill=(167, 167, 167)) special_items = { "CID_029_Athena_Commando_F_Halloween": "cache/pink_ghoul.png", "CID_030_Athena_Commando_M_Halloween": "cache/purple_skull_old.png", "CID_116_Athena_Commando_M_CarbideBlack": "cache/omega_max.png", "CID_694_Athena_Commando_M_CatBurglar": "cache/gold_midas.png", "CID_693_Athena_Commando_M_BuffCat": "cache/gold_cat.png", "CID_691_Athena_Commando_F_TNTina": "cache/gold_tntina.png", "CID_690_Athena_Commando_F_Photographer": "cache/gold_skye.png", "CID_701_Athena_Commando_M_BananaAgent": "cache/gold_peely.png", "CID_315_Athena_Commando_M_TeriyakiFish": "cache/worldcup_fish.png", "CID_971_Athena_Commando_M_Jupiter_S0Z6M": "cache/black_masterchief.png", "CID_028_Athena_Commando_F": "cache/og_rene.png", "CID_017_Athena_Commando_M": "cache/og_aat.png" } for cosmetic in arr: special_icon = False is_banner = cosmetic.is_banner photo = None if cosmetic.rarity_value.lower() == "mythic" and cosmetic.cosmetic_id in special_items: special_icon = True icon_path = special_items[cosmetic.cosmetic_id] if os.path.exists(icon_path): try: photo = Image.open(icon_path) except Exception as e: special_icon = False else: special_icon = False else: photo = fortnite_cache.get_cosmetic_icon_from_cache(cosmetic.small_icon, cosmetic.cosmetic_id) if is_banner: scaled_width = int(photo.width * 1.5) scaled_height = int(photo.height * 1.5) photo = photo.resize((scaled_width, scaled_height), Image.Resampling.LANCZOS) x_offset = 32 y_offset = 10 new_img = Image.open(f'styles/aqua/rarity/{cosmetic.rarity_value.lower()}.png').convert('RGBA') new_img.paste(photo, (x_offset, y_offset), mask=photo) photo = new_img photo.thumbnail((thumbnail_width, thumbnail_height)) else: new_img = Image.open(f'styles/aqua/rarity/{cosmetic.rarity_value.lower()}.png').convert('RGBA').resize(photo.size) new_img.paste(photo, mask=photo) photo = new_img photo.thumbnail((thumbnail_width, thumbnail_height)) # black box for cosmetic name box = Image.new('RGBA', (128, 27), (0, 0, 0, 100)) photo.paste(box, (0, new_img.size[1] - 27), mask=box) if header != "Exclusives" and cosmetic.cosmetic_id in popular_cosmetics: star_image = Image.open('cosmetic_icons/WantedStar.png').resize((128, 128), Image.BILINEAR) photo.paste(star_image, (0, 0), star_image.convert("RGBA")) x = thumbnail_width * current_column y = thumbnail_width + (thumbnail_height + padding_height) * current_row image.paste(photo, (x, y)) name = cosmetic.name.upper() max_text_width = thumbnail_width - 10 max_text_height = 20 # fixed font size font_size = 16 offset = 6 while True: font = ImageFont.truetype(font_path, font_size) bbox = draw.textbbox((0, 0), name, font=font) name_width = bbox[2] - bbox[0] name_height = bbox[3] - bbox[1] if name_width > max_text_width or name_height > max_text_height: font_size -= 1 offset += 0.5 else: break # cosmetic name bbox = draw.textbbox((0, 0), name, font=font) name_width = bbox[2] - bbox[0] draw.text((x + (thumbnail_width - name_width) // 2, y + (thumbnail_height - padding + offset)), name, font=font, fill=(255, 255, 255)) # make the cosmetics show ordered in rows(cosmetic_per_row is hardcoded) current_column += 1 if current_column >= cosmetic_per_row: current_row += 1 current_column = 0 # footer current_date = datetime.now().strftime('%d %B %Y') # custom logo custom_logo_path = f"users/logos/{user_data['ID']}.png" if os.path.isfile(custom_logo_path): custom_logo = Image.open(custom_logo_path).resize((125, 125), Image.Resampling.BILINEAR) image.paste(custom_logo, (25, image_height - 140), mask=custom_logo) else: # original logo if not found logo = Image.open('img/logo.png').resize((125, 125), Image.Resampling.BILINEAR) image.paste(logo, (25, image_height - 140), mask=logo) draw.text((179, image_height - 114), '{}'.format(current_date), font=ImageFont.truetype(font_path, 30), fill=(0, 0, 0)) # shadow draw.text((175, image_height - 118), '{}'.format(current_date), font=ImageFont.truetype(font_path, 30), fill=(255, 255, 255)) draw.text((179, image_height - 84), 'Submitted By: @{}'.format(user_data['username']), font=ImageFont.truetype(font_path, 30), fill=(0, 0, 0)) # shadow draw.text((175, image_height - 88), 'Submitted By: @{}'.format(user_data['username']), font=ImageFont.truetype(font_path, 30), fill=(255, 255, 255)) draw.text((179, image_height - 31), "t.me/RiftCheckerBot", font=ImageFont.truetype(font_path, 20), fill=(0, 0, 0)) # shadow draw.text((175, image_height - 55), "t.me/RiftCheckerBot", font=ImageFont.truetype(font_path, 20), fill=(255, 255, 255)) image.save(nametosave) def command_start(bot, message): if message.chat.type != "private": return user = RiftUser(message.from_user.id, message.from_user.username) user_data = user.register() if not user_data: bot.reply_to(message, "You have already used this command, you don't have to use it anymore!") return bot.reply_to(message, f''' What is Rift Checker Bot? > Rift checker is provably safe open-sourced telegram fortnite skin checker bot, it visualises your locker into an image and sends it back to you, aswell it does display info about your account. Why should we use Rift and not other skincheckers? > Unlike majority of skincheckers, we make NO profit from our service, the bot is entirely hosted by choice, as well unlike Raika we DO NOT store your account information anywhere, for security reasons all account credentials are private and inaccessible. How do i know you're not stealing our accounts? > Rift checker is open-sourced bot, if you don't believe that it's safe, you could check the source code yourself to make sure that rift is completely safe! > Rift Checker's Official Github Repository: https://github.com/Debugtopia/RiftCheckerBot > Yes, you could use rift's source code to create your own bot aswell! just make to credit @xhexago for his work :) > Other open sourced projects by @xhexago: - GrowBase(Growtopia Private Server - Game Server source code): https://github.com/Debugtopia/GrowBase Who are the developers of Rift Checker Bot? > Rift is a single-handedly developed by @xhexago and nobody else. Commands: /start - register to rift checker /help - displays info about the bot, it's commands. /login - skincheck your epic games fortnite account. /style - choose your style based on your need, we support multiple skincheck styles. /badges - toggle your achieved badges on your skincheck. /stats - shows statistics of how many accounts have u checked with our bot or the badges you have enabled. ''') markup = InlineKeyboardMarkup([ [InlineKeyboardButton("🔗 Source Code", url="https://github.com/Debugtopia/RiftCheckerBot")], [InlineKeyboardButton("🔗 Stock Channel", url="https://t.me/riftstock"), InlineKeyboardButton("🔗 News Channel", url="https://t.me/RiftCheckerNews")] ]) bot.send_message( chat_id=message.chat.id, text=( "Welcome to Rift Checker Bot!\n" "Developed by @xhexago\n" "Interested in purchasing Fortnite accounts?\n" "- @riftstock\n\n" "Rift Checker Bot's News channel:\n" "- @riftcheckernews" ), reply_markup=markup, parse_mode="Markdown" ) def command_help(bot, message): bot.reply_to(message, f''' What is Rift Checker Bot? > Rift checker is provably safe open-sourced telegram fortnite skin checker bot, it visualises your locker into an image and sends it back to you, aswell it does display info about your account. Why should we use Rift and not other skincheckers? > Unlike majority of skincheckers, we make NO profit from our service, the bot is entirely hosted by choice, as well unlike Raika we DO NOT store your account information anywhere, for security reasons all account credentials are private and inaccessible. How do i know you're not stealing our accounts? > Rift checker is open-sourced bot, if you don't believe that it's safe, you could check the source code yourself to make sure that rift is completely safe! > Rift Checker's Official Github Repository: https://github.com/Debugtopia/RiftCheckerBot > Yes, you could use rift's source code to create your own bot aswell! just make to credit @xhexago for his work :) > Other open sourced projects by @xhexago: - GrowBase(Growtopia Private Server - Game Server source code): https://github.com/Debugtopia/GrowBase Who are the developers of Rift Checker Bot? > Rift is a single-handedly developed by @xhexago and nobody else. Commands: /start - register to rift checker /help - displays info about the bot, it's commands. /login - skincheck your epic games fortnite account. /style - choose your style based on your need, we support multiple skincheck styles. /badges - toggle your achieved badges on your skincheck. /stats - shows statistics of how many accounts have u checked with our bot or the badges you have enabled. ''') markup = InlineKeyboardMarkup([ [InlineKeyboardButton("🔗 Source Code", url="https://github.com/Debugtopia/RiftCheckerBot")], [InlineKeyboardButton("🔗 Stock Channel", url="https://t.me/riftstock"), InlineKeyboardButton("🔗 News Channel", url="https://t.me/RiftCheckerNews")] ]) bot.send_message( chat_id=message.chat.id, text=( "Welcome to Rift Checker Bot!\n" "Developed by @xhexago\n" "Interested in purchasing Fortnite accounts?\n" "- @riftstock\n\n" "Rift Checker Bot's News channel:\n" "- @riftcheckernews" ), reply_markup=markup, parse_mode="Markdown" ) async def command_login(bot, message): if message.chat.type != "private": return user = RiftUser(message.from_user.id, message.from_user.username) user_data = user.load_data() if user_data == {}: bot.reply_to(message, "You haven't setup your user yet, please use /start before skinchecking!") return msg = bot.reply_to(message, "⏳ Creating authorization login link...") epic_generator = EpicGenerator() await epic_generator.start() device_data = await epic_generator.create_device_code() epic_games_auth_link = f"https://www.epicgames.com/activate?userCode={device_data['user_code']}" # login link message(embed link button) markup = InlineKeyboardMarkup() button = InlineKeyboardButton("🔗 Login", url=epic_games_auth_link) markup.add(button) bot.edit_message_text( chat_id=msg.chat.id, message_id=msg.message_id, text=f"Open [this link](<{epic_games_auth_link}>) to log in to your account.", reply_markup=markup, parse_mode="Markdown") epic_user = await epic_generator.wait_for_device_code_completion(bot, message, code=device_data['device_code']) if not epic_user: # something went wrong so we can't check the account await epic_generator.kill() return account_data = await epic_generator.get_account_metadata(epic_user) accountID = account_data.get('id', "INVALID_ACCOUNT_ID") if (accountID == "INVALID_ACCOUNT_ID"): bot.edit_message_text(chat_id=msg.chat.id, message_id=msg.message_id, text="Invalid account(banned or fortnite has not been launched).") return bot.delete_message(msg.chat.id, msg.message_id) msg = bot.send_message(message.chat.id, f'✅ Logged in account {account_data.get("displayName", "HIDDEN_ID_ACCOUNT")}') # account information account_public_data = await epic_generator.get_public_account_info(epic_user) bot.send_message(message.chat.id,f''' ━━━━━━━━━━━ Account Information ━━━━━━━━━━━ #️⃣ Account ID: {mask_account_id(accountID)} 📧 Email: {mask_email(account_data.get('email', ''))} 🧑‍🦱 Display Name: {account_data.get('displayName', 'DeletedUser')} 📛 Full Name: {account_data.get('name', '')} {account_data.get('lastName', '')} 🌐 Country: {account_data.get('country', 'US')} {country_to_flag(account_data.get('country', 'US'))} 🔐 Email Verified: {bool_to_emoji(account_data.get('emailVerified', False))} 🔒 Mandatory 2FA Security: {bool_to_emoji(account_data.get('tfaEnabled', False))} ''') # external connections connected_accounts = 0 connected_accounts_message = f""" ━━━━━━━━━━━ Connected Account ━━━━━━━━━━━\n""" external_auths = account_public_data.get('externalAuths', []) for auth in external_auths: auth_type = auth.get('type', '?').lower() display_name = auth.get('externalDisplayName', '?') external_id = auth.get('externalAuthId', '?') date_added = auth.get('dateAdded', '?') if date_added != '?': parsed_date = datetime.strptime(date_added, "%Y-%m-%dT%H:%M:%S.%fZ") date_added = parsed_date.strftime("%d/%m/%Y") connected_accounts += 1 connected_accounts_message += f""" Connection Type: {escape_markdown(auth_type.upper())} External Display Name: {escape_markdown(display_name)} Date of Connection: {escape_markdown(date_added)} """ if connected_accounts == 0: connected_accounts_message += "No connected accounts." markup = InlineKeyboardMarkup() button = InlineKeyboardButton("🔗 Remove Restrictions", url='https://www.epicgames.com/help/en/wizards/w4') markup.add(button) bot.send_message( chat_id=msg.chat.id, text=connected_accounts_message, reply_markup=markup, parse_mode="Markdown") # purchases infos vbucks_categories = [ "Currency:MtxPurchased", "Currency:MtxEarned", "Currency:MtxGiveaway", "Currency:MtxPurchaseBonus" ] total_vbucks = 0 refunds_used = 0 refund_credits = 0 receipts = [] vbucks_purchase_history = { "1000": 0, "2800": 0, "5000": 0, "7500": 0, "13500": 0 } gift_received = 0 gift_sent = 0 pending_gifts_amount = 0 common_profile_data = await epic_generator.get_common_profile(epic_user) for item_id, item_data in common_profile_data.get("profileChanges", [{}])[0].get("profile", {}).get("items", {}).items(): if item_data.get("templateId") in vbucks_categories: # getting vbucks total_vbucks += item_data.get("quantity", 0) for profileChange in common_profile_data.get("profileChanges", []): attributes = profileChange["profile"]["stats"]["attributes"] mtx_purchases = attributes.get("mtx_purchase_history", {}) if mtx_purchases: refunds_used = mtx_purchases.get("refundsUsed", 0) refund_credits = mtx_purchases.get("refundCredits", 0) iap = attributes.get("in_app_purchases", {}) if iap: receipts = iap.get("receipts", []) purchases = iap.get("fulfillmentCounts", {}) if purchases: # vbucks purchases packs amount vbucks_purchase_history["1000"] = purchases.get("FN_1000_POINTS", 0) vbucks_purchase_history["2800"] = purchases.get("FN_2800_POINTS", 0) vbucks_purchase_history["5000"] = purchases.get("FN_5000_POINTS", 0) vbucks_purchase_history["7500"] = purchases.get("FN_7500_POINTS", 0) vbucks_purchase_history["13500"] = purchases.get("FN_13500_POINTS", 0) gift_history = attributes.get("gift_history", {}) if gift_history: # pending gifts count gifts_pending = gift_history.get("gifts", []) pending_gifts_amount = len(gifts_pending) # gifts sent & received count gift_sent = gift_history.get("num_sent", 0) gift_received = gift_history.get("num_received", 0) total_vbucks_bought = 1000 * vbucks_purchase_history["1000"] + 2800 * vbucks_purchase_history["2800"] + 5000 * vbucks_purchase_history["5000"] + 7500 * vbucks_purchase_history["7500"] + 13500 * vbucks_purchase_history["13500"] bot.send_message(message.chat.id,f''' ━━━━━━━━━━━ Purchases Information ━━━━━━━━━━━ 💰 VBucks: {total_vbucks} 🎟 Refunds Used: {refunds_used} 🎟 Refund Tickets: {refund_credits} ━━━━━━━━━━━ Vbucks Purchases ━━━━━━━━━━━ #️⃣ Receipts: {len(receipts)} 💰 1000 Vbucks Packs: {vbucks_purchase_history["1000"]} 💰 2800 Vbucks Packs: {vbucks_purchase_history["2800"]} 💰 5000 Vbucks Packs: {vbucks_purchase_history["5000"]} 💰 7500 Vbucks Packs: {vbucks_purchase_history["7500"]} 💰 13500 Vbucks Packs: {vbucks_purchase_history["13500"]} 💰 Total Vbucks Purchased: {total_vbucks_bought} ━━━━━━━━━━━ Gifts Information ━━━━━━━━━━━ 🎁 Pending Gifts: {pending_gifts_amount} 🎁 Gifts Sent: {gift_sent} 🎁 Gifts Received: {gift_received} ''') # season history seasons_msg = await epic_generator.get_seasons_message(epic_user) bot.send_message(message.chat.id, seasons_msg) # locker data locker_data = await epic_generator.get_locker_data(epic_user) # activity info bot.send_message(message.chat.id,f''' ━━━━━━━━━━━ Activity Information ━━━━━━━━━━━ 👪 Parental Control: {bool_to_emoji(account_data.get('minorVerified', False))} 🕘 Last Match: {locker_data.last_match} 🤯 Headless: {bool_to_emoji(account_data.get("headless", False))} ✏️ Display Name Changes: {account_data.get("numberOfDisplayNameChanges", 0)} ✏️ Display Name Changeable: {bool_to_emoji(account_data.get("canUpdateDisplayName", False))} #️⃣ Hashed email: {bool_to_emoji(account_data.get("hasHashedEmail", False))} ''') bot.send_message(message.chat.id,f''' ━━━━━━━━━━━ Locker Information ━━━━━━━━━━━ 🧍‍♂️ Outfits: {len(locker_data.cosmetic_array.get('AthenaCharacter', []))} 🎒 Backpacks: {len(locker_data.cosmetic_array.get('AthenaBackpack', []))} ⛏️ Pickaxes: {len(locker_data.cosmetic_array.get('AthenaPickaxe', []))} 🕺 Emotes: {len(locker_data.cosmetic_array.get('AthenaDance', []))} ✈️ Gliders: {len(locker_data.cosmetic_array.get('AthenaGlider', []))} ⭐ Most Wanted Cosmetics: {len(locker_data.cosmetic_array.get('AthenaPopular', []))} 🌟 Exclusives: {len(locker_data.cosmetic_array.get('AthenaExclusive', []))} ''') homebase_data = await epic_generator.get_homebase_profile(epic_user) stats = homebase_data.get("profileChanges", [{}])[0].get("profile", {}).get("stats", {}).get("attributes", {}) if stats: stw_level = 1 research_offence = 1 research_fortitude = 1 research_resistance = 1 research_tech = 1 collection_book_level = 1 stw_claimed = False legacy_research_points = 0 matches_played = 0 stats = homebase_data.get("profileChanges", [{}])[0].get("profile", {}).get("stats", {}).get("attributes", {}) if stats: stw_level = stats.get("level", 1) research_offence = stats.get("research_levels", {}).get("offence", 1) research_fortitude = stats.get("research_levels", {}).get("fortitude", 1) research_resistance = stats.get("research_levels", {}).get("resistance", 1) research_tech = stats.get("research_levels", {}).get("technology", 1) collection_book_level = stats.get("collection_book", {}).get("maxBookXpLevelAchieved", 1) stw_claimed = stats.get("mfa_reward_claimed", False) legacy_research_points = stats.get("legacy_research_points_spent", 0) matches_played = stats.get("matches_played", 0) bot.send_message(message.chat.id,f''' ━━━━━━━━━━━ Homebase Information ━━━━━━━━━━━ #️⃣ Level: {stw_level} 🎒 Collection Book Level: {collection_book_level} 🎁 Claimed Rewards: {bool_to_emoji(stw_claimed)} Research: ⭐ Total spent points: {legacy_research_points} ⛏️ Offence: {research_offence} ⚔️ Fortitude: {research_fortitude} 🪖 Resistance: {research_resistance} 🔧 Tech: {research_tech} Activity: 🎟 Matches Played: {matches_played} ''') # saved data path # note: it only saves the rendered images for locker, data that DOES NOT contain private or login information!!! save_path = f"accounts/{accountID}" if not os.path.exists(save_path): os.mkdir(save_path) for category in locker_categories: if category not in locker_data.cosmetic_array or len(locker_data.cosmetic_array[category]) < 1: continue header = 'Outfits' if category == 'AthenaBackpack': header = 'Backblings' elif category == 'AthenaPickaxe': header = 'Pickaxes' elif category == 'AthenaDance': header = 'Emotes' elif category == 'AthenaGlider': header = 'Gliders' elif category == 'AthenaExclusive': header = 'Exclusives' elif category == 'AthenaPopular': header = 'Popular' if user_data['style'] == 0: # rift style render_rift_style(header, user_data, locker_data.cosmetic_array[category], f'{save_path}/{category}.png') elif user_data['style'] == 1: # easy style render_easy_style(header, user_data, locker_data.cosmetic_array[category], f'{save_path}/{category}.png') elif user_data['style'] == 2: # raika style render_raika_style(header, user_data, locker_data.cosmetic_array[category], f'{save_path}/{category}.png') elif user_data['style'] == 3: # kayy style render_kayy_style(header, user_data, locker_data.cosmetic_array[category], f'{save_path}/{category}.png') elif user_data['style'] == 4: # storm style render_storm_style(header, user_data, locker_data.cosmetic_array[category], f'{save_path}/{category}.png') elif user_data['style'] == 5: # aqua style render_aqua_style(header, user_data, locker_data.cosmetic_array[category], f'{save_path}/{category}.png') with open(f'{save_path}/{category}.png', 'rb') as photo_file: file_size = os.path.getsize(f'{save_path}/{category}.png') if file_size > 10 * 1024 * 1024: # 10 MBs print("File too large for photo, sending as a document.") bot.send_document(msg.chat.id, photo_file) else: bot.send_photo(msg.chat.id, photo_file) skins = len(locker_data.cosmetic_array['AthenaCharacter']) excl = locker_data.cosmetic_array['AthenaExclusive'] cosmetic_list = '' desc = '' cosmetics_listed = 0 for cosmetic in excl: cosmetic_list += cosmetic.name + " + " cosmetics_listed += 1 if cosmetics_listed >= 10: break cosmetic_list = cosmetic_list.rstrip(" + ") desc = f'{skins} + {cosmetic_list} + {total_vbucks}VB' bot.send_message(message.chat.id,f'{desc}') await epic_generator.kill() # button-embed message markup = InlineKeyboardMarkup([ [InlineKeyboardButton("🔗 Source Code", url="https://github.com/Debugtopia/RiftCheckerBot")], [InlineKeyboardButton("🔗 Rift Stock", url="https://t.me/riftstock"), InlineKeyboardButton("🔗 News Channel", url="https://t.me/RiftCheckerNews")] ]) bot.send_message( chat_id=message.chat.id, text=( "Thanks for using Rift Checker Bot!\n" "Developed by @xhexago\n" "Interested in purchasing Fortnite accounts?\n" "- @riftstock\n\n" "Rift Checker Bot's News channel:\n" "- @riftcheckernews" ), reply_markup=markup, parse_mode="Markdown" ) async def command_style(bot, message): if message.chat.type != "private": return user = RiftUser(message.from_user.id, message.from_user.username) user_data = user.load_data() if not user_data: bot.reply_to(message, "You haven't setup your user yet, please use /start before skinchecking!") return current_style_index = user_data['style'] send_style_message(bot, message.chat.id, current_style_index) async def command_badges(bot, message): if message.chat.type != "private": return user = RiftUser(message.from_user.id, message.from_user.username) user_data = user.load_data() if not user_data: bot.reply_to(message, "You haven't setup your user yet, please use /start before skinchecking!") return badges_unlocked = 0 for badge in avaliable_badges: if user_data[badge['data']] == True: badges_unlocked += 1 if badges_unlocked < 1: msg = bot.reply_to(message, "You don't have any badges unlocked.") return current_badge_index = 0 send_badges_message(bot, message.chat.id, current_badge_index, user_data) async def command_stats(bot, message): if message.chat.type != "private": return user = RiftUser(message.from_user.id, message.from_user.username) user_data = user.load_data() if not user_data: bot.reply_to(message, "You haven't setup your user yet, please use /start before skinchecking!") return style = 'rift' if user_data['style'] == 0: style = 'rift' elif user_data['style'] == 1: style = 'easy' elif user_data['style'] == 2: style = 'raika' elif user_data['style'] == 3: style = 'kayy' elif user_data['style'] == 4: style = 'storm' elif user_data['style'] == 5: style = 'aqua' else: style = 'unknown style' msg = bot.reply_to(message, f''' Stats for user {message.from_user.username}(#{user_data['ID']}): Checked accounts: {user_data['accounts_checked']} Style: {style} Badges: Alpha Tester 1 Badge: {bool_to_emoji(user_data['alpha_tester_1_badge'])} > Badge Enabled: {bool_to_emoji(user_data['alpha_tester_1_badge_active'])} Alpha Tester 2 Badge: {bool_to_emoji(user_data['alpha_tester_2_badge'])} > Badge Enabled: {bool_to_emoji(user_data['alpha_tester_2_badge_active'])} Alpha Tester 3 Badge: {bool_to_emoji(user_data['alpha_tester_3_badge'])} > Badge Enabled: {bool_to_emoji(user_data['alpha_tester_3_badge_active'])} Epic Games Badge: {bool_to_emoji(user_data['epic_badge'])} > Badge Enabled: {bool_to_emoji(user_data['epic_badge_active'])} 100 Checks Badge: {bool_to_emoji(user_data['newbie_badge'])} > Badge Enabled: {bool_to_emoji(user_data['newbie_badge_active'])} 200 Checks Badge: {bool_to_emoji(user_data['advanced_badge'])} > Badge Enabled: {bool_to_emoji(user_data['advanced_badge_active'])} ''') def send_style_message(bot, chat_id, style_index): style = available_styles[style_index] markup = InlineKeyboardMarkup() if style_index > 0: markup.add(InlineKeyboardButton("◀️", callback_data=f"style_{style_index - 1}")) if style_index < len(available_styles) - 1: markup.add(InlineKeyboardButton("▶️", callback_data=f"style_{style_index + 1}")) markup.add(InlineKeyboardButton("✅ Select This Style", callback_data=f"select_{style_index}")) with open(style['image'], 'rb') as img_file: img = Image.open(style['image']).convert("RGBA") bot.send_photo( chat_id, img, caption=f"{style['name']}", reply_markup=markup, parse_mode="Markdown" ) def send_badges_message(bot, chat_id, badge_index, user_data): unlocked_badges = [ (i, badge) for i, badge in enumerate(avaliable_badges) if user_data.get(badge['data'], False) ] if not unlocked_badges: bot.send_message(chat_id, "You don't have any badges unlocked.") return badge_index = min(max(0, badge_index), len(unlocked_badges) - 1) actual_index, badge = unlocked_badges[badge_index] badge_status = user_data.get(badge['data2'], False) toggle_text = "✅ Enabled" if badge_status else "❌ Disabled" markup = InlineKeyboardMarkup() if badge_index > 0: markup.add(InlineKeyboardButton("◀️", callback_data=f"badge_{badge_index - 1}")) if badge_index < len(unlocked_badges) - 1: markup.add(InlineKeyboardButton("▶️", callback_data=f"badge_{badge_index + 1}")) markup.add(InlineKeyboardButton(toggle_text, callback_data=f"toggle_{actual_index}")) try: with open(badge['image'], 'rb') as img: bot.send_photo( chat_id, img, caption=f"{badge['name']}", reply_markup=markup, parse_mode="Markdown" ) except FileNotFoundError: bot.send_message(chat_id, f"Image for badge {badge['name']} not found.")