Reactions to buttons replacement

Question: Can I use buttons instead of reactions to hit or stand?

Current behavior: When I click the tick I hit and when I hit the X it stands.

Desired behavior: I want to have buttons called Hit and Stand which do the same thing as the reactions done before.

Repl link: https://replit.com/@chaosok/IdenticalLavishIntroductory#main.py

This is what I had for my reactions to hit and stand.

If anyone is able to make them into buttons that would be grately appreaciated as I don’t know how to do that.

  await message.add_reaction("✅")
  await message.add_reaction("❌")

  def check(reaction, user):
      return user == ctx.author and str(reaction.emoji) in ["✅", "❌"]

  while True:
      try:
          reaction, user = await bot.wait_for("reaction_add", timeout=60, check=check)

          if str(reaction.emoji) == "✅":
              player_hand.append(draw_card())
              player_total = calculate_total(player_hand)
              embed.set_field_at(0, name=f"Your Cards: {player_total}", value=format_hand(player_hand), inline=False)
              await message.edit(embed=embed)

              if player_total > 21:
                  await handle_loss(ctx, accounts, author_id, bet, message, dealer_hand, dealer_total, player_total, player_hand)
                  return

          else:
              while dealer_total < 17:
                  dealer_hand.append(draw_card())
                  dealer_total = calculate_total(dealer_hand)

              embed.set_field_at(1, name=f"Dealer's Cards: {dealer_total}", value=format_hand(dealer_hand), inline=False)
              await message.edit(embed=embed)

              if dealer_total >= 17:
                  if dealer_total > 21 or player_total > dealer_total:
                      await handle_win(ctx, accounts, author_id, bet, message, dealer_hand, dealer_total, player_total, player_hand)
                      return
                  elif player_total > 21 or player_total < dealer_total:
                      await handle_loss(ctx, accounts, author_id, bet, message, dealer_hand, dealer_total, player_total, player_hand)
                      return
                  else:
                      await handle_tie(ctx, accounts, author_id, bet, message, dealer_hand, dealer_total, player_total, player_hand)
                      return

          await message.remove_reaction(reaction, user)
      except asyncio.TimeoutError:
          await message.delete()
          break

You can use the Discord’s Button feature. Which involves creating a discord.ui.View that contains discord.ui.Button objects.

Going into your code snippet:

import discord
from discord.ui import Button, View

class BlackjackView(View):
    def __init__(self, ctx, bot, accounts, author_id, bet, message, dealer_hand, dealer_total, player_hand, player_total):
        super().__init__()
        self.ctx = ctx
        self.bot = bot
        self.accounts = accounts
        self.author_id = author_id
        self.bet = bet
        self.message = message
        self.dealer_hand = dealer_hand
        self.dealer_total = dealer_total
        self.player_hand = player_hand
        self.player_total = player_total

    @discord.ui.button(label="Hit", style=discord.ButtonStyle.green)
    async def hit_button_callback(self, button, interaction):
        if interaction.user != self.ctx.author:
            return
        # Hit logic
        self.player_hand.append(draw_card())
        self.player_total = calculate_total(self.player_hand)
        embed = self.message.embeds[0]  # Assuming embed is the first one
        embed.set_field_at(0, name=f"Your Cards: {self.player_total}", value=format_hand(self.player_hand), inline=False)
        await self.message.edit(embed=embed)

        if self.player_total > 21:
            await handle_loss(self.ctx, self.accounts, self.author_id, self.bet, self.message, self.dealer_hand, self.dealer_total, self.player_total, self.player_hand)
            self.stop()

    @discord.ui.button(label="Stand", style=discord.ButtonStyle.red)
    async def stand_button_callback(self, button, interaction):
        if interaction.user != self.ctx.author:
            return
        # Stand logic
        while self.dealer_total < 17:
            self.dealer_hand.append(draw_card())
            self.dealer_total = calculate_total(self.dealer_hand)

        embed = self.message.embeds[0]  # Assuming embed is the first one
        embed.set_field_at(1, name=f"Dealer's Cards: {self.dealer_total}", value=format_hand(self.dealer_hand), inline=False)
        await self.message.edit(embed=embed)

        if self.dealer_total >= 17:
            if self.dealer_total > 21 or self.player_total > self.dealer_total:
                await handle_win(self.ctx, self.accounts, self.author_id, self.bet, self.message, self.dealer_hand, self.dealer_total, self.player_total, self.player_hand)
            elif self.player_total > 21 or self.player_total < self.dealer_total:
                await handle_loss(self.ctx, self.accounts, self.author_id, self.bet, self.message, self.dealer_hand, self.dealer_total, self.player_total, self.player_hand)
            else:
                await handle_tie(self.ctx, self.accounts, self.author_id, self.bet, self.message, self.dealer_hand, self.dealer_total, self.player_total, self.player_hand)
            self.stop()

