modified: bot.py
This commit is contained in:
346
bot.py
346
bot.py
@@ -2090,350 +2090,6 @@ async def updategiveawaymessage(ctx, giveaway_id: str):
|
||||
logger.error(f"Error updating giveaway message: {e}")
|
||||
await ctx.send(f"❌ Error updating giveaway message: {str(e)}")
|
||||
|
||||
@client.hybrid_command()
|
||||
async def endgiveaway(ctx, giveaway_id: str, force: bool = False):
|
||||
"""Manually end a giveaway and pick winners (Only available for admins)
|
||||
|
||||
Parameters:
|
||||
- giveaway_id: The giveaway ID (first 8 characters of UUID are enough)
|
||||
- force: Force end even if not expired (default: False)
|
||||
"""
|
||||
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 end giveaways.")
|
||||
return
|
||||
|
||||
# Find giveaway by partial ID (check both memory and database)
|
||||
matching_giveaway = None
|
||||
matching_id = None
|
||||
source = "memory"
|
||||
|
||||
# First check memory
|
||||
for giv_id, giveaway in giveaways.items():
|
||||
if giv_id.startswith(giveaway_id) or giveaway_id in giv_id:
|
||||
matching_giveaway = giveaway
|
||||
matching_id = giv_id
|
||||
break
|
||||
|
||||
# If not in memory, check database
|
||||
if not matching_giveaway:
|
||||
active_processes = get_active_processes(process_type="giveaway", guild_id=guild_id)
|
||||
|
||||
for process in active_processes:
|
||||
if str(process["uuid"]).startswith(giveaway_id) or giveaway_id in str(process["uuid"]):
|
||||
# Load giveaway from database
|
||||
try:
|
||||
guild = ctx.guild
|
||||
channel = guild.get_channel(process["channel_id"])
|
||||
|
||||
if not channel:
|
||||
await ctx.send(f"❌ Original channel not found for giveaway `{giveaway_id}`")
|
||||
return
|
||||
|
||||
# Create minimal context
|
||||
class MinimalContext:
|
||||
def __init__(self, channel):
|
||||
self.channel = channel
|
||||
self.guild = channel.guild
|
||||
|
||||
temp_ctx = MinimalContext(channel)
|
||||
matching_giveaway = Giveaway.from_process_data(temp_ctx, process["data"])
|
||||
matching_giveaway.end_time = process["end_time"]
|
||||
matching_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"])
|
||||
matching_giveaway.participants.append(user)
|
||||
except:
|
||||
pass
|
||||
|
||||
matching_id = str(process["uuid"])
|
||||
source = "database"
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error loading giveaway from database: {e}")
|
||||
await ctx.send(f"❌ Error loading giveaway: {str(e)}")
|
||||
return
|
||||
|
||||
if not matching_giveaway:
|
||||
await ctx.send(f"❌ No giveaway found with ID starting with `{giveaway_id}`")
|
||||
return
|
||||
|
||||
# Check if giveaway can be ended
|
||||
is_expired = matching_giveaway.is_finished()
|
||||
|
||||
if not is_expired and not force:
|
||||
remaining = matching_giveaway.end_time - datetime.now()
|
||||
embed = discord.Embed(
|
||||
title="⚠️ Giveaway Not Expired",
|
||||
description=f"This giveaway is still active and not expired yet.",
|
||||
color=0xffa500,
|
||||
timestamp=datetime.now()
|
||||
)
|
||||
embed.add_field(name="🆔 Giveaway ID", value=f"`{matching_id[:8]}...`", inline=True)
|
||||
embed.add_field(name="⏰ Time Left", value=f"<t:{int(matching_giveaway.end_time.timestamp())}:R>", inline=True)
|
||||
embed.add_field(name="🔧 Force End", value="Use `/endgiveaway <id> True` to force end", inline=False)
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
return
|
||||
|
||||
# Check if giveaway has participants
|
||||
if len(matching_giveaway.participants) == 0:
|
||||
embed = discord.Embed(
|
||||
title="❌ No Participants",
|
||||
description=f"This giveaway has no participants, so no winners can be selected.",
|
||||
color=0xff0000,
|
||||
timestamp=datetime.now()
|
||||
)
|
||||
embed.add_field(name="🆔 Giveaway ID", value=f"`{matching_id[:8]}...`", inline=True)
|
||||
embed.add_field(name="👥 Participants", value="0", inline=True)
|
||||
|
||||
# Still mark as completed
|
||||
matching_giveaway.complete_giveaway()
|
||||
if source == "memory" and matching_id in giveaways:
|
||||
del giveaways[matching_id]
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
return
|
||||
|
||||
try:
|
||||
# Pick winners
|
||||
winners = matching_giveaway.pick_winners()
|
||||
|
||||
if not winners:
|
||||
await ctx.send(f"❌ Could not pick winners from {len(matching_giveaway.participants)} participants.")
|
||||
return
|
||||
|
||||
# Create winner announcement
|
||||
winner_embed = discord.Embed(
|
||||
title="🎉 Giveaway Ended - Winners Announced!",
|
||||
description=f"**{matching_giveaway.title}** has been manually ended!",
|
||||
color=0xFFD700,
|
||||
timestamp=datetime.now()
|
||||
)
|
||||
|
||||
# Enhanced prize display with game info
|
||||
if hasattr(matching_giveaway, 'game_info') and matching_giveaway.game_info and matching_giveaway.game_info.get('name'):
|
||||
if hasattr(matching_giveaway, 'game_url') and matching_giveaway.game_url:
|
||||
prize_text = f"**[{matching_giveaway.game_info['name']}]({matching_giveaway.game_url})**"
|
||||
else:
|
||||
prize_text = f"**{matching_giveaway.game_info['name']}**"
|
||||
else:
|
||||
if hasattr(matching_giveaway, 'game_url') and matching_giveaway.game_url:
|
||||
prize_text = f"**[{matching_giveaway.prize}]({matching_giveaway.game_url})**"
|
||||
else:
|
||||
prize_text = f"**{matching_giveaway.prize}**"
|
||||
|
||||
winner_embed.add_field(name="🎁 Prize", value=prize_text, inline=True)
|
||||
winner_embed.add_field(name="🎮 Platform", value=f"**{matching_giveaway.platform}**", inline=True)
|
||||
winner_embed.add_field(name="👥 Total Participants", value=f"**{len(matching_giveaway.participants)}**", inline=True)
|
||||
|
||||
# List winners
|
||||
winner_list = []
|
||||
for i, winner in enumerate(winners, 1):
|
||||
winner_list.append(f"🏆 **#{i}** {winner.mention}")
|
||||
|
||||
winner_embed.add_field(name="🎊 Winners", value="\n".join(winner_list), inline=False)
|
||||
|
||||
# Add sponsor info if available
|
||||
if hasattr(matching_giveaway, 'sponsor') and matching_giveaway.sponsor:
|
||||
winner_embed.add_field(name="💝 Sponsored by", value=f"**{matching_giveaway.sponsor}**", inline=False)
|
||||
|
||||
# Add manual end notice
|
||||
end_type = "🔧 Manually ended" if not is_expired else "⏰ Expired (manually processed)"
|
||||
winner_embed.add_field(name="📋 Status", value=f"{end_type} by {ctx.author.mention}", inline=False)
|
||||
|
||||
winner_embed.add_field(name="📧 Next Steps",
|
||||
value="Winners have been sent a DM with prize claim instructions!",
|
||||
inline=False)
|
||||
|
||||
# Set game image if available
|
||||
if hasattr(matching_giveaway, 'game_info') and matching_giveaway.game_info and matching_giveaway.game_info.get('image_url'):
|
||||
winner_embed.set_image(url=matching_giveaway.game_info['image_url'])
|
||||
winner_embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1028701098145587302.png")
|
||||
else:
|
||||
winner_embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1028701098145587302.png")
|
||||
|
||||
winner_embed.set_footer(text="Congratulations to all winners! 🎉")
|
||||
|
||||
await ctx.send(embed=winner_embed)
|
||||
|
||||
# Process winners with enhanced DM
|
||||
for i, winner in enumerate(winners):
|
||||
try:
|
||||
if i < len(matching_giveaway.winner_uuids):
|
||||
winner_uuid = matching_giveaway.winner_uuids[i]
|
||||
assign_winner_to_uuid(winner_uuid, winner.id)
|
||||
|
||||
# Enhanced winner DM
|
||||
dm_embed = discord.Embed(
|
||||
title="🎉 Congratulations! You Won!",
|
||||
description=f"You are a winner in the **{matching_giveaway.title}** giveaway!",
|
||||
color=0xFFD700,
|
||||
timestamp=datetime.now()
|
||||
)
|
||||
|
||||
dm_embed.add_field(name="🎁 Prize", value=prize_text, inline=True)
|
||||
dm_embed.add_field(name="🎮 Platform", value=f"**{matching_giveaway.platform}**", inline=True)
|
||||
dm_embed.add_field(name="🏆 Position", value=f"Winner #{i+1}", inline=True)
|
||||
|
||||
dm_embed.add_field(name="📋 Prize Claim Instructions",
|
||||
value="Please contact the server administrators to claim your prize. "
|
||||
"Make sure to mention this giveaway and your winner position!",
|
||||
inline=False)
|
||||
|
||||
if hasattr(matching_giveaway, 'sponsor') and matching_giveaway.sponsor:
|
||||
dm_embed.add_field(name="💝 Sponsored by", value=f"**{matching_giveaway.sponsor}**", inline=False)
|
||||
|
||||
dm_embed.set_footer(text=f"Server: {ctx.guild.name} • Giveaway ID: {matching_id[:8]}...")
|
||||
|
||||
try:
|
||||
await winner.send(embed=dm_embed)
|
||||
logger.info(f"Sent winner DM to {winner.name} for giveaway {matching_id[:8]}")
|
||||
except discord.Forbidden:
|
||||
logger.warning(f"Could not send DM to winner {winner.name}")
|
||||
# Notify in channel that DM failed
|
||||
await ctx.send(f"⚠️ Could not send DM to {winner.mention}. Please contact them manually!")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing winner {winner.name}: {e}")
|
||||
|
||||
# Mark giveaway as completed and remove from memory
|
||||
matching_giveaway.complete_giveaway()
|
||||
if source == "memory" and matching_id in giveaways:
|
||||
del giveaways[matching_id]
|
||||
|
||||
logger.info(f"Manually ended giveaway {matching_id[:8]} by {ctx.author.id}, {len(winners)} winners selected")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error ending giveaway: {e}")
|
||||
await ctx.send(f"❌ Error ending giveaway: {str(e)}")
|
||||
|
||||
@client.hybrid_command()
|
||||
async def expiredgiveaways(ctx):
|
||||
"""List all expired giveaways that haven't been processed yet (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 view expired giveaways.")
|
||||
return
|
||||
|
||||
expired_giveaways = []
|
||||
|
||||
# Check memory giveaways
|
||||
for giv_id, giveaway in giveaways.items():
|
||||
if giveaway.guild_id == guild_id and giveaway.is_finished():
|
||||
expired_giveaways.append((giv_id, giveaway, "memory"))
|
||||
|
||||
# Check database giveaways
|
||||
try:
|
||||
active_processes = get_active_processes(process_type="giveaway", guild_id=guild_id)
|
||||
|
||||
for process in active_processes:
|
||||
process_uuid = str(process["uuid"])
|
||||
|
||||
# Skip if already in memory
|
||||
if process_uuid in giveaways:
|
||||
continue
|
||||
|
||||
# Check if expired
|
||||
if process["end_time"] <= datetime.now():
|
||||
try:
|
||||
# Create minimal giveaway object for display
|
||||
class MinimalGiveaway:
|
||||
def __init__(self, data, end_time, uuid):
|
||||
self.title = data.get("title", "Unknown Giveaway")
|
||||
self.prize = data.get("prize", "Unknown Prize")
|
||||
self.platform = data.get("platform", "Unknown")
|
||||
self.num_winners = data.get("num_winners", 1)
|
||||
self.end_time = end_time
|
||||
self.process_uuid = uuid
|
||||
self.participants_count = len(data.get("participants", []))
|
||||
|
||||
temp_giveaway = MinimalGiveaway(process["data"], process["end_time"], process["uuid"])
|
||||
expired_giveaways.append((process_uuid, temp_giveaway, "database"))
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing expired giveaway {process_uuid}: {e}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking database for expired giveaways: {e}")
|
||||
|
||||
if not expired_giveaways:
|
||||
embed = discord.Embed(
|
||||
title="✅ No Expired Giveaways",
|
||||
description="There are currently no expired giveaways that need processing.",
|
||||
color=0x00ff00
|
||||
)
|
||||
await ctx.send(embed=embed)
|
||||
return
|
||||
|
||||
# Create list embed
|
||||
embed = discord.Embed(
|
||||
title="⏰ Expired Giveaways",
|
||||
description=f"Found **{len(expired_giveaways)}** expired giveaway(s) that need processing:",
|
||||
color=0xff6b6b,
|
||||
timestamp=datetime.now()
|
||||
)
|
||||
|
||||
for giv_id, giveaway, source in expired_giveaways[:10]: # Limit to 10
|
||||
# Calculate how long expired
|
||||
time_since_expired = datetime.now() - giveaway.end_time
|
||||
days = time_since_expired.days
|
||||
hours, remainder = divmod(time_since_expired.seconds, 3600)
|
||||
|
||||
if days > 0:
|
||||
expired_for = f"{days}d {hours}h ago"
|
||||
elif hours > 0:
|
||||
expired_for = f"{hours}h ago"
|
||||
else:
|
||||
minutes = remainder // 60
|
||||
expired_for = f"{minutes}m ago"
|
||||
|
||||
# Source indicator
|
||||
source_emoji = "🧠" if source == "memory" else "💾"
|
||||
|
||||
# Participant count
|
||||
if hasattr(giveaway, 'participants'):
|
||||
participant_count = len(giveaway.participants)
|
||||
else:
|
||||
participant_count = getattr(giveaway, 'participants_count', 0)
|
||||
|
||||
embed.add_field(
|
||||
name=f"{source_emoji} {giveaway.title}",
|
||||
value=(
|
||||
f"**ID:** `{giv_id[:8]}...`\n"
|
||||
f"**Prize:** {giveaway.prize}\n"
|
||||
f"**Platform:** {giveaway.platform}\n"
|
||||
f"**Winners:** {giveaway.num_winners}\n"
|
||||
f"**Participants:** {participant_count}\n"
|
||||
f"**Expired:** {expired_for}"
|
||||
),
|
||||
inline=True
|
||||
)
|
||||
|
||||
if len(expired_giveaways) > 10:
|
||||
embed.add_field(
|
||||
name="📋 Additional Info",
|
||||
value=f"... and {len(expired_giveaways) - 10} more expired giveaways",
|
||||
inline=False
|
||||
)
|
||||
|
||||
embed.add_field(
|
||||
name="🔧 How to Process",
|
||||
value="Use `/endgiveaway <id>` to manually end and pick winners for any expired giveaway.",
|
||||
inline=False
|
||||
)
|
||||
|
||||
embed.set_footer(text="💾 = Database | 🧠 = Memory | Use /endgiveaway <id> to process")
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@client.hybrid_command()
|
||||
async def listgiveaways(ctx):
|
||||
"""List all active giveaways in this server (Only available for admins)"""
|
||||
@@ -3612,9 +3268,7 @@ async def modhelp(ctx):
|
||||
"`/processes <action> [type]` - Manage active processes\n"
|
||||
"`/startgiveaway` - Create server giveaways with Steam/Epic integration\n"
|
||||
"`/editgiveaway <id> <field> <value>` - Edit active giveaways (auto-updates post)\n"
|
||||
"`/endgiveaway <id> [force]` - Manually end giveaway and pick winners\n"
|
||||
"`/listgiveaways` - List all active giveaways (memory + database)\n"
|
||||
"`/expiredgiveaways` - Show expired giveaways that need processing\n"
|
||||
"`/loadgiveaway <id>` - Load specific giveaway from database\n"
|
||||
"`/loadallgiveaways` - Load all giveaways from database\n"
|
||||
"`/updategiveawaymessage <id>` - Manually refresh giveaway post\n"
|
||||
|
||||
Reference in New Issue
Block a user