discordbot/cogs/random_timeout_cog.py
2025-06-05 21:31:06 -06:00

368 lines
14 KiB
Python

import discord
from discord.ext import commands
from discord import app_commands
import random
import datetime
import logging
import json
import os
# Set up logging
logger = logging.getLogger(__name__)
# Define the path for the JSON file to store timeout chance
TIMEOUT_CONFIG_FILE = os.path.join(
os.path.dirname(__file__), "../data/timeout_config.json"
)
class RandomTimeoutCog(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.target_user_id = 748405715520978965 # The specific user ID to target
self.timeout_chance = 0.005 # Default: 0.5% chance (0.005)
self.timeout_duration = 60 # 1 minute in seconds
self.log_channel_id = 1363007131980136600 # Channel ID to log all events
# Ensure data directory exists
os.makedirs(os.path.dirname(TIMEOUT_CONFIG_FILE), exist_ok=True)
# Load timeout chance from JSON file
self.load_timeout_config()
logger.info(
f"RandomTimeoutCog initialized with target user ID: {self.target_user_id} and timeout chance: {self.timeout_chance}"
)
def load_timeout_config(self):
"""Load timeout configuration from JSON file"""
if os.path.exists(TIMEOUT_CONFIG_FILE):
try:
with open(TIMEOUT_CONFIG_FILE, "r") as f:
data = json.load(f)
if "timeout_chance" in data:
self.timeout_chance = data["timeout_chance"]
logger.info(f"Loaded timeout chance: {self.timeout_chance}")
except Exception as e:
logger.error(f"Error loading timeout configuration: {e}")
def save_timeout_config(self):
"""Save timeout configuration to JSON file"""
try:
config_data = {
"timeout_chance": self.timeout_chance,
"target_user_id": self.target_user_id,
"timeout_duration": self.timeout_duration,
}
with open(TIMEOUT_CONFIG_FILE, "w") as f:
json.dump(config_data, f, indent=4)
logger.info(
f"Saved timeout configuration with chance: {self.timeout_chance}"
)
except Exception as e:
logger.error(f"Error saving timeout configuration: {e}")
async def create_log_embed(self, message, roll, was_timed_out):
"""Create an embed for logging the timeout event"""
# Create the embed with appropriate color based on timeout status
color = discord.Color.red() if was_timed_out else discord.Color.green()
embed = discord.Embed(
title=f"{'⚠️ TIMEOUT TRIGGERED' if was_timed_out else '✅ No Timeout'}",
description=f"Message from <@{self.target_user_id}> was processed",
color=color,
timestamp=datetime.datetime.now(datetime.timezone.utc),
)
# Add user information
embed.add_field(
name="👤 User Information",
value=f"**User:** {message.author.mention}\n**User ID:** {message.author.id}",
inline=False,
)
# Add roll information
embed.add_field(
name="🎲 Roll Information",
value=f"**Roll:** {roll:.6f}\n**Threshold:** {self.timeout_chance:.6f}\n**Chance:** {self.timeout_chance * 100:.2f}%\n**Result:** {'TIMEOUT' if was_timed_out else 'SAFE'}",
inline=False,
)
# Add message information
embed.add_field(
name="💬 Message Information",
value=f"**Channel:** {message.channel.mention}\n**Message Link:** [Click Here]({message.jump_url})",
inline=False,
)
# Set footer
embed.set_footer(
text=f"Random Timeout System | {datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d %H:%M:%S UTC')}"
)
# Set author with user avatar
embed.set_author(
name=f"{message.author.name}#{message.author.discriminator}",
icon_url=message.author.display_avatar.url,
)
return embed
@commands.Cog.listener()
async def on_message(self, message: discord.Message):
"""Event listener for messages to randomly timeout the target user"""
# Ignore bot messages
if message.author.bot:
return
# Check if the message author is the target user
if message.author.id == self.target_user_id:
# Generate a random number between 0 and 1
roll = random.random()
was_timed_out = False
# If the roll is less than the timeout chance (1%), timeout the user
if roll < self.timeout_chance:
try:
# Calculate timeout until time (1 minute from now)
timeout_until = datetime.datetime.now(
datetime.timezone.utc
) + datetime.timedelta(seconds=self.timeout_duration)
# Apply the timeout
await message.author.timeout(
timeout_until, reason="Random 0.5% chance timeout"
)
was_timed_out = True
# Send a message to the channel
await message.channel.send(
f"🎲 Bad luck! {message.author.mention} rolled a {roll:.4f} and got timed out for 1 minute! (0.5% chance)",
delete_after=10, # Delete after 10 seconds
)
logger.info(
f"User {message.author.id} was randomly timed out for 1 minute"
)
except discord.Forbidden:
logger.warning(
f"Bot doesn't have permission to timeout user {message.author.id}"
)
except discord.HTTPException as e:
logger.error(f"Failed to timeout user {message.author.id}: {e}")
except Exception as e:
logger.error(
f"Unexpected error when timing out user {message.author.id}: {e}"
)
# Log the event to the specified channel regardless of timeout result
try:
# Get the log channel
log_channel = self.bot.get_channel(self.log_channel_id)
if log_channel:
# Create and send the embed
embed = await self.create_log_embed(message, roll, was_timed_out)
await log_channel.send(embed=embed)
else:
logger.warning(
f"Log channel with ID {self.log_channel_id} not found"
)
except Exception as e:
logger.error(f"Error sending log message: {e}")
@commands.command(name="set_timeout_chance")
@commands.has_permissions(moderate_members=True)
async def set_timeout_chance(self, ctx, percentage: float):
"""Set the random timeout chance percentage (Moderator only, max 10% unless owner)"""
# Convert percentage to decimal (e.g., 5% -> 0.05)
decimal_chance = percentage / 100.0
# Check if user is owner
is_owner = await self.bot.is_owner(ctx.author)
# Validate the percentage
if not is_owner and (percentage < 0 or percentage > 10):
await ctx.reply(
f"❌ Error: Moderators can only set timeout chance between 0% and 10%. Current: {self.timeout_chance * 100:.2f}%"
)
return
elif percentage < 0 or percentage > 100:
await ctx.reply(
f"❌ Error: Timeout chance must be between 0% and 100%. Current: {self.timeout_chance * 100:.2f}%"
)
return
# Store the old value for logging
old_chance = self.timeout_chance
# Update the timeout chance
self.timeout_chance = decimal_chance
# Save the updated timeout chance to the JSON file
self.save_timeout_config()
# Create an embed for the response
embed = discord.Embed(
title="Timeout Chance Updated",
description=f"The random timeout chance has been updated.",
color=discord.Color.blue(),
timestamp=datetime.datetime.now(datetime.timezone.utc),
)
embed.add_field(
name="Previous Chance", value=f"{old_chance * 100:.2f}%", inline=True
)
embed.add_field(
name="New Chance", value=f"{self.timeout_chance * 100:.2f}%", inline=True
)
embed.add_field(
name="Updated By",
value=f"{ctx.author.mention} {' (Owner)' if is_owner else ' (Moderator)'}",
inline=False,
)
embed.set_footer(text=f"Random Timeout System | User ID: {self.target_user_id}")
# Send the response
await ctx.reply(embed=embed)
# Log the change
logger.info(
f"Timeout chance changed from {old_chance:.4f} to {self.timeout_chance:.4f} by {ctx.author.name} (ID: {ctx.author.id})"
)
# Also log to the log channel if available
try:
log_channel = self.bot.get_channel(self.log_channel_id)
if log_channel:
await log_channel.send(embed=embed)
except Exception as e:
logger.error(f"Error sending log message: {e}")
@set_timeout_chance.error
async def set_timeout_chance_error(self, ctx, error):
"""Error handler for the set_timeout_chance command"""
if isinstance(error, commands.MissingPermissions):
await ctx.reply(
"❌ You need the 'Moderate Members' permission to use this command."
)
elif isinstance(error, commands.MissingRequiredArgument):
await ctx.reply(
f"❌ Please provide a percentage. Example: `!set_timeout_chance 0.5` for 0.5%. Current: {self.timeout_chance * 100:.2f}%"
)
elif isinstance(error, commands.BadArgument):
await ctx.reply(
f"❌ Please provide a valid number. Example: `!set_timeout_chance 0.5` for 0.5%. Current: {self.timeout_chance * 100:.2f}%"
)
else:
await ctx.reply(f"❌ An error occurred: {error}")
logger.error(f"Error in set_timeout_chance command: {error}")
@app_commands.command(
name="set_timeout_chance",
description="Set the random timeout chance percentage",
)
@app_commands.describe(
percentage="The percentage chance (0-10% for moderators, 0-100% for owner)"
)
@app_commands.checks.has_permissions(moderate_members=True)
async def set_timeout_chance_slash(
self, interaction: discord.Interaction, percentage: float
):
"""Slash command version of set_timeout_chance"""
# Convert percentage to decimal (e.g., 5% -> 0.05)
decimal_chance = percentage / 100.0
# Check if user is owner
is_owner = await self.bot.is_owner(interaction.user)
# Validate the percentage
if not is_owner and (percentage < 0 or percentage > 10):
await interaction.response.send_message(
f"❌ Error: Moderators can only set timeout chance between 0% and 10%. Current: {self.timeout_chance * 100:.2f}%",
ephemeral=True,
)
return
elif percentage < 0 or percentage > 100:
await interaction.response.send_message(
f"❌ Error: Timeout chance must be between 0% and 100%. Current: {self.timeout_chance * 100:.2f}%",
ephemeral=True,
)
return
# Store the old value for logging
old_chance = self.timeout_chance
# Update the timeout chance
self.timeout_chance = decimal_chance
# Save the updated timeout chance to the JSON file
self.save_timeout_config()
# Create an embed for the response
embed = discord.Embed(
title="Timeout Chance Updated",
description=f"The random timeout chance has been updated.",
color=discord.Color.blue(),
timestamp=datetime.datetime.now(datetime.timezone.utc),
)
embed.add_field(
name="Previous Chance", value=f"{old_chance * 100:.2f}%", inline=True
)
embed.add_field(
name="New Chance", value=f"{self.timeout_chance * 100:.2f}%", inline=True
)
embed.add_field(
name="Updated By",
value=f"{interaction.user.mention} {' (Owner)' if is_owner else ' (Moderator)'}",
inline=False,
)
embed.set_footer(text=f"Random Timeout System | User ID: {self.target_user_id}")
# Send the response
await interaction.response.send_message(embed=embed)
# Log the change
logger.info(
f"Timeout chance changed from {old_chance:.4f} to {self.timeout_chance:.4f} by {interaction.user.name} (ID: {interaction.user.id})"
)
# Also log to the log channel if available
try:
log_channel = self.bot.get_channel(self.log_channel_id)
if log_channel:
await log_channel.send(embed=embed)
except Exception as e:
logger.error(f"Error sending log message: {e}")
@set_timeout_chance_slash.error
async def set_timeout_chance_slash_error(
self, interaction: discord.Interaction, error
):
"""Error handler for the set_timeout_chance slash command"""
if isinstance(error, app_commands.errors.MissingPermissions):
await interaction.response.send_message(
"❌ You need the 'Moderate Members' permission to use this command.",
ephemeral=True,
)
else:
await interaction.response.send_message(
f"❌ An error occurred: {error}", ephemeral=True
)
logger.error(f"Error in set_timeout_chance slash command: {error}")
@commands.Cog.listener()
async def on_ready(self):
logger.info(f"{self.__class__.__name__} cog has been loaded.")
async def setup(bot: commands.Bot):
await bot.add_cog(RandomTimeoutCog(bot))
print("RandomTimeoutCog loaded successfully!")