Scripting Guide

Lesson 5: Channels, Threads, and the Server

Fetching and creating channels, editing channel settings, threads, invites, webhooks, and server-level management.

Getting hold of a channel

Channels come from three places: the payload (message.channel, or payload.channel on channel events), a lookup by ID with guild.channels.fetch(id), or the cached list guild.channels.list() which returns plain snapshots of every channel.

channel-info.js (event: messageCreate)
export async function onMessage(message) {
  if (message.author.bot) return;
  if (message.content !== "!channels") return;

  // list() is synchronous data: id, name, type, parentId, topic, ...
  const channels = message.guild.channels.list();
  const text = channels
    .filter((c) => c.type === 0) // 0 = GuildText (see ChannelType in Lesson 6)
    .map((c) => "#" + c.name)
    .join(", ");

  await message.reply("Text channels: " + text);
}

Creating and editing channels

ticket-channel.js (event: messageCreate)
export async function onMessage(message) {
  if (message.author.bot) return;
  if (message.content !== "!ticket") return;

  const channel = await message.guild.channels.create({
    name: "ticket-" + message.author.username,
    type: 0, // text channel
    topic: "Support ticket for " + message.author.username,
    permissionOverwrites: [
      // Hide from everyone...
      { id: message.guild.id, deny: ["ViewChannel"] },
      // ...except the requester.
      { id: message.author.id, allow: ["ViewChannel", "SendMessages"] },
    ],
    reason: "Ticket requested",
  });

  if (channel.error) {
    await message.reply("Could not create the ticket: " + channel.error);
    return;
  }

  await channel.send("Ticket opened. A moderator will be with you shortly.");
  await message.reply("Created <#" + channel.id + ">");
}

Existing channels expose setters that each return the updated channel snapshot: setName, setTopic, setNSFW, setRateLimitPerUser (slowmode seconds), setParent (move to a category), setPosition, setBitrate and setUserLimit (voice), plus clone(), delete(reason), and lockPermissions() to re-sync with the category.

Permission overwrites

Fine-grained access is managed through channel.permissionOverwrites: create(id, { ViewChannel: false }), edit(id, options), delete(id), and set([...]) to replace everything. The id is a role ID or a member ID; permission names are the same strings used in member.permissions.

Threads

weekly-thread.js (event: messageCreate)
export async function onMessage(message) {
  if (message.author.bot) return;
  if (message.content !== "!discuss") return;

  // Start a thread from the triggering message...
  const thread = await message.startThread({
    name: "Discussion: " + message.content.slice(0, 50),
    autoArchiveDuration: 1440, // minutes: 60, 1440, 4320, or 10080
  });
  if (thread.error) return;

  // ...or create one on a channel: message.channel.threads.create({ name })
  await thread.send("Keep the discussion in here, please.");
  await thread.members.add(message.author.id);
}

Thread objects support send, setName, setArchived, setLocked, setAutoArchiveDuration, join, leave, delete, member management via members.add/remove, and history via messages.fetch. A channel's active threads are listed with channel.threads.fetchActive().

Invites and webhooks

  • channel.createInvite({ maxAge, maxUses, temporary, unique, reason }) returns invite data including url.
  • channel.fetchInvites() lists the channel's invites with use counts.
  • channel.createWebhook({ name, avatar? }) returns a webhook you can send through (useful for posting with a custom name/avatar); fetchWebhooks() lists them; webhooks support editMessage, deleteMessage, edit, and delete.

Server-level management

  • guild.roles.create({ name, color, permissions }), guild.roles.fetch(id), guild.roles.list(); fetched roles expose setName, setColor, setHoist, setMentionable, setPermissions, setPosition, edit, and delete.
  • guild.emojis.list() / create({ attachment, name }) / delete(id) and the same trio for guild.stickers.
  • guild.setName(name) and guild.setIcon(url) change server identity (icon URLs must be public http(s)).
  • guild.fetchOwner() returns the owner as a member object.
  • guild.fetchAuditLogs({ limit, type, userId }) returns up to 50 audit entries (Lesson 13 uses this).

Common pitfalls

  • Channel types are numbers. Text is 0, voice is 2, category is 4, announcements 5, forums 15. Import ChannelType from "discord" for readable names (Lesson 6).
  • Overwrites need IDs as strings. Pass role/member IDs, not names or mention strings.
  • `autoArchiveDuration` only accepts 60, 1440, 4320, or 10080 (minutes).
  • Creating channels in a loop. Channel creation is heavily rate limited by Discord; create one per run, not dozens.

Exercise

Build a !slowmode <seconds> command for users with ManageChannels that calls setRateLimitPerUser, confirms the new value, and replies with an error message when the action result has an error property (try it in a channel where the bot lacks permission).