Events I'd expect to update a DiscordGuild's Member list do not, leading to inconsistency in functionality of VoiceNext and presumably other features like Interactions

Created by: alexhorner

Summary

Certain events which I would expect to update a DiscordGuild's exposed Members property, and by extension its internal _members dictionary do not.

Details

I have been experimenting with voice channel members, diagnosing this issue with some help. This doesn't appear to be limited in scope, however, and could affect other things, specifically any interactions.

A DiscordGuild has an exposed property Members which can be used to access the member list of a guild. This property is linked directly to the internal field _members inside of DiscordGuild. The Members property is leveraged when accessing the Users property of a DiscordChannel, the implementation of which can be found here.

When someone sends a message, the Members property of a DiscordGuild appears to be updated to reflect their presence. They are added to the list. Prior to this, they do not appear in the list.

It believe it would be reasonable to expect that the execution of an interaction, maybe specifically a slash command should update this Members list in DiscordGuild but it does not do so at this time.

I also believe it would be reasonable to expect that receiving a Voice State Update event should also update this Members list in DiscordGuild but it also does not do so at this time.

As a result of these two events not updating the Members list of a DiscordGuild, the member which caused the interaction or voice state update does not appear in the Members list of the DiscordGuild and by extension, does not appear in the Users property on the DiscordChannel which leverages the Members property of the DiscordGuild.

A notable example of this being an issue, which is the basis for me raising this issue, is when a DiscordChannel which is a voice or stage channel is read in order to retrieve the members of the guild currently in it.

My implementation checks voice channels to see if any members are present. If no members are present, the bot will disconnect itself. Because the Members property of the DiscordGuild is not updated properly, as better explained in the bullet points below, the bot automatically disconnects from the voice channel believing there are no other users in the channel, even when they are actually in the channel. As a result of my testing and issue above, I have observed the following:

  • If members are in a voice channel when the bot comes online (meaning the guild download runs whilst they're in the channel) the members appear in the Members property of the DiscordGuild and therefore can be seen in the voice channel's Users property.
  • If members join a voice channel whilst the bot is online, they do not appear in the Members property of the DiscordGuild and therefore cannot be seen in the voice channel's Users property.
  • If members join a voice channel whilst the bot is online, and they also send a message in any guild channel that the bot can read, the members appear in the Members property of the DiscordGuild and therefore can be seen in the voice channel's Users property.
  • If members join a voice channel whilst the bot is online, and they send an interaction (such as a slash command), they do not appear in the Members property of the DiscordGuild and therefore cannot be seen in the voice channel's Users property.

Steps to reproduce

This one can be a bit tricky to reproduce as it requires a little setup, but the general idea of a tested and working example would be:

  1. Add a slash command which takes a voice channel as a parameter and have that command list the users present in the channel
  2. Make sure the voice channel is empty
  3. Do not send any messages in the guild whilst following the remainder of this procedure unless asked to
  4. Start the bot
  5. Run the slash command. Observe the list is empty as it should be
  6. Join the voice channel
  7. Run the slash command. Observe the list is empty which is incorrect
  8. Send a message in any channel of the guild which the bot can see, whilst in the voice channel
  9. Run the slash command. Observe the list has updated to show your presence

You may also see an example where, if you replace step 2 with Join the voice channel so you are already in the channel before the bot starts, you will observe that at step 6, you correctly appear in the list.

Notes

Here is the test command for your convenience, using @IDoEverything's SlashCommands library:

[SlashCommand("checkvoice", "Check the members in a voice channel")]
public async Task CheckVoice(InteractionContext ctx, [Option("VoiceChannel", "The channel to observe")]DiscordChannel voiceChannel)
{
    DiscordEmbedBuilder embed = new()
    {
        Title = "Users in voice channel"
    };

    if (!voiceChannel.Users.Any())
    {
        embed.Description = "There are no users present.";
    }
    else
    {
        embed.Description = "```fix";
        
        foreach (DiscordMember member in voiceChannel.Users)
        {
            embed.Description += $"\n{member.Username}#{member.Discriminator} ({member.Id})";
        }

        embed.Description += "\n```";
    }

    embed.AddField("Users", voiceChannel.Users.Count().ToString());

    await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AddEmbed(embed));
}

Should any further testing, examples, diagnosis etc be required, please let me know here, or on Discord if you know who I am!