Event Listeners

To hook onto any background actions happening within your bot—such as message creation (messageCreate), voice channel state updates (voiceStateUpdate), or interaction handling (interactionCreate)—you utilize Disapp's robust Event module.

Disapp seamlessly catches the Event classes you construct, registers them, and passes them along to their corresponding Discord API hook methods.

Constructing an Event

To create an Event, you begin by structuring your module using the Event class originating from the core package.

Every file dropped under the events/ subdirectory is scanned by the DisappClient upon initialization.

import { Event, DisappClient } from "@disapp/core";
import { Events, PresenceUpdateStatus, ActivityType } from "discord.js";

export default class ReadyEvent extends Event {
  constructor() {
    super({
      name: Events.ClientReady,
      once: true,
      execute: async () => {},
    });
  }

  async execute(client: DisappClient) {
    client.logger.info(`🔥 Successfully logged in as: ${client.user?.tag}`);

    client.user?.setPresence({
      activities: [
        { name: "Running Disapp Core!", type: ActivityType.Playing },
      ],
      status: PresenceUpdateStatus.Online,
    });
  }
}

Event Types

Once vs On

  • once: true - Event triggers only once (e.g., ready event)
  • once: false - Event triggers every time (e.g., messageCreate event)
export default class MessageCreateEvent extends Event {
  constructor() {
    super({
      name: Events.MessageCreate,
      once: false,
      execute: async () => {},
    });
  }

  async execute(message: Message) {
    if (message.author.bot) return;

    console.log(`Message from ${message.author.tag}: ${message.content}`);
  }
}

Common Events

Ready Event

import { Event } from "@disapp/core";
import { Events } from "discord.js";

export default class ReadyEvent extends Event {
  constructor() {
    super({
      name: Events.ClientReady,
      once: true,
      execute: async () => {},
    });
  }

  async execute(client: any) {
    console.log(`Logged in as ${client.user.tag}`);
  }
}

Message Create Event

import { Event } from "@disapp/core";
import { Events, Message } from "discord.js";

export default class MessageCreateEvent extends Event {
  constructor() {
    super({
      name: Events.MessageCreate,
      once: false,
      execute: async () => {},
    });
  }

  async execute(message: Message) {
    if (message.author.bot) return;

    if (message.content.toLowerCase() === "hello") {
      await message.reply("Hello! 👋");
    }
  }
}

Interaction Create Event

import { Event } from "@disapp/core";
import { Events, Interaction } from "discord.js";

export default class InteractionCreateEvent extends Event {
  constructor() {
    super({
      name: Events.InteractionCreate,
      once: false,
      execute: async () => {},
    });
  }

  async execute(interaction: Interaction) {
    if (!interaction.isChatInputCommand()) return;

    console.log(`Command used: ${interaction.commandName}`);
  }
}

VoiceTracker Implementation

If you wish to measure tracking analytics on your Discord servers regarding voice channel activity, you can use the VoiceTracker in conjunction with the Repository ecosystem inside Disapp Core.

Voice State Update Event

import { Event, VoiceTracker } from "@disapp/core";
import { Events, VoiceState } from "discord.js";

export default class VoiceStateUpdateEvent extends Event {
  constructor() {
    super({
      name: Events.VoiceStateUpdate,
      once: false,
      execute: async () => {},
    });
  }

  async execute(oldState: VoiceState, newState: VoiceState) {
    const voiceTracker = new VoiceTracker(oldState.client);
    await voiceTracker.handleVoiceStateUpdate(oldState, newState);
  }
}

Required Intents

For VoiceTracker to accurately record data, you must include GuildVoiceStates intent:

import { GatewayIntentBits } from "discord.js";

const client = new DisappClient({
  intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates],
});

i18n (Multi-Language) in Events

You can leverage the power of i18n within your Events to determine dynamic user languages:

import { Event, I18nHelper } from "@disapp/core";
import { Events, Message } from "discord.js";

export default class MessageCreateEvent extends Event {
  constructor() {
    super({
      name: Events.MessageCreate,
      once: false,
      execute: async () => {},
    });
  }

  async execute(message: Message) {
    if (message.author.bot) return;

    const userId = message.author.id;
    const greeting = I18nHelper.tUser(userId, "messages.greeting");

    if (message.content.toLowerCase() === "hello") {
      await message.reply(greeting);
    }
  }
}

Error Handling

Always wrap event logic in try-catch blocks:

async execute(message: Message) {
  try {
    if (message.author.bot) return;

    // Your event logic here
  } catch (error) {
    console.error('Error in MessageCreate event:', error);
  }
}

Best Practices

  • Use once: true for one-time events - Like ready event
  • Check for bots - Always check message.author.bot in message events
  • Handle errors - Wrap logic in try-catch blocks
  • Keep events focused - One event per file
  • Use type guards - Check interaction types before processing

Learn More