Files
Discord_Bots/Everything-Bot
andrew 932cfc57d3 Add Everything-Bot Discord bot with reaction roles
Initial setup of a modular Discord bot featuring reaction-role management. Includes slash commands for creating, listing, removing, and clearing reaction roles with support for unicode and custom emojis. Features persistent SQLite storage, automatic command and event loading, graceful shutdown handling, and structured logging. Designed with extensibility in mind for future commands and events.
2026-07-16 15:38:43 +12:00
..

Everything Bot

Modular Discord bot built with Node.js, Discord.js, and SQLite. The first feature is reaction roles, with an architecture designed so new commands and events can be added without rewriting the core.

Requirements

  • Node.js 20 or newer (Node 22 recommended)
  • A Discord application and bot user
  • npm

Features

  • Guild-only slash commands for creating, listing, removing, and clearing reaction roles
  • Unicode, static custom, and animated custom emoji support
  • Multiple mappings per message, shared roles across messages, multi-guild storage
  • Persistent SQLite storage that survives restarts
  • Automatic command and event loading from folders
  • Graceful shutdown and structured logging (secrets never logged)

Installation

npm install
cp .env.example .env

Edit .env with your Discord credentials (see below).

Discord application setup

  1. Open the Discord Developer Portal.
  2. Click New Application, name it, and create it.
  3. Open the Bot tab and click Add Bot if needed.
  4. Under Token, click Reset Token / Copy and save it for DISCORD_TOKEN.
  5. Open the OAuth2 → General tab and copy the Application ID for DISCORD_CLIENT_ID.

Required gateway intents

In the Bot tab, enable:

Intent Required Notes
Server Members Intent Yes Needed to fetch members and assign/remove roles
Message Content Intent No Not required for reaction roles

The bot also requests these gateway intents in code:

  • Guilds
  • Guild Members
  • Guild Message Reactions

Required bot permissions

When inviting the bot, grant at least:

View Channels
Read Message History
Add Reactions
Manage Roles
Use Application Commands

Role hierarchy: the bots highest role must sit above every role it needs to assign. In Server Settings → Roles, drag the bots role above those roles.

Invite the bot

  1. Open OAuth2 → URL Generator.
  2. Scopes: bot and applications.commands.
  3. Bot permissions: select the permissions listed above (or use permission integer 268438560 as a starting point — verify in the UI).
  4. Open the generated URL, choose your server, and authorize.

Environment variables

Copy .env.example to .env:

DISCORD_TOKEN=
DISCORD_CLIENT_ID=
DISCORD_GUILD_ID=
DATABASE_PATH=./data/bot.sqlite
Variable Required Description
DISCORD_TOKEN Yes Bot token
DISCORD_CLIENT_ID Yes Application ID
DISCORD_GUILD_ID No Development server ID for fast command updates
DATABASE_PATH No SQLite file path (default ./data/bot.sqlite)

The bot validates required variables on startup and exits with a clear error if any are missing.

Database

SQLite is initialized automatically on startup. The data/ directory and database file are created if missing. No manual migration step is required.

The reaction_roles table stores:

  • id, guild_id, channel_id, message_id, role_id
  • emoji_identifier, emoji_display
  • created_by, created_at

A uniqueness constraint on (guild_id, message_id, emoji_identifier) prevents duplicate emoji mappings on the same message.

Slash command deployment

npm run deploy-commands
  • With DISCORD_GUILD_ID set: commands are registered to that guild and usually appear within a few seconds. Prefer this while developing.
  • Without DISCORD_GUILD_ID: commands are registered globally and may take up to about an hour to propagate to all servers.

Re-run deployment whenever you add or change slash command definitions.

Running the bot

Development (restarts on file changes):

npm run dev

Production:

npm start

Lint:

npm run lint

Reaction-role commands

All of these require Manage Roles and are guild-only. Responses are ephemeral.

Create

/reaction-role message_id:<message ID> role:<Discord role> emoji:<emoji>

Example:

/reaction-role message_id:123456789012345678 role:@Game Updates emoji:🎮

