diff --git a/bot.py b/bot.py index 96d2f35..34d354c 100644 --- a/bot.py +++ b/bot.py @@ -1913,11 +1913,50 @@ async def listgiveaways(ctx): await ctx.send("You don't have permission to list giveaways.") return - # Filter giveaways for this guild server_giveaways = [] + + # First, check memory giveaways for giv_id, giveaway in giveaways.items(): if giveaway.guild_id == guild_id and not giveaway.is_finished(): - server_giveaways.append((giv_id, giveaway)) + server_giveaways.append((giv_id, giveaway, "memory")) + + # Also check active_processes table for giveaways not in memory + try: + active_processes = get_active_processes(process_type="giveaway", guild_id=guild_id) + + for process in active_processes: + process_uuid = process["uuid"] + + # Skip if already in memory + if str(process_uuid) in giveaways: + continue + + # Check if process is still active and not expired + if process["status"] == "active" and process["end_time"] > datetime.now(): + try: + # Create a temporary giveaway object from process data + guild = ctx.guild + channel = guild.get_channel(process["channel_id"]) + + if channel: + # Create minimal context + class MinimalContext: + def __init__(self, channel): + self.channel = channel + self.guild = channel.guild + + temp_ctx = MinimalContext(channel) + temp_giveaway = Giveaway.from_process_data(temp_ctx, process["data"]) + temp_giveaway.end_time = process["end_time"] + temp_giveaway.process_uuid = process_uuid + + server_giveaways.append((str(process_uuid), temp_giveaway, "database")) + + except Exception as e: + logger.error(f"Error creating temp giveaway from process {process_uuid}: {e}") + + except Exception as e: + logger.error(f"Error fetching active processes for giveaways: {e}") if not server_giveaways: embed = discord.Embed( @@ -1936,7 +1975,7 @@ async def listgiveaways(ctx): timestamp=datetime.now() ) - for giv_id, giveaway in server_giveaways[:10]: # Limit to 10 for readability + for giv_id, giveaway, source in server_giveaways[:10]: # Limit to 10 for readability # Calculate remaining time remaining = giveaway.end_time - datetime.now() if remaining.total_seconds() > 0: @@ -1967,7 +2006,11 @@ async def listgiveaways(ctx): if hasattr(giveaway, 'sponsor_display') and giveaway.sponsor_display: giveaway_info += f"**Sponsor:** {giveaway.sponsor_display}\n" - giveaway_info += f"**ID:** `{giv_id[:8]}...`" + # Add source indicator + source_emoji = "💾" if source == "database" else "🧠" + source_text = "DB" if source == "database" else "MEM" + + giveaway_info += f"**ID:** `{giv_id[:8]}...` {source_emoji}" embed.add_field( name=f"🎁 {giveaway.title}", @@ -1982,10 +2025,279 @@ async def listgiveaways(ctx): inline=False ) - embed.set_footer(text="Use /editgiveaway to edit a giveaway") + # Add helpful footer + db_count = sum(1 for _, _, source in server_giveaways if source == "database") + if db_count > 0: + embed.set_footer(text=f"💾 = Loaded from DB | Use /editgiveaway to edit | Use /loadgiveaway to load into memory") + else: + embed.set_footer(text="Use /editgiveaway to edit a giveaway") await ctx.send(embed=embed) +@client.hybrid_command() +async def loadgiveaway(ctx, giveaway_id: str): + """Load a giveaway from database into memory (Only available for admins)""" + guild_id = ctx.guild.id + user_data = load_user_data_sync(ctx.author.id, guild_id) + if user_data["permission"] < 5: + await ctx.send("You don't have permission to load giveaways.") + return + + try: + # Find giveaway in active_processes + active_processes = get_active_processes(process_type="giveaway", guild_id=guild_id) + + matching_process = None + for process in active_processes: + if str(process["uuid"]).startswith(giveaway_id) or giveaway_id in str(process["uuid"]): + matching_process = process + break + + if not matching_process: + await ctx.send(f"❌ No giveaway found with ID starting with `{giveaway_id}`") + return + + process_uuid = matching_process["uuid"] + + # Check if already loaded in memory + if str(process_uuid) in giveaways: + await ctx.send(f"â„šī¸ Giveaway `{str(process_uuid)[:8]}...` is already loaded in memory.") + return + + # Create giveaway object and load into memory + guild = ctx.guild + channel = guild.get_channel(matching_process["channel_id"]) + + if not channel: + await ctx.send(f"❌ Original channel not found. Cannot load giveaway.") + return + + # Create minimal context + class MinimalContext: + def __init__(self, channel): + self.channel = channel + self.guild = channel.guild + + temp_ctx = MinimalContext(channel) + giveaway = Giveaway.from_process_data(temp_ctx, matching_process["data"]) + giveaway.end_time = matching_process["end_time"] + giveaway.process_uuid = process_uuid + + # Restore participants from stored data + stored_participants = matching_process["data"].get("participants", []) + for participant_data in stored_participants: + try: + user = await client.fetch_user(participant_data["id"]) + giveaway.participants.append(user) + except Exception as e: + logger.error(f"Could not fetch participant {participant_data}: {e}") + + # Load into memory + giveaways[str(process_uuid)] = giveaway + + # Success message + embed = discord.Embed( + title="✅ Giveaway Loaded", + description=f"Successfully loaded giveaway into memory!", + color=0x00ff00, + timestamp=datetime.now() + ) + + embed.add_field(name="🎁 Title", value=giveaway.title, inline=True) + embed.add_field(name="🆔 ID", value=f"`{str(process_uuid)[:8]}...`", inline=True) + embed.add_field(name="đŸ‘Ĩ Participants", value=f"{len(giveaway.participants)}", inline=True) + + remaining = giveaway.end_time - datetime.now() + if remaining.total_seconds() > 0: + embed.add_field(name="⏰ Time Left", value=f"", inline=True) + else: + embed.add_field(name="⏰ Status", value="âš ī¸ Expired (will process soon)", inline=True) + + embed.set_footer(text="Giveaway is now fully functional in memory") + + await ctx.send(embed=embed) + + logger.info(f"Loaded giveaway {str(process_uuid)[:8]} into memory by {ctx.author.id}") + + except Exception as e: + logger.error(f"Error loading giveaway: {e}") + await ctx.send(f"❌ Error loading giveaway: {str(e)}") + +@client.hybrid_command() +async def loadallgiveaways(ctx): + """Load all giveaways from database into memory (Only available for admins)""" + guild_id = ctx.guild.id + user_data = load_user_data_sync(ctx.author.id, guild_id) + if user_data["permission"] < 5: + await ctx.send("You don't have permission to load giveaways.") + return + + try: + # Get all active giveaway processes for this guild + active_processes = get_active_processes(process_type="giveaway", guild_id=guild_id) + + if not active_processes: + await ctx.send("📋 No giveaways found in database.") + return + + loaded_count = 0 + already_loaded = 0 + failed_count = 0 + failed_details = [] + + embed = discord.Embed( + title="🔄 Loading Giveaways...", + description="Processing giveaways from database...", + color=0xffaa00, + timestamp=datetime.now() + ) + + message = await ctx.send(embed=embed) + + for process in active_processes: + process_uuid = process["uuid"] + + # Skip if already in memory + if str(process_uuid) in giveaways: + already_loaded += 1 + continue + + try: + # Get channel + guild = ctx.guild + channel = guild.get_channel(process["channel_id"]) + + if not channel: + failed_count += 1 + failed_details.append(f"Channel not found for {str(process_uuid)[:8]}...") + continue + + # Create minimal context + class MinimalContext: + def __init__(self, channel): + self.channel = channel + self.guild = channel.guild + + temp_ctx = MinimalContext(channel) + giveaway = Giveaway.from_process_data(temp_ctx, process["data"]) + giveaway.end_time = process["end_time"] + giveaway.process_uuid = process_uuid + + # Restore participants + stored_participants = process["data"].get("participants", []) + for participant_data in stored_participants: + try: + user = await client.fetch_user(participant_data["id"]) + giveaway.participants.append(user) + except: + pass # Skip invalid participants + + # Load into memory + giveaways[str(process_uuid)] = giveaway + loaded_count += 1 + + except Exception as e: + failed_count += 1 + failed_details.append(f"{str(process_uuid)[:8]}...: {str(e)[:50]}") + logger.error(f"Failed to load giveaway {process_uuid}: {e}") + + # Final result + embed = discord.Embed( + title="✅ Giveaway Loading Complete", + color=0x00ff00, + timestamp=datetime.now() + ) + + embed.add_field(name="✅ Loaded", value=f"{loaded_count} giveaways", inline=True) + embed.add_field(name="â„šī¸ Already in Memory", value=f"{already_loaded} giveaways", inline=True) + embed.add_field(name="❌ Failed", value=f"{failed_count} giveaways", inline=True) + + if failed_details: + embed.add_field(name="📋 Failed Details", value="\n".join(failed_details[:5]), inline=False) + if len(failed_details) > 5: + embed.add_field(name="", value=f"... and {len(failed_details) - 5} more", inline=False) + + embed.set_footer(text="All available giveaways have been processed") + + await message.edit(embed=embed) + + logger.info(f"Loaded {loaded_count} giveaways into memory by {ctx.author.id}") + + except Exception as e: + logger.error(f"Error loading all giveaways: {e}") + await ctx.send(f"❌ Error loading giveaways: {str(e)}") + +async def load_active_giveaways(): + """Load all active giveaways from database into memory on startup""" + try: + # Get all active giveaway processes + active_processes = get_active_processes(process_type="giveaway") + + if not active_processes: + logger.info("No active giveaways found in database") + return + + loaded_count = 0 + failed_count = 0 + + for process in active_processes: + try: + process_uuid = process["uuid"] + + # Skip if already in memory + if str(process_uuid) in giveaways: + continue + + # Get guild and channel + guild_id = process["guild_id"] + channel_id = process["channel_id"] + + guild = client.get_guild(guild_id) + if not guild: + failed_count += 1 + continue + + channel = guild.get_channel(channel_id) + if not channel: + failed_count += 1 + continue + + # Create minimal context + class MinimalContext: + def __init__(self, channel): + self.channel = channel + self.guild = channel.guild + + temp_ctx = MinimalContext(channel) + giveaway = Giveaway.from_process_data(temp_ctx, process["data"]) + giveaway.end_time = process["end_time"] + giveaway.process_uuid = process_uuid + + # Restore participants + stored_participants = process["data"].get("participants", []) + for participant_data in stored_participants: + try: + user = await client.fetch_user(participant_data["id"]) + giveaway.participants.append(user) + except: + pass # Skip invalid participants + + # Load into memory + giveaways[str(process_uuid)] = giveaway + loaded_count += 1 + + except Exception as e: + failed_count += 1 + logger.error(f"Failed to load giveaway {process.get('uuid', 'unknown')}: {e}") + + if loaded_count > 0: + logger.info(f"Loaded {loaded_count} active giveaways into memory") + if failed_count > 0: + logger.warning(f"Failed to load {failed_count} giveaways") + + except Exception as e: + logger.error(f"Error loading active giveaways: {e}") + # ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- live_chats = {} @@ -2419,6 +2731,9 @@ async def on_ready(): # Initialize process management system await restore_active_processes_on_startup() + # Load active giveaways from database + await load_active_giveaways() + # Start the process manager if not process_manager.is_running(): process_manager.start()