Install
$ agentstack add skill-therocksss-hermes-skills-portfolio-discord-bot-build ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
discord-bot-build
Overview
Build a Discord bot with slash commands, event handlers, and moderation capabilities using discord.js. The bot runs as a Node.js process and connects to Discord via the Gateway.
When to Use
- The user wants a Discord bot for their server.
- The user wants to automate moderation, send announcements, or add custom commands.
- The user says "build a Discord bot", "make a bot for my server", or "I need a Discord mod bot".
Prerequisites
- Node.js 18+ — check with
node --version - A Discord bot token — create a bot at https://discord.com/developers/applications
- Privileged Gateway Intents — enable in the Developer Portal:
- Presence Intent (if you need online/offline tracking)
- Server Members Intent (if you need member lists)
- Message Content Intent (required for reading message text)
Bot Setup
Step 1: Create the application
- Go to https://discord.com/developers/applications
- Click "New Application" → name it → go to the "Bot" tab
- Click "Add Bot" → copy the token (keep it secret)
- Under "Privileged Gateway Intents", enable Message Content Intent
- Under "OAuth2 → URL Generator", select
bot+applications.commandsscopes and the permissions you need - Open the generated URL to invite the bot to your server
Step 2: Initialize the project
mkdir my-bot && cd my-bot
npm init -y
npm install discord.js dotenv
Step 3: Create the bot file
// index.js
const { Client, GatewayIntentBits, SlashCommandBuilder, Events } = require('discord.js');
require('dotenv').config();
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
GatewayIntentBits.GuildMembers,
],
});
client.once(Events.ClientReady, c => {
console.log(`Logged in as ${c.user.tag}`);
});
// Register slash commands
client.on(Events.InteractionCreate, async interaction => {
if (!interaction.isChatInputCommand()) return;
if (interaction.commandName === 'ping') {
await interaction.reply('Pong!');
}
if (interaction.commandName === 'kick') {
const target = interaction.options.getUser('user');
const reason = interaction.options.getString('reason') ?? 'No reason provided';
if (!interaction.memberPermissions.has('KickMembers')) {
await interaction.reply('You do not have permission to kick members.');
return;
}
try {
await interaction.guild.members.kick(target, reason);
await interaction.reply(`Kicked ${target.tag} for: ${reason}`);
} catch (error) {
await interaction.reply(`Failed to kick: ${error.message}`);
}
}
});
client.login(process.env.DISCORD_TOKEN);
Step 4: Create .env
DISCORD_TOKEN=your_bot_token_here
Step 5: Register slash commands
// deploy-commands.js
const { REST, Routes, SlashCommandBuilder } = require('discord.js');
require('dotenv').config();
const commands = [
new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with pong'),
new SlashCommandBuilder()
.setName('kick')
.setDescription('Kick a member')
.addUserOption(opt => opt.setName('user').setDescription('The user to kick').setRequired(true))
.addStringOption(opt => opt.setName('reason').setDescription('Reason for kicking')),
].map(cmd => cmd.toJSON());
const rest = new REST().setToken(process.env.DISCORD_TOKEN);
(async () => {
try {
await rest.put(Routes.applicationCommands(process.env.CLIENT_ID), { body: commands });
console.log('Slash commands registered.');
} catch (error) {
console.error(error);
}
})();
Run once: node deploy-commands.js
Step 6: Start the bot
node index.js
Command Registration
Commands must be registered before they appear in Discord. Two scopes:
| Scope | Where it appears | Registration | |---|---|---| | Global | All servers the bot is in | Up to 1 hour to propagate | | Guild | One specific server | Instant |
For development, use guild commands (instant). For production, use global commands.
// Guild command (instant, for testing)
await rest.put(
Routes.applicationGuildCommands(clientId, guildId),
{ body: commands }
);
// Global command (production, up to 1hr delay)
await rest.put(
Routes.applicationCommands(clientId),
{ body: commands }
);
Event Handling
// Member joined
client.on(Events.GuildMemberAdd, member => {
const channel = member.guild.systemChannel;
if (channel) channel.send(`Welcome ${member} to the server!`);
});
// Message deleted
client.on(Events.MessageDelete, message => {
console.log(`Message deleted in #${message.channel.name}: ${message.content}`);
});
// Reaction added
client.on(Events.MessageReactionAdd, (reaction, user) => {
if (reaction.emoji.name === '📌') {
// Pin the message
reaction.message.pin();
}
});
Moderation Commands
| Command | What it does | Required permission | |---|---|---| | /kick @user [reason] | Remove a member | KickMembers | | /ban @user [reason] | Ban a member | BanMembers | | /mute @user [duration] | Timeout a member | ModerateMembers | | /purge | Delete recent messages | ManageMessages | | /warn @user | Issue a warning | ModerateMembers |
Keeping the Bot Running
With PM2 (recommended):
npm install -g pm2
pm2 start index.js --name my-bot
pm2 save
pm2 startup # auto-restart on reboot
With Docker:
FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
CMD ["node", "index.js"]
Common Pitfalls
- Message Content Intent not enabled. The bot can't read message text without this. Enable it in the Developer Portal under "Privileged Gateway Intents" — without it,
message.contentis always empty even though the event still fires. - Token committed or leaked. Never commit the
.envfile — add it to.gitignore. If the token leaks, regenerate it immediately in the Developer Portal; a leaked token gives full control of the bot. - Commands not appearing after deploy. Global commands take up to 1 hour to propagate. Use guild commands (
applicationGuildCommands) for testing since they're instant. Also re-rundeploy-commands.jsafter adding or changing any command definition — editingindex.jsalone doesn't re-register them. - Bot can't kick/ban despite having the permission. The bot's role must sit higher in the role hierarchy than the target user's highest role, in addition to having the Kick/Ban permission — Discord enforces hierarchy regardless of permission flags.
- Rate limits on bulk operations. Discord enforces per-route rate limits. Bulk operations (mass ban, mass delete) should use
bulkDeleteand expect the library's automatic rate-limit handling to introduce delays — don't assume every call completes instantly. - No error handling crashes the whole bot. An unhandled exception inside an interaction handler can crash the process if not caught. Always wrap command logic in try/catch and reply with the error so the user gets feedback instead of a silently dead bot.
Verification Checklist
- [ ]
node index.jslogs "Logged in as ..." with no uncaught errors on startup - [ ]
deploy-commands.jswas run and slash commands appear in Discord (guild commands show instantly; confirm before assuming global propagation) - [ ] Message Content Intent is enabled in the Developer Portal if any command reads
message.content - [ ]
.envis listed in.gitignoreand the token was never committed - [ ] Moderation commands (
/kick,/ban, etc.) were tested against a low-permission test account, confirming both the permission check and the Discord role-hierarchy behavior - [ ] Every interaction handler has a try/catch that replies with an error message instead of leaving the interaction unanswered
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: THEROCKSSS
- Source: THEROCKSSS/hermes-skills-portfolio
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.