849 lines
34 KiB
Python
849 lines
34 KiB
Python
import discord
|
||
from discord.ext import commands
|
||
from discord import app_commands
|
||
import logging
|
||
import asyncio
|
||
import random
|
||
import datetime
|
||
from typing import Optional
|
||
|
||
# Import command classes and db functions from submodules
|
||
from .economy.database import init_db, close_db, get_balance, update_balance, set_cooldown, check_cooldown
|
||
from .economy.database import get_user_job, set_user_job, remove_user_job, get_available_jobs, get_leaderboard
|
||
from .economy.earning import EarningCommands
|
||
from .economy.gambling import GamblingCommands
|
||
from .economy.utility import UtilityCommands
|
||
from .economy.risky import RiskyCommands
|
||
from .economy.jobs import JobsCommands # Import the new JobsCommands
|
||
|
||
# Create a database object for function calls
|
||
class DatabaseWrapper:
|
||
async def get_balance(self, user_id):
|
||
return await get_balance(user_id)
|
||
|
||
async def update_balance(self, user_id, amount):
|
||
return await update_balance(user_id, amount)
|
||
|
||
async def set_cooldown(self, user_id, command_name):
|
||
return await set_cooldown(user_id, command_name)
|
||
|
||
async def check_cooldown(self, user_id, command_name):
|
||
return await check_cooldown(user_id, command_name)
|
||
|
||
async def get_user_job(self, user_id):
|
||
return await get_user_job(user_id)
|
||
|
||
async def set_user_job(self, user_id, job_name):
|
||
return await set_user_job(user_id, job_name)
|
||
|
||
async def remove_user_job(self, user_id):
|
||
return await remove_user_job(user_id)
|
||
|
||
async def get_available_jobs(self):
|
||
return await get_available_jobs()
|
||
|
||
async def get_leaderboard(self, limit=10):
|
||
return await get_leaderboard(limit)
|
||
|
||
# Create an instance of the wrapper
|
||
database = DatabaseWrapper()
|
||
|
||
log = logging.getLogger(__name__)
|
||
|
||
# --- Main Cog Implementation ---
|
||
|
||
# Inherit from commands.Cog and all the command classes
|
||
class EconomyCog(
|
||
EarningCommands,
|
||
GamblingCommands,
|
||
UtilityCommands,
|
||
RiskyCommands,
|
||
JobsCommands, # Add JobsCommands to the inheritance list
|
||
commands.Cog # Ensure commands.Cog is included
|
||
):
|
||
"""Main cog for the economy system, combining all command groups."""
|
||
|
||
def __init__(self, bot: commands.Bot):
|
||
# Initialize all parent cogs (important!)
|
||
super().__init__(bot) # Calls __init__ of the first parent in MRO (EarningCommands)
|
||
# If other parent cogs had complex __init__, we might need to call them explicitly,
|
||
# but in this case, they only store the bot instance, which super() handles.
|
||
self.bot = bot
|
||
|
||
# Create the main command group for this cog
|
||
self.economy_group = app_commands.Group(
|
||
name="economy",
|
||
description="Economy system commands"
|
||
)
|
||
|
||
# Create subgroups
|
||
self.earning_group = app_commands.Group(
|
||
name="earning",
|
||
description="Commands for earning currency",
|
||
parent=self.economy_group
|
||
)
|
||
|
||
self.gambling_group = app_commands.Group(
|
||
name="gambling",
|
||
description="Gambling and games of chance",
|
||
parent=self.economy_group
|
||
)
|
||
|
||
self.utility_group = app_commands.Group(
|
||
name="utility",
|
||
description="Utility commands for the economy system",
|
||
parent=self.economy_group
|
||
)
|
||
|
||
self.risky_group = app_commands.Group(
|
||
name="risky",
|
||
description="High-risk, high-reward commands",
|
||
parent=self.economy_group
|
||
)
|
||
|
||
self.jobs_group = app_commands.Group(
|
||
name="jobs",
|
||
description="Job-related commands",
|
||
parent=self.economy_group
|
||
)
|
||
|
||
# Register commands
|
||
self.register_commands()
|
||
|
||
# Add command groups to the bot's tree
|
||
self.bot.tree.add_command(self.economy_group)
|
||
|
||
log.info("EconomyCog initialized with command groups.")
|
||
|
||
def register_commands(self):
|
||
"""Register all commands for this cog"""
|
||
|
||
# --- Earning Group Commands ---
|
||
# Daily command
|
||
daily_command = app_commands.Command(
|
||
name="daily",
|
||
description="Claim your daily reward",
|
||
callback=self.economy_daily_callback,
|
||
parent=self.earning_group
|
||
)
|
||
self.earning_group.add_command(daily_command)
|
||
|
||
# Beg command
|
||
beg_command = app_commands.Command(
|
||
name="beg",
|
||
description="Beg for some spare change",
|
||
callback=self.economy_beg_callback,
|
||
parent=self.earning_group
|
||
)
|
||
self.earning_group.add_command(beg_command)
|
||
|
||
# Work command
|
||
work_command = app_commands.Command(
|
||
name="work",
|
||
description="Do some work for a guaranteed reward",
|
||
callback=self.economy_work_callback,
|
||
parent=self.earning_group
|
||
)
|
||
self.earning_group.add_command(work_command)
|
||
|
||
# Scavenge command
|
||
scavenge_command = app_commands.Command(
|
||
name="scavenge",
|
||
description="Scavenge around for some spare change",
|
||
callback=self.economy_scavenge_callback,
|
||
parent=self.earning_group
|
||
)
|
||
self.earning_group.add_command(scavenge_command)
|
||
|
||
# --- Gambling Group Commands ---
|
||
# Coinflip command
|
||
coinflip_command = app_commands.Command(
|
||
name="coinflip",
|
||
description="Bet on a coin flip",
|
||
callback=self.economy_coinflip_callback,
|
||
parent=self.gambling_group
|
||
)
|
||
self.gambling_group.add_command(coinflip_command)
|
||
|
||
# Slots command
|
||
slots_command = app_commands.Command(
|
||
name="slots",
|
||
description="Play the slot machine",
|
||
callback=self.economy_slots_callback,
|
||
parent=self.gambling_group
|
||
)
|
||
self.gambling_group.add_command(slots_command)
|
||
|
||
# --- Utility Group Commands ---
|
||
# Balance command
|
||
balance_command = app_commands.Command(
|
||
name="balance",
|
||
description="Check your balance",
|
||
callback=self.economy_balance_callback,
|
||
parent=self.utility_group
|
||
)
|
||
self.utility_group.add_command(balance_command)
|
||
|
||
# Transfer command
|
||
transfer_command = app_commands.Command(
|
||
name="transfer",
|
||
description="Transfer money to another user",
|
||
callback=self.economy_transfer_callback,
|
||
parent=self.utility_group
|
||
)
|
||
self.utility_group.add_command(transfer_command)
|
||
|
||
# Leaderboard command
|
||
leaderboard_command = app_commands.Command(
|
||
name="leaderboard",
|
||
description="View the economy leaderboard",
|
||
callback=self.economy_leaderboard_callback,
|
||
parent=self.utility_group
|
||
)
|
||
self.utility_group.add_command(leaderboard_command)
|
||
|
||
# --- Risky Group Commands ---
|
||
# Rob command
|
||
rob_command = app_commands.Command(
|
||
name="rob",
|
||
description="Attempt to rob another user",
|
||
callback=self.economy_rob_callback,
|
||
parent=self.risky_group
|
||
)
|
||
self.risky_group.add_command(rob_command)
|
||
|
||
# --- Jobs Group Commands ---
|
||
# Apply command
|
||
apply_command = app_commands.Command(
|
||
name="apply",
|
||
description="Apply for a job",
|
||
callback=self.economy_apply_callback,
|
||
parent=self.jobs_group
|
||
)
|
||
self.jobs_group.add_command(apply_command)
|
||
|
||
# Quit command
|
||
quit_command = app_commands.Command(
|
||
name="quit",
|
||
description="Quit your current job",
|
||
callback=self.economy_quit_callback,
|
||
parent=self.jobs_group
|
||
)
|
||
self.jobs_group.add_command(quit_command)
|
||
|
||
# List command
|
||
list_command = app_commands.Command(
|
||
name="list",
|
||
description="List available jobs",
|
||
callback=self.economy_joblist_callback,
|
||
parent=self.jobs_group
|
||
)
|
||
self.jobs_group.add_command(list_command)
|
||
|
||
async def cog_load(self):
|
||
"""Called when the cog is loaded, ensures DB is initialized."""
|
||
log.info("Loading EconomyCog (combined)...")
|
||
try:
|
||
await init_db()
|
||
log.info("EconomyCog database initialization complete.")
|
||
except Exception as e:
|
||
log.error(f"EconomyCog failed to initialize database during load: {e}", exc_info=True)
|
||
# Prevent the cog from loading if DB init fails
|
||
raise commands.ExtensionFailed(self.qualified_name, e) from e
|
||
|
||
# --- Command Callbacks ---
|
||
# Earning group callbacks
|
||
async def economy_daily_callback(self, interaction: discord.Interaction):
|
||
"""Callback for /economy earning daily command"""
|
||
user_id = interaction.user.id
|
||
command_name = "daily"
|
||
cooldown_duration = datetime.timedelta(hours=24)
|
||
reward_amount = 100 # Example daily reward
|
||
|
||
last_used = await database.check_cooldown(user_id, command_name)
|
||
|
||
if last_used:
|
||
now_utc = datetime.datetime.now(datetime.timezone.utc)
|
||
# Ensure last_used is timezone-aware for comparison
|
||
if last_used.tzinfo is None:
|
||
last_used = last_used.replace(tzinfo=datetime.timezone.utc)
|
||
|
||
time_since_last_used = now_utc - last_used
|
||
if time_since_last_used < cooldown_duration:
|
||
time_left = cooldown_duration - time_since_last_used
|
||
hours, remainder = divmod(int(time_left.total_seconds()), 3600)
|
||
minutes, seconds = divmod(remainder, 60)
|
||
embed = discord.Embed(description=f"🕒 You've already claimed your daily reward. Try again in **{hours}h {minutes}m {seconds}s**.", color=discord.Color.orange())
|
||
await interaction.response.send_message(embed=embed, ephemeral=True)
|
||
return
|
||
|
||
# Not on cooldown or cooldown expired
|
||
await database.update_balance(user_id, reward_amount)
|
||
await database.set_cooldown(user_id, command_name)
|
||
current_balance = await database.get_balance(user_id)
|
||
embed = discord.Embed(
|
||
title="Daily Reward Claimed!",
|
||
description=f"🎉 You claimed your daily reward of **${reward_amount:,}**!",
|
||
color=discord.Color.green()
|
||
)
|
||
embed.add_field(name="New Balance", value=f"${current_balance:,}", inline=False)
|
||
await interaction.response.send_message(embed=embed)
|
||
|
||
async def economy_beg_callback(self, interaction: discord.Interaction):
|
||
"""Callback for /economy earning beg command"""
|
||
user_id = interaction.user.id
|
||
command_name = "beg"
|
||
cooldown_duration = datetime.timedelta(minutes=5) # 5-minute cooldown
|
||
success_chance = 0.4 # 40% chance of success
|
||
min_reward = 1
|
||
max_reward = 20
|
||
|
||
last_used = await database.check_cooldown(user_id, command_name)
|
||
|
||
if last_used:
|
||
now_utc = datetime.datetime.now(datetime.timezone.utc)
|
||
if last_used.tzinfo is None:
|
||
last_used = last_used.replace(tzinfo=datetime.timezone.utc)
|
||
|
||
time_since_last_used = now_utc - last_used
|
||
if time_since_last_used < cooldown_duration:
|
||
time_left = cooldown_duration - time_since_last_used
|
||
minutes, seconds = divmod(int(time_left.total_seconds()), 60)
|
||
embed = discord.Embed(description=f"🕒 You can't beg again so soon. Try again in **{minutes}m {seconds}s**.", color=discord.Color.orange())
|
||
await interaction.response.send_message(embed=embed, ephemeral=True)
|
||
return
|
||
|
||
# Set cooldown regardless of success/failure
|
||
await database.set_cooldown(user_id, command_name)
|
||
|
||
# Determine success
|
||
if random.random() < success_chance:
|
||
reward_amount = random.randint(min_reward, max_reward)
|
||
await database.update_balance(user_id, reward_amount)
|
||
current_balance = await database.get_balance(user_id)
|
||
embed = discord.Embed(
|
||
title="Begging Successful!",
|
||
description=f"🙏 Someone took pity on you! You received **${reward_amount:,}**.",
|
||
color=discord.Color.green()
|
||
)
|
||
embed.add_field(name="New Balance", value=f"${current_balance:,}", inline=False)
|
||
await interaction.response.send_message(embed=embed)
|
||
else:
|
||
embed = discord.Embed(
|
||
title="Begging Failed",
|
||
description="🤷 Nobody gave you anything. Better luck next time!",
|
||
color=discord.Color.red()
|
||
)
|
||
await interaction.response.send_message(embed=embed)
|
||
|
||
async def economy_work_callback(self, interaction: discord.Interaction):
|
||
"""Callback for /economy earning work command"""
|
||
user_id = interaction.user.id
|
||
command_name = "work"
|
||
cooldown_duration = datetime.timedelta(hours=1) # 1-hour cooldown
|
||
reward_amount = random.randint(15, 35) # Small reward range - This is now fallback if no job
|
||
|
||
# --- Check if user has a job ---
|
||
job_info = await database.get_user_job(user_id)
|
||
if job_info and job_info.get("name"):
|
||
job_key = job_info["name"]
|
||
command_to_use = f"`/economy jobs {job_key}`" # Updated command path
|
||
embed = discord.Embed(description=f"💼 You have a job! Use {command_to_use} instead of the generic work command.", color=discord.Color.blue())
|
||
await interaction.response.send_message(embed=embed, ephemeral=True)
|
||
return
|
||
# --- End Job Check ---
|
||
|
||
# Proceed with generic work only if no job
|
||
last_used = await database.check_cooldown(user_id, command_name)
|
||
|
||
if last_used:
|
||
now_utc = datetime.datetime.now(datetime.timezone.utc)
|
||
if last_used.tzinfo is None:
|
||
last_used = last_used.replace(tzinfo=datetime.timezone.utc)
|
||
|
||
time_since_last_used = now_utc - last_used
|
||
if time_since_last_used < cooldown_duration:
|
||
time_left = cooldown_duration - time_since_last_used
|
||
hours, remainder = divmod(int(time_left.total_seconds()), 3600)
|
||
minutes, seconds = divmod(remainder, 60)
|
||
embed = discord.Embed(description=f"🕒 You need to rest after working. Try again in **{hours}h {minutes}m {seconds}s**.", color=discord.Color.orange())
|
||
await interaction.response.send_message(embed=embed, ephemeral=True)
|
||
return
|
||
|
||
# Set cooldown and give reward
|
||
await database.set_cooldown(user_id, command_name)
|
||
await database.update_balance(user_id, reward_amount)
|
||
# Add some flavor text
|
||
work_messages = [
|
||
f"You worked hard and earned **${reward_amount:,}**!",
|
||
f"After a solid hour of work, you got **${reward_amount:,}**.",
|
||
f"Your efforts paid off! You received **${reward_amount:,}**.",
|
||
]
|
||
current_balance = await database.get_balance(user_id)
|
||
embed = discord.Embed(
|
||
title="Work Complete!",
|
||
description=random.choice(work_messages),
|
||
color=discord.Color.green()
|
||
)
|
||
embed.add_field(name="New Balance", value=f"${current_balance:,}", inline=False)
|
||
await interaction.response.send_message(embed=embed)
|
||
|
||
async def economy_scavenge_callback(self, interaction: discord.Interaction):
|
||
"""Callback for /economy earning scavenge command"""
|
||
user_id = interaction.user.id
|
||
command_name = "scavenge"
|
||
cooldown_duration = datetime.timedelta(minutes=30) # 30-minute cooldown
|
||
success_chance = 0.25 # 25% chance to find something
|
||
min_reward = 1
|
||
max_reward = 10
|
||
|
||
last_used = await database.check_cooldown(user_id, command_name)
|
||
|
||
if last_used:
|
||
now_utc = datetime.datetime.now(datetime.timezone.utc)
|
||
if last_used.tzinfo is None:
|
||
last_used = last_used.replace(tzinfo=datetime.timezone.utc)
|
||
|
||
time_since_last_used = now_utc - last_used
|
||
if time_since_last_used < cooldown_duration:
|
||
time_left = cooldown_duration - time_since_last_used
|
||
minutes, seconds = divmod(int(time_left.total_seconds()), 60)
|
||
embed = discord.Embed(description=f"🕒 You've searched recently. Try again in **{minutes}m {seconds}s**.", color=discord.Color.orange())
|
||
await interaction.response.send_message(embed=embed, ephemeral=True)
|
||
return
|
||
|
||
# Set cooldown regardless of success
|
||
await database.set_cooldown(user_id, command_name)
|
||
|
||
# Flavor text for scavenging
|
||
scavenge_locations = [
|
||
"under the sofa cushions", "in an old coat pocket", "behind the dumpster",
|
||
"in a dusty corner", "on the sidewalk", "in a forgotten drawer"
|
||
]
|
||
location = random.choice(scavenge_locations)
|
||
|
||
if random.random() < success_chance:
|
||
reward_amount = random.randint(min_reward, max_reward)
|
||
await database.update_balance(user_id, reward_amount)
|
||
current_balance = await database.get_balance(user_id)
|
||
embed = discord.Embed(
|
||
title="Scavenging Successful!",
|
||
description=f"🔍 You scavenged {location} and found **${reward_amount:,}**!",
|
||
color=discord.Color.green()
|
||
)
|
||
embed.add_field(name="New Balance", value=f"${current_balance:,}", inline=False)
|
||
await interaction.response.send_message(embed=embed)
|
||
else:
|
||
embed = discord.Embed(
|
||
title="Scavenging Failed",
|
||
description=f"🔍 You scavenged {location} but found nothing but lint.",
|
||
color=discord.Color.red()
|
||
)
|
||
await interaction.response.send_message(embed=embed)
|
||
|
||
# Gambling group callbacks
|
||
async def economy_coinflip_callback(self, interaction: discord.Interaction, bet: int, choice: app_commands.Choice[str]):
|
||
"""Callback for /economy gambling coinflip command"""
|
||
user_id = interaction.user.id
|
||
|
||
# Validate bet amount
|
||
if bet <= 0:
|
||
await interaction.response.send_message("❌ Your bet must be greater than 0.", ephemeral=True)
|
||
return
|
||
|
||
# Check if user has enough money
|
||
balance = await database.get_balance(user_id)
|
||
if bet > balance:
|
||
await interaction.response.send_message(f"❌ You don't have enough money. Your balance: ${balance:,}", ephemeral=True)
|
||
return
|
||
|
||
# Process the bet
|
||
result = "Heads" if random.random() < 0.5 else "Tails"
|
||
user_choice = choice.value
|
||
|
||
# Determine outcome
|
||
if result == user_choice:
|
||
# Win - double the bet
|
||
winnings = bet
|
||
await database.update_balance(user_id, winnings)
|
||
new_balance = await database.get_balance(user_id)
|
||
embed = discord.Embed(
|
||
title="Coinflip Win!",
|
||
description=f"The coin landed on **{result}**! You won **${winnings:,}**!",
|
||
color=discord.Color.green()
|
||
)
|
||
embed.add_field(name="New Balance", value=f"${new_balance:,}", inline=False)
|
||
else:
|
||
# Lose - subtract the bet
|
||
await database.update_balance(user_id, -bet)
|
||
new_balance = await database.get_balance(user_id)
|
||
embed = discord.Embed(
|
||
title="Coinflip Loss",
|
||
description=f"The coin landed on **{result}**. You lost **${bet:,}**.",
|
||
color=discord.Color.red()
|
||
)
|
||
embed.add_field(name="New Balance", value=f"${new_balance:,}", inline=False)
|
||
|
||
await interaction.response.send_message(embed=embed)
|
||
|
||
async def economy_slots_callback(self, interaction: discord.Interaction, bet: int):
|
||
"""Callback for /economy gambling slots command"""
|
||
user_id = interaction.user.id
|
||
|
||
# Validate bet amount
|
||
if bet <= 0:
|
||
await interaction.response.send_message("❌ Your bet must be greater than 0.", ephemeral=True)
|
||
return
|
||
|
||
# Check if user has enough money
|
||
balance = await database.get_balance(user_id)
|
||
if bet > balance:
|
||
await interaction.response.send_message(f"❌ You don't have enough money. Your balance: ${balance:,}", ephemeral=True)
|
||
return
|
||
|
||
# Define slot symbols and their payouts
|
||
symbols = ["🍒", "🍊", "🍋", "🍇", "🍉", "💎", "7️⃣"]
|
||
payouts = {
|
||
"🍒🍒🍒": 2, # 2x bet
|
||
"🍊🍊🍊": 3, # 3x bet
|
||
"🍋🍋🍋": 4, # 4x bet
|
||
"🍇🍇🍇": 5, # 5x bet
|
||
"🍉🍉🍉": 8, # 8x bet
|
||
"💎💎💎": 10, # 10x bet
|
||
"7️⃣7️⃣7️⃣": 20, # 20x bet
|
||
}
|
||
|
||
# Spin the slots
|
||
result = [random.choice(symbols) for _ in range(3)]
|
||
result_str = "".join(result)
|
||
|
||
# Check for win
|
||
win_multiplier = payouts.get(result_str, 0)
|
||
|
||
if win_multiplier > 0:
|
||
# Win
|
||
winnings = bet * win_multiplier
|
||
await database.update_balance(user_id, winnings - bet) # Subtract bet, add winnings
|
||
new_balance = await database.get_balance(user_id)
|
||
embed = discord.Embed(
|
||
title="🎰 Slots Win!",
|
||
description=f"[ {result[0]} | {result[1]} | {result[2]} ]\n\nYou won **${winnings:,}**! ({win_multiplier}x)",
|
||
color=discord.Color.green()
|
||
)
|
||
embed.add_field(name="New Balance", value=f"${new_balance:,}", inline=False)
|
||
else:
|
||
# Lose
|
||
await database.update_balance(user_id, -bet)
|
||
new_balance = await database.get_balance(user_id)
|
||
embed = discord.Embed(
|
||
title="🎰 Slots Loss",
|
||
description=f"[ {result[0]} | {result[1]} | {result[2]} ]\n\nYou lost **${bet:,}**.",
|
||
color=discord.Color.red()
|
||
)
|
||
embed.add_field(name="New Balance", value=f"${new_balance:,}", inline=False)
|
||
|
||
await interaction.response.send_message(embed=embed)
|
||
|
||
# Utility group callbacks
|
||
async def economy_balance_callback(self, interaction: discord.Interaction, user: discord.Member = None):
|
||
"""Callback for /economy utility balance command"""
|
||
target_user = user or interaction.user
|
||
user_id = target_user.id
|
||
|
||
balance = await database.get_balance(user_id)
|
||
|
||
if target_user == interaction.user:
|
||
embed = discord.Embed(
|
||
title="Your Balance",
|
||
description=f"💰 You have **${balance:,}**",
|
||
color=discord.Color.blue()
|
||
)
|
||
else:
|
||
embed = discord.Embed(
|
||
title=f"{target_user.display_name}'s Balance",
|
||
description=f"💰 {target_user.mention} has **${balance:,}**",
|
||
color=discord.Color.blue()
|
||
)
|
||
|
||
await interaction.response.send_message(embed=embed)
|
||
|
||
async def economy_transfer_callback(self, interaction: discord.Interaction, user: discord.Member, amount: int):
|
||
"""Callback for /economy utility transfer command"""
|
||
sender_id = interaction.user.id
|
||
receiver_id = user.id
|
||
|
||
# Validate transfer
|
||
if sender_id == receiver_id:
|
||
await interaction.response.send_message("❌ You can't transfer money to yourself.", ephemeral=True)
|
||
return
|
||
|
||
if amount <= 0:
|
||
await interaction.response.send_message("❌ Transfer amount must be greater than 0.", ephemeral=True)
|
||
return
|
||
|
||
# Check if sender has enough money
|
||
sender_balance = await database.get_balance(sender_id)
|
||
if amount > sender_balance:
|
||
await interaction.response.send_message(f"❌ You don't have enough money. Your balance: ${sender_balance:,}", ephemeral=True)
|
||
return
|
||
|
||
# Process transfer
|
||
await database.update_balance(sender_id, -amount)
|
||
await database.update_balance(receiver_id, amount)
|
||
|
||
# Get updated balances
|
||
new_sender_balance = await database.get_balance(sender_id)
|
||
|
||
embed = discord.Embed(
|
||
title="Transfer Complete",
|
||
description=f"💸 You sent **${amount:,}** to {user.mention}",
|
||
color=discord.Color.green()
|
||
)
|
||
embed.add_field(name="Your New Balance", value=f"${new_sender_balance:,}", inline=False)
|
||
|
||
await interaction.response.send_message(embed=embed)
|
||
|
||
async def economy_leaderboard_callback(self, interaction: discord.Interaction):
|
||
"""Callback for /economy utility leaderboard command"""
|
||
# Get top 10 users by balance
|
||
leaderboard_data = await database.get_leaderboard(limit=10)
|
||
|
||
if not leaderboard_data:
|
||
await interaction.response.send_message("No users found in the economy system yet.", ephemeral=True)
|
||
return
|
||
|
||
embed = discord.Embed(
|
||
title="Economy Leaderboard",
|
||
description="Top 10 richest users",
|
||
color=discord.Color.gold()
|
||
)
|
||
|
||
for i, (user_id, balance) in enumerate(leaderboard_data):
|
||
try:
|
||
user = await self.bot.fetch_user(user_id)
|
||
username = user.display_name
|
||
except:
|
||
username = f"User {user_id}"
|
||
|
||
medal = "🥇" if i == 0 else "🥈" if i == 1 else "🥉" if i == 2 else f"{i+1}."
|
||
embed.add_field(
|
||
name=f"{medal} {username}",
|
||
value=f"${balance:,}",
|
||
inline=False
|
||
)
|
||
|
||
await interaction.response.send_message(embed=embed)
|
||
|
||
# Risky group callbacks
|
||
async def economy_rob_callback(self, interaction: discord.Interaction, user: discord.Member):
|
||
"""Callback for /economy risky rob command"""
|
||
robber_id = interaction.user.id
|
||
victim_id = user.id
|
||
|
||
# Validate rob attempt
|
||
if robber_id == victim_id:
|
||
await interaction.response.send_message("❌ You can't rob yourself.", ephemeral=True)
|
||
return
|
||
|
||
# Check cooldown
|
||
command_name = "rob"
|
||
cooldown_duration = datetime.timedelta(hours=1)
|
||
last_used = await database.check_cooldown(robber_id, command_name)
|
||
|
||
if last_used:
|
||
now_utc = datetime.datetime.now(datetime.timezone.utc)
|
||
if last_used.tzinfo is None:
|
||
last_used = last_used.replace(tzinfo=datetime.timezone.utc)
|
||
|
||
time_since_last_used = now_utc - last_used
|
||
if time_since_last_used < cooldown_duration:
|
||
time_left = cooldown_duration - time_since_last_used
|
||
hours, remainder = divmod(int(time_left.total_seconds()), 3600)
|
||
minutes, seconds = divmod(remainder, 60)
|
||
embed = discord.Embed(description=f"🕒 You can't rob again so soon. Try again in **{hours}h {minutes}m {seconds}s**.", color=discord.Color.orange())
|
||
await interaction.response.send_message(embed=embed, ephemeral=True)
|
||
return
|
||
|
||
# Set cooldown regardless of outcome
|
||
await database.set_cooldown(robber_id, command_name)
|
||
|
||
# Get balances
|
||
robber_balance = await database.get_balance(robber_id)
|
||
victim_balance = await database.get_balance(victim_id)
|
||
|
||
# Minimum balance requirements
|
||
min_robber_balance = 50
|
||
min_victim_balance = 100
|
||
|
||
if robber_balance < min_robber_balance:
|
||
embed = discord.Embed(
|
||
title="Rob Failed",
|
||
description=f"❌ You need at least **${min_robber_balance}** to attempt a robbery.",
|
||
color=discord.Color.red()
|
||
)
|
||
await interaction.response.send_message(embed=embed)
|
||
return
|
||
|
||
if victim_balance < min_victim_balance:
|
||
embed = discord.Embed(
|
||
title="Rob Failed",
|
||
description=f"❌ {user.mention} doesn't have enough money to be worth robbing.",
|
||
color=discord.Color.red()
|
||
)
|
||
await interaction.response.send_message(embed=embed)
|
||
return
|
||
|
||
# Determine success (30% chance)
|
||
success_chance = 0.3
|
||
if random.random() < success_chance:
|
||
# Success - steal 10-30% of victim's balance
|
||
steal_percent = random.uniform(0.1, 0.3)
|
||
steal_amount = int(victim_balance * steal_percent)
|
||
|
||
# Update balances
|
||
await database.update_balance(robber_id, steal_amount)
|
||
await database.update_balance(victim_id, -steal_amount)
|
||
|
||
# Get updated balance
|
||
new_robber_balance = await database.get_balance(robber_id)
|
||
|
||
embed = discord.Embed(
|
||
title="Rob Successful!",
|
||
description=f"💰 You successfully robbed {user.mention} and got away with **${steal_amount:,}**!",
|
||
color=discord.Color.green()
|
||
)
|
||
embed.add_field(name="Your New Balance", value=f"${new_robber_balance:,}", inline=False)
|
||
else:
|
||
# Failure - lose 10-20% of your balance as a fine
|
||
fine_percent = random.uniform(0.1, 0.2)
|
||
fine_amount = int(robber_balance * fine_percent)
|
||
|
||
# Update balance
|
||
await database.update_balance(robber_id, -fine_amount)
|
||
|
||
# Get updated balance
|
||
new_robber_balance = await database.get_balance(robber_id)
|
||
|
||
embed = discord.Embed(
|
||
title="Rob Failed",
|
||
description=f"🚔 You were caught trying to rob {user.mention} and had to pay a fine of **${fine_amount:,}**!",
|
||
color=discord.Color.red()
|
||
)
|
||
embed.add_field(name="Your New Balance", value=f"${new_robber_balance:,}", inline=False)
|
||
|
||
await interaction.response.send_message(embed=embed)
|
||
|
||
# Jobs group callbacks
|
||
async def economy_apply_callback(self, interaction: discord.Interaction, job: app_commands.Choice[str]):
|
||
"""Callback for /economy jobs apply command"""
|
||
user_id = interaction.user.id
|
||
job_name = job.value
|
||
|
||
# Check if user already has a job
|
||
current_job = await database.get_user_job(user_id)
|
||
if current_job and current_job.get("name"):
|
||
embed = discord.Embed(
|
||
description=f"❌ You already have a job as a {current_job['name']}. You must quit first before applying for a new job.",
|
||
color=discord.Color.red()
|
||
)
|
||
await interaction.response.send_message(embed=embed, ephemeral=True)
|
||
return
|
||
|
||
# Apply for the job
|
||
success = await database.set_user_job(user_id, job_name)
|
||
|
||
if success:
|
||
embed = discord.Embed(
|
||
title="Job Application Successful!",
|
||
description=f"🎉 Congratulations! You are now employed as a **{job_name}**.",
|
||
color=discord.Color.green()
|
||
)
|
||
embed.add_field(name="Next Steps", value=f"Use `/economy jobs {job_name}` to work at your new job!", inline=False)
|
||
else:
|
||
embed = discord.Embed(
|
||
title="Job Application Failed",
|
||
description="❌ There was an error processing your job application. Please try again later.",
|
||
color=discord.Color.red()
|
||
)
|
||
|
||
await interaction.response.send_message(embed=embed)
|
||
|
||
async def economy_quit_callback(self, interaction: discord.Interaction):
|
||
"""Callback for /economy jobs quit command"""
|
||
user_id = interaction.user.id
|
||
|
||
# Check if user has a job
|
||
current_job = await database.get_user_job(user_id)
|
||
if not current_job or not current_job.get("name"):
|
||
embed = discord.Embed(
|
||
description="❌ You don't currently have a job to quit.",
|
||
color=discord.Color.red()
|
||
)
|
||
await interaction.response.send_message(embed=embed, ephemeral=True)
|
||
return
|
||
|
||
job_name = current_job.get("name")
|
||
|
||
# Quit the job
|
||
success = await database.remove_user_job(user_id)
|
||
|
||
if success:
|
||
embed = discord.Embed(
|
||
title="Job Resignation",
|
||
description=f"✅ You have successfully quit your job as a **{job_name}**.",
|
||
color=discord.Color.blue()
|
||
)
|
||
else:
|
||
embed = discord.Embed(
|
||
title="Error",
|
||
description="❌ There was an error processing your resignation. Please try again later.",
|
||
color=discord.Color.red()
|
||
)
|
||
|
||
await interaction.response.send_message(embed=embed)
|
||
|
||
async def economy_joblist_callback(self, interaction: discord.Interaction):
|
||
"""Callback for /economy jobs list command"""
|
||
# Get list of available jobs
|
||
jobs = await database.get_available_jobs()
|
||
|
||
if not jobs:
|
||
await interaction.response.send_message("No jobs are currently available.", ephemeral=True)
|
||
return
|
||
|
||
embed = discord.Embed(
|
||
title="Available Jobs",
|
||
description="Here are the jobs you can apply for:",
|
||
color=discord.Color.blue()
|
||
)
|
||
|
||
for job in jobs:
|
||
embed.add_field(
|
||
name=f"{job['name']} - ${job['base_pay']} per shift",
|
||
value=job['description'],
|
||
inline=False
|
||
)
|
||
|
||
embed.set_footer(text="Apply for a job with /economy jobs apply")
|
||
await interaction.response.send_message(embed=embed)
|
||
|
||
async def cog_unload(self):
|
||
"""Called when the cog is unloaded, closes DB connections."""
|
||
log.info("Unloading EconomyCog (combined)...")
|
||
# Schedule the close_db function to run in the bot's event loop
|
||
# Using ensure_future or create_task is generally safer within cogs
|
||
asyncio.ensure_future(close_db())
|
||
log.info("Scheduled database connection closure.")
|
||
|
||
|
||
# --- Setup Function ---
|
||
|
||
async def setup(bot: commands.Bot):
|
||
"""Sets up the combined EconomyCog."""
|
||
print("Setting up EconomyCog...")
|
||
cog = EconomyCog(bot)
|
||
await bot.add_cog(cog)
|
||
log.info("Combined EconomyCog added to bot with command groups.")
|
||
print(f"EconomyCog setup complete with command groups: {[cmd.name for cmd in bot.tree.get_commands() if cmd.name == 'economy']}")
|
||
print(f"Available subgroups: {[group.name for group in cog.economy_group.walk_commands() if isinstance(group, app_commands.Group)]}")
|