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

discord.py 2.1.0机器人语音频道接入问题及代码现代化咨询

Discord.py 2.1.0 语音频道功能问题及现代写法指南

问题描述

我为自己的Discord服务器开发了一款机器人,除语音频道接入功能外,其余功能(如发送图片)均正常运行。但我无法实现机器人加入语音频道的功能,查阅资料时发现discord.py的使用方式众说纷纭。当前我使用的是discord.py 2.1.0版本,代码写法与示例差异较大,怀疑使用了过时方法。现咨询:

  • 当前开发此类机器人的标准写法是什么?
  • 如何将代码升级为现代写法?
  • 是否需要升级到discord.py rewrite?

附当前代码:

import os

import discord
import nacl
from dotenv import load_dotenv

load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
GUILD = os.getenv('DISCORD_GUILD')

intents = discord.Intents.all()
client = discord.Client(intents=intents)
user = client.get_user(X)
voiceChannel = client.get_channel(X)
intents.message_content = True

# Sends info in the terminal on connection
@client.event
async def on_ready():
    for guild in client.guilds:
        if guild.name == GUILD:
            break

    print(
        f'{client.user} is connected to the following guild:\n'
        f'{guild.name}(id: {guild.id})'
    )

@client.event
async def on_message(message):
    if message.author == client.user:
        return
    # Sends a picture of a Tarkov map when a command is sent in chat
    if message.content.startswith('.customs'):
        await message.channel.send(file=discord.File('/home/container/Bot/tarkovmaps/customs.png'))
   
    
    # Sends a picture of Tarkov ammo stats when a command is sent in chat
    if message.content.startswith('.4.6x30'):
        await message.channel.send(file=discord.File('/home/container/Bot/tarkovammo/4.6x30.png'))
    if message.content.startswith('.5.7x28'):
        await message.channel.send(file=discord.File('/home/container/Bot/tarkovammo/5.7x28.png'))
    
    #There are more commands like the ones above and they all work 
    
    #Makes the bot join a voice channel and say stuff when a command is sent; doesn't work
    if message.content.startswith('.join'):
        #await client.join_voice_channel(voiceChannel)
        await voiceChannel.connect()
        print('Bot joined the channel.')
    #This format didn't work either    
    if message.contents.startswith('.leave'):
        await client.leave_voice_channel(voiceChannel)
        print('Bot left the channel.')

client.run(TOKEN)

解答

1. 关于discord.py rewrite的疑问

discord.py rewrite就是官方的v2.x分支,你当前使用的2.1.0版本已经属于rewrite版本,不需要额外升级。旧版的v1.x写法才是过时的,你的核心问题是代码中的错误和过时API的使用。

2. 当前代码的核心问题

  • 提前调用客户端方法:在client.run(TOKEN)之前调用client.get_user(X)和client.get_channel(X),此时客户端还未连接到Discord,这些方法会返回None,导致后续voiceChannel.connect()调用失败。
  • 拼写错误:message.contents.startswith('.leave')中的contents应为content。
  • 过时的语音API:client.join_voice_channel()和client.leave_voice_channel()是v1.x的方法,v2.x中已移除,需使用VoiceChannel.connect()和VoiceClient.disconnect()。

3. 现代写法改造方案

方案一:基于原有on_message事件修复

import os

import discord
import nacl
from dotenv import load_dotenv

load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
GUILD = os.getenv('DISCORD_GUILD')
# 提前定义频道ID,不要提前调用get_channel
VOICE_CHANNEL_ID = X  # 替换成你的语音频道ID

intents = discord.Intents.all()
intents.message_content = True  # 启用消息内容意图,要放在client初始化前
client = discord.Client(intents=intents)

@client.event
async def on_ready():
    for guild in client.guilds:
        if guild.name == GUILD:
            break

    print(
        f'{client.user} is connected to the following guild:\n'
        f'{guild.name}(id: {guild.id})'
    )

@client.event
async def on_message(message):
    if message.author == client.user:
        return
    
    # 地图和弹药命令保留
    if message.content.startswith('.customs'):
        await message.channel.send(file=discord.File('/home/container/Bot/tarkovmaps/customs.png'))
    if message.content.startswith('.4.6x30'):
        await message.channel.send(file=discord.File('/home/container/Bot/tarkovammo/4.6x30.png'))
    if message.content.startswith('.5.7x28'):
        await message.channel.send(file=discord.File('/home/container/Bot/tarkovammo/5.7x28.png'))
    
    # 修复后的.join命令
    if message.content.startswith('.join'):
        # 客户端连接后再获取频道
        voice_channel = client.get_channel(VOICE_CHANNEL_ID)
        if voice_channel and isinstance(voice_channel, discord.VoiceChannel):
            # 检查是否已经在语音频道中
            if message.guild.voice_client is None:
                await voice_channel.connect()
                print('Bot joined the channel.')
            else:
                await message.channel.send("Bot已经在语音频道中了")
        else:
            await message.channel.send("指定的语音频道不存在")
    
    # 修复后的.leave命令
    if message.content.startswith('.leave'):
        voice_client = message.guild.voice_client
        if voice_client and voice_client.is_connected():
            await voice_client.disconnect()
            print('Bot left the channel.')
        else:
            await message.channel.send("Bot不在任何语音频道中")

client.run(TOKEN)

方案二:使用discord.ext.commands框架(推荐)

这是discord.py官方推荐的现代命令开发方式,更简洁易维护:

import os

import discord
from discord.ext import commands
import nacl
from dotenv import load_dotenv

load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
GUILD = os.getenv('DISCORD_GUILD')
VOICE_CHANNEL_ID = X  # 替换成你的语音频道ID

# 定义命令前缀,启用必要的意图
intents = discord.Intents.all()
intents.message_content = True
bot = commands.Bot(command_prefix='.', intents=intents)

@bot.event
async def on_ready():
    for guild in bot.guilds:
        if guild.name == GUILD:
            break

    print(
        f'{bot.user} is connected to the following guild:\n'
        f'{guild.name}(id: {guild.id})'
    )

# 地图命令
@bot.command()
async def customs(ctx):
    await ctx.send(file=discord.File('/home/container/Bot/tarkovmaps/customs.png'))

# 弹药命令
@bot.command()
async def _46x30(ctx):
    await ctx.send(file=discord.File('/home/container/Bot/tarkovammo/4.6x30.png'))

@bot.command()
async def _57x28(ctx):
    await ctx.send(file=discord.File('/home/container/Bot/tarkovammo/5.7x28.png'))

# 语音频道加入命令
@bot.command()
async def join(ctx):
    voice_channel = bot.get_channel(VOICE_CHANNEL_ID)
    if not voice_channel or not isinstance(voice_channel, discord.VoiceChannel):
        await ctx.send("指定的语音频道不存在")
        return
    
    if ctx.voice_client is None:
        await voice_channel.connect()
        await ctx.send("Bot已加入语音频道")
    else:
        await ctx.send("Bot已经在语音频道中了")

# 语音频道离开命令
@bot.command()
async def leave(ctx):
    if ctx.voice_client and ctx.voice_client.is_connected():
        await ctx.voice_client.disconnect()
        await ctx.send("Bot已离开语音频道")
    else:
        await ctx.send("Bot不在任何语音频道中")

bot.run(TOKEN)

关键注意事项

  • 意图设置:确保在Discord开发者门户中启用了Message Content Intent,否则机器人无法读取消息内容。
  • 依赖安装:确保安装了nacl(pip install pynacl),这是语音功能必需的依赖。
  • 频道ID获取:在Discord中开启开发者模式后,右键点击频道即可复制ID。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 02:05:18