Files
DiscordBot/cogs/gamble.py
T

980 lines
36 KiB
Python
Executable File

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="<bet_on: heads(H) or tails(T)> <amount: integer>",
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="<amount: integer> <bet_on: integer (1-6)>",
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="<bet: integer or 'all'>",
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 = "<a:slots:1381060458999713843>"
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="<bet_type> <amount: integer>",
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="<bet: integer>",
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="<bet: integer>",
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="<bet: integer>",
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 `invite <user>` and `kick <user>` 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="<user: id|name>",
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="<user: mention>",
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))