2025-05-02 19:32:50 -06:00

102 lines
4.8 KiB
Python

import discord
from discord.ext import commands
import datetime
import random
import logging
from typing import Optional
# Import database functions from the sibling module
from . import database
log = logging.getLogger(__name__)
class RiskyCommands(commands.Cog):
"""Cog containing risky economy commands like robbing."""
def __init__(self, bot: commands.Bot):
self.bot = bot
@commands.hybrid_command(name="rob", description="Attempt to rob another user (risky!).")
async def rob(self, ctx: commands.Context, target: discord.User):
"""Attempts to steal money from another user."""
robber_id = ctx.author.id
target_id = target.id
command_name = "rob"
cooldown_duration = datetime.timedelta(hours=6) # 6-hour cooldown
success_chance = 0.30 # 30% base chance of success
min_target_balance = 100 # Target must have at least this much to be robbed
fine_multiplier = 0.5 # Fine is 50% of what you tried to steal if caught
steal_percentage_min = 0.05 # Steal between 5%
steal_percentage_max = 0.20 # and 20% of target's balance
if robber_id == target_id:
await ctx.send("You can't rob yourself!", ephemeral=True)
return
# Check cooldown
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)
await ctx.send(f"You need to lay low after your last attempt. Try again in **{hours}h {minutes}m {seconds}s**.", ephemeral=True)
return
# Check target balance
target_balance = await database.get_balance(target_id)
if target_balance < min_target_balance:
await ctx.send(f"{target.display_name} doesn't have enough money to be worth robbing (minimum ${min_target_balance:,}).", ephemeral=True)
# Don't apply cooldown if target wasn't viable
return
# Set cooldown now that a valid attempt is being made
await database.set_cooldown(robber_id, command_name)
# Check robber balance (needed for potential fine)
robber_balance = await database.get_balance(robber_id)
# Determine success
if random.random() < success_chance:
# Success!
steal_percentage = random.uniform(steal_percentage_min, steal_percentage_max)
stolen_amount = int(target_balance * steal_percentage)
if stolen_amount <= 0: # Ensure at least 1 is stolen if percentage is too low
stolen_amount = 1
await database.update_balance(robber_id, stolen_amount)
await database.update_balance(target_id, -stolen_amount)
current_robber_balance = await database.get_balance(robber_id)
await ctx.send(f"🚨 Success! You skillfully robbed **${stolen_amount:,}** from {target.mention}! Your new balance is **${current_robber_balance:,}**.")
try:
await target.send(f"🚨 Oh no! {ctx.author.mention} robbed you for **${stolen_amount:,}**!")
except discord.Forbidden:
pass # Ignore if DMs are closed
else:
# Failure! Calculate potential fine
# Fine based on what they *could* have stolen (using average percentage for calculation)
potential_steal_amount = int(target_balance * ((steal_percentage_min + steal_percentage_max) / 2))
if potential_steal_amount <= 0: potential_steal_amount = 1
fine_amount = int(potential_steal_amount * fine_multiplier)
# Ensure fine doesn't exceed robber's balance
fine_amount = min(fine_amount, robber_balance)
if fine_amount > 0:
await database.update_balance(robber_id, -fine_amount)
# Optional: Give the fine to the target? Or just remove it? Let's remove it.
# await database.update_balance(target_id, fine_amount)
current_robber_balance = await database.get_balance(robber_id)
await ctx.send(f"👮‍♂️ You were caught trying to rob {target.mention}! You paid a fine of **${fine_amount:,}**. Your new balance is **${current_robber_balance:,}**.")
else:
# Robber is broke, can't pay fine
await ctx.send(f"👮‍♂️ You were caught trying to rob {target.mention}, but you're too broke to pay the fine!")
# No setup function needed here