import requests import discord from discord.ext import commands import asyncio import json import re from collections import defaultdict from collections import deque from datetime import datetime, timedelta import pytz import os TOKEN = os.environ['TOKEN'] CHANNEL_ID = 1298830206387228682 MONITORED_ITEMS_CHANNEL_ID = 1307752080139878500 AUTHORIZED_USER_ID = 1219416244806094883 PETS_API = 'https://petsapi.deno.dev/' EXISTS_API = 'https://existsapi.deno.dev/' RAP_API = 'https://rapapi.deno.dev/' last_rap_values = { "Crystal Key": None, "Huge Shiba": None, "Huge Dragon": None, "Huge Inferno Cat": None, "Autumn God Potion": None } LIMITED_PETS = { "Huge Shiba": 15000, "Huge Dragon": 7500, "Huge Nightmare Corgi": 15000, "Huge Jelly Axolotl": 5000 } JELLY_LEAVE_TIMES = { "Huge Pilgrim Turkey": datetime.strptime('2024-12-06 12:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=pytz.timezone('US/Eastern')), "Acorn": datetime.strptime('2024-12-06 12:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=pytz.timezone('US/Eastern')) } intents = discord.Intents.default() intents.message_content = True bot = commands.Bot(command_prefix='!', intents=intents) pet_images = {} pet_difficulties = {} pet_raps = {} pet_changes = {} recent_notifications = defaultdict(lambda: deque(maxlen=5)) async def fetch_exists_data(): try: response = requests.get('https://biggamesexists.deno.dev') if response.status_code == 200: return response.json().get('data', []) except Exception as e: print(f"Error fetching exists data: {e}") return [] def format_number(number): if not isinstance(number, (int, float)): return str(number) abs_number = abs(number) if abs_number < 1000: return str(number) elif abs_number < 1000000: return f"{number/1000:.1f}k" elif abs_number < 1000000000: return f"{number/1000000:.1f}m" elif abs_number < 1000000000000: return f"{number/1000000000:.1f}b" else: return f"{number/1000000000000:.1f}t" async def fetch_pet_collection_data(pet_name): try: response = requests.get('https://collectionpet.deno.dev') if response.status_code == 200: data = response.json().get('data', []) for pet in data: if pet.get('configName', '').lower() == pet_name.lower(): return pet except Exception as e: print(f"Error fetching pet collection data: {e}") return None async def fetch_pet_exists_data(pet_name): try: response = requests.get('https://biggamesexists.deno.dev') if response.status_code == 200: data = response.json().get('data', []) variants = { 'Normal': 0, 'Golden': 0, 'Rainbow': 0, 'Shiny': 0, 'Shiny Golden': 0, 'Shiny Rainbow': 0 } for pet_data in data: config_data = pet_data.get('configData', {}) value = pet_data.get('value', 0) pet_id = config_data.get('id', '') is_shiny = config_data.get('sh', False) pt_value = config_data.get('pt', 0) if pet_id.lower() == pet_name.lower(): if is_shiny and pt_value == 1: variants['Shiny Golden'] = max(variants['Shiny Golden'], value) elif is_shiny and pt_value == 2: variants['Shiny Rainbow'] = max(variants['Shiny Rainbow'], value) elif is_shiny: variants['Shiny'] = max(variants['Shiny'], value) elif pt_value == 1: variants['Golden'] = max(variants['Golden'], value) elif pt_value == 2: variants['Rainbow'] = max(variants['Rainbow'], value) else: variants['Normal'] = max(variants['Normal'], value) return variants except Exception as e: print(f"Error fetching exists data: {e}") return None @bot.command(name='petdata') async def pet_data(ctx, *, pet_name): collection_data = await fetch_pet_collection_data(pet_name) exists_data = await fetch_pet_exists_data(pet_name) if not collection_data or not exists_data: await ctx.send("Pet does not exist!") return embed = discord.Embed( title=collection_data.get('configName', 'Unknown Pet'), color=int('11ff00', 16) ) config_data = collection_data.get('configData', {}) description = f"**Index Description:** {config_data.get('indexDesc', 'N/A')}\n" description += f"**Obtainable in Index:** {config_data.get('indexObtainable', 'N/A')}" embed.description = description total_exists = sum(exists_data.values()) exists_text = "\n".join([ f"{variant}: **{count:,}**" for variant, count in exists_data.items() if count > 0 ]) exists_text += f"\n\n**Total Exist: {total_exists:,}**" embed.add_field(name="**Pet Exists:**", value=exists_text, inline=False) thumbnail_url = config_data.get('thumbnail') if thumbnail_url: if thumbnail_url.startswith('rbxassetid://'): asset_id = re.search(r'\d+', thumbnail_url.replace('rbxassetid://', '')).group(0) thumbnail_url = f"https://rbxgleaks.pythonanywhere.com/asset/{asset_id}" embed.set_image(url=thumbnail_url) await ctx.send(embed=embed) @bot.command(name='commonhuges') async def common_huge_pets(ctx, count: int = 10): if ctx.author.id != AUTHORIZED_USER_ID: await ctx.send("You are not authorized to use this command.") return count = max(1, min(count, 25)) exists_data = await fetch_exists_data() if not exists_data: await ctx.send("Could not retrieve pet data. Please try again later.") return huge_pet_counts = defaultdict(int) for pet in exists_data: config_data = pet.get('configData', {}) value = pet.get('value', 0) if config_data.get('id', '').startswith('Huge'): pet_name = config_data.get('id', 'Unknown') is_shiny = config_data.get('sh', False) pt_value = config_data.get('pt', 0) if is_shiny and pt_value == 1: variant_key = f"Shiny Golden {pet_name}" elif is_shiny and pt_value == 2: variant_key = f"Shiny Rainbow {pet_name}" elif is_shiny: variant_key = f"Shiny {pet_name}" elif pt_value == 1: variant_key = f"Golden {pet_name}" elif pt_value == 2: variant_key = f"Rainbow {pet_name}" else: variant_key = f"Normal {pet_name}" huge_pet_counts[variant_key] = max(huge_pet_counts[variant_key], value) sorted_huge_pets = sorted(huge_pet_counts.items(), key=lambda x: x[1], reverse=True) embed = discord.Embed(title=f"Top {count} Most Common Huge Pets", color=discord.Color.blue()) if sorted_huge_pets: for i, (pet_name, count_value) in enumerate(sorted_huge_pets[:count], 1): embed.add_field( name=f"{i}. {pet_name}", value=f"Exists: **{format_number(count_value)}**", inline=False ) else: embed.description = "No huge pets found in the data." await ctx.send(embed=embed) @bot.command(name='commontitanics') async def common_titanic_pets(ctx, count: int = 10): if ctx.author.id != AUTHORIZED_USER_ID: await ctx.send("You are not authorized to use this command.") return count = max(1, min(count, 25)) exists_data = await fetch_exists_data() if not exists_data: await ctx.send("Could not retrieve pet data. Please try again later.") return huge_pet_counts = defaultdict(int) for pet in exists_data: config_data = pet.get('configData', {}) value = pet.get('value', 0) if config_data.get('id', '').startswith('Titanic'): pet_name = config_data.get('id', 'Unknown') is_shiny = config_data.get('sh', False) pt_value = config_data.get('pt', 0) if is_shiny and pt_value == 1: variant_key = f"Shiny Golden {pet_name}" elif is_shiny and pt_value == 2: variant_key = f"Shiny Rainbow {pet_name}" elif is_shiny: variant_key = f"Shiny {pet_name}" elif pt_value == 1: variant_key = f"Golden {pet_name}" elif pt_value == 2: variant_key = f"Rainbow {pet_name}" else: variant_key = f"Normal {pet_name}" huge_pet_counts[variant_key] = max(huge_pet_counts[variant_key], value) sorted_huge_pets = sorted(huge_pet_counts.items(), key=lambda x: x[1], reverse=True) embed = discord.Embed(title=f"Top {count} Most Common Titanic Pets", color=discord.Color.blue()) if sorted_huge_pets: for i, (pet_name, count_value) in enumerate(sorted_huge_pets[:count], 1): embed.add_field( name=f"{i}. {pet_name}", value=f"Exists: **{format_number(count_value)}**", inline=False ) else: embed.description = "No titanic pets found in the data." await ctx.send(embed=embed) @bot.command(name='leastcommonhuges') async def least_common_huge_pets(ctx, count: int = 10): if ctx.author.id != AUTHORIZED_USER_ID: await ctx.send("You are not authorized to use this command.") return count = max(1, min(count, 25)) exists_data = await fetch_exists_data() if not exists_data: await ctx.send("Could not retrieve pet data. Please try again later.") return huge_pet_counts = defaultdict(int) for pet in exists_data: config_data = pet.get('configData', {}) value = pet.get('value', 0) if config_data.get('id', '').startswith('Huge'): pet_name = config_data.get('id', 'Unknown') is_shiny = config_data.get('sh', False) pt_value = config_data.get('pt', 0) if is_shiny and pt_value == 1: variant_key = f"Shiny Golden {pet_name}" elif is_shiny and pt_value == 2: variant_key = f"Shiny Rainbow {pet_name}" elif is_shiny: variant_key = f"Shiny {pet_name}" elif pt_value == 1: variant_key = f"Golden {pet_name}" elif pt_value == 2: variant_key = f"Rainbow {pet_name}" else: variant_key = f"Normal {pet_name}" huge_pet_counts[variant_key] = max(huge_pet_counts[variant_key], value) sorted_huge_pets = sorted(huge_pet_counts.items(), key=lambda x: x[1]) embed = discord.Embed(title=f"Top {count} Least Common Huge Pets", color=discord.Color.dark_red()) if sorted_huge_pets: for i, (pet_name, count_value) in enumerate(sorted_huge_pets[:count], 1): embed.add_field( name=f"{i}. {pet_name}", value=f"Exists: **{format_number(count_value)}**", inline=False ) else: embed.description = "No huge pets found in the data." await ctx.send(embed=embed) @bot.command(name='leastcommontitanics') async def least_common_titanic_pets(ctx, count: int = 10): if ctx.author.id != AUTHORIZED_USER_ID: await ctx.send("You are not authorized to use this command.") return count = max(1, min(count, 25)) exists_data = await fetch_exists_data() if not exists_data: await ctx.send("Could not retrieve pet data. Please try again later.") return titanic_pet_counts = defaultdict(int) for pet in exists_data: config_data = pet.get('configData', {}) value = pet.get('value', 0) if config_data.get('id', '').startswith('Titanic'): pet_name = config_data.get('id', 'Unknown') is_shiny = config_data.get('sh', False) pt_value = config_data.get('pt', 0) if is_shiny and pt_value == 1: variant_key = f"Shiny Golden {pet_name}" elif is_shiny and pt_value == 2: variant_key = f"Shiny Rainbow {pet_name}" elif is_shiny: variant_key = f"Shiny {pet_name}" elif pt_value == 1: variant_key = f"Golden {pet_name}" elif pt_value == 2: variant_key = f"Rainbow {pet_name}" else: variant_key = f"Normal {pet_name}" titanic_pet_counts[variant_key] = max(titanic_pet_counts[variant_key], value) sorted_titanic_pets = sorted(titanic_pet_counts.items(), key=lambda x: x[1]) embed = discord.Embed(title=f"Top {count} Least Common Titanic Pets", color=discord.Color.dark_red()) if sorted_titanic_pets: for i, (pet_name, count_value) in enumerate(sorted_titanic_pets[:count], 1): embed.add_field( name=f"{i}. {pet_name}", value=f"Exists: **{format_number(count_value)}**", inline=False ) else: embed.description = "No titanic pets found in the data." await ctx.send(embed=embed) def calculate_jelly_prediction(pet_name, current_value, hourly_rate): if pet_name not in JELLY_LEAVE_TIMES: return None current_time = datetime.now(pytz.timezone('US/Eastern')) leave_time = JELLY_LEAVE_TIMES[pet_name] if current_time >= leave_time: return None time_diff = leave_time - current_time hours_remaining = time_diff.total_seconds() / 3600 predicted_increase = hourly_rate * hours_remaining predicted_final = current_value + predicted_increase return { 'predicted_value': int(predicted_final), 'hours_remaining': round(hours_remaining, 1) } def calculate_time_until_limited(pet_name, current_value, hourly_rate): if pet_name not in LIMITED_PETS or hourly_rate <= 0: return None limit = LIMITED_PETS[pet_name] remaining = limit - current_value if remaining <= 0: return "Already Limited!" hours = remaining / hourly_rate if hours < 0: return "Error calculating time" days = hours // 24 remaining_hours = hours % 24 if days > 0: return f"~{int(days)}d {int(remaining_hours)}h" else: return f"~{int(remaining_hours)}h" async def send_rap_change_notification(channel, item_name, previous_rap, current_rap): percent_change = ((current_rap - previous_rap) / previous_rap) * 100 if current_rap > previous_rap: message = f"🚀 {item_name} RAP ROSE BY {percent_change:.2f}%!\n" + \ f"Previous RAP: {previous_rap:,}\n" + \ f"Current RAP: {current_rap:,}" await channel.send(message) else: message = f"📉 {item_name} RAP DROPPED BY {abs(percent_change):.2f}%!\n" + \ f"Previous RAP: {previous_rap:,}\n" + \ f"Current RAP: {current_rap:,}" await channel.send(message) async def update_rap_values(): try: response = requests.get(RAP_API) if response.status_code == 200: data = response.json() if data['status'] == 'ok': pet_raps.clear() monitored_channel = bot.get_channel(MONITORED_ITEMS_CHANNEL_ID) if monitored_channel: for pet_data in data['data']: config_data = pet_data['configData'] pet_id = config_data['id'] value = pet_data['value'] is_shiny = config_data.get('sh', False) monitored_key = f"Shiny {pet_id}" if is_shiny else pet_id if monitored_key in last_rap_values: current_rap = value previous_rap = last_rap_values[monitored_key] if previous_rap is not None and current_rap != previous_rap: await send_rap_change_notification(monitored_channel, monitored_key, previous_rap, current_rap) last_rap_values[monitored_key] = current_rap if is_shiny: pet_raps[f"Shiny {pet_id}"] = value else: pet_raps[pet_id] = value print(f"Updated RAP values for {len(data['data'])} pets and items") except Exception as e: print(f"Error fetching RAP values: {e}") def calculate_hourly_rate(pet_name): if pet_name not in pet_changes: return 0 changes = pet_changes[pet_name] if not changes: return 0 current_time = datetime.now() one_hour_ago = current_time - timedelta(hours=1) recent_changes = [change for change in changes if change['timestamp'] > one_hour_ago] if not recent_changes: return 0 if len(recent_changes) >= 2: earliest_value = recent_changes[0]['value'] latest_value = recent_changes[-1]['value'] return latest_value - earliest_value return 1 async def get_huge_secret_pets(): try: response = requests.get(PETS_API) if response.status_code == 200: data = response.json() if data['status'] == 'ok': huge_secret_pets = [] for pet in data['data']: config_data = pet['configData'] if config_data.get('huge') or config_data.get('secret'): pet_name = config_data['name'] huge_secret_pets.append(pet_name) huge_secret_pets.append(f"Shiny {pet_name}") difficulty = config_data.get('difficulty', 'Unknown') pet_difficulties[pet_name] = difficulty pet_difficulties[f"Shiny {pet_name}"] = difficulty * 100 if 'thumbnail' in config_data: pet_images[pet_name] = config_data['thumbnail'] pet_images[f"Shiny {pet_name}"] = config_data['thumbnail'] print(f"Stored image URL for {pet_name}: {config_data['thumbnail']}") for pet_name in huge_secret_pets: if pet_name not in pet_changes: pet_changes[pet_name] = deque(maxlen=100) print(f"Found {len(huge_secret_pets)} pets to track (including shiny versions)") return huge_secret_pets except Exception as e: print(f"Error fetching pets list: {e}") return [] async def send_embed_message(channel, pet_name, previous_value, current_value, is_change=False): notification_key = f"{pet_name}_{previous_value}_{current_value}" if notification_key in recent_notifications.get(pet_name, []): print(f"Skipping duplicate notification for {pet_name}") return if pet_name not in recent_notifications: recent_notifications[pet_name] = [] recent_notifications[pet_name].append(notification_key) pet_image_url = pet_images.get(pet_name, None) difficulty = pet_difficulties.get(pet_name, "Unknown") rap_value = pet_raps.get(pet_name, "No RAP") hourly_rate = calculate_hourly_rate(pet_name) difficulty_display = format_number(difficulty) if isinstance(difficulty, (int, float)) else difficulty rap_display = format_number(rap_value) if isinstance(rap_value, (int, float)) else rap_value current_display = f"{current_value:,}" if isinstance(current_value, (int, float)) else current_value previous_display = format_number(previous_value) if isinstance(previous_value, (int, float)) else previous_value hourly_rate_display = format_number(hourly_rate) if isinstance(hourly_rate, (int, float)) else hourly_rate is_shiny = pet_name.startswith("Shiny ") base_name = pet_name.replace("Shiny ", "") if is_shiny else pet_name time_until_limited = None if base_name in LIMITED_PETS: time_until_limited = calculate_time_until_limited(base_name, current_value, hourly_rate) jelly_prediction = None if base_name in JELLY_LEAVE_TIMES: jelly_prediction = calculate_jelly_prediction(base_name, current_value, hourly_rate) value_change = current_value - previous_value if previous_value is not None else current_value if is_shiny: title = f"✨ {f'A **SHINY** {base_name} was' if value_change == 1 else f'{value_change} **SHINY** {base_name}s were'} rolled! ✨" embed_color = discord.Color.from_rgb(255, 255, 255) else: title = f"🎲 {f'A {pet_name}' if value_change == 1 else f'{value_change} {pet_name}s'} were rolled! 🎲" embed_color = discord.Color.blue() if not is_change else discord.Color.orange() description = f"{pet_name} exists: **{current_display}**\n" \ f"Difficulty: **1 in {difficulty_display}**\n" \ f"RAP Value: **{rap_display}**\n" \ f"Hourly Rate: **{hourly_rate_display}/h**" if time_until_limited is not None: description += f"\nTime Remaining Until Limited: **{time_until_limited}**" if base_name in LIMITED_PETS: limit = LIMITED_PETS[base_name] remaining = limit - current_value if remaining > 0: description += f"\nQuantity Remaining: **{remaining:,}**" if jelly_prediction is not None: description += f"\nEstimated Final Count: **{jelly_prediction['predicted_value']:,}**" description += f"\nGoing Limited In: **{jelly_prediction['hours_remaining']}h**" embed = discord.Embed( title=title, description=description, color=embed_color ) if pet_image_url: if isinstance(pet_image_url, str): if pet_image_url.startswith('rbxassetid://'): asset_id = re.search(r'^\d+', pet_image_url.replace('rbxassetid://', '')).group(0) pet_image_url = f"https://rbxgleaks.pythonanywhere.com/asset/{asset_id}" print(f"Using image URL for {pet_name}: {pet_image_url}") embed.set_thumbnail(url=pet_image_url) try: await channel.send(embed=embed) except discord.HTTPException as e: print(f"Failed to send embed for {pet_name}: {e}") await channel.send(f"🎲 {'A' if value_change == 1 else value_change} {pet_name} was rolled! Exists: {current_display} (Previous: {previous_display})") async def petdata(tracked_pets): try: response = requests.get(EXISTS_API) if response.status_code == 200: data = response.json() if data['status'] == 'ok': pet_values = {pet: 0 for pet in tracked_pets} for pet in data['data']: pet_id = pet['configData']['id'] is_shiny = pet['configData'].get('sh', False) value = pet['value'] pet_name = f"Shiny {pet_id}" if is_shiny else pet_id if pet_name in pet_values: print(f"Found pet: {pet_name} with exist count {value}") pet_values[pet_name] = value return pet_values print(f"Error code: {response.status_code}") except Exception as e: print(f"Error: {e}") return None async def main_loop(): channel = bot.get_channel(CHANNEL_ID) if channel is None: print("Invalid channel ID. Please check your channel ID.") return tracked_pets = await get_huge_secret_pets() if not tracked_pets: print("No pets fetched, retrying in 60 seconds...") await asyncio.sleep(60) return lastknown = {pet: None for pet in tracked_pets} print(f"Initially tracking {len(tracked_pets)} pets") while True: try: if not tracked_pets: tracked_pets = await get_huge_secret_pets() or [] lastknown.update({pet: None for pet in tracked_pets if pet not in lastknown}) await update_rap_values() vvalues = await petdata(tracked_pets) if vvalues is not None: for pet, value in vvalues.items(): if lastknown[pet] is None: print(f"Initial value for {pet}: {value}") elif value != lastknown[pet]: pet_changes[pet].append({ 'timestamp': datetime.now(), 'value': value, 'previous': lastknown[pet] }) await send_embed_message(channel, pet, lastknown[pet], value, is_change=True) lastknown[pet] = value else: print("Broken check") new_pets = await get_huge_secret_pets() or [] if new_pets and set(new_pets) != set(tracked_pets): print("Pet list updated!") tracked_pets = new_pets lastknown.update({pet: None for pet in tracked_pets if pet not in lastknown}) await asyncio.sleep(60) except Exception as e: print(f"Error in main loop: {e}") await asyncio.sleep(60) @bot.event async def on_ready(): print(f'Logged in as {bot.user.name}') bot.loop.create_task(main_loop()) bot.run(TOKEN)