import discord from random import shuffle, choices, choice from discord.ext import commands from utils.sql_commands import DatabaseManager from utils.bank_functions import bank_data, update_money from datetime import datetime import asyncio import random # --- Constants and Configs --- lottery_list = ["🎁", "🎮", "🎷", "🔫", "📸", "🎃", "🏅"] weights = [0.15, 0.1, 0.02, 0.2, 0.1, 0.28, 0.2] lottery_win = { "🎁": 2, "🎁🎁": 5, "🎁🎁🎁": 50, "🔫🔫🔫": 3, "📸📸📸": 5, "🎃🎃🎃": 7, "🏅🏅🏅": 10, "🎮🎮🎮": 25, "🎷🎷🎷": 1000, } bj_values = { "2♦️": 2, "2♥️": 2, "2♣️": 2, "2♠️": 2, "3♦️": 3, "3♥️": 3, "3♣️": 3, "3♠️": 3, "4♦️": 4, "4♥️": 4, "4♣️": 4, "4♠️": 4, "5♦️": 5, "5♥️": 5, "5♣️": 5, "5♠️": 5, "6♦️": 6, "6♥️": 6, "6♣️": 6, "6♠️": 6, "7♦️": 7, "7♥️": 7, "7♣️": 7, "7♠️": 7, "8♦️": 8, "8♥️": 8, "8♣️": 8, "8♠️": 8, "9♦️": 9, "9♥️": 9, "9♣️": 9, "9♠️": 9, "10♦️": 10, "10♥️": 10, "10♣️": 10, "10♠️": 10, "J♦️": 10, "J♥️": 10, "J♣️": 10, "J♠️": 10, "Q♦️": 10, "Q♥️": 10, "Q♣️": 10, "Q♠️": 10, "K♦️": 10, "K♥️": 10, "K♣️": 10, "K♠️": 10, "A♦️": 11, "A♥️": 11, "A♣️": 11, "A♠️": 11, } ROOMS_FILE = "active_rooms.json" # --- Blackjack Classes and Helpers --- class Deck: def __init__(self): self.deck = list(bj_values.keys()) shuffle(self.deck) async def deal(self): return self.deck.pop() class Hand: def __init__(self, name:str, bet:int): self.cards = [] self.value = 0 self.aces = 0 self.bust = False self.name = name self.bet = bet async def add_card(self, card): self.cards.append(card) self.aces = ("".join(self.cards)).count("A") value = 0 for card in self.cards: value += bj_values[card] self.value = value while self.value > 21 and self.aces: self.value -= 10 self.aces -= 1 async def hit(deck, hand): await hand.add_card(await deck.deal()) async def payout(ctx, play, amount, multiplier, db): player_balance = int( db.fetch_one("SELECT WALLET FROM economy WHERE ID = %s", (ctx.author.id,)).get( "WALLET", 0 ) ) db.execute_query( "UPDATE economy SET WALLET = %s WHERE ID = %s", (player_balance + amount * (multiplier - 1), ctx.author.id), ) await ctx.reply( f"{play}\nBet: {amount:,}\nYou won {amount * multiplier:,} <:flooney:1194943899765051473>" ) async def check_winner(msg: discord.Interaction, player_hand, dealer_hand, db): p_value, d_value = player_hand.value, dealer_hand.value embed = discord.Embed(description="Blackjack") embed.add_field( name="Bet: ", value=f"{player_hand.bet:,}<:flooney:1194943899765051473>", inline=True, ) embed.add_field( name=player_hand.name, value=f"{', '.join(player_hand.cards)}: {player_hand.value}", inline=False, ) embed.add_field( name=dealer_hand.name, value=f"{', '.join(dealer_hand.cards)}: {dealer_hand.value}", inline=False, ) # Check for Blackjack if p_value == 21 and len(player_hand.cards) == 2: if d_value == 21 and len(dealer_hand.cards) == 2: embed.add_field( name="Winner:", value="Draw (Both got Blackjack!)", inline=False ) player_balance = int( db.fetch_one( "SELECT WALLET FROM economy WHERE ID = %s", (player_hand.name.id,) ).get("WALLET", 0) ) db.execute_query( "UPDATE economy SET WALLET = %s WHERE ID = %s", (player_balance + player_hand.bet, player_hand.name.id), ) else: embed.add_field( name="Winner:", value=f"{player_hand.name} (Blackjack!)", inline=False ) player_balance = int( db.fetch_one( "SELECT WALLET FROM economy WHERE ID = %s", (player_hand.name.id,) ).get("WALLET", 0) ) db.execute_query( "UPDATE economy SET WALLET = %s WHERE ID = %s", (player_balance + int(player_hand.bet * 3), player_hand.name.id), ) elif d_value == 21 and len(dealer_hand.cards) == 2: embed.add_field( name="Winner:", value=f"{dealer_hand.name} (Blackjack!)", inline=False ) else: if (p_value > d_value and not player_hand.bust) or dealer_hand.bust: embed.add_field(name="Winner:", value=f"{player_hand.name}", inline=False) player_balance = int( db.fetch_one( "SELECT WALLET FROM economy WHERE ID = %s", (player_hand.name.id,) ).get("WALLET", 0) ) db.execute_query( "UPDATE economy SET WALLET = %s WHERE ID = %s", (player_balance + player_hand.bet * 2, player_hand.name.id), ) elif (p_value < d_value and not dealer_hand.bust) or player_hand.bust: embed.add_field(name="Winner:", value=f"{dealer_hand.name}", inline=False) else: embed.add_field(name="Winner:", value="Draw", inline=False) player_balance = int( db.fetch_one( "SELECT WALLET FROM economy WHERE ID = %s", (player_hand.name.id,) ).get("WALLET", 0) ) db.execute_query( "UPDATE economy SET WALLET = %s WHERE ID = %s", (player_balance + player_hand.bet, player_hand.name.id), ) await msg.edit_original_response(embed=embed) async def dealer(msg: discord.Interaction, player_hand, dealer_hand, cards, db): while dealer_hand.value < 17 and not player_hand.bust and not dealer_hand.bust: await hit(cards, dealer_hand) if dealer_hand.value > 21: dealer_hand.bust = True embed = discord.Embed(description="Blackjack") embed.add_field( name="Bet: ", value=f"{player_hand.bet:,}<:flooney:1194943899765051473>", inline=True, ) embed.add_field( name=player_hand.name, value=f"{', '.join(player_hand.cards)}: {player_hand.value}", inline=False, ) embed.add_field( name=dealer_hand.name, value=f"{', '.join(dealer_hand.cards)}: {dealer_hand.value}", inline=False, ) await msg.edit_original_response(embed=embed) await check_winner(msg, player_hand, dealer_hand, db) class MyView(discord.ui.View): def __init__(self, player_hand, dealer_hand, cards, db): super().__init__() self.player_hand = player_hand self.dealer_hand = dealer_hand self.cards = cards self.db = db @discord.ui.button(label="Hit", style=discord.ButtonStyle.primary, emoji="✅") async def hit(self, interaction: discord.Interaction, button: discord.ui.Button): if interaction.user.id == self.player_hand.name.id: await hit(self.cards, self.player_hand) embed = discord.Embed(description="Blackjack") embed.add_field( name="Bet: ", value=f"{self.player_hand.bet:,}<:flooney:1194943899765051473>", inline=True, ) embed.add_field( name=self.player_hand.name, value=f"{', '.join(self.player_hand.cards)}: {self.player_hand.value}", inline=False, ) embed.add_field( name=self.dealer_hand.name, value=f"{(self.dealer_hand.cards)[0]}", inline=False, ) msg = interaction if self.player_hand.value < 21: await msg.response.edit_message(embed=embed) else: await msg.response.edit_message(embed=embed, view=None) if self.player_hand.value > 21: self.player_hand.bust = True await dealer( msg, self.player_hand, self.dealer_hand, self.cards, self.db ) @discord.ui.button(label="Stand", style=discord.ButtonStyle.secondary, emoji="🛑") async def stand(self, interaction: discord.Interaction, button: discord.ui.Button): if interaction.user.id == self.player_hand.name.id: embed = discord.Embed(description="Blackjack") embed.add_field( name="Bet: ", value=f"{self.player_hand.bet:,}<:flooney:1194943899765051473>", inline=True, ) embed.add_field( name=self.player_hand.name, value=f"{', '.join(self.player_hand.cards)}: {self.player_hand.value}", inline=False, ) embed.add_field( name=self.dealer_hand.name, value=f"{(self.dealer_hand.cards)[0]}", inline=False, ) msg = interaction await msg.response.edit_message(embed=embed, view=None) await dealer(msg, self.player_hand, self.dealer_hand, self.cards, self.db) # --- Main Gamble Cog --- class Gamble(commands.Cog): """Cog for all gambling-related commands.""" def __init__(self, client): self.client = client self.db = DatabaseManager() self.active_rooms = {} self.room_timers = {} def save_rooms(self): """Save all active rooms to the database.""" try: for channel_id, room in self.active_rooms.items(): invited_str = ",".join(str(uid) for uid in room["invited"]) self.db.execute_query( "REPLACE INTO gamble_rooms (channel_id, host_id, invited, inactivity) VALUES (%s, %s, %s, %s)", (channel_id, room["host"], invited_str, room["inactivity"]), ) except Exception as e: print(f"[DB ERROR] Failed to save rooms: {e}") def load_rooms(self): """Load all rooms from the database.""" try: self.active_rooms = {} rows = self.db.fetch_all("SELECT * FROM gamble_rooms") for row in rows: invited = set(int(uid) for uid in row["invited"].split(",") if uid) self.active_rooms[int(row["channel_id"])] = { "host": int(row["host_id"]), "invited": invited, "inactivity": int(row["inactivity"]), } except Exception as e: print(f"[DB ERROR] Failed to load rooms: {e}") def delete_room(self, channel_id): """Delete a room from the database and memory.""" try: self.db.execute_query( "DELETE FROM gamble_rooms WHERE channel_id = %s", (channel_id,) ) except Exception as e: print(f"[DB ERROR] Failed to delete room: {e}") self.active_rooms.pop(channel_id, None) self.save_rooms() async def cog_load(self): """Load rooms on cog load.""" self.load_rooms() async def cog_unload(self): """Save rooms on cog unload.""" self.save_rooms() async def _check_limit_and_exclusion(self, ctx, amount: int) -> bool: """Returns True if user is allowed to gamble, else sends a message and returns False.""" user = ctx.author if amount <= 0: await ctx.reply("⚠️ Bet amount must be positive.", mention_author=False) return False try: limit_row = self.db.fetch_one( "SELECT * FROM gamble_limits WHERE USERID = %s", (user.id,) ) except Exception as e: await ctx.reply( "⚠️ Database error. Please try again later.", mention_author=False ) print(f"[DB ERROR] Limit check: {e}") return False if limit_row: if ( limit_row.get("EXCLUDED_UNTIL") and datetime.utcnow() < limit_row["EXCLUDED_UNTIL"] ): await ctx.reply( "🚫 You are currently self-excluded from gambling.", mention_author=False, ) return False if limit_row.get("DAILY_LIMIT") and amount > limit_row["DAILY_LIMIT"]: await ctx.reply( f"🚫 Your personal bet limit is {limit_row['DAILY_LIMIT']:,}.", mention_author=False, ) return False return True async def _send_embed( self, ctx, description: str, color: discord.Color = discord.Color.blurple() ): """Helper to send an embed message.""" embed = discord.Embed(description=description, color=color) await ctx.reply(embed=embed, mention_author=False) # --- Coinflip --- @commands.cooldown(1, 2, commands.BucketType.user) @commands.command( name="coinflip", aliases=["cf", "coin_flip"], usage=" ", description="Flip a coin and bet on heads or tails.", brief="Bet on heads or tails.", ) @commands.guild_only() async def coin_flip(self, ctx, bet_on: str, amount: int): if not await self._check_limit_and_exclusion(ctx, amount): return user = ctx.author bet_on = "heads" if "h" in bet_on.lower() else "tails" reward = round(amount / 2) users = await bank_data(user) if int(users["WALLET"]) < amount: return await self._send_embed( ctx, "❌ You don't have enough money.", discord.Color.red() ) coin = ["heads", "tails"] result = choice(coin) if result != bet_on: await update_money(user, wallet=-abs(amount)) return await self._send_embed( ctx, f"🪙 Got **{result}**, you lost **{amount:,}**.", discord.Color.red(), ) await update_money(user, wallet=+abs(amount)) return await self._send_embed( ctx, f"🪙 Got **{result}**, you won **{amount + reward:,}**!", discord.Color.green(), ) # --- Dice --- @commands.cooldown(1, 2, commands.BucketType.user) @commands.command( name="dice", usage=" ", description="Bet on a dice roll (choose a number 1-6).", brief="Bet on a dice roll.", ) async def dice(self, ctx, amount: int, bet_on: int = 6): if not await self._check_limit_and_exclusion(ctx, amount): return user = ctx.author rdice = [1, 2, 3, 4, 5, 6] if bet_on not in rdice: return await self._send_embed( ctx, "🎲 Enter a number of dice (1 - 6).", discord.Color.orange() ) users = await bank_data(user) if int(users["WALLET"]) < amount: return await self._send_embed( ctx, "❌ You don't have enough money.", discord.Color.red() ) rand_num = choice(rdice) if rand_num != bet_on: await update_money(user, wallet=-abs(amount)) return await self._send_embed( ctx, f"🎲 Got **{rand_num}**, you lost **{amount:,}**.", discord.Color.red(), ) reward = round(amount / 2) await update_money(user, wallet=+abs(reward)) await self._send_embed( ctx, f"🎲 Got **{rand_num}**, you won **{amount + reward:,}**!", discord.Color.green(), ) # --- Slots --- @commands.cooldown(1, 1, commands.BucketType.user) @commands.command( name="slots", usage="", description="Spin the slot machine for a chance to win big!", brief="Spin the slot machine.", ) async def slots(self, ctx, bet): user = ctx.author player_balance = int( self.db.fetch_one( "SELECT WALLET FROM economy WHERE ID = %s", (user.id,) ).get("WALLET", 0) ) bet = int(bet.replace("all", str(player_balance))) if not await self._check_limit_and_exclusion(ctx, bet): return if player_balance < bet: await self._send_embed( ctx, "❌ You have insufficient funds.", discord.Color.red() ) return # Animation: edit the embed to show spinning, then result spin_emoji = "" embed = discord.Embed( description=f"{spin_emoji} {spin_emoji} {spin_emoji}\nSpinning...", color=discord.Color.blurple(), ) msg = await ctx.send(embed=embed) await asyncio.sleep(1.2) play = "".join(choices(lottery_list, weights=weights, k=3)) result_embed = discord.Embed( description=f"{play[0]} {play[1]} {play[2]}", color=( discord.Color.green() if play in lottery_win or play.count("🎁") > 0 else discord.Color.red() ), ) # Ensure description is always a string if result_embed.description is None: result_embed.description = "" if play in lottery_win: mult = lottery_win[play] result_embed.description = (result_embed.description or "") + ( f"\nYou won **{bet * mult:,}** <:flooney:1194943899765051473>" ) await update_money(user, wallet=bet * (mult - 1)) elif play.count("🎁") == 2: mult = lottery_win["🎁🎁"] result_embed.description = (result_embed.description or "") + ( f"\nYou won **{bet * mult:,}** <:flooney:1194943899765051473>" ) await update_money(user, wallet=bet * (mult - 1)) elif play.count("🎁") == 1: mult = lottery_win["🎁"] result_embed.description = (result_embed.description or "") + ( f"\nYou won **{bet * mult:,}** <:flooney:1194943899765051473>" ) await update_money(user, wallet=bet * (mult - 1)) else: await update_money(user, wallet=-bet) result_embed.description = (result_embed.description or "") + ( f"\nYou lost **{int(bet):,}** <:flooney:1194943899765051473>" ) await msg.edit(embed=result_embed) # --- Roulette --- @commands.cooldown(1, 2, commands.BucketType.user) @commands.command( name="roulette", usage=" ", description=( "Bet on roulette! Options: " "`red`, `black`, `green`, `even`, `odd`, " "`1st12`, `2nd12`, `3rd12` (dozens), " "`col1`, `col2`, `col3` (columns), or a number (0-36)." ), brief="Bet on roulette.", ) async def roulette(self, ctx, bet_type: str, amount: int): if not await self._check_limit_and_exclusion(ctx, amount): return user = ctx.author colors = { "red": [1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36], "black": [ 2, 4, 6, 8, 10, 11, 13, 15, 17, 20, 22, 24, 26, 28, 29, 31, 33, 35, ], "green": [0], } bet_type = bet_type.lower() valid_bets = [ "red", "black", "green", "even", "odd", "1st12", "2nd12", "3rd12", "col1", "col2", "col3", ] + [str(i) for i in range(37)] if bet_type not in valid_bets: return await self._send_embed( ctx, "Bet on 'red', 'black', 'green', 'even', 'odd', '1st12', '2nd12', '3rd12', 'col1', 'col2', 'col3', or a number (0-36).", discord.Color.orange(), ) result = choice(range(37)) color = ( "green" if result == 0 else "red" if result in colors["red"] else "black" ) payout = 0 # Number bet if bet_type.isdigit() and int(bet_type) == result: payout = amount * 35 msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!" # Color bet elif bet_type == color: payout = amount * (14 if color == "green" else 2) msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!" # Even/Odd elif bet_type == "even" and result != 0 and result % 2 == 0: payout = amount * 2 msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!" elif bet_type == "odd" and result % 2 == 1: payout = amount * 2 msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!" # Dozens elif bet_type == "1st12" and 1 <= result <= 12: payout = amount * 3 msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!" elif bet_type == "2nd12" and 13 <= result <= 24: payout = amount * 3 msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!" elif bet_type == "3rd12" and 25 <= result <= 36: payout = amount * 3 msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!" # Columns elif bet_type == "col1" and result in [i for i in range(1, 37, 3)]: payout = amount * 3 msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!" elif bet_type == "col2" and result in [i for i in range(2, 37, 3)]: payout = amount * 3 msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!" elif bet_type == "col3" and result in [i for i in range(3, 37, 3)]: payout = amount * 3 msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!" else: payout = -amount msg = f"🎲 The ball landed on {result} ({color}). You lost {amount:,}." await update_money(user, wallet=payout) await self._send_embed( ctx, msg, discord.Color.green() if payout > 0 else discord.Color.red() ) # --- Poker --- @commands.cooldown(1, 2, commands.BucketType.user) @commands.is_owner() @commands.command( name="poker", usage="", description="Play a simple 5-card draw poker game against the bot.", brief="Play poker against the bot.", ) async def poker(self, ctx, bet: int): if not await self._check_limit_and_exclusion(ctx, bet): return user = ctx.author user_data = await bank_data(user) if int(user_data["WALLET"]) < bet: await self._send_embed( ctx, "❌ You don't have enough money.", discord.Color.red() ) return # Poker deck and hands suits = ["♠️", "♥️", "♦️", "♣️"] ranks = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"] deck = [f"{r}{s}" for r in ranks for s in suits] random.shuffle(deck) player_hand = [deck.pop() for _ in range(5)] bot_hand = [deck.pop() for _ in range(5)] # Simple hand value: count pairs, triples, etc. (not full poker logic) def hand_value(hand): values = [card[:-2] if card[:-2] != "10" else "10" for card in hand] counts = {v: values.count(v) for v in set(values)} if 4 in counts.values(): return 7 # Four of a kind if sorted(counts.values()) == [2, 3]: return 6 # Full house if 3 in counts.values(): return 3 # Three of a kind if list(counts.values()).count(2) == 2: return 2 # Two pair if 2 in counts.values(): return 1 # One pair return 0 # High card player_score = hand_value(player_hand) bot_score = hand_value(bot_hand) if player_score > bot_score: await update_money(user, wallet=bet) msg = f"🃏 Your hand: {', '.join(player_hand)}\n🤖 Bot's hand: {', '.join(bot_hand)}\nYou win {bet:,} coins!" elif player_score < bot_score: await update_money(user, wallet=-bet) msg = f"🃏 Your hand: {', '.join(player_hand)}\n🤖 Bot's hand: {', '.join(bot_hand)}\nYou lose {bet:,} coins!" else: msg = f"🃏 Your hand: {', '.join(player_hand)}\n🤖 Bot's hand: {', '.join(bot_hand)}\nIt's a draw! No coins won or lost." await self._send_embed( ctx, msg, ( discord.Color.green() if "win" in msg else discord.Color.red() if "lose" in msg else discord.Color.blurple() ), ) # --- Higher/Lower --- @commands.cooldown(1, 2, commands.BucketType.user) @commands.command( name="higherlower", usage="", description="Guess if the next number will be higher or lower.", brief="Guess higher or lower.", ) async def higherlower(self, ctx, bet: int): if not await self._check_limit_and_exclusion(ctx, bet): return user = ctx.author number = random.randint(1, 100) await self._send_embed( ctx, f"The number is **{number}**. Will the next number be higher or lower? Type `higher` or `lower`.", discord.Color.blurple(), ) def check(m): return ( m.author == ctx.author and m.channel == ctx.channel and m.content.lower() in ["higher", "lower"] ) try: guess_msg = await self.client.wait_for("message", check=check, timeout=15) except Exception: await self._send_embed( ctx, "Timed out. Please try again.", discord.Color.orange() ) return next_number = random.randint(1, 100) win = (guess_msg.content.lower() == "higher" and next_number > number) or ( guess_msg.content.lower() == "lower" and next_number < number ) if next_number == number: msg = f"The next number was also **{next_number}**. It's a tie! No coins won or lost." elif win: await update_money(user, wallet=bet) msg = f"The next number was **{next_number}**. You win {bet:,} coins!" else: await update_money(user, wallet=-bet) msg = f"The next number was **{next_number}**. You lose {bet:,} coins!" await self._send_embed( ctx, msg, discord.Color.green() if win else discord.Color.red() ) # --- Scratch --- @commands.cooldown(1, 2, commands.BucketType.user) @commands.command( name="scratch", usage="", description="Buy a scratch card for a chance to win up to 10x your bet.", brief="Buy a scratch card.", ) async def scratch(self, ctx, bet: int): if not await self._check_limit_and_exclusion(ctx, bet): return user = ctx.author symbols = ["🍀", "💎", "⭐", "🍒", "7️⃣"] card = [random.choice(symbols) for _ in range(3)] payout = 0 if card.count(card[0]) == 3: payout = bet * 10 msg = f"Scratch Card: {' '.join(card)}\nJackpot! You win {payout:,} coins!" elif len(set(card)) == 2: payout = bet * 2 msg = f"Scratch Card: {' '.join(card)}\nTwo of a kind! You win {payout:,} coins!" else: payout = -bet msg = f"Scratch Card: {' '.join(card)}\nNo match. You lose {bet:,} coins." await update_money(user, wallet=payout) await self._send_embed( ctx, msg, discord.Color.green() if payout > 0 else discord.Color.red() ) # --- All other user-facing responses (invite, kick, end_gameroom, etc.) --- # Replace ctx.reply(...) or ctx.send(...) with self._send_embed(...) for consistency. @commands.command( name="gameroom", usage="[game: str] [inactivity: int (minutes)]", description="Create a private game room that auto-deletes after inactivity.", brief="Create a private game room.", ) @commands.guild_only() async def gameroom(self, ctx, game: str = "game", inactivity: int = 10): """Create a private text channel for a game session.""" guild = ctx.guild overwrites = { guild.default_role: discord.PermissionOverwrite(read_messages=False), ctx.author: discord.PermissionOverwrite( read_messages=True, send_messages=True ), } category = ctx.channel.category channel_name = f"{game}-room-{ctx.author.display_name}".replace( " ", "-" ).lower() channel = await guild.create_text_channel( name=channel_name, overwrites=overwrites, category=category, reason="Private game room", ) await channel.send( f"{ctx.author.mention} Your private **{game}** session has started!\n" f"Use `py invite ` and `py kick ` in this channel to manage access.\n" f"This channel will be deleted after **{inactivity} minutes of inactivity**." ) self.active_rooms[channel.id] = { "host": ctx.author.id, "invited": set([ctx.author.id]), "inactivity": inactivity, } self.save_rooms() self.reset_inactivity_timer(channel.id, inactivity) def reset_inactivity_timer(self, channel_id, inactivity): """Reset the inactivity timer for a game room.""" if channel_id in self.room_timers: self.room_timers[channel_id].cancel() task = asyncio.create_task(self.inactivity_task(channel_id, inactivity)) self.room_timers[channel_id] = task async def inactivity_task(self, channel_id, inactivity): """Delete the channel after inactivity period.""" try: await asyncio.sleep(inactivity * 60) channel = self.client.get_channel(channel_id) if channel: await channel.send("Room deleted due to inactivity.") await channel.delete(reason="Game room inactive") self.delete_room(channel_id) self.room_timers.pop(channel_id, None) except asyncio.CancelledError: pass @commands.Cog.listener() async def on_message(self, message): """Reset inactivity timer on message in a game room.""" if ( hasattr(self, "active_rooms") and message.channel.id in self.active_rooms and not message.author.bot ): inactivity = self.active_rooms[message.channel.id].get("inactivity", 10) self.reset_inactivity_timer(message.channel.id, inactivity) @commands.command( name="invite", usage="", description="Invite a user to your private game room.", brief="Invite a user to your game room.", hidden=True, ) async def invite( self, ctx, member: discord.Member | None = None, *, identifier: str | None = None, ): """Invite a user to the game room by mention, ID, or username.""" room = self.active_rooms.get(ctx.channel.id) if not room or ctx.author.id != room["host"]: await ctx.reply( "Only the host can invite others, and only in a private game room channel.", mention_author=False, ) return # If member is not provided, try to resolve by ID or name if member is None and identifier: try: member = ctx.guild.get_member(int(identifier)) except ValueError: member = discord.utils.find( lambda m: m.name.lower() == identifier.lower() or m.display_name.lower() == identifier.lower(), ctx.guild.members, ) if member is None: await ctx.reply( "User not found. Please use a mention, user ID, or username.", mention_author=False, ) return await ctx.channel.set_permissions( member, read_messages=True, send_messages=True ) room["invited"].add(member.id) self.save_rooms() await ctx.send( f"{member.mention} has been invited to the game room!", allowed_mentions=discord.AllowedMentions(users=True), ) @commands.command( name="kick", usage="", description="Kick a user from your private game room.", brief="Kick a user from your game room.", hidden=True, ) async def kick(self, ctx, member: discord.Member): """Kick a user from the game room.""" room = self.active_rooms.get(ctx.channel.id) if not room or ctx.author.id != room["host"]: await ctx.reply( "Only the host can kick others, and only in a private game room channel.", mention_author=False, ) return if member.id == ctx.author.id: await ctx.reply("You cannot kick yourself (host).", mention_author=False) return if member.id not in room["invited"]: await ctx.reply( f"{member.mention} is not in this game room.", mention_author=False ) return await ctx.channel.set_permissions(member, overwrite=None) room["invited"].discard(member.id) self.save_rooms() await ctx.reply( f"{member.mention} has been kicked from the game room!", mention_author=False, ) @commands.command( name="end", aliases=["delete"], description="End and delete your private game room.", brief="Delete your game room.", hidden=True, ) async def end_gameroom(self, ctx): """Allows the host to manually delete the game room channel.""" room = self.active_rooms.get(ctx.channel.id) if not room or ctx.author.id != room["host"]: await ctx.reply( "Only the host can end this game room, and only in a private game room channel.", mention_author=False, ) return await ctx.send("This game room will now be deleted by the host.") self.delete_room(ctx.channel.id) await ctx.channel.delete(reason="Game room ended by host") async def setup(client): await client.add_cog(Gamble(client))