title: How to determine if a user joined/switched/left a voice channel?
body: <p>I'm using Discord.Net and observe several voice channels. If these voice channels have a muted state, set by the bot (not via permissions), users in that voice channel should be muted too.</p>
  <p>Simply removing the speak permission from a voice channel won't affect people instantly as you can see here</p>
  <p><a href="https://support.discord.com/hc/en-us/community/posts/360052856033-Directly-affect-people-in-channels-on-permission-changes" rel="nofollow noreferrer">https://support.discord.com/hc/en-us/community/posts/360052856033-Directly-affect-people-in-channels-on-permission-changes</a></p>
  <p>If they leave it they should get unmuted.</p>
  <p>So this one is holding all the required information</p>
  <pre><code>public sealed class ObservedVoiceChannel
  {
      public ulong VoiceChannelId { get; set; }
      public bool IsMuted { get; set; }
      // ... other information go here ...
  }
  </code></pre>
  <p>and I have a service holding all the observed voice channels</p>
  <pre><code>public sealed class ObservedVoiceChannelsCache : Dictionary&lt;ulong, ObservedVoiceChannel&gt;
  {
  }
  </code></pre>
  <p>Since there only is a <code>UserVoiceStateUpdated</code> event I came up with the following code.</p>
  <p>After some tests I think this code works fine for me. Although I know the if statements can be improved in terms of readability with &quot;or&quot; operators I will do it after fixing the last problem.</p>
  <p>When leaving an observed muted voice channel, see the comment</p>
  <blockquote>
  <p>// user left observed muted voice channel</p>
  </blockquote>
  <p>the user won't get unmuted by the bot. Sometimes, when joining and leaving fast enough the handler throws an exception</p>
  <blockquote>
  <p>The server responded with error 400: BadRequest</p>
  </blockquote>
  <blockquote>
  <p>at Discord.Net.Queue.RequestBucket.SendAsync(RestRequest request)<br />
  at Discord.Net.Queue.RequestQueue.SendAsync(RestRequest request)    at
  Discord.API.DiscordRestApiClient.SendInternalAsync(String method,
  String endpoint, RestRequest request)    at
  Discord.API.DiscordRestApiClient.SendJsonAsync(String method, String
  endpoint, Object payload, BucketId bucketId, ClientBucketType
  clientBucket, RequestOptions options)    at
  Discord.API.DiscordRestApiClient.ModifyGuildMemberAsync(UInt64
  guildId, UInt64 userId, ModifyGuildMemberParams args, RequestOptions
  options)    at Discord.Rest.UserHelper.ModifyAsync(IGuildUser user,
  BaseDiscordClient client, Action`1 func, RequestOptions options)    at
  ...OnUserVoiceStateUpdated(SocketUser socketUser, SocketVoiceState
  oldSocketVoiceState, SocketVoiceState newSocketVoiceState) in
  /.../UserVoiceStateUpdatedEventHandler.cs:line 52</p>
  </blockquote>
  <p>This is the code I'm currently using</p>
  <pre><code>public sealed class UserVoiceStateUpdatedEventHandler
  {
      private readonly ObservedVoiceChannelsCache _observedVoiceChannelsCache;
      
      public UserVoiceStateUpdatedEventHandler(ObservedVoiceChannelsCache observedVoiceChannelsCache)
      {
          _observedVoiceChannelsCache = observedVoiceChannelsCache;
      }
      
      public async Task OnUserVoiceStateUpdated(
          SocketUser socketUser, 
          SocketVoiceState oldSocketVoiceState,
          SocketVoiceState newSocketVoiceState)
      {
          if (socketUser is SocketGuildUser socketGuildUser)
          {
              bool userIsMuted = socketGuildUser.VoiceState?.IsMuted == true;
              bool userIsNotOffline = socketGuildUser.Status != UserStatus.Offline;
              
              // user left observed muted voice channel
              if (oldSocketVoiceState.VoiceChannel != null &amp;&amp; 
                  newSocketVoiceState.VoiceChannel == null &amp;&amp;
                  _observedVoiceChannelsCache.TryGetValue(oldSocketVoiceState.VoiceChannel.Id, out ObservedVoiceChannel observedLeftVoiceChannel) &amp;&amp;
                  observedLeftVoiceChannel.IsMuted &amp;&amp;
                  userIsMuted &amp;&amp;
                  userIsNotOffline
                  )
              {
                  await SetUserMuteState(socketGuildUser, false);
              }
              // user joined observed muted voice channel
              else if (oldSocketVoiceState.VoiceChannel == null &amp;&amp; 
                       newSocketVoiceState.VoiceChannel != null &amp;&amp;
                       _observedVoiceChannelsCache.TryGetValue(newSocketVoiceState.VoiceChannel.Id, out ObservedVoiceChannel observedJoinedVoiceChannel) &amp;&amp;
                       observedJoinedVoiceChannel.IsMuted &amp;&amp;
                       !userIsMuted &amp;&amp;
                       userIsNotOffline)
              {
                  await SetUserMuteState(socketGuildUser, true);
              }
              // user changed voice channels
              else if (oldSocketVoiceState.VoiceChannel != null &amp;&amp; 
                       newSocketVoiceState.VoiceChannel != null &amp;&amp;
                       userIsNotOffline)
              {
                  bool oldVoiceChannelObserved = _observedVoiceChannelsCache.TryGetValue(
                      oldSocketVoiceState.VoiceChannel.Id, out ObservedVoiceChannel oldObservedVoiceChannel);
                  
                  bool newVoiceChannelObserved = _observedVoiceChannelsCache.TryGetValue(
                      newSocketVoiceState.VoiceChannel.Id, out ObservedVoiceChannel newObservedVoiceChannel);
  
                  // user moved from observed muted voice channel to unobserved voice channel
                  if (oldVoiceChannelObserved &amp;&amp; 
                      !newVoiceChannelObserved &amp;&amp;
                      oldObservedVoiceChannel.IsMuted &amp;&amp;
                      userIsMuted)
                  {
                      await SetUserMuteState(socketGuildUser, false);
                  }
                  // user moved from unobserved voice channel to observed muted voice channel
                  else if (!oldVoiceChannelObserved &amp;&amp; 
                           newVoiceChannelObserved &amp;&amp;
                           newObservedVoiceChannel.IsMuted &amp;&amp;
                           !userIsMuted)
                  {
                      await SetUserMuteState(socketGuildUser, true);
                  }
                  // both voice channels are observed
                  else if (oldVoiceChannelObserved &amp;&amp; newVoiceChannelObserved)
                  {
                      // user moved from muted to unmuted voice channel
                      if (oldObservedVoiceChannel.IsMuted &amp;&amp; 
                          !newObservedVoiceChannel.IsMuted &amp;&amp;
                          userIsMuted)
                      {
                          await SetUserMuteState(socketGuildUser, false);
                      }
                      // user moved from unmuted to muted voice channel
                      else if (!oldObservedVoiceChannel.IsMuted &amp;&amp; 
                               newObservedVoiceChannel.IsMuted &amp;&amp; 
                               !userIsMuted)
                      {
                          await SetUserMuteState(socketGuildUser, true);
                      }
                      // user moved from muted to muted voice channel
                      else if (oldObservedVoiceChannel.IsMuted &amp;&amp; 
                               newObservedVoiceChannel.IsMuted &amp;&amp; 
                               !userIsMuted)
                      {
                          await SetUserMuteState(socketGuildUser, true);
                      }
                  }
              }
          }
      }
  
      private Task SetUserMuteState(SocketGuildUser socketGuildUser, bool muteUser)
          =&gt; socketGuildUser.ModifyAsync(guildUserProperties =&gt; guildUserProperties.Mute = muteUser);
  }
  </code></pre>
  <p>and I would like to know how to unmute users which left the observed muted voice channel.</p>
  <p>I found out that this line here</p>
  <pre><code>bool userIsMuted = socketGuildUser.VoiceState?.IsMuted == true;
  </code></pre>
  <p>returns false after leaving the voice channel because the voice state is null. So it seems there is no way to check if the user would be muted when joining again.</p>