Discord.Net机器人设置guildUserProperties.Mute为false时无法解除用户静音
问题分析与解决方案
我一眼就看出问题所在了——你在用户离开语音频道之后尝试修改他的静音状态,但Discord的API根本不允许这么做!
核心原因
Discord用户的语音静音状态是和其当前所在的语音频道绑定的。当用户离开所有语音频道(newSocketVoiceState.VoiceChannel == null)时,调用ModifyAsync设置Mute属性是无效的——因为没有对应的语音频道来应用这个静音/解除静音的操作。这就是为什么你能成功从列表移除用户ID,但解除静音的操作毫无效果。
解决方案:延迟解除静音到用户加入下一个频道
我们需要调整逻辑:当用户离开监控静音频道时,先记录这个用户需要解除静音,等用户加入任意语音频道时,再执行解除操作。
步骤1:扩展缓存结构
首先更新你的缓存类,增加一个全局列表来记录需要解除静音的用户:
public class ObservedVoiceChannelInfo { public ulong VoiceChannelId { get; set; } public bool Muted { get; set; } public List<ulong> MutedUserIds { get; set; } = new List<ulong>(); } // 新增全局缓存:记录需要解除静音的用户ID private List<ulong> _usersToUnmute = new List<ulong>();
步骤2:修改事件处理逻辑
调整OnUserVoiceStateUpdated的逻辑,把解除静音的操作延迟到用户加入新频道时执行:
private async Task OnUserVoiceStateUpdated(SocketUser socketUser, SocketVoiceState oldSocketVoiceState, SocketVoiceState newSocketVoiceState) { SocketGuildUser socketGuildUser = socketUser as SocketGuildUser; if (socketGuildUser == null) return; // 确保操作对象是服务器内的用户 // 场景1:用户离开处于静音状态的监控频道 if (oldSocketVoiceState.VoiceChannel != null && _observedVoiceChannelsCache.TryGetValue(oldSocketVoiceState.VoiceChannel.Id, out ObservedVoiceChannelInfo oldObservedChannel) && oldObservedChannel.Muted && (newSocketVoiceState.VoiceChannel == null || newSocketVoiceState.VoiceChannel.Id != oldSocketVoiceState.VoiceChannel.Id)) { // 从监控频道的静音列表移除用户 bool removedFromMuteList = oldObservedChannel.MutedUserIds.Remove(socketGuildUser.Id); if (removedFromMuteList) { // 不立刻解除静音,而是加入待解除列表(加锁保证线程安全) lock (_usersToUnmute) { if (!_usersToUnmute.Contains(socketGuildUser.Id)) { _usersToUnmute.Add(socketGuildUser.Id); } } } } // 场景2:用户加入任意频道,先检查是否需要解除静音 if (newSocketVoiceState.VoiceChannel != null) { lock (_usersToUnmute) { if (_usersToUnmute.Remove(socketGuildUser.Id)) { await SetUserVoiceState(socketGuildUser, false); } } // 场景3:用户加入处于静音状态的监控频道,执行静音操作 if (_observedVoiceChannelsCache.TryGetValue(newSocketVoiceState.VoiceChannel.Id, out ObservedVoiceChannelInfo newObservedChannel) && newObservedChannel.Muted && !newSocketVoiceState.IsMuted) { await SetUserVoiceState(socketGuildUser, true); newObservedChannel.MutedUserIds.Add(socketGuildUser.Id); } } } private async Task SetUserVoiceState(SocketGuildUser socketGuildUser, bool muted) { try { await socketGuildUser.ModifyAsync(props => props.Mute = muted); } catch (Exception ex) { // 可选:添加日志记录操作失败的情况 Console.WriteLine($"修改用户静音状态失败:{ex.Message}"); } }
关键调整点
- 延迟解除静音:用户离开监控频道时仅记录需求,不直接操作,避免Discord API限制
- 加入频道时检查:用户加入任何频道时自动触发解除静音操作,符合Discord API的规则
- 线程安全:使用
lock保护全局列表,防止多线程事件触发时的并发冲突 - 异常处理:给静音状态修改方法添加捕获,避免操作失败导致机器人崩溃
这样调整后,用户离开监控静音频道后,下次加入任何语音频道时都会自动解除静音,完美解决你的问题。
内容的提问来源于stack exchange,提问作者Question3r
相关产品推荐
相关产品推荐