# When sending the message
view = BlackjackView(ctx, bot, accounts, author_id, bet, message, dealer_hand, dealer_total, player_hand, player_total)
await message.edit(view=view)  # Add the view to your message

Also two things:

  1. Remember to adjust the embed and other parts according to your code.
  2. I’m assuming that your message already contains an embed that you’re editing. Buuuut, if that’s not the case, you’ll need to modify the embed creation and editing as you see more “fit” to your situation.
2 Likes

Thank you! Ill try this code once I’m home

The embed doesnt send anymore for some reason.

Im going to just send the code for my blackjack. I hope you’re able to fix this

class BlackjackView(View):
  def __init__(self, ctx, bot, accounts, author_id, bet, message, dealer_hand, dealer_total, player_hand, player_total):
      super().__init__()
      self.ctx = ctx
      self.bot = bot
      self.accounts = accounts
      self.author_id = author_id
      self.bet = bet
      self.message = message
      self.dealer_hand = dealer_hand
      self.dealer_total = dealer_total
      self.player_hand = player_hand
      self.player_total = player_total

  @discord.ui.button(label="Hit", style=discord.ButtonStyle.green)
  async def hit_button_callback(self, button, interaction):
      if interaction.user != self.ctx.author:
          return
      # Hit logic
      self.player_hand.append(draw_card())
      self.player_total = calculate_total(self.player_hand)
      embed = self.message.embeds[0]  # Assuming embed is the first one
      embed.set_field_at(0, name=f"Your Cards: {self.player_total}", value=format_hand(self.player_hand), inline=False)
      await self.message.edit(embed=embed)

      if self.player_total > 21:
          await handle_loss(self.ctx, self.accounts, self.author_id, self.bet, self.message, self.dealer_hand, self.dealer_total, self.player_total, self.player_hand)
          self.stop()

  @discord.ui.button(label="Stand", style=discord.ButtonStyle.red)
  async def stand_button_callback(self, button, interaction):
      if interaction.user != self.ctx.author:
          return
      # Stand logic
      while self.dealer_total < 17:
          self.dealer_hand.append(draw_card())
          self.dealer_total = calculate_total(self.dealer_hand)

      embed = self.message.embeds[0]  # Assuming embed is the first one
      embed.set_field_at(1, name=f"Dealer's Cards: {self.dealer_total}", value=format_hand(self.dealer_hand), inline=False)
      await self.message.edit(embed=embed)

      if self.dealer_total >= 17:
          if self.dealer_total > 21 or self.player_total > self.dealer_total:
              await handle_win(self.ctx, self.accounts, self.author_id, self.bet, self.message, self.dealer_hand, self.dealer_total, self.player_total, self.player_hand)
          elif self.player_total > 21 or self.player_total < self.dealer_total:
              await handle_loss(self.ctx, self.accounts, self.author_id, self.bet, self.message, self.dealer_hand, self.dealer_total, self.player_total, self.player_hand)
          else:
              await handle_tie(self.ctx, self.accounts, self.author_id, self.bet, self.message, self.dealer_hand, self.dealer_total, self.player_total, self.player_hand)
          self.stop()

# Define your bot instanc

