980 lines
36 KiB
Python
Executable File
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, timedelta
|
|
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, bet):
|
|
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 `py invite <user>` and `py 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))
|