Merge pull request #1 from 0xVoodoo/add-code

Initial commit
This commit is contained in:
0xVoodoo
2025-08-12 19:29:53 -06:00
committed by GitHub
5 changed files with 327 additions and 0 deletions
+42
View File
@@ -0,0 +1,42 @@
# Discord Blackjack
This all started when some of my buddies and I saw the games feature in Discord voice chat. We wanted to play the built in Blackjack game but didn't like the account permissions it asked for. The next day, this bot was up and running, letting us play all the Blackjack we want on Discord!
# Usage
Create a .env file containing your Discord bot token and optionally the channel Id that you'd like scoreboard messages sent to.
```
.env:
DISCORD_KEY=<Bot Token>
DISCORD_CHAN=<Channel Id>
```
Create a virtual environment and install the required packages.
```
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
Finally, from your venv, run the bot.
```
python3 bot.py
```
# Requirements
```
discord.py
python-dotenv
```
# Playing the game
- Each player begins with $100.
- To begin, place a bet with: $deal \<bet amount> | $d \<bet amount>
- To draw an additional card: $hit | $h
- To stand: $stand | $s
- To get your current stats: $info | $i
- For help: $help | $h
# Scoreboard and Allowance
The scoreboard is optional and will send a message containing the stats of all players. This can be configured with Cron jobs or scheduled tasks to repeat automatically. The scoreboard also gives every player an additional $10 by default, preventing lockout by way of repeated losses.
# License
GPLV3, as all good software should be :)
+159
View File
@@ -0,0 +1,159 @@
import random
class Game:
def __init__(game, player, phand, dhand):
game.player = player
game.phand = phand
game.dhand = dhand
def __str__(game):
return f"{game.player} {game.phand}"
class Player:
def __init__(player, name, wins, losses, money, bet):
player.name = name
player.wins = wins
player.losses = losses
player.money = money
player.bet = bet
def __str__(player):
return f"{player.name} WL: {player.wins}/{player.losses} | ${player.money}"
def getgame(activegames, author):
gameexists = False
i = -1
for x in activegames:
i += 1
if x.player == author:
gameexists = True
return [gameexists, i]
def newhand():
hand = []
hand.append(random.randint(2, 11))
hand.append(random.randint(2, 11))
return aces(hand)
def aces(hand):
i = 0
total = totalhand(hand)
if total > 21:
for card in hand:
if card == 11:
hand[i] = 1
break
i = i + 1
return hand
def totalhand(hand):
total = 0
for card in hand:
total = total + card
return total
def showhand(hand, player, action):
strlist = [str(card) for card in hand]
handstr = ",".join(strlist)
msg = f"> :joker::joker:{player} {action}: __{handstr}__ :joker::joker:\n>\t\t\t\t\tTotal: {totalhand(hand)}"
return msg
def score(dtotal, ptotal):
won = False
draw = False
winmsg = "> :tada: **__YOU WIN!__** :tada:"
losemsg = "> :sob: **__You lose__** :sob:"
if dtotal > 21 and ptotal <= 21:
won = True
elif dtotal < 21 and ptotal > 21:
won = False
elif dtotal < 21 and ptotal < 21:
if dtotal > ptotal:
won = False
elif dtotal < ptotal:
won = True
elif dtotal == ptotal:
draw = True
elif dtotal == 21 and ptotal < 21:
won = False
elif dtotal < 21 and ptotal == 21:
won = True
elif dtotal == 21 and ptotal > 21:
won = False
elif dtotal > 21 and ptotal == 21:
won = True
elif dtotal > 21 and ptotal > 21:
draw = True
elif dtotal == ptotal:
draw = True
else: return ["SCORING ERROR", won, draw]
if won == True:
return [winmsg, won, draw]
elif draw == True:
return ["> :clown: It's a push :clown:", won, draw]
else:
return [losemsg, won, draw]
def getstats(player):
fn = "stats/"+player+".txt"
try:
with open(fn, "r") as f:
currentstats = f.read().split(",")
if len(currentstats) < 5:
print("[-] Error parsing player stats")
else:
stats = Player(currentstats[0], currentstats[1], currentstats[2], currentstats[3], currentstats[4])
except FileNotFoundError:
initplayer(player)
stats = getstats(player)
return stats
def initplayer(player):
fn = "stats/"+player+".txt"
try:
with open(fn, "x") as f:
f.write(player+",0,0,100,0")
except FileExistsError:
print("[-] Error parsing player data!")
def writestats(player):
fn = "stats/"+player.name+".txt"
with open(fn, "w") as f:
statsline = player.name+","+player.wins+","+player.losses+","+str(player.money)+","+str(player.bet)
f.writelines(statsline)
async def hit(message, currentgame, player):
gameover = False
if totalhand(currentgame.phand) >= 21:
await stand(message, currentgame, player)
gameover = True
else:
currentgame.phand.append(random.randint(2,11))
currentgame.phand = aces(currentgame.phand)
await message.channel.send(showhand(currentgame.phand, currentgame.player, "drew"))
if totalhand(currentgame.phand) > 21:
await message.channel.send(f"> :boom: **BUST** :boom:")
await stand(message, currentgame, player)
gameover = True
return gameover
async def stand(message, currentgame, player):
await message.channel.send(showhand(currentgame.dhand, "Dealer", "hand"))
if totalhand(currentgame.dhand) >= 17:
pass
else:
while totalhand(currentgame.dhand) < 17:
currentgame.dhand.append(random.randint(2,11))
currentgame.dhand = aces(currentgame.dhand)
await message.channel.send(showhand(currentgame.dhand, "Dealer", "drew"))
result = score(totalhand(currentgame.dhand), totalhand(currentgame.phand))
player = player
if result[1] == True:
payout = int(player.money) + int(player.bet)
updatedplayer = Player(player.name, str(int(player.wins) + 1), str(player.losses), payout, 0)
elif result[1] == False and result[2] == True:
updatedplayer = Player(player.name, player.wins, player.losses, player.money, 0)
else:
payout = int(player.money) - int(player.bet)
updatedplayer = Player(player.name, player.wins, str(int(player.losses) + 1), payout, 0)
writestats(updatedplayer)
await message.channel.send(result[0])
+73
View File
@@ -0,0 +1,73 @@
import blackjack as b
import random
import discord
from dotenv import load_dotenv
from os import getenv
if __name__ == "__main__":
load_dotenv()
api_key = getenv("DISCORD_KEY")
activegames = []
gameexists = False
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
@client.event
async def on_ready():
print(f'Dealer on duty: {client.user}')
@client.event
async def on_message(message):
msg = message.content.lower()
if message.author == client.user:
return
player = b.getstats(message.author.name.lower())
if msg.startswith("$deal") or msg.startswith("$d"):
bet = msg.split()
gameinfo = b.getgame(activegames, player.name)
if gameinfo[0] == True:
await message.channel.send("> **Game in progress, please !hit | !stand**")
elif len(bet) < 2:
await message.channel.send(">>> :clown: **Please place a bet!** :clown:\n\t\t\t\t__$u for help__")
elif bet[1].isnumeric() == False:
await message.channel.send(">>> :clown: **Please place a bet!** :clown:\n\t\t\t\t__$u for help__")
elif int(bet[1]) > int(player.money):
await message.channel.send("> :clown: **BET INVALID NOT ENOUGH CASH** :clown:")
else:
player = b.Player(player.name, player.wins, player.losses, player.money, bet[1])
b.writestats(player)
newgame = b.Game(player.name, b.newhand(), b.newhand())
await message.channel.send(f"> :clubs::diamonds: **Welcome __{newgame.player}!__** :hearts::spades:\n{b.showhand(newgame.phand, 'Player', 'hand')}\n> :joker::joker:Dealer hand: __{newgame.dhand[0]},?__ :joker::joker:\n>\t\t\t\t\tTotal: ?")
activegames.append(newgame)
elif msg.startswith("$hit") or msg.startswith("$h"):
player = b.getstats(message.author.name.lower())
gameinfo = b.getgame(activegames, player.name)
if gameinfo[0] == False:
await message.channel.send("No game found, please start one with $deal or get help with $usage!")
else:
currentgame = activegames[gameinfo[1]]
gameover = await b.hit(message, currentgame, player)
if gameover == True:
del activegames[gameinfo[1]]
elif msg.startswith("$stand") or msg.startswith("$s"):
player = b.getstats(message.author.name.lower())
gameinfo = b.getgame(activegames, player.name)
if gameinfo[0] == False:
await message.channel.send("No game found, please start one with !deal")
else:
currentgame = activegames[gameinfo[1]]
await b.stand(message, currentgame, player)
del activegames[gameinfo[1]]
elif msg.startswith("$useage") or msg.startswith("$u"):
await message.channel.send(">>> Welcome to the :clubs::hearts:**__Blackjack Bot__**:diamonds::spades:\nTo sart a game, use: $deal/$d + bet | ex: $d 10\nTo hit, use: $hit/$h\nTo stand, use: $stand/$s\nWritten by **__0xVoodoo__**")
elif msg.startswith("$info") or msg.startswith("$i"):
await message.channel.send(f">>> :joker: Blackjack Player: **__{player.name}__**\n:diamonds: W|L: {player.wins} | {player.losses}\n:money_with_wings: Money: {player.money}")
client.run(api_key)
+2
View File
@@ -0,0 +1,2 @@
discord.py
python-dotenv
+51
View File
@@ -0,0 +1,51 @@
import discord
import os
import blackjack as b
from dotenv import load_dotenv
if __name__ == "__main__":
load_dotenv()
api_key = os.getenv("DISCORD_KEY")
channel_id = os.getenv("DISCORD_CHAN")
print(channel_id)
print("[*] Initializing scoreboard")
scoreboard = []
msg = f">>> **__---LEADERBOARD---__**\n"
players = os.listdir(path="stats/")
for player in players:
currentplayer = os.path.basename(player).split('.')[0]
playerstats = b.getstats(currentplayer)
scoreboard.append(playerstats)
updatedplayer = b.Player(playerstats.name, playerstats.wins, playerstats.losses, str(int(playerstats.money) + 10), playerstats.bet)
b.writestats(updatedplayer)
scoreboard.sort(reverse=True, key=lambda player: player.money)
print("[*] Scoreboard ready")
place = 0;
while place < len(scoreboard):
if place == 0:
msg = msg + f':crown: - **__{scoreboard[place].name}__** - {scoreboard[place].money}\n'
elif place == 1:
msg = msg + f':second_place: - **__{scoreboard[place].name}__** - {scoreboard[place].money}\n'
elif place == 2:
msg = msg + f':third_place: - **__{scoreboard[place].name}__** - {scoreboard[place].money}\n'
else:
msg = msg + f'**{place + 1}** - **{scoreboard[place].name}** - {scoreboard[place].money}\n'
place += 1
print("[*] Message created")
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
print("[*] Bot configured")
@client.event
async def on_ready():
print("[*] Bot authenticated")
channel = client.get_channel(channel_id)
if channel:
await channel.send(msg)
print("[+] Scoreboard sent!")
else:
print("[-] CHANNEL NOT FOUND!")
await client.close()
client.run(api_key)