The command:

  1. Checks Manage Roles for the user
  2. Finds the message in the current channel
  3. Validates the role and bot hierarchy / permissions
  4. Rejects integration-managed roles
  5. Validates the emoji and adds the bots reaction
  6. Saves the mapping in SQLite
  7. Confirms with message, role, and emoji

List

/reaction-role-list

Shows all mappings for the current server in an embed (channel, message ID, emoji, role), with pagination when needed.

Remove one mapping

/reaction-role-remove message_id:<message ID> emoji:<emoji>

Deletes the mapping and attempts to remove only the bots reaction.

Clear a message

/reaction-role-clear message_id:<message ID>

Deletes every mapping for that message and attempts to remove the bots configured reactions.

Custom emoji syntax

Type Example Stored identifier
Unicode 🎮 The character itself
Static custom <:gaming:123456789012345678> Custom emoji ID
Animated custom <a:dance:123456789012345678> Custom emoji ID

To insert custom emoji markup in the command: in Discord, type \:emojiName: in chat to reveal the raw form, then paste it into the emoji option.

Custom emojis must be available to the bot (from a server the bot shares). Matching always uses the stored identifier so unicode and custom emojis stay consistent across restarts.

Discord role hierarchy

Discord only allows a bot to assign roles strictly below its highest role. If assignment fails:

  1. Open Server Settings → Roles
  2. Move the bots role above the target role
  3. Confirm the bot still has Manage Roles
  4. Confirm the target role is not managed by an integration (boosts, bots, Linked Roles, etc.)

The bot also refuses to assign the @everyone role and roles managed by integrations.

Project structure

src/
  commands/
    reactionRoles/
      createReactionRole.js
      listReactionRoles.js
      removeReactionRole.js
      clearReactionRoles.js
  events/
    interactionCreate.js
    messageReactionAdd.js
    messageReactionRemove.js
    ready.js
  services/
    reactionRoleService.js
  database/
    database.js
    migrations.js
  utilities/
    emoji.js
    permissions.js
    logger.js
    errors.js
    loadCommands.js
    loadEvents.js
    reactionRoleHandler.js
  config/
    environment.js
  deployCommands.js
  index.js

Responsibilities are split across commands, events, services, database, utilities, and config so features stay isolated.

Adding future commands

  1. Create a new file under src/commands/ (any subfolder).
  2. Export a default object:
import { SlashCommandBuilder } from 'discord.js';

export default {
  data: new SlashCommandBuilder()
    .setName('ping')
    .setDescription('Replies with Pong'),
  async execute(interaction) {
    await interaction.reply('Pong!');
  },
};
  1. Run npm run deploy-commands.
  2. Restart the bot if it is already running.

Commands are discovered automatically — no central switch statement to edit.

Adding future event handlers

  1. Create a file under src/events/.
  2. Export a default object:
import { Events } from 'discord.js';

export default {
  name: Events.GuildCreate,
  once: false,
  async execute(guild) {
    // ...
  },
};

Set once: true for one-time events such as ClientReady. Events are registered automatically on startup.

Troubleshooting

Problem What to try
Commands do not appear Run npm run deploy-commands. For global commands, wait up to an hour, or set DISCORD_GUILD_ID for instant guild deploy.
“Message not found” Use a message in the same channel where you run the command. Enable Developer Mode to copy the message ID.
invalid ELF header / better-sqlite3 crash node_modules was installed on a different OS (e.g. Windows) then copied to Linux. On the server, run rm -rf node_modules && npm install. Do not copy node_modules between machines.
Roles are not assigned Enable Server Members Intent. Ensure the bot role is above the target role and has Manage Roles.
Custom emoji rejected The bot must be in a server that has that emoji. Paste full <:name:id> / <a:name:id> markup.
Duplicate mapping error That emoji is already configured on the message — remove it first or pick another emoji.
Bot cannot react Grant Add Reactions, View Channels, and Read Message History in that channel.
Database errors Ensure the process can write to DATABASE_PATH (default ./data/).
Login fails Check DISCORD_TOKEN in .env (no quotes/spaces). Reset the token in the Developer Portal if needed.

Graceful shutdown

The bot handles SIGINT, SIGTERM, unhandled rejections, and uncaught exceptions. On shutdown it stops accepting new work, destroys the Discord client, closes SQLite, and logs completion.

License

MIT