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

Discord.net/Discord.py录制用户/频道音频流失败的解决方案咨询

问题

尝试录制Discord特定用户的音频,无法实现则录制整个语音频道。最初使用Discord.py未成功,现在用C#开发的机器人会在特定用户加入频道时自动加入并启动录制,但参考旧帖子和GitHub线程的方案已失效,怀疑Discord是否移除了音频流获取支持?

当前C#代码中,var bytesRead = await inputStream.ReadAsync(buffer, 0, buffer.Length, _cts.Token);一行一直阻塞无返回值,音频流始终没有数据。求C#或Python的可行解决方案。

用户提供的C#代码:

private Task OnVoiceStateUpdated(SocketUser? socketUser, SocketVoiceState before, SocketVoiceState after)
{
    _ = Task.Run(async () =>
    {
        try
        {
            Logger.Output($"Starting to handle user movements", Logger.LogType.Default);
            var currentChannel = after.VoiceChannel ?? before.VoiceChannel;
            if (currentChannel == null) return;
            var botUser = currentChannel.Guild.Users.FirstOrDefault(u => u.IsBot);

            if (before.VoiceChannel == null && after.VoiceChannel != null) // If member joins a voice channel
            {
                var channel = after.VoiceChannel;

                try
                {
                    Logger.Output($"Connect bot to channel '{channel.Name}'", Logger.LogType.Default);
                    await Task.Delay(1000);
                    _audioClient = await channel.ConnectAsync();
                    _cts = new CancellationTokenSource();


                    if (socketUser is not SocketGuildUser socketGuildUser) return;
                    if (socketGuildUser.AudioStream is not InputStream inputStream) return;

                    _ = Task.Run(() =>
                        ProcessAudioAsync(inputStream)
                    );
                }
                catch (Exception e)
                {
                    Logger.Output($"Failed to connect and start recording: {e}", Logger.LogType.Error);
                }
            }
        }
        catch (Exception e)
        {
            Logger.Output($"Handling user movements failed! {e.Message} ", Logger.LogType.Error);
        }
    });
    return Task.CompletedTask;
}

private async Task ProcessAudioAsync(InputStream inputStream)
{
    // Create a new wave file writer to save the audio stream
    string currentTime = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
    string filename = Path.Combine(AudioRecordingsPath, $"output_{currentTime}.wav");

    // Create the wave file writer to save the audio stream
    _writer = new WaveFileWriter(filename, new WaveFormat(48000, 16, 2));

    try
    {
        var buffer = new byte[81920]; // Buffer size can be adjusted as needed

        while (!_cts.IsCancellationRequested)
        {
            var bytesRead = await inputStream.ReadAsync(buffer, 0, buffer.Length, _cts.Token);

            // If bytesRead is zero, end of the stream has been reached
            if (bytesRead == 0)
            {
                Task.Delay(1000).Wait();
                continue;
            }

            // Write from buffer to wave file
            await _writer.WriteAsync(buffer, 0, bytesRead);

            // Flush the writer to ensure data is written to disk
            await _writer.FlushAsync();
        }
    }
    catch (OperationCanceledException e)
    {
        Logger.Output($"Audio processing was cancelled. {e.Message} {e.StackTrace}", Logger.LogType.Error);
    }
    catch (TimeoutException e)
    {
        Logger.Output($"Audio processing timed out. {e.Message} {e.StackTrace}", Logger.LogType.Error);
    }
    catch (InvalidOperationException e)
    {
        Logger.Output($"Audio processing failed due to stream issue. {e.Message} {e.StackTrace}",
            Logger.LogType.Error);
    }
    catch (Exception e)
    {
        Logger.Output($"Audio processing failed. {e.Message} {e.StackTrace}", Logger.LogType.Error);
    }
    finally
    {
        await inputStream.FlushAsync();
    }
}
解决方案

C# 基于DSharpPlus的可行方案