@bot.command(name="blackjack", aliases=["bj"])
async def blackjack(ctx, bet: int):
    author_id = str(ctx.author.id)
    player_hand = [draw_card(), draw_card()]
    dealer_hand = [draw_card(), draw_card()]
    player_total = calculate_total(player_hand)
    dealer_total = calculate_total(dealer_hand[1:])

    embed = discord.Embed(title="", color=0x232428)
    embed.add_field(name=f"Your Cards: {player_total}", value=format_hand(player_hand), inline=False)
    embed.add_field(name=f"Dealer's Cards: {dealer_total}", value=f"{format_hand(dealer_hand[1:])} ??? ", inline=False)
    embed.add_field(name="Result", value="In progress", inline=False)
    embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar.url)
    embed.set_thumbnail(url="https://i.imgur.com/JBPnaZz.png")

    view = BlackjackView(ctx, bot, accounts, author_id, bet, message, dealer_hand, dealer_total, player_hand, player_total)
    await message.edit(view=view)

def draw_card():
  return random.choice([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10])

def calculate_total(hand):
  total = sum(hand)
  num_aces = hand.count(1)
  while total <= 11 and num_aces > 0:
      total += 10
      num_aces -= 1
  return total

def format_hand(hand):
  card_names = {1: 'A', 10: 'J', 11: 'Q', 12: 'K'}
  return ", ".join(str(card_names.get(card, card)) for card in hand)

async def handle_win(ctx, bet, message, dealer_hand, dealer_total, player_total, player_hand):
  embed = discord.Embed(title="", color=0x232428)
  embed.add_field(name=f"Your Cards: {player_total}", value=f"{format_hand(player_hand)}", inline=False)
  embed.add_field(name=f"Dealer's Cards: {dealer_total}", value=format_hand(dealer_hand), inline=False)
  embed.add_field(name="Result", value=f"You won! Your new balance is: **{bet * 2}**", inline=False)
  embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar.url)
  embed.set_thumbnail(url="https://i.imgur.com/JBPnaZz.png")
  await message.edit(embed=embed)

async def handle_loss(ctx, bet, message, dealer_hand, dealer_total, player_total, player_hand):
  embed = discord.Embed(title="", color=0x232428)
  embed.add_field(name=f"Your Cards: {player_total}", value=f"{format_hand(player_hand)}", inline=False)
  embed.add_field(name=f"Dealer's Cards: {dealer_total}", value=format_hand(dealer_hand), inline=False)
  embed.add_field(name="Result", value=f"You lost. Your new balance is: **0**", inline=False)
  embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar.url)
  embed.set_thumbnail(url="https://i.imgur.com/JBPnaZz.png")
  await message.edit(embed=embed)

async def handle_tie(ctx, bet, message, dealer_hand, dealer_total, player_total, player_hand):
  embed = discord.Embed(title="", color=0x232428)
  embed.add_field(name=f"Your Cards: {player_total}", value=f"{format_hand(player_hand)}", inline=False)
  embed.add_field(name=f"Dealer's Cards: {dealer_total}", value=format_hand(dealer_hand), inline=False)
  embed.add_field(name="Result", value=f"The hand is a push. Your new balance is: **{bet}**", inline=False)
  embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar.url)
  embed.set_thumbnail(url="https://i.imgur.com/JBPnaZz.png")
  await message.edit(embed=embed)

You have to send the initial message with the embed, and then pass this message to the blackjackview

    # Like I said, send the initial message with the embed
    message = await ctx.send(embed=embed)
    # And now create the view with this message
    view = BlackjackView(ctx, bot, accounts, author_id, bet, message, dealer_hand, dealer_total, player_hand, player_total)
    await message.edit(view=view)
1 Like

The embeds sent now. But the buttons aren’t showing up for some reason i’ve checked the code myself but I cant find the problem. If you can find the problem and fix it that would be very helpful

Try to attach the view along with the embed and see if it works

await message.edit(embed=embed, view=view) 

Also, your bot token is in public for everyone to see. Use the secrets function from Replit to hide your bot token.

2 Likes

I’ve tried that now the embed doesn’t send.

I saw inside of the functions of handle_loss, handle_tie, handle_win the parameters weren’t updated so I changed them around and it still doesn’t send the embed.

Now i’ve got no clue what the error possibly could be sorry for bugging you so much but it would be great if you’re able to find the error and fix it.

Also i’ve reset my token and put it into a secret. Thank you for telling me

You still have to send the initial message, I just said to try to attach the view

