Scripting Guide

Lesson 4: Members, Roles, and Moderation

Reading member data, assigning roles, sending DMs, and the moderation toolkit: timeout, kick, and ban.

The member object

A member is a user inside a specific server. You meet members in guildMemberAdd/Remove/Update payloads, as message.member, and via guild.members.fetch(id). Useful data fields:

FieldMeaning
idThe user's ID.
displayNameNickname if set, otherwise username.
nicknameServer nickname or null.
roleIdsArray of role IDs the member has.
permissionsArray of permission names, e.g. "ManageMessages".
joinedTimestampWhen they joined (Unix ms).
kickable / bannable / moderatableWhether the bot is allowed to act on them.
userThe underlying user (username, bot, avatarURL, ...).

Autorole on join

autorole.js (event: guildMemberAdd)
const ROLE_ID = "123456789012345678"; // copy with Developer Mode

export async function onJoin({ member, guild }) {
  // Give every new human member a role. Skip bots.
  if (member.user.bot) return;

  const ok = await member.roles.add(ROLE_ID, "Autorole on join");
  // Role actions return true on success or { error } on failure
  // (missing Manage Roles, or the role is above the bot's highest role).
  if (ok !== true) {
    // Optional: report somewhere instead of failing silently.
  }
}

The roles helper

  • member.roles.add(roleId, reason?) adds one role.
  • member.roles.remove(roleId, reason?) removes one role.
  • member.roles.set([id1, id2], reason?) replaces all roles (up to 100).
  • member.roles.list() returns the member's roles as { id, name, color, position }.
  • member.roleIds.includes(id) is the quick membership check, no extra call needed.

Moderation actions

mod-commands.js (event: messageCreate)
export async function onMessage(message) {
  if (message.author.bot) return;
  if (!message.content.startsWith("!")) return;

  // Gate moderation behind real permissions, not role names.
  const isMod = message.member.permissions.includes("ModerateMembers");
  if (!isMod) return;

  const [cmd, userId] = message.content.split(/\s+/);
  if (!userId) return;

  const target = await message.guild.members.fetch(userId);
  if (!target || target.error) {
    await message.reply("Could not find that member.");
    return;
  }

  if (cmd === "!mute") {
    // Timeout takes milliseconds. 10 minutes:
    await target.timeout(10 * 60 * 1000, "Muted by " + message.author.username);
    await message.reply("Muted for 10 minutes.");
  }

  if (cmd === "!unmute") {
    await target.timeout(null); // null clears the timeout
    await message.reply("Unmuted.");
  }

  if (cmd === "!kick" && target.kickable) {
    await target.kick("Kicked by " + message.author.username);
    await message.reply("Kicked.");
  }

  if (cmd === "!ban" && target.bannable) {
    await target.ban({
      reason: "Banned by " + message.author.username,
      deleteMessageSeconds: 60 * 60 * 24, // also remove their last day of messages
    });
    await message.reply("Banned.");
  }
}

Bans can also be managed at the server level without fetching the member first: guild.members.ban(id, opts), guild.members.unban(id, reason), guild.members.kick(id, reason), and guild.bans.fetch() to list existing bans (up to 100).

Other member actions

  • member.setNickname("NewNick", reason?) changes the nickname (null clears it).
  • member.send(content) sends a DM; returns { error } if DMs are closed.
  • member.permissionsIn(channelId) returns the member's permission names in a specific channel.
  • member.voice.setChannel(id) / member.voice.disconnect() / setMute / setDeaf control voice state if the member is in a voice channel.
  • guild.members.list({ limit }) and guild.members.search({ query, limit }) fetch up to 100 members.
  • guild.members.prune({ days, dry: true }) counts (or removes) members inactive for 1 to 30 days.

Expected behavior

With the moderation agent uploaded on messageCreate, a moderator typing !mute 123456789012345678 times the member out for 10 minutes and the bot confirms. Members without ModerateMembers get no response at all (the agent returns early).

Common pitfalls

  • Role hierarchy. The bot cannot manage roles above its own highest role, or moderate members above it. Check kickable, bannable, and moderatable first.
  • Timeout units. timeout() takes milliseconds, not seconds. The Discord maximum is 28 days.
  • Checking role names instead of permissions. Names change; permission flags do not. Prefer permissions.includes("...").
  • `guildMemberRemove` fires for kicks and leaves alike. If you need to distinguish, check the audit log (Lesson 13).

Exercise

Build a !warn <id> command agent that DMs the member a warning, and pairs with Lesson 9 later: for now reply with how many warnings you *would* have stored. Then add a !modinfo <id> command that replies with the member's display name, join date (new Date(joinedTimestamp).toDateString()), and role count.