Scripting Guide
Lesson 3: Working with Messages
Reading message content, mentions, and attachments, and acting on messages: reply, react, edit, delete, pin, and bulk operations.
The message object
On messageCreate (and messageDelete) the payload is the message itself. The most useful fields:
| Field | Type | Notes |
|---|---|---|
content | string | Raw text. cleanContent has mentions resolved to names. |
author | object | The user: id, username, displayName, bot, plus send() for DMs. |
member | object | The author as a server member: roleIds, permissions, moderation methods (Lesson 4). |
channel | object | Where it was sent: send(), messages, bulkDelete() and more. |
guild | object | The server: channels, members, roles managers (Lesson 5). |
mentions | object | { everyone, users, roles, channels, repliedUserId }, all as ID arrays. |
attachments | array | Uploaded files: name, url, size, contentType. |
reference | object or null | Set when the message is a reply; fetchReference() loads the original. |
createdTimestamp | number | Unix milliseconds. |
Replying and sending
message.reply(...) answers with a visible reference to the original; message.channel.send(...) posts plainly in the same channel. Both accept either a string or an options object:
export async function onMessage(message) {
if (message.author.bot) return;
if (message.content === "!simple") {
await message.reply("Just text.");
}
if (message.content === "!fancy") {
// The object form unlocks embeds, components, and files.
await message.channel.send({
content: "Text plus extras.",
// embeds: [...] -> Lesson 6
// components: [...] -> Lesson 7
// files: ["https://example.com/image.png"] (http/https URLs only)
});
}
}Message content is capped at 4096 characters (longer strings are truncated). A send can carry at most 10 files, and file attachments must be public http(s) URLs; private or internal addresses are rejected.
A keyword auto-moderator
const BANNED = ["badword1", "badword2"];
export async function onMessage(message) {
if (message.author.bot) return;
const text = message.content.toLowerCase();
if (!BANNED.some((w) => text.includes(w))) return;
// Remove the message. delete() returns true, or { error } if it failed
// (for example missing Manage Messages permission).
const deleted = await message.delete();
if (deleted !== true) return;
// Warn the author privately. DMs can fail if the user blocks DMs,
// so check the result instead of assuming.
const dm = await message.author.send(
"Your message in #" + message.channel.name + " was removed."
);
if (dm && dm.error) {
await message.channel.send(
message.author.username + ", watch your language (could not DM you)."
);
}
}Reactions, pins, edits, threads
await message.react(emoji)adds a reaction. Use a Unicode emoji string or a custom emoji inname:idform.await message.pin()/await message.unpin()manage pins.await message.edit(content)edits the bot's own messages (you cannot edit other users' messages).await message.startThread({ name: "Discussion" })opens a thread on the message.await message.fetchReference()returns the message this one replies to.await message.forward(channelId)forwards the message to another channel in the same server.
Reading history and bulk deleting
export async function onMessage(message) {
if (message.author.bot) return;
if (message.content !== "!purge") return;
// Only let moderators run this.
if (!message.member.permissions.includes("ManageMessages")) {
await message.reply("You need Manage Messages to do that.");
return;
}
// Fetch recent messages (returns an array of light snapshots)...
const recent = await message.channel.messages.fetch({ limit: 25 });
// ...and bulk delete. Returns the number deleted. Messages older than
// 14 days are skipped by Discord.
const count = await message.channel.bulkDelete(25);
await message.channel.send("Deleted " + count + " messages.");
}Common pitfalls
- Case-sensitive matching. Normalize with
toLowerCase()before comparing user text. - Assuming DMs work.
author.send()returns{ error }when the user disallows DMs. Check it. - Editing other people's messages. Not possible on Discord; delete and repost instead.
- Forgetting `cleanContent`. If you log messages,
contentshows raw mention markup like<@1234>;cleanContentshows@Name. - Bulk delete limits. Discord refuses to bulk delete messages older than two weeks; the helper silently skips them.
Exercise
Write a link-guard.js agent on messageCreate that deletes any message containing discord.gg/ unless the author has the ManageGuild permission, then posts a short notice in the channel that deletes itself when you have learned timers do not exist (hint: you cannot delete it later, so make the notice useful enough to keep, for example mentioning the rule it enforces).