Scripting Guide

Lesson 2: Events and Payloads

The 15 events agents can subscribe to, the exact payload shape each one delivers, and how to pick the right one.

One agent, one event

When you create an agent you bind it to exactly one event from a fixed list. The event determines two things: when your function runs and what the payload argument contains. Getting the payload shape right is most of the work of writing a correct agent.

The event catalog

EventFires whenPayload shape
messageCreateA user (not a bot) sends a messageThe message object itself
messageUpdateA user edits a message{ before, after } (before is a light snapshot, after is a full message)
messageDeleteA user's message is deletedThe deleted message object
guildMemberAddSomeone joins the server{ member, guild }
guildMemberRemoveSomeone leaves or is kicked{ member, guild }
guildMemberUpdateNickname, roles, or timeout changes{ before, after, guild }
guildBanAddSomeone is banned{ user, reason, guild }
guildBanRemoveSomeone is unbanned{ user, reason, guild }
channelCreateA channel is created{ channel, guild }
channelDeleteA channel is deleted{ channel, guild }
roleCreateA role is created{ role, guild }
roleDeleteA role is deleted{ role, guild }
messageReactionAddA user adds a reaction{ reaction, emoji, message, user, guild }
messageReactionRemoveA user removes a reaction{ reaction, emoji, message, user, guild }
interactionCreateA button, menu, or modal your agents created is usedThe interaction object
Bots are filtered for you

Message and reaction events never fire for actions performed by bots, and interactionCreate never fires for GuildScript's own commands and menus. You still write if (message.author.bot) return; in message agents as a safety habit, but the platform already prevents the worst feedback loops.

Reading a payload

Payload objects are plain data snapshots with async action methods attached. For example, a guildMemberAdd payload:

welcome.js (event: guildMemberAdd)
export async function onJoin(payload) {
  // This event's payload has two top-level keys: member and guild.
  const { member, guild } = payload;

  // Plain data you can read directly:
  // member.id, member.displayName, member.user.username,
  // member.joinedTimestamp, member.roleIds, ...
  // guild.id, guild.name, guild.memberCount, ...

  // Async lookups go through the guild object:
  const channel = await guild.channels.fetch("123456789012345678");
  if (!channel || channel.error) return; // bad ID or missing access

  await channel.send(
    "Welcome " + member.displayName + "! You are member #" + guild.memberCount + "."
  );
}

Before-and-after events

messageUpdate and guildMemberUpdate deliver a before and an after so you can detect what changed. before is a plain snapshot (data only); after carries the action methods.

nickname-watch.js (event: guildMemberUpdate)
export async function onMemberUpdate({ before, after, guild }) {
  if (before.nickname === after.nickname) return; // something else changed

  const log = await guild.channels.fetch("123456789012345678");
  if (!log || log.error) return;

  await log.send(
    after.user.username + " changed nickname: " +
    (before.nickname ?? "(none)") + " -> " + (after.nickname ?? "(none)")
  );
}

Choosing the right event

  • Want to respond to text people type? messageCreate.
  • Want welcome flows or autoroles? guildMemberAdd.
  • Want goodbye logs? guildMemberRemove.
  • Want moderation logging? messageDelete, messageUpdate, guildBanAdd, guildMemberUpdate.
  • Want reaction roles or starboards? messageReactionAdd and messageReactionRemove.
  • Want clickable UI (buttons, menus, modals)? Build it from any event, handle it with interactionCreate (Lesson 7).

Common pitfalls

  • Destructuring the wrong shape. const { member } = payload works on guildMemberAdd but is undefined on messageCreate, where the payload is the message itself.
  • Expecting events that are not in the list. There is no voiceStateUpdate, no presenceUpdate, and no scheduled/timer event. If it is not in the table above, an agent cannot subscribe to it.
  • Comparing `before` methods. before in update events is data only; call actions on after.
  • Reacting to deletes with replies. On messageDelete the message is gone; you cannot reply to it. Send to a channel instead.

Exercise

Create a goodbye.js agent on guildMemberRemove that posts <username> left the server to a log channel of your choice. Then create a second agent on guildBanAdd that posts the ban reason (payload.reason may be null; print no reason given in that case).