# Send the initial message with the embed
message = await ctx.send(embed=embed)
# And now create the view with this message
    view = BlackjackView(ctx, bot, accounts, author_id, bet, message, dealer_hand, dealer_total, player_hand, player_total)
    await message.edit(embed=embed, view=view)

I had done that initally but I didn’t write it down in the message, but using that code the embed sends but the buttons don’t send

I’ve made some changes and now it works fine but whenever I click the button it says this interaction failed even though the buttons work as intended. The updated code is in my repl if u want to see it

I had some work to do and couldn’t reply before.

At using Discord Bots, when a button is pressed, the bot needs to acknowledge the interaction within 3 seconds. If it fails I think they give the message you said.

You can try to acknowledge the interaction within your code.

class BlackjackView(discord.ui.View):

    @discord.ui.button(label="Hit", style=discord.ButtonStyle.green)
    async def hit_button_callback(self, button, interaction):
        # As I said you acknowledge the interaction first
        await interaction.response.defer()

    @discord.ui.button(label="Stand", style=discord.ButtonStyle.red)
    async def stand_button_callback(self, button, interaction):
        # And here too
        await interaction.response.defer()

Thank you this worked!

Sorry to ask for help again but when I draw cards for the Jack, King, Queen they don’t send. I know its because the emojis aren’t in the draw_card function but if I change them to 11, 12, 13 the cards are worth 11, 12, 13 but I need them to be worth 10. I don’t know how to fix it so if you’re able to that would be very helpful

def draw_card():
  return random.choice([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10])

def calculate_total(hand):
  total = sum(hand)
  num_aces = hand.count(1)
  while total <= 11 and num_aces > 0:
      total += 10
      num_aces -= 1
  return total

def format_hand(hand):
  custom_emojis = {
      1: '<:ACE:1222736603525546104>',
      2: '<:TWO:1222736601461821550>',
      3: '<:THREE:1222736640955650048>',
      4: '<:FOUR:1222736598161166357>',
      5: '<:FIVE:1222736597028573285>',
      6: '<:SIX:1222736581467705455>',
      7: '<:SEVEN:1222736595434737696>',
      8: '<:EIGHT:1222736594424041573>',
      9: '<:NINE:1222736593329328189>',
      10: '<:TEN:1222736592158855218>',
      11: '<:JACK:1222736587935322242>',
      12: '<:QUEEN:1222736589575295100>',
      13: '<:KING:1222736590510493851>'
  }
  return " ".join(str(custom_emojis.get(card, f'**{card}**')) for card in hand)

Instead of representing the card as numbers try to use tuples. Tuples are more organized and :star2: pretty :star2: and since the cards have a dual nature in your game you will be able to encapsulates all the information about a card in one place too.

def draw_card():
    cards = [
        (1, '<:ACE:1222736603525546104>'), (2, '<:TWO:1222736601461821550>'), 
        (3, '<:THREE:1222736640955650048>'), (4, '<:FOUR:1222736598161166357>'), 
        (5, '<:FIVE:1222736597028573285>'), (6, '<:SIX:1222736581467705455>'), 
        (7, '<:SEVEN:1222736595434737696>'), (8, '<:EIGHT:1222736594424041573>'), 
        (9, '<:NINE:1222736593329328189>'), (10, '<:TEN:1222736592158855218>'), 
        (10, '<:JACK:1222736587935322242>'), (10, '<:QUEEN:1222736589575295100>'), 
        (10, '<:KING:1222736590510493851>')
    ]
    return random.choice(cards)

def calculate_total(hand):
    total = sum(card[0] for card in hand) 
    num_aces = len([card for card in hand if card[0] == 1])
    while total <= 11 and num_aces > 0:
        total += 10
        num_aces -= 1
    return total

def format_hand(hand):
    return " ".join(card[1] for card in hand) 
2 Likes

Thank you this works perfectly!

Sorry to bug you again but upon releasing my bot publiclly theres been a couple bugs such as losing money when you win im going to send 2 example photos:

I have no idea what the error is i’ve looked into my code and I haven’t found anything wrong if you’re able to find it and fix it you would be a lifesaver


Please, create another topic since we are deviating from the original problem

2 Likes

Okay, I’ve created a new one sorry about that.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.