You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python Discord机器人Blackjack命令报错:'bj'无to_components属性

问题描述

我正在用Python开发Discord机器人的二十一点(Blackjack)命令,采用分步编写、逐步测试优化的方式开发。但执行代码中的await interaction.response.send_message(embed=match, view=bj(match ,game_sum, player_cards))语句时,触发了AttributeError: 'bj' object has no attribute 'to_components'错误,导致命令无法正常运行。相关代码及报错信息如下:

原代码

import discord
from discord.ext import commands
from discord import app_commands
import random, json, datetime
class bj(discord.ui.Button):
   def __init__(self, game ,gamesum, player_cards):
     super().__init__()
     self.value = None
     @discord.ui.button (label="Hit", style=discord.ButtonStyle.blurple)
     async def confirm(self, button: discord.ui.Button, interaction: discord. Interaction):
       self.value = True
       self.stop()
       value_1=["A", 1,2,3,4,5,6,7,8,9,10,"J","Q","K",]
       nype_1=["♦️","♠️","♥️","♣️"]
       card_value_3 = random.randint(0, len(value_1) - 1)
       nype_card_3 = random.randint(0, len(nype_1) - 1)
       card_3 = f"{card_value_3[card_value_3]} {card_value_3[nype_card_3]}"
       await match.edit()
       match.edit_field(name='Your hand', value=f"{player_cards} {card_value_3}{nype_card_3}")
       if type(card_value_3)==str:
         if value_card_3=="A":
           value_letter_3=11
         else:
           value_letter_3=10
       else:
         value_letter_3=value_card_3