Discord.NET的旧音频流获取方式已被废弃,目前推荐使用DSharpPlus的VoiceNext扩展实现音频捕获,步骤如下:

  1. 安装依赖包:
Install-Package DSharpPlus
Install-Package DSharpPlus.VoiceNext
  1. 核心实现代码:
using DSharpPlus;
using DSharpPlus.VoiceNext;
using NAudio.Wave;
using System.IO;

private async Task OnVoiceStateUpdated(DiscordClient client, VoiceStateUpdateEventArgs e)
{
    // 替换为目标用户的ID
    ulong targetUserId = 1234567890;
    if (e.User.Id != targetUserId) return;

    // 用户加入语音频道时,机器人自动连接并开始捕获
    if (e.Before.Channel == null && e.After.Channel != null)
    {
        var voiceNext = client.GetVoiceNext();
        var connection = await voiceNext.ConnectAsync(e.After.Channel);
        
        // 注册音频接收事件
        connection.AudioReceived += async (s, audioArgs) =>
        {
            // 仅捕获目标用户的音频
            if (audioArgs.User.Id != targetUserId) return;
            
            // 生成保存路径
            string recordingsDir = Path.Combine(AppContext.BaseDirectory, "audio_recordings");
            Directory.CreateDirectory(recordingsDir);
            string filePath = Path.Combine(recordingsDir, $"{e.User.Username}_{DateTime.Now:yyyyMMddHHmmss}.wav");
            
            // 将PCM数据写入WAV文件
            using var writer = new WaveFileWriter(filePath, new WaveFormat(48000, 16, 2));
            await writer.WriteAsync(audioArgs.PcmData, 0, audioArgs.PcmData.Length);
            await writer.FlushAsync();
        };
    }
}

关键说明:

  • VoiceNext是DSharpPlus官方维护的音频模块,支持最新Discord音频协议
  • AudioReceived事件会主动推送每个说话用户的PCM音频数据,无需手动读取流,避免阻塞问题
  • 可通过audioArgs.User.Id精准过滤目标用户的音频

Python 基于discord.py的可行方案

Discord.py最新版本仍支持音频捕获,此前失败大概率是未正确配置Intents或音频监听逻辑,核心实现:

  1. 安装依赖:
pip install discord.py[voice]
  1. 核心代码:
import discord
from discord.ext import commands
import wave
import os
from datetime import datetime

# 配置机器人
intents = discord.Intents.all()
bot = commands.Bot(command_prefix="!", intents=intents)

# 目标用户ID和录制目录
TARGET_USER_ID = 1234567890
RECORD_DIR = "discord_recordings"
os.makedirs(RECORD_DIR, exist_ok=True)

@bot.event
async def on_voice_state_update(member, before, after):
    if member.id != TARGET_USER_ID:
        return
    
    # 用户加入频道时,机器人连接并开始录制
    if before.channel is None and after.channel is not None:
        voice_client = await after.channel.connect()
        
        def audio_callback(user, data):
            # 仅处理目标用户的音频
            if user.id != TARGET_USER_ID:
                return
            
            # 生成文件名并写入WAV
            file_name = f"{member.name}_{datetime.now().strftime('%Y%m%d%H%M%S')}.wav"
            file_path = os.path.join(RECORD_DIR, file_name)
            
            with wave.open(file_path, 'wb') as wf:
                wf.setnchannels(2)
                wf.setsampwidth(2)
                wf.setframerate(48000)
                wf.writeframes(data)
        
        # 注册音频监听回调
        voice_client.listen(audio_callback)

# 启动机器人,替换为你的Bot Token
bot.run("YOUR_DISCORD_BOT_TOKEN")

关键说明:

  • 必须启用Intents.all(),尤其是语音相关的权限,否则无法获取语音状态更新和音频数据
  • voice_client.listen()会自动监听频道内所有用户的音频,通过回调参数user可过滤目标用户
  • 直接写入PCM数据到WAV文件,无需处理流阻塞问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 21:15:00