# 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 ```bash npm install cp .env.example .env ``` Edit `.env` with your Discord credentials (see below). ## Discord application setup 1. Open the [Discord Developer Portal](https://discord.com/developers/applications). 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: ```text View Channels Read Message History Add Reactions Manage Roles Use Application Commands ``` **Role hierarchy:** the bot’s highest role must sit **above** every role it needs to assign. In **Server Settings → Roles**, drag the bot’s 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`: ```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 ```bash 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): ```bash npm run dev ``` Production: ```bash npm start ``` Lint: ```bash npm run lint ``` ## Reaction-role commands All of these require **Manage Roles** and are guild-only. Responses are ephemeral. ### Create ```text /reaction-role message_id: role: emoji: ``` Example: ```text /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 bot’s reaction 6. Saves the mapping in SQLite 7. Confirms with message, role, and emoji ### List ```text /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 ```text /reaction-role-remove message_id: emoji: ``` Deletes the mapping and attempts to remove **only the bot’s** reaction. ### Clear a message ```text /reaction-role-clear message_id: ``` Deletes every mapping for that message and attempts to remove the bot’s configured reactions. ## Custom emoji syntax | Type | Example | Stored identifier | | --- | --- | --- | | Unicode | `🎮` | The character itself | | Static custom | `<:gaming:123456789012345678>` | Custom emoji ID | | Animated custom | `` | 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 bot’s 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 ```text 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: ```javascript import { SlashCommandBuilder } from 'discord.js'; export default { data: new SlashCommandBuilder() .setName('ping') .setDescription('Replies with Pong'), async execute(interaction) { await interaction.reply('Pong!'); }, }; ``` 3. Run `npm run deploy-commands`. 4. 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: ```javascript 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>` / `` 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