How to detect when users enter voice channels in Discord bot

I’m working on a Discord bot and I want it to send a welcome message whenever someone enters a voice channel. The problem is that I can’t figure out which event to use for this. I tried using botClient.GuildMemberUpdated and similar events but they don’t seem to work the way I expected.

I need this to work for all users, not just new server members. So when someone who’s already in the server joins a voice channel, the bot should detect it and respond.

Here’s what I have so far:

public class DiscordBot
{
    private DiscordSocketClient botClient;
    private CommandService commandHandler;
    private IServiceProvider serviceProvider;

    static void Main(string[] args)
    => new DiscordBot().StartBot().GetAwaiter().GetResult();

    public async Task StartBot()
    {
        botClient = new DiscordSocketClient();
        commandHandler = new CommandService();
        serviceProvider = new ServiceCollection()
            .AddSingleton(botClient)
            .AddSingleton(commandHandler)
            .BuildServiceProvider();

        string token = // hidden

        botClient.Log += LogEvents;

        await SetupCommands();
        await botClient.LoginAsync(TokenType.Bot, token);
        await botClient.StartAsync();
        await Task.Delay(-1);
    }

    private Task LogEvents(LogMessage message)
    {
        Console.WriteLine(message);
        return Task.CompletedTask;
    }

    public async Task SetupCommands()
    {
        botClient.MessageReceived += ProcessMessage;
        botClient.GuildMemberUpdated += UserEnteredChannel; // This doesn't work as expected

        await commandHandler.AddModulesAsync(Assembly.GetEntryAssembly());
    }

    private Task UserEnteredChannel(SocketGuildUser before, SocketGuildUser after)
    {
        throw new NotImplementedException();
    }

    private async Task ProcessMessage(SocketMessage msg)
    {
        var userMessage = msg as SocketUserMessage;

        if(userMessage is null || userMessage.Author.IsBot)
        {
            return;
        }

        int position = 0;

        if (userMessage.HasStringPrefix("?", ref position))
        {
            var context = new SocketCommandContext(botClient, userMessage);
            await commandHandler.ExecuteAsync(context, position);
        }
    }
}

What event should I be using to detect when users join voice channels? Any help would be great.

I’ve built a bunch of Discord bots and Alex_Brave’s right - UserVoiceStateUpdated is the way to go. GuildMemberUpdated technically works but it fires for tons of stuff beyond voice changes, making it inefficient. UserVoiceStateUpdated is built specifically for voice monitoring and gives you cleaner data.

In your SetupCommands method:

botClient.UserVoiceStateUpdated += OnUserVoiceStateUpdated;

Then handle it:

private async Task OnUserVoiceStateUpdated(SocketUser user, SocketVoiceState before, SocketVoiceState after)
{
    if (before.VoiceChannel == null && after.VoiceChannel != null)
    {
        // User joined a voice channel
        var textChannel = after.VoiceChannel.Guild.DefaultChannel;
        await textChannel.SendMessageAsync($"Welcome to {after.VoiceChannel.Name}, {user.Mention}!");
    }
}

This has worked perfectly in my production bots for voice channel events.

You’re on the right track with GuildMemberUpdated but you need to actually check the voice state changes. The problem is you’re not implementing any logic inside your UserEnteredChannel method. Here’s how to fix it: csharp private Task UserEnteredChannel(SocketGuildUser before, SocketGuildUser after) { // Check if user wasn't in a voice channel before but is now if (before.VoiceChannel == null && after.VoiceChannel != null) { // User joined a voice channel var channel = after.VoiceChannel; // Send your welcome message here Console.WriteLine($"{after.Username} joined {channel.Name}"); } return Task.CompletedTask; } I’ve used this in my Discord bots for months - works great. Just compare the VoiceChannel property between before and after states. When before.VoiceChannel is null but after.VoiceChannel isn’t, they just joined. You can catch channel switching and leaving by checking other combinations too.

You could also use the UserVoiceStateUpdated event instead of GuildMemberUpdated. It’s way more specific for voice stuff and honestly easier to work with. Just hook it up with botClient.UserVoiceStateUpdated += HandleVoiceStateChange; and check if the before state was null but after isn’t.