Events

Commands handle explicit user actions. Event handlers react to Discord activity such as messages, members joining, roles changing, or threads being created.

Register an event

Use an event constant with discord.on(). The handler parameter is typed for that event.

discord.on(discord.events.GUILD_MEMBER_ADD, async (member) => {
  const channel = await discord.fetchGuildTextChannel("123456789012345678");

  if (channel) {
    await channel.send(`Welcome ${member.toMention()}!`);
  }
});

Your code registers handlers when the revision is warmed. Weeble owns the Discord gateway session and invokes the matching handlers when events arrive.

Use the data that arrived

Discord objects arrive populated from the event payload. Prefer that data before making another request.

discord.on(discord.events.MESSAGE_CREATE, async (message) => {
  if (message.author?.bot) return;

  console.log("Message received", {
    messageId: message.id,
    channelId: message.channelId,
  });
});

Methods named fetch*() perform Discord requests. They are useful when the event does not contain something you need, but unnecessary fetches make handlers slower and consume the event’s Discord request budget.

Methods named get*() read Weeble’s gateway cache instead, which answers in a few milliseconds. Use them for lookups where a few fields are enough:

const member = await guild.getMember(userId); // user, nick, roles, joinedAt
const roles = await guild.getRoles();         // name, permissions, position
const channel = await discord.getChannel(id); // name, type, category, overwrites

Cached objects only carry the fields listed above. When you need everything Discord has, such as a member’s avatar or a channel’s topic, use the matching fetch*() method. A get*() call falls back to Discord on its own when the item isn’t cached yet.

fetch*() always asks Discord, so it returns current data. Use get*() when speed matters more than having every field.

Update events

Many update handlers receive the current object and the previous cached object. The previous value can be null when Weeble did not observe the earlier state.

discord.on(discord.events.CHANNEL_UPDATE, async (channel, previous) => {
  if (!previous || channel.name === previous.name) return;

  console.log("Channel renamed", {
    before: previous.name,
    after: channel.name,
  });
});

Always handle a missing previous value instead of assuming it exists.

Concurrency

Handlers are asynchronous and events may run concurrently. Do not update shared counters with a read followed by a separate write.

Use a KV transaction when multiple events can change the same value:

const metrics = new weeble.KVNamespace("metrics");

discord.on(discord.events.MESSAGE_CREATE, async () => {
  await metrics.transact<number>("messages", (current) => {
    return (current ?? 0) + 1;
  });
});

Event delivery

Discord only sends events allowed by the bot’s configured intents. Message content, member events, and presence events have additional intent requirements.

An uncaught handler error is recorded in the runtime logs. See Debugging for finding the active revision and stack trace. Catch an error only when the handler can recover meaningfully; otherwise let Weeble report the original failure.

See the SDK reference for the complete typed event list.