class client(discord.Client):
     def __init__(self):
         super().__init__(intents=discord.Intents.default())
         self.synced = False
     async def setup_hook(self) -> None:
       self.add_view(DropdownView())
     async def on_ready(self):
       await aclient.change_presence(activity = discord.Streaming(name = "/news", url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"))
       await self.wait_until_ready()
       if not self.synced:
           await tree.sync()
           self.synced = True
       print(f"We entered as {self.user}!")
       print(f"That has the ID {self.user.id}")
client = client()
tree = app_commands.CommandTree(aclient)
@tree.command(name='blackjack', description='Play a game of blackjack')
async def blackjack(interaction: discord.Interaction, amount:int):
   await open_account(interaction.user)
   money = await add_money(interaction.user)
   if amount>money[0]:
     await interaction.response.send_message(f"You don't have all this amount {interaction.user.mention}.")
   else:
     if quantity<0:
       await interaction.response.send_message(f"You must enter a positive value")
     else:
       value_1=["A",2,3,4,5,6,7,8,9,10,'J','Q',"K"]
       nype_1=["♦️","♠️","♥️","♣️"]      
       value_card_a = random.randint(0, len(value_1) - 1)
       nype_card_a = random.randint(0, len(nype_1) - 1)
       letter_a = f"{value_1[value_card_a]}{nype_1[nype_letter_a]}"
       b_card_value = random.randint(0, len(1_value) - 1)
       nype_card_b = random.randint(0, len(nype_1) - 1)
       letter_b = f"{value_1[value_card_b]}{nype_1[nype_letter_a]}"
       player_cards = f"{card_a} {card_b}"
       if type(value_card_a)==str:
         if value_card_a=="A":
           value_letter_a=11
         else:
           value_letter_a=10
       else:
         value_card_a=value_card_a
       if type(value_card_b)==str:
         if value_card_b=="A":
           value_letter_b=11
         else:
           value_letter_b=10
       else:
         value_card_b=value_card_b
       sum_of_game=value_card_a+value_card_b
       await add_money(interaction.user, -1*amount)
       if sum_of_game==21:
         result='Blackjack'
         titulo_resultado=f'You won ~~K~~ {1.5*amount}'
       else:
         result=sum_of_game
         titulo_resultado=f'You invested ~~K~~ {amount} in this blackjack'
       departure=discord.Embed(
         description=f'{result_title}',
         colour=discord.Color.random()
       )
       match.add_field(name='Your hand', value=f"{player_cards}\{result}")
       if result=='Blackjack':
         await add_money(interaction.user, 1.5*amount)
       await interaction.response.send_message(embed=game, view=bj(game ,gamesum, player_cards))  
async def open_account(user):
   users = await bank_data()
   if str(user.id) in users:
     return False
   else:
     users[str(user.id)] = {}
     users[str(user.id)]['wallet'] = 0
     users[str(user.id)]['bank']= 0
   with open("money.json", "w") as f:
     json.dump(users, f)
   return true
async def bank_data():
   with open("money.json", "r") as f:
     users = json.load(f)
   return users
async def add_money(user, change=0, mode = "wallet"):
   users = await bank_data()
   users[str(user.id)][mode] +=change
   with open("money.json", "w") as f:
     json.dump(users, f)
   money = [users[str(user.id)]["wallet"], users[str(user.id)]["bank"]]
   return money
aclient.run(Token)

报错信息

2023-02-06 19:14:31 ERROR    discord.app_commands.tree Ignoring exception in command 'blackjack'
Traceback (most recent call last):
  File "/home/runner/Kllin/venv/lib/python3.10/site-packages/discord/app_commands/commands.py", line 862, in _do_call
    return await self._callback(interaction, **params)  # type: ignore
  File "main.py", line 1155, in blackjack
    await interaction.response.send_message(embed=partida, view=bj(partida ,soma_de_jogo, cartas_jogador))
  File "/home/runner/Kllin/venv/lib/python3.10/site-packages/discord/interactions.py", line 754, in send_message
    params = interaction_message_response_params(
  File "/home/runner/Kllin/venv/lib/python3.10/site-packages/discord/webhook/async_.py", line 577, in interaction_message_response_params
    data['components'] = view.to_components()
AttributeError: 'bj' object has no attribute 'to_components'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/runner/Kllin/venv/lib/python3.10/site-packages/discord/app_commands/tree.py", line 1242, in _call
    await command._invoke_with_namespace(interaction, namespace)
  File "/home/runner/Kllin/venv/lib/python3.10/site-packages/discord/app_commands/commands.py", line 887, in _invoke_with_namespace
    return await self._do_call(interaction, transformed_values)
  File "/home/runner/Kllin/venv/lib/python3.10/site-packages/discord/app_commands/commands.py", line 880, in _do_call
    raise CommandInvokeError(self, e) from e
discord.app_commands.errors.CommandInvokeError: Command 'blackjack' raised an exception: AttributeError: 'bj' object has no attribute 'to_components'
问题原因与修复方案

核心错误原因

send_message方法的view参数要求传入**discord.ui.View类的实例**,但原代码中bj类继承自discord.ui.Button,单个Button组件不具备to_components()方法,因此触发AttributeError。所有交互组件(如Button、Select)必须嵌套在View容器中才能作为view参数使用。

此外,原代码还存在多处变量名不统一、未定义变量、逻辑错误,需要一并修正:

  • 变量名混淆:比如departure与match、game_sum与sum_of_game、letter_a与card_a等
  • 未定义变量:nype_letter_a、quantity、1_value等
  • 逻辑错误:计算卡牌数值时直接使用索引而非对应值,字符串索引错误等

修正后的代码

import discord
from discord import app_commands
import random, json

class BJView(discord.ui.View):
    def __init__(self, embed, player_cards, current_sum, bet_amount, user):
        super().__init__(timeout=60)
        self.embed = embed
        self.player_cards = player_cards
        self.current_sum = current_sum
        self.bet_amount = bet_amount
        self.user = user

    @discord.ui.button(label="Hit", style=discord.ButtonStyle.blurple)
    async def hit(self, button: discord.ui.Button, interaction: discord.Interaction):
        # 抽新牌
        value_list = ["A", 2, 3, 4, 5, 6, 7, 8, 9, 10, "J", "Q", "K"]
        suit_list = ["♦️", "♠️", "♥️", "♣️"]
        card_value_idx = random.randint(0, len(value_list)-1)
        card_suit_idx = random.randint(0, len(suit_list)-1)
        new_card = f"{value_list[card_value_idx]}{suit_list[card_suit_idx]}"
        
        # 计算新牌数值
        if isinstance(value_list[card_value_idx], str):
            add_value = 11 if value_list[card_value_idx] == "A" else 10
        else:
            add_value = value_list[card_value_idx]
        
        self.current_sum += add_value
        self.player_cards += f" {new_card}"
        
        # 更新Embed
        self.embed.set_field_at(0, name='Your hand', value=f"{self.player_cards}\n当前点数: {self.current_sum}")
        
        # 检查爆牌或Blackjack
        if self.current_sum > 21:
            self.embed.description = f"你爆牌了!输掉了~~K~~{self.bet_amount}"
            self.stop()
        elif self.current_sum == 21:
            self.embed.description = f"Blackjack!赢得了~~K~~{self.bet_amount * 1.5}"
            await add_money(self.user, self.bet_amount * 1.5)
            self.stop()
        
        await interaction.response.edit_message(embed=self.embed, view=self)

    @discord.ui.button(label="Stand", style=discord.ButtonStyle.green)
    async def stand(self, button: discord.ui.Button, interaction: discord.Interaction):
        # 后续可添加庄家逻辑,这里先简化
        self.embed.description = f"你选择停牌,当前点数: {self.current_sum}"
        self.stop()
        await interaction.response.edit_message(embed=self.embed, view=self)

class MyClient(discord.Client):
    def __init__(self):
        super().__init__(intents=discord.Intents.default())
        self.synced = False

    async def on_ready(self):
        await self.change_presence(activity=discord.Streaming(name="/news", url="https://www.youtube.com/watch?v=dQw4w9WgXcQ"))
        await self.wait_until_ready()
        if not self.synced:
            await tree.sync()
            self.synced = True
        print(f"已登录为 {self.user}\n用户ID: {self.user.id}")

client = MyClient()
tree = app_commands.CommandTree(client)

@tree.command(name='blackjack', description='Play a game of blackjack')
async def blackjack(interaction: discord.Interaction, amount: int):
    await open_account(interaction.user)
    money_data = await add_money(interaction.user)
    wallet_balance = money_data[0]

    if amount <= 0:
        await interaction.response.send_message(f"{interaction.user.mention},你必须输入正数金额!")
        return
    if amount > wallet_balance:
        await interaction.response.send_message(f"{interaction.user.mention},你的余额不足!当前钱包余额: ~~K~~{wallet_balance}")
        return

    # 扣除下注金额
    await add_money(interaction.user, -amount)

    # 初始化玩家手牌
    value_list = ["A", 2, 3, 4, 5, 6, 7, 8, 9, 10, "J", "Q", "K"]
    suit_list = ["♦️", "♠️", "♥️", "♣️"]

    # 第一张牌
    card1_idx = random.randint(0, len(value_list)-1)
    suit1_idx = random.randint(0, len(suit_list)-1)
    card1 = f"{value_list[card1_idx]}{suit_list[suit1_idx]}"
    # 计算牌值
    if isinstance(value_list[card1_idx], str):
        val1 = 11 if value_list[card1_idx] == "A" else 10
    else:
        val1 = value_list[card1_idx]

    # 第二张牌
    card2_idx = random.randint(0, len(value_list)-1)
    suit2_idx = random.randint(0, len(suit_list)-1)
    card2 = f"{value_list[card2_idx]}{suit_list[suit2_idx]}"
    if isinstance(value_list[card2_idx], str):
        val2 = 11 if value_list[card2_idx] == "A" else 10
    else:
        val2 = value_list[card2_idx]

    player_cards = f"{card1} {card2}"
    total_sum = val1 + val2

    # 创建Embed
    if total_sum == 21:
        embed_desc = f"Blackjack!赢得了~~K~~{amount * 1.5}"
        await add_money(interaction.user, amount * 1.5)
        view = None
    else:
        embed_desc = f"你下注了~~K~~{amount},开始游戏!"
        view = BJView(embed=None, player_cards=player_cards, current_sum=total_sum, bet_amount=amount, user=interaction.user)

    game_embed = discord.Embed(description=embed_desc, colour=discord.Color.random())
    game_embed.add_field(name='Your hand', value=f"{player_cards}\n当前点数: {total_sum}")
    
    if view:
        view.embed = game_embed
        await interaction.response.send_message(embed=game_embed, view=view)
    else:
        await interaction.response.send_message(embed=game_embed)

async def open_account(user):
    users = await bank_data()
    if str(user.id) in users:
        return False
    users[str(user.id)] = {'wallet': 0, 'bank': 0}
    with open("money.json", "w") as f:
        json.dump(users, f)
    return True

async def bank_data():
    try:
        with open("money.json", "r") as f:
            users = json.load(f)
    except FileNotFoundError:
        users = {}
    return users

async def add_money(user, change=0, mode="wallet"):
    users = await bank_data()
    users[str(user.id)][mode] += change
    with open("money.json", "w") as f:
        json.dump(users, f)
    return [users[str(user.id)]["wallet"], users[str(user.id)]["bank"]]

# 替换为你的机器人Token
client.run("YOUR_BOT_TOKEN")

关键修复点说明

  1. View容器改造:将原bj类改为继承discord.ui.View的BJView,并在其中定义Hit和Stand按钮,符合Discord组件的使用规范。
  2. 变量名统一:修正所有变量名混淆问题,确保变量引用一致。
  3. 逻辑修正:修复卡牌数值计算逻辑,正确使用卡牌列表中的值而非索引;添加爆牌、停牌的基础逻辑。
  4. 错误处理:增加钱包文件不存在时的默认处理,避免读取文件报错。

内容的提问来源于stack exchange,提问作者Luca

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 00:01:13