How to solve bot abandoning previously started game from a different user - discord

I am currently in the process of making a blackjack / gambling bot for me and my friends and the bot works, only the problem(screenshot) is that when user A makes a game and user B makes a Game when user A responds after that user User B has started a game, the original game from user A gets abandoned and takes Users A's response and applies to User
I currently have an active game list which tracks who is in an active game and puts them in/out of one when they complete or start a game but this only solves it when a users is not an in game.
I have edited the code down below to make it less obstructed by a lot of repetitive code
any suggestions on how to fix this code for this problem will be welcome.
from datetime import datetime
import string
import discord
from discord.ext import commands
import random
import sqlite3
class BlackjackCog(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.game_id = "AAAAAAAA"
self.players_in_game = {}
self.deck = []
self.reset_deck()
self.player_hand = None
self.dealer_hand = None
self.outcome = ""
self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.bet = None
self.conn = sqlite3.connect("credits.db")
self.conn2 = sqlite3.connect("logs.db")
self.cursor = self.conn.cursor()
self.cursor2 =self.conn2.cursor()
def unload(self):
self.conn.close()
self.conn2.close()
def check_hand_value(self, hand)
# game logic to count cards
def reset_deck
# game logic to prevent the deck from going empty
def draw_card
# game logic to append a card to the current player or reset the deck if its empty
#commands.command(name="bj", aliases=["Bj", "blackjack", "BJ" , "Blackjack"])
async def blackjack(self, ctx, bet_amount: int = None):
user_id = ctx.author.id
if bet_amount is None:
embed=discord.Embed(title="Example Higher Lower" , color=0x3498db)
embed.add_field(name="How to Play", value="`You need to get higher amount of card total than the dealer's hand`", inline=False)
embed.add_field(name="Aliases", value="`!hit , !stand`", inline=False)
embed.add_field(name="Win value", value="`x2`", inline=False)
embed.add_field(name="Example command", value="`!bj <credit amount>`", inline=False)
embed.set_footer(text='Do !bj for this message')
await ctx.send(embed=embed)
return
if bet_amount <= 0:
await ctx.send("Invalid bet amount. Please enter a positive number.")
return
self.cursor.execute("SELECT credits FROM credits WHERE user_id=?", (ctx.author.id,))
result = self.cursor.fetchone()
if result is None:
await ctx.send("You do not have any credits use !daily ")
return
self.player_credits = result[0]
if result is None:
await ctx.send("You do not have any credits. Please add some with !daily")
return
if bet_amount > result[0]:
await ctx.send("You do not have enough credits to make that bet.")
return
if ctx.author.id in self.players_in_game:
await ctx.send("You are already in a game. Please finish your current game before starting a new one.")
return
self.bet_amount = bet_amount
self.player_hand = [self.draw_card(), self.draw_card()]
self.dealer_hand = [self.draw_card(), self.draw_card()]
player_total = self.check_hand_value(self.player_hand)
dealer_total = self.check_hand_value([self.dealer_hand[0]])
bet_amount = "{:,}".format(bet_amount)
player_credits = "{:,}".format(self.player_credits)
self.game_id = "BJ" + "".join(random.choices(string.ascii_uppercase + string.digits, k=6))
self.msg = await ctx.send(embed=discord.Embed(title="Blackjack | " + 'User: ' + ctx.author.display_name + " - ID: " + self.game_id, color=0x3498db)
.add_field(name="Your hand", value=' '.join(self.player_hand) + f"\nTotal: **{player_total}**", inline=True)
.add_field(name="Dealers hand", value=' '.join([self.dealer_hand[0]])+ f"\nTotal: **{dealer_total}**", inline=True)
.add_field(name="Bet", value=f"**{bet_amount}** credits", inline=False)
.add_field(name="Credits", value=f"You have **{player_credits}** credits", inline=False)
.add_field(name="Options", value="Use !hit \nUse !stand \nUse !split\n Use !dd", inline=False)
.set_footer(text='Active game')
)
self.players_in_game[user_id] = {}
#commands.command(names="hit", aliases=["h", "Hit" , "H"])
async def hit(self, ctx):
user_id = ctx.author.id
if user_id not in self.players_in_game:
await ctx.send("You don't have an active game to stand in.")
return
game_id = self.players_in_game[user_id]
player_hand = self.player_hand
player_hand.append(self.draw_card())
player_total = self.check_hand_value(player_hand)
dealer_total = self.check_hand_value([self.dealer_hand[0]])
self.player_hand2 = "Empty"
self.player_hand_str = " ".join(self.player_hand)
self.dealer_hand_str = " ".join(self.dealer_hand)
if player_total > 21:
self.outcome = "Lost hit"
self.player_credits -= self.bet_amount
bet_amount = "{:,}".format(self.bet_amount)
player_credits = "{:,}".format(self.player_credits)
self.cursor.execute(f"UPDATE credits SET credits = {self.player_credits} WHERE user_id=?", (ctx.author.id,))
self.conn.commit()
self.cursor2.execute("""INSERT INTO blackjack_logs (game_id, player_hand, player_hand2 ,dealer_hand, player_credits ,bet_amount, outcome, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?)""", (self.game_id, self.player_hand_str, self.player_hand2, self.dealer_hand_str, self.player_credits ,self.bet_amount,self.outcome, self.timestamp,))
self.conn2.commit()
await self.msg.edit(embed=discord.Embed(title="Blackjack | " + 'User: ' + ctx.author.display_name+ " - ID: " + self.game_id , color=0xe74c3c)
.add_field(name="Your hand", value=' '.join(self.player_hand) + f"\nTotal: **{player_total}**", inline=True)
.add_field(name="Dealers hand", value=' '.join([self.dealer_hand[0]])+ f"\nTotal: **{dealer_total}**", inline=True)
.add_field(name="Lost", value=f"**{bet_amount}** credits", inline=False)
.add_field(name="Credits", value=f"You have **{player_credits}** credits", inline=False)
.set_footer(text='Finished game')
)
del self.players_in_game[ctx.author.id]
self.player_hand = None
self.dealer_hand = None
self.bet_amount = None
elif player_total == 21:
self.outcome = "Won hit"
self.player_credits += self.bet_amount
bet_amount = "{:,}".format(self.bet_amount)
player_credits = "{:,}".format(self.player_credits)
self.cursor.execute(f"UPDATE credits SET credits = {self.player_credits} WHERE user_id=?", (ctx.author.id,))
self.conn.commit()
self.cursor2.execute("""INSERT INTO blackjack_logs (game_id, player_hand, player_hand2 ,dealer_hand, player_credits ,bet_amount, outcome, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?)""", (self.game_id, self.player_hand_str, self.player_hand2, self.dealer_hand_str, self.player_credits ,self.bet_amount,self.outcome, self.timestamp,))
self.conn2.commit()
await self.msg.edit(embed=discord.Embed(title="Blackjack | " + 'User: ' + ctx.author.display_name + " - ID: " + self.game_id, color=0x109319)
.add_field(name="Your hand", value=' '.join(self.player_hand) + f"\nTotal: **{player_total}**", inline=True)
.add_field(name="Dealers hand", value=' '.join([self.dealer_hand[0]])+ f"\nTotal: **{dealer_total}**", inline=True)
.add_field(name="Win", value=f"**{bet_amount}** credits", inline=False)
.add_field(name="Credits", value=f"You have **{player_credits}** credits", inline=False)
.set_footer(text='Finished game')
)
del self.players_in_game[ctx.author.id]
self.player_hand = None
self.dealer_hand = None
self.bet_amount = None
else:
bet_amount = "{:,}".format(self.bet_amount)
player_credits = "{:,}".format(self.player_credits)
await self.msg.edit(embed=discord.Embed(title="Blackjack | " + 'User: ' + ctx.author.display_name + " - ID: " + self.game_id, color=0x3498db)
.add_field(name="Your hand", value=' '.join(self.player_hand) + f"\n**Total: **{player_total}", inline=True)
.add_field(name="Dealers hand", value=' '.join([self.dealer_hand[0]])+ f"\n**Total: **{dealer_total}", inline=True)
.add_field(name="Bet", value=f"**{bet_amount}** credits", inline=False)
.add_field(name="Credits", value=f"You have **{player_credits}** credits", inline=False)
.add_field(name="Options", value="Use !hit \n Use !stand\nUse !dd", inline=False)
.set_footer(text='Active game')
)
I also have stand, split and dd command but this amount of code should be enough to give suggestions/edits I believe.
I have tried to edit and add multiple active game lists and assigned an id to to player in the active game list but that also hasn't worked and I tried looking for a basic solution only
So far my conclusion is that I will need to implement a while loop and edit standard bj function and make it all run in 1 command instead of multiple.
The game itself works fine if you are playing solo the problem is really the screenshot.

As far as I can tell, these are all the class variables you have (that are important to a "game"):
game_id
players_in_game
deck
player_hand
dealer_hand
outcome
Now, let's run through some players playing your game. Player A comes along and does blackjack. This initialises all those above variables with values for them. If they were to keep playing by themselves - it would work just fine. However, before Player A can do anything, Player B comes along and runs the blackjack command. This overwrites all the values you had for Player A stored in those variables. If Player A now calls one of your other commands, they'll be using game state from Player B's game.
There's a fundamental flaw in your logic and saving of game state that'll always fail when more than one player tries to play at the same time.
What you need to do, is create some sort of game state object that can be used for that. This can be as simple as a dict, a custom class, DB entry, etc. But it needs to be something.
Using the dict idea:
# when a user uses the `blackjack` command
player_game_state = {"game_id": game_id, "user_id": user_id, "player_hand": player_hand, "dealer_hand": dealer_hand, "bet": bet} # etc, etc, etc
Then, we actually use your self.players_in_game class variable to keep track of all these game states.
# still in the blackjack command - once you setup everything
self.players_in_game[user_id] = game_state
Now, when they call one of the other commands, like hit:
# at the start of the command
player_game_state = self.players_in_game[user_id]
# instead of doing self.player_hand
player_game_state["player_hand"].append(self.draw_card())
Though, maybe your draw_card method, and reset_deck methods need to take the game state as a parameter and modify them. As I'd wager they're currently resetting your class variables as well.
Does that make sense? Hopefully enough that you can try to apply some of it.
TLDR; stop using class variables for storing individual parts of your game state.

Related

Stop 2 consecutive uses discord.py

I am making a discord bot in which an auction could take place.So I want someone to bid only once unless someone bids after him/her.
`async def bid(ctx):
embed1=discord.Embed(description= f'Bid has been placed by {ctx.author}', title='bid placed')
await ctx.send(embed=embed1)
`
That's what I have so far made.
You could put the author id in a variable:
bidderid = 0 #this will reset whenever your bot restarts
#client.command()
async def bid(ctx):
global bidderid
if ctx.author.id != bidderid: #if it is not the same bidder
bidderid = ctx.author.id
embed1=discord.Embed(description= f'Bid has been placed by {ctx.author}', title='bid placed')
await ctx.send(embed=embed1)
else: #if it is the same bidder
await ctx.send('You cannot bid twice in a row!') #replace with whatever message
note: It doesn't have to be the id, you could store ctx.author instead, idea is the same

AttributeError: 'Context' object has no attribute 'guild_icon'

I am trying to set up a command to print the information of the server the bot is in. The code below gives the error AttributeError: 'Context' object has no attribute 'guild_icon'
#commands.command()
async def serverinfo(self,ctx):
name = str(ctx.guild.name)
description = str(ctx.guild.description)
owner = str(ctx.guild.owner)
id = str(ctx.guild.id)
region = str(ctx.guild.region)
memberCount = str(ctx.guild.member_count)
embed = discord.Embed(
title=name + " Server Information",
description=description,
color=discord.Color.blue()
)
embed.set_thumbnail(ctx.guild_icon)
embed.add_field(name="Owner", value=owner, inline=True)
embed.add_field(name="Server ID", value=id, inline=True)
embed.add_field(name="Region", value=region, inline=True)
embed.add_field(name="Member Count", value=memberCount, inline=True)
await ctx.send(embed=embed)
When trying to find the solution I tried to change the code to be
#commands.command()
async def serverinfo(self,ctx):
name = str(ctx.guild.name)
description = str(ctx.guild.description)
owner = str(ctx.guild.owner)
id = str(ctx.guild.id)
region = str(ctx.guild.region)
memberCount = str(ctx.guild.member_count)
embed = discord.Embed(
title=name + " Server Information",
description=description,
color=discord.Color.blue()
)
embed.set_thumbnail(ctx.guild.icon)
embed.add_field(name="Owner", value=owner, inline=True)
embed.add_field(name="Server ID", value=id, inline=True)
embed.add_field(name="Region", value=region, inline=True)
embed.add_field(name="Member Count", value=memberCount, inline=True)
await ctx.send(embed=embed)
But then I get the error discord.ext.commands.errors.CommandInvokeError: Command raised an exception: TypeError: set_thumbnail() takes 1 positional argument but 2 were given
Could someone tell me what I have done wrong and point me in the right direction?
Problem 1
ctx.guild_icon does not exist, you fix this in your second code by using ctx.guild.icon
Problem 2
discord.Embed.set_thumbnail takes in a named argument for the image.
In fact, it doesn't take in an image at all, but rather a url
Solution
To solve your issue, you should use the following:
embed.set_thumbnail(url=ctx.guild.icon.url)
More info
discord.ext.commands.Context
discord.Embed.set_thumbnail

Command raised an exception: TypeError: Object of type File is not JSON serializable but no other solution working for me

The Function of discord command named server
#bot.command(name = 'server')
async def _minecraftserverinfo(ctx, ip:str, port:str, servername:str):
if servername == "survival":
svname = "Survival"
elif servername == "lobby":
svname = "Lobby"
#serverembed = createEmbed(ip, port)
#value = f"The server has {status.players.online} players and replied in {status.latency} ms\nThe server replied in {latency} ms\nThe server has the following players online: {','.join(query.players.names)}"
server = MinecraftServer.lookup(ip+':'+port)
#=======================================
#=======================================
# 'status' is supported by all Minecraft servers that are version 1.7 or higher.
status = server.status()
latency = server.ping()
query = server.query()
serverembed = discord.Embed(title = ip, url = "https://www.zer02infinity.com/",description = f"Zero To Infinity Minecraft {svname} Server")#\nThe server has the following players online: {','.join(query.players.names)}")
serverembed.set_thumbnail(url="https://cdn.discordapp.com/attachments/833829693588373534/920017091325747220/2021-12-13_11.png")
serverembed.add_field(name="Players Count", value=f"{status.players.online}/{query.players.max}", inline=True)
serverembed.add_field(name="Direct Connect:", value=f"```mc.zer02infinity.com```", inline=True)
#serverembed.add_field(name="Names:", value=f"{latency} ms", inline=True)
#nl = '\n'
#serverembed.add_field(name="Names:", value=f"{nl}{nl.join(query.players.names)}", inline=False)
#serverembed.add_field(name="Names:", value={'\n'.join(query.players.ping)}, inline=True)
#sending embed
embed = await ctx.send(embed=serverembed)
await embed.add_reaction(emoji)
message_id = embed.id
while True:
server = MinecraftServer.lookup(ip+':'+port)
# 'status' is supported by all Minecraft servers that are version 1.7 or higher.
status = server.status()
latency = server.ping()
query = server.query()
serverembed = discord.Embed(title = ip, url = "https://www.zer02infinity.com/",description = f"Zero To Infinity Minecraft {svname} Server")#\nThe server has the following players online: {','.join(query.players.names)}")
serverembed.set_thumbnail(url="https://cdn.discordapp.com/attachments/833829693588373534/920017091325747220/2021-12-13_11.png")
serverembed.add_field(name="Players Count", value=f"{status.players.online}/{query.players.max}", inline=True)
serverembed.add_field(name="Direct Connect:", value=f"```mc.zer02infinity.com```", inline=True)
#serverembed.add_field(name="Names:", value=f"{latency} ms", inline=True)
#nl = '\n'
problem started after i added a image from local file
file = discord.File("./foo.png", filename="foo.png")
serverembed.add_field(name="Names:", value={','.join(query.players.names)}, inline=False)
serverembed.set_image(url="attachment://foo.png")
#print('Winners are:', *names, sep='\n')
serverembed = await embed.edit(file=file, embed = serverembed)
await asyncio.sleep(60)
pass
So this is the recent code i added where foo.png is maintained by graph.py given below
import discord
from discord.ext import commands
import matplotlib.pyplot as plt
class Equations(commands.Cog):
def __init__(self, bot):
self.bot = bot
def plotlineareq(self, a, b, clr):
x = [0, 10]
y = [(a * i + b) for i in x]
plt.figure(figsize=(10, 10)) # #Size of Graph
plt.xlim(x) # #X Range [-6,6]
plt.ylim(x) # #Y Range [-6,6]
axis = plt.gca() # #Get Current Axis
plt.plot(axis.get_xlim(), [0, 0], 'k--') # #X Axis Plots Line Across
plt.plot([0, 0], axis.get_ylim(), 'k--') # #Y Axis Plots Line Across
plt.locator_params(axis="x", nbins=20)
plt.locator_params(axis="y", nbins=20)
plt.plot(x, y, label='linear', linestyle='solid', color=clr)
plt.ylabel('Players')
plt.xlabel('Time')
mm = str(a)
bb = str(b)
plt.title('Live Status')
#plt.grid()
plt.savefig("foo.png")
#commands.command()
async def linear(self, ctx, equation):
try:
equation = equation.replace(" ", "")
mx = equation.split("x")[0]
mx = equation.replace("x", "").replace("y=", "")
bx = equation.split("+")[1]
self.plotlineareq(mx, bx, 'b')
file = discord.File("foo.png", filename='foo.png')
embed = discord.Embed(color=0xff0000)
embed = embed.set_image(url="attachment://foo.png")
await ctx.send(file=file, embed=embed)
except Exception as e:
await ctx.send(f"An error occured: {e}")
def setup(bot):
bot.add_cog(Equations(bot))
After executing it gives the following error in error log-
Command raised an exception: TypeError: Object of type File is not JSON serializable

discord bot. database. embed doesn't appear

I want my bot react to a command.
If ?2 print line 2 of database
?3 print line 3 of database
etc...
I don't know why my embed doesn't appear I have no console errors...
My bot is connected and respond. My database is ok
if message.content.startswith('$'):
arg_id = message.content
conn = None
try:
conn = sqlite3.connect(database_file)
except Error as e:
print(e)
req = conn.cursor()
req.execute("SELECT * FROM space2 WHERE id=?", (arg_id,))
rows = req.fetchall()
for row in rows:
embed = discord.Embed(title='test', color=0x0000FF)
embed.set_thumbnail(url='https://www.pngkit.com/png/detail/231-2316751_database-database-icon-png.png')
embed.add_field(name='id', value=row[0], inline=False)
embed.add_field(name='CATEGORIE', value=row[1], inline=False)
embed.add_field(name='RANK', value=row[2], inline=False)
embed.add_field(name='SCORE', value=row[3], inline=False)
embed.add_field(name='POWER AREA GRAVITY 0', value=row[4], inline=False)
embed.add_field(name='POWER EYES WEAPONS', value=row[5], inline=False)
embed.set_footer(icon_url='https://pbs.twimg.com/profile_images/1325672283881484289/oaGtVIOD_400x400.png', text='Created by #Expected')
msg = await message.channel.send(embed=embed)
print(row)
NEW VERSION
there are advances but still mistakes...a little help would be appreciated...thanks
#client.event
async def on_message(message):
if message.author == client.user:
return
if message.content.startswith('$'):
message_slices = message.content.split()
try:
arg_id = int(message_slices[0][1:])
except ValueError:
embed = discord.Embed(title="Invalid argument", color=0x0000FF)
embed.set_footer(icon_url='https://pbs.twimg.com/profile_images/1325672283881484289/oaGtVIOD_400x400.png', text='Created by #Expected')
await message.channel.send(embed=embed)
else:
conn = None
try:
conn = sqlite3.connect(database_file)
except Error as e:
print(e)
exit(-1)
req = conn.cursor()
try:
req.execute("SELECT * FROM space2 WHERE id=?", (arg_id,))
except Error as e:
print(e)
else:
row = req.fetchone()
if row is not None:
embed = discord.Embed(title='NFT', color=0x0000FF)
embed.set_thumbnail(url='https://www.pngkit.com/png/detail/231-2316751_database-database-icon-png.png')
embed.add_field(name='id', value=str(row[0]), inline=False)
embed.add_field(name='CATEGORIE', value=str(row[1]), inline=False)
embed.add_field(name='RANK', value=str(row[2]), inline=False)
embed.add_field(name='SCORE', value=str(row[3]), inline=False)
embed.add_field(name='POWER AREA GRAVITY 0', value=str(row[4]), inline=False)
embed.add_field(name='POWER EYES WEAPONS', value=str(row[5]), inline=False)
embed.set_footer(icon_url='https://pbs.twimg.com/profile_images/1325672283881484289/oaGtVIOD_400x400.png', text='Created by #Expected')
await message.channel.send(embed=embed)
else:
embed = discord.Embed(title="Not found", color=0x0000FF)
embed.set_footer(icon_url='https://pbs.twimg.com/profile_images/1325672283881484289/oaGtVIOD_400x400.png', text='Created by #Expected')
await message.channel.send(embed=embed)
req.close()
conn.close()```
enter code here

Getting the users total invites in Discord.py

I am trying to add a command to my bot which replies with the total people the user has invited to the server
My code:
if message.content.startswith('!invites'):
totalInvites = message.guild.invites
await message.channel.send("You have invited: " + totalInvites + " members to the server")
The bot replies with:
You have invited: <bound method Guild.invites of <Guild id=server_id_goes_here name='my bot' shard_id=None chunked=True member_count=12>> members to the server
What is it that I am doing wrong?
You've almost got the right idea!
on_message event usage:
#bot.event
async def on_message(message):
if message.content.startswith('!invites'):
totalInvites = 0
for i in await message.guild.invites():
if i.inviter == message.author:
totalInvites += i.uses
await message.channel.send(f"You've invited {totalInvites}
member{'' if totalInvites == 1 else 's'} to the server!")
Command decorator usage:
#bot.command()
async def invites(ctx):
totalInvites = 0
for i in await ctx.guild.invites():
if i.inviter == ctx.author:
totalInvites += i.uses
await ctx.send(f"You've invited {totalInvites} member{'' if totalInvites == 1 else 's'} to the server!")
First I'm iterating through each invite in the guild, checking who created each one. If the creator of the invite matches the user that executed the command, it then adds the number of times that invite has been used, to a running total.
You don't need to include the {'' if totalInvites == 1 else 's'}, that's just for the odd case that they've invited 1 person (turns member into the plural - members).
References:
Guild.invites - the code originally didn't work because I forgot this was a coroutine (had to be called () and awaited).
Invite.uses
Invite.inviter
commands.command()
F-strings Python 3.6+

Resources