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

Discord.Net音频机器人遇System.TimeoutException超时问题求助

Discord.Net开发机器人执行play命令触发TimeoutException求助

执行play命令播放音频时触发以下错误:

System.TimeoutException: The operation has timed out.
at Discord.WebSocket.SocketGuild.<ConnectAudioAsync>d__349.MoveNext()

已确认使用最新版本的Discord.Net,搜索相关解决方案但未找到有效办法,以下是核心代码:

public async Task<bool> ExecuteCommand(SocketMessage message, string cmd, string[] args)
{
    // Check if command is on cooldown
    if (_cooldowns.ContainsKey(cmd))
    {
        var cooldownEnd = _cooldowns[cmd].AddSeconds(_commandCooldowns[cmd]);
        if (DateTime.UtcNow < cooldownEnd)
        {
            await message.Channel.SendMessageAsync($"Command \"{cmd}\" is on cooldown. Please wait {(cooldownEnd - DateTime.UtcNow).TotalSeconds:F1} seconds.");
            return false;
        }
    }

    // Check if command has reached its limit
    if (_commandLimits.ContainsKey(cmd))
    {
        if (!_commandUsages.ContainsKey(cmd))
            _commandUsages[cmd] = 0;
        if (_commandUsages[cmd] >= _commandLimits[cmd])
        {
            await message.Channel.SendMessageAsync($"Command \"{cmd}\" has reached its usage limit.");
            return false;
        }
        _commandUsages[cmd]++;
    }

    // Execute the command
    await _commands[cmd](message, args);

    // Set cooldown
    if (_commandCooldowns.ContainsKey(cmd))
        _cooldowns[cmd] = DateTime.UtcNow;

    return true;
}

public async Task OnMessageReceived(SocketMessage message)
{
    // Ignore messages from bots and system messages
    if (message.Author.IsBot || message.Author.IsWebhook) return;

    if (message.Content.StartsWith(_prefix))
    {
        // Remove the prefix from the message
        string command = message.Content.Substring(_prefix.Length);

        // Split the message into words
        string[] words = command.Split(' ');

        // The first word is the command
        string cmd = words[0];

        // The rest of the words are the arguments
        string[] args = words.Skip(1).ToArray();

        // Handle the command
        await ExecuteCommand(message, cmd, args);
    }
}

public void RegisterCommand(string command, Func<SocketMessage, string[], Task> action, int limit = 0, int cooldown = 0)
{
    _commands[command] = action;
    if (limit > 0)
        _commandLimits[command] = limit;
    if (cooldown > 0)
        _commandCooldowns[command] = cooldown;
}

RegisterCommand("play", async (message, args) =>
{
    if (args.Length < 1)
    {
        await bot.SendMessage(message.Channel.Id, "Usage: !play <url>");
        return;
    }
    var url = args[0]; 
    try
    {
        bot._audioClient = await (message.Author as IVoiceState).VoiceChannel.ConnectAsync();
        var stream = bot.CreateStream(url);
        var output = bot._audioClient.CreatePCMStream(AudioApplication.Music);

        await stream.StandardOutput.BaseStream.CopyToAsync(output);
        await output.FlushAsync();
    }
    catch (Exception ex)
    {
        await bot.SendMessage(message.Channel.Id, "Error: " + ex.ToString());
    }
});

bot.RegisterCommand("stop", async (message, args) =>
{
    if (bot._audioClient != null)
    {
        await bot._audioClient.StopAsync();
    }
});

排查与解决建议

  • 验证语音权限与网络:确保机器人在目标语音频道拥有连接和发言权限,同时检查服务器网络是否允许Discord语音UDP端口(50000-65535)的通信,部分防火墙可能会拦截这类端口。
  • 复用现有语音连接:当前代码每次执行play都会重新调用ConnectAsync,如果机器人已经在语音频道中,重复连接会引发冲突导致超时。可以先判断bot._audioClient是否处于有效连接状态,再决定是否创建新连接:
    if (bot._audioClient == null || !bot._audioClient.ConnectionState.Equals(ConnectionState.Connected))
    {
        bot._audioClient = await (message.Author as IVoiceState).VoiceChannel.ConnectAsync();
    }
    
  • 调整连接超时参数:给ConnectAsync设置更长的超时时间,避免因网络延迟导致超时:
    bot._audioClient = await (message.Author as IVoiceState).VoiceChannel.ConnectAsync(timeout: TimeSpan.FromSeconds(30));
    
  • 清理失效的语音客户端:在stop命令中,调用StopAsync后需要将bot._audioClient置为null,避免下次play时尝试使用已失效的客户端:
    bot.RegisterCommand("stop", async (message, args) =>
    {
        if (bot._audioClient != null)
        {
            await bot._audioClient.StopAsync();
            bot._audioClient = null;
        }
    });
    
  • 检查音频流创建逻辑:CreateStream方法如果同步阻塞或耗时过长,可能导致连接后等待流的过程中触发超时,确保该方法是异步实现且高效的,避免阻塞主线程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 20:35:08