Skip to main content

Overview

JDA’s event system allows your bot to respond to real-time events from Discord, such as messages being sent, users joining guilds, reactions being added, and much more.

Listening to Events

Using ListenerAdapter

The recommended way to listen to events is by extending ListenerAdapter:
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;

public class MyListener extends ListenerAdapter {
    @Override
    public void onMessageReceived(MessageReceivedEvent event) {
        if (event.getAuthor().isBot()) return;
        
        if (event.getMessage().getContentRaw().equals("!ping")) {
            event.getMessage().reply("Pong!").queue();
        }
    }
}
Then register the listener:
JDA jda = JDABuilder.createDefault(token)
    .addEventListeners(new MyListener())
    .build();

Using EventListener Interface

For more control, implement the EventListener interface:
import net.dv8tion.jda.api.events.GenericEvent;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.hooks.EventListener;

public class MyEventListener implements EventListener {
    @Override
    public void onEvent(GenericEvent event) {
        if (event instanceof MessageReceivedEvent) {
            MessageReceivedEvent e = (MessageReceivedEvent) event;
            System.out.println("Message: " + e.getMessage().getContentRaw());
        }
    }
}

Event Hierarchy

JDA events follow a hierarchy. Generic events fire for all specific events:
GenericEvent
├── GenericMessageEvent
│   ├── MessageReceivedEvent
│   ├── MessageUpdateEvent
│   └── MessageDeleteEvent
├── GenericGuildEvent
│   ├── GuildReadyEvent
│   ├── GuildJoinEvent
│   └── ...
└── ...
You can listen to generic events to catch all related events:
@Override
public void onGenericMessage(GenericMessageEvent event) {
    System.out.println("Some message event occurred!");
}

Common Event Types

Message Events

@Override
public void onMessageReceived(MessageReceivedEvent event) {
    Message message = event.getMessage();
    User author = event.getAuthor();
    MessageChannel channel = event.getChannel();
    
    if (message.getContentRaw().equals("!ping")) {
        channel.sendMessage("Pong!").queue();
    }
}

@Override
public void onMessageUpdate(MessageUpdateEvent event) {
    System.out.println("Message edited: " + event.getMessage().getContentRaw());
}

@Override
public void onMessageDelete(MessageDeleteEvent event) {
    System.out.println("Message deleted: " + event.getMessageId());
}

Guild Member Events

@Override
public void onGuildMemberJoin(GuildMemberJoinEvent event) {
    Member member = event.getMember();
    Guild guild = event.getGuild();
    
    System.out.println(member.getEffectiveName() + " joined " + guild.getName());
}

@Override
public void onGuildMemberRemove(GuildMemberRemoveEvent event) {
    User user = event.getUser();
    System.out.println(user.getAsTag() + " left the server");
}

Interaction Events

@Override
public void onSlashCommandInteraction(SlashCommandInteractionEvent event) {
    if (event.getName().equals("ping")) {
        event.reply("Pong!").queue();
    }
}

@Override
public void onButtonInteraction(ButtonInteractionEvent event) {
    if (event.getComponentId().equals("confirm")) {
        event.reply("Confirmed!").setEphemeral(true).queue();
    }
}

Ready Event

@Override
public void onReady(ReadyEvent event) {
    System.out.println("Bot is ready! Logged in as: " + event.getJDA().getSelfUser().getAsTag());
}

Registering Listeners

You can register listeners at any time:
// During builder
JDABuilder.createDefault(token)
    .addEventListeners(new Listener1(), new Listener2())
    .build();

// After JDA is built
JDA jda = JDABuilder.createDefault(token).build();
jda.addEventListener(new Listener3());

// Remove a listener
jda.removeEventListener(listener3);

One-Time Listeners

Listen to an event only once:
jda.listenOnce(ReadyEvent.class).thenAccept(event -> {
    System.out.println("Bot is ready!");
});

Gateway Intents

Many events require specific gateway intents to be enabled. Without the required intent, the event won’t fire.
Event TypeRequired Intent
Message events (guild)GUILD_MESSAGES
Message content accessMESSAGE_CONTENT (privileged)
Member join/leaveGUILD_MEMBERS (privileged)
Presence updatesGUILD_PRESENCES (privileged)
Voice stateGUILD_VOICE_STATES
ReactionsGUILD_MESSAGE_REACTIONS
Direct messagesDIRECT_MESSAGES
Example:
JDABuilder.createLight(token, EnumSet.of(
    GatewayIntent.GUILD_MESSAGES,
    GatewayIntent.MESSAGE_CONTENT  // Required to read message content
))
.addEventListeners(new MyListener())
.build();

Best Practices

Check for bots - Most commands shouldn’t respond to other bots:
@Override
public void onMessageReceived(MessageReceivedEvent event) {
    if (event.getAuthor().isBot()) return;
    // Your logic here
}
Don’t block the event thread - Long-running operations should be run asynchronously:
@Override
public void onMessageReceived(MessageReceivedEvent event) {
    // ❌ Bad - blocks event thread
    Thread.sleep(5000);
    
    // ✅ Good - runs asynchronously
    CompletableFuture.runAsync(() -> {
        // Long operation
    });
}
Handle exceptions - Uncaught exceptions in event listeners are logged but don’t stop your bot:
@Override
public void onMessageReceived(MessageReceivedEvent event) {
    try {
        // Your logic
    } catch (Exception e) {
        System.err.println("Error handling message: " + e.getMessage());
    }
}

Complete Example

import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.events.session.ReadyEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import net.dv8tion.jda.api.requests.GatewayIntent;

import java.util.EnumSet;

public class CommandBot extends ListenerAdapter {
    public static void main(String[] args) {
        String token = System.getenv("BOT_TOKEN");
        
        JDABuilder.createLight(token, EnumSet.of(
            GatewayIntent.GUILD_MESSAGES,
            GatewayIntent.MESSAGE_CONTENT
        ))
        .addEventListeners(new CommandBot())
        .build();
    }
    
    @Override
    public void onReady(ReadyEvent event) {
        System.out.println("Bot is ready!");
    }
    
    @Override
    public void onMessageReceived(MessageReceivedEvent event) {
        // Ignore bots
        if (event.getAuthor().isBot()) return;
        
        Message message = event.getMessage();
        String content = message.getContentRaw();
        
        // Command handling
        if (content.equals("!ping")) {
            event.getMessage().reply("Pong!").queue();
        }
        else if (content.startsWith("!echo ")) {
            String text = content.substring(6);
            event.getChannel().sendMessage(text).queue();
        }
    }
}

Build docs developers (and LLMs) love