Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: scaffold community server discord bot #220

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

JowiAoun
Copy link
Collaborator

@JowiAoun JowiAoun commented Dec 18, 2024

Scaffolding the community server discord bot as a pre-requisite for linking server roles to 3rd party applications (beginning with portal in issue #217)

Checklist

  • I've thoroughly read the latest contribution guidelines.
  • I've rebased my branch onto main before creating this PR.
  • I've added tests to cover my changes (if applicable).
  • I've verified that all new and existing tests have passed locally for mobile, tablet, and desktop screen sizes.
  • My commit messages follow guidelines
  • My change requires documentation updates.
  • I've updated the documentation accordingly.
  • My code follows existing patterns of this project and/or improves upon them.

Summary by CodeRabbit

  • New Features

    • Added a new MIT License for the Discord bot application.
    • Introduced various command types including message commands, button commands, select menus, modal forms, context menus, and slash commands.
    • Implemented a comprehensive README with sections for features, installation, contribution guidelines, and a changelog.
    • Added a new configuration file for Nodemon to streamline development.
    • Introduced a new server for handling OAuth2 communication with Discord.
    • Implemented in-memory storage for Discord tokens.
    • Added functionality for managing user metadata with Discord's API.
  • Documentation

    • Updated the README.md file with detailed project information and usage instructions.
  • Chores

    • Added entries to the .gitignore file to exclude unnecessary files from version control.
    • Introduced a TypeScript configuration file to enhance type safety and code quality checks.

@JowiAoun JowiAoun self-assigned this Dec 18, 2024
@JowiAoun JowiAoun linked an issue Dec 18, 2024 that may be closed by this pull request
1 task
Copy link

coderabbitai bot commented Dec 18, 2024

📝 Walkthrough
📝 Walkthrough

Walkthrough

This pull request introduces a comprehensive Discord bot template with a modular and extensible architecture. The project is built using TypeScript and Discord.js, providing a structured approach to bot development. It includes various managers for handling different types of interactions like message commands, slash commands, button interactions, and more. The template offers robust configuration options, permission checks, cooldown management, and a flexible event system.

Changes

File Change Summary
.gitignore Added entries for dist/, node_modules/, cooldownDB.sqlite, and tsconfig.tsbuildinfo
LICENSE Added MIT License
README.md Comprehensive documentation update with changelog, features, installation, and contribution guidelines
nodemon.json Added configuration for development environment with TypeScript compilation
package.json Defined project dependencies, scripts, and configuration
src/bot.ts Main bot initialization file with command and event management
src/config.ts Configuration constants for bot settings
src/events/ Added event handlers for error management, interaction creation, message handling, and bot readiness
src/interactions/ Implemented example interactions including buttons, context menus, modal forms, select menus, and slash commands
src/structures/ Added managers for different command types and utility functions for command processing
src/types.ts TypeScript type definitions and interface extensions
src/utils/fileReader.ts Utility function for reading JavaScript files from directories
tsconfig.json TypeScript compiler configuration with strict type checking

Possibly related issues

  • cuhacking/2025#216 (Scaffold community server Discord bot)
    • This PR directly addresses the need for a Discord bot scaffolding with a flexible template.
  • cuhacking/2025#215 (Create community server Discord bot)
    • The implemented bot template provides a foundation for building features like OAuth2 authentication and role management.

Poem

🐰 A Discord Bot Hops to Life 🤖
With TypeScript and Discord's might,
Commands dance, interactions bright,
Managers weave their magic spell,
A bot that serves, and serves quite well!
Hop, hop, hooray! 🎉

Tip

CodeRabbit's docstrings feature is now available as part of our Early Access Program! Simply use the command @coderabbitai generate docstrings to have CodeRabbit automatically generate docstrings for your pull request. We would love to hear your feedback on Discord.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 49

♻️ Duplicate comments (1)
apps/discord-bot/src/structures/managers/messageCommands.ts (1)

11-11: ⚠️ Potential issue

Security: Avoid using file:/// protocol in dynamic imports

Similar to the EventManager, using the file:/// protocol in dynamic imports poses security risks.

🧹 Nitpick comments (50)
apps/discord-bot/src/events/errorManager.ts (3)

1-15: Consider implementing a more robust error handling strategy

The current error handling implementation could be enhanced with several production-ready features.

Recommendations:

  1. Implement error categorization to handle different types of errors differently
  2. Add error reporting service integration (e.g., Sentry)
  3. Implement recovery mechanisms for non-fatal errors
  4. Add rate limiting for error logging to prevent spam
  5. Include context information in error logs (e.g., command being executed, user info)
  6. Consider implementing a custom error class for bot-specific errors

Example implementation structure:

import type { ClientEvent } from '../types.js';

interface BotError extends Error {
  context?: {
    command?: string;
    userId?: string;
    guildId?: string;
  };
}

class ErrorManager {
  private static errorCount = 0;
  private static readonly ERROR_THRESHOLD = 10;
  private static readonly RESET_INTERVAL = 60000; // 1 minute

  static initialize(): void {
    setInterval(() => {
      this.errorCount = 0;
    }, this.RESET_INTERVAL);
  }

  static handleError(error: BotError): void {
    this.errorCount++;
    
    if (this.errorCount > this.ERROR_THRESHOLD) {
      console.error('[CRITICAL] Error threshold exceeded');
      process.exit(1);
    }

    // Log error with context
    console.error('[Bot Error]', {
      name: error.name,
      message: error.message,
      stack: error.stack,
      context: error.context
    });

    // TODO: Add error reporting service integration
  }
}

export const Event: ClientEvent = {
  name: 'errorManager',
  customEvent: true,
  run: (): void => {
    ErrorManager.initialize();

    process.on('unhandledRejection', (error: Error) => {
      ErrorManager.handleError(error as BotError);
    });

    process.on('uncaughtException', (error: Error) => {
      ErrorManager.handleError(error as BotError);
      process.exit(1);
    });
  },
};

1-2: Consider using more specific error types

The current implementation uses the generic Error type, which might miss specific Discord.js error properties.

Consider using Discord.js specific error types:

- import type { ClientEvent } from '../types.js'
+ import type { ClientEvent } from '../types.js'
+ import { DiscordAPIError, HTTPError } from 'discord.js'

4-7: Add JSDoc documentation for the event handler

Adding documentation will help other developers understand the purpose and behavior of this error manager.

Consider adding this documentation:

+/**
+ * Global error handler for the Discord bot
+ * Catches and processes unhandled rejections and uncaught exceptions
+ * to prevent the bot from crashing silently
+ */
export const Event: ClientEvent = {
  name: 'errorManager',
  customEvent: true,
  run: (): void => {
apps/discord-bot/src/structures/commandOptions/onlyChannels.ts (2)

5-8: Consider splitting complex condition for better readability

The compound condition could be more maintainable if split into separate checks.

-  if (!command.onlyChannels || !Array.isArray(command.onlyChannels) || !message.guild)
+  if (!command.onlyChannels) return true
+  if (!Array.isArray(command.onlyChannels)) return true
+  if (!message.guild) return true

27-27: Fix grammatical error in error message

There's a grammatical error in the error message description.

-  .setDescription(`The command you tried to execute cannot be ran in the current channel. Please execute the command in of these authorized channels:\n${command.onlyChannels.map(channelId => `↳ <#${channelId}>`).join('\n')}`)
+  .setDescription(`The command you tried to execute cannot be run in the current channel. Please execute the command in one of these authorized channels:\n${command.onlyChannels.map(channelId => `↳ <#${channelId}>`).join('\n')}`)
apps/discord-bot/src/structures/commandOptions/onlyGuilds.ts (1)

5-8: Consider improving the early return condition readability

The current condition combines multiple checks in one line. Consider breaking it down for better maintainability.

-  if (!command.onlyGuilds || !Array.isArray(command.onlyGuilds) || !message.guild)
+  if (!command.onlyGuilds) return true;
+  if (!Array.isArray(command.onlyGuilds)) return true;
+  if (!message.guild) return true;
apps/discord-bot/src/interactions/slashCommands/ping.ts (1)

5-5: Enhance command description for better user experience

The current description "Pong" is too brief. Consider providing a more informative description that explains what the command does.

-  description: 'Pong',
+  description: 'Check the bot\'s current WebSocket latency',
apps/discord-bot/src/messageCommands/ping.ts (2)

4-15: Consider adding cooldown mechanism

To prevent potential abuse, consider implementing a cooldown mechanism for the ping command.

Would you like me to provide an implementation of a cooldown system that can be reused across commands?


1-15: Add JSDoc documentation

Consider adding JSDoc documentation to improve code maintainability and help other contributors understand the command's purpose and usage.

+/**
+ * @command Ping
+ * @description Check the bot's current WebSocket latency
+ * @alias pong
+ * @example !ping
+ */
 export const MsgCommand: MessageCommand = {
apps/discord-bot/src/structures/commandOptions/guildCooldown.ts (1)

5-8: Consider improving function name and early return clarity.

While the implementation is correct, consider these improvements:

  1. The function name could be more concise (e.g., checkGuildCooldown)
  2. The early return conditions could be more explicit by separating them into individual checks
-export async function guildCooldownFN(
+export async function checkGuildCooldown(
   client: DiscordClient,
   message: Message | Interaction<'cached'>,
   command: AnyCommand,
   interactionType: InteractionTypeOptions
 ): Promise<boolean> {
-  if (!command.guildCooldown || Number.isNaN(command.guildCooldown) || !message.guild)
+  if (!command.guildCooldown) {
+    return true
+  }
+  if (Number.isNaN(command.guildCooldown)) {
+    return true
+  }
+  if (!message.guild) {
     return true
+  }
apps/discord-bot/src/messageCommands/eval.ts (2)

28-39: Clarify optional flags usage
The code removes certain flags (e.g., --silent) and modifies the snippet based on others. Consider documenting or logging the flags applied, so it’s clear why and how certain lines are removed or transformed during runtime.


60-66: Restrict error content
In case of errors, the entire error message (potentially including stack traces) is sent to the channel. Even though restricted to owners, you may want to ensure that secret details such as environment variables or stack details are not exposed.

apps/discord-bot/src/bot.ts (3)

17-33: Consider error handling around client login
No try/catch is wrapping the login process. If the token is invalid, the script may fail silently. Consider wrapping this in a try/catch to provide explicit error logs or fallback.


35-45: Persist cooldown management carefully
The JSON file stands as a single point of failure. If the file is removed or corrupted, cooldown data is lost. Consider a persistent store or error handling to gracefully recover from file I/O failures.


46-53: Tests and documentation
No tests or user-facing documentation accompany this scaffold, per your PR notes. Consider adding basic test coverage for the initialization flow and step-by-step instructions for new contributors.

apps/discord-bot/src/events/interactionCreate.ts (2)

8-16: Provide user feedback if slash command not found
Currently, the code returns without notifying the user if a slash command doesn’t exist. For clarity, consider responding with a short message like “Command not recognized.”


18-26: Autocomplete fallback
Same as slash commands, if an autocomplete is not recognized or not implemented, consider providing a fallback or log message for debugging.

apps/discord-bot/src/structures/managers/slashCommands.ts (3)

38-40: Skip reason
In case slash commands are ignored for not having a name or description, consider logging the skip reason. It helps debugging misconfigured commands.


74-76: Context menu validation
Similar to slash commands, a short log line would help you track which context menus are ignored.


99-113: Handle the commented out error block
The error handling is commented out. Consider reintroducing this to provide feedback if guild or global command registration fails.

apps/discord-bot/src/types.ts (4)

19-47: Extensive command options may benefit from grouping or partials.
You have a large set of optional properties for permissions, cooldown, and restrictions. Consider grouping related properties (e.g., all cooldown options) or using separate partial interfaces for improved maintainability.


49-55: MessageCommand interface is clear.
Returning void or Promise is straightforward for message commands. Consider specifying an explicit Promise to standardize asynchronous command patterns.


57-63: Event handling remains flexible.
The generic run signature is flexible but can obscure type safety. If feasible, refine argument typings for better IntelliSense and error checking, rather than using (...args: any[]).


96-105: SlashCommand interface is well-formed.
Everything is consistent with standard slash commands usage. Possibly consider an additional property to specify ephemeral replies if desired in the future.

apps/discord-bot/src/interactions/buttons/deleteOutput.ts (1)

1-9: Handle potential message state errors.
The command strictly deletes the interaction’s message. Consider adding checks to handle ephemeral or already-deleted messages, as Discord may throw errors if the message is no longer available.

apps/discord-bot/src/interactions/modalForms/exampleModal.ts (1)

1-10: Simple and effective modal usage.
The reply handling is straightforward. Consider making the reply ephemeral if it only concerns the user submitting the modal to keep the channel clutter-free.

apps/discord-bot/src/interactions/selectMenus/selectMenuExample.ts (1)

4-12: Consider if this example code should be in production

This appears to be template/example code. Consider either:

  1. Removing it if not needed for production
  2. Making it more meaningful for actual use
  3. Moving it to a examples/ or templates/ directory

Also, add JSDoc documentation for the exported Menu constant.

+/**
+ * Example select menu handler demonstrating basic Discord interaction patterns.
+ * @todo Replace with actual implementation or move to examples directory
+ */
 export const Menu: SelectMenu = {
apps/discord-bot/src/structures/managers/selectMenus.ts (1)

15-16: Simplify optional chaining usage

The double optional chaining on buttonCommand?.ignore is redundant since we've already checked for buttonCommand's existence.

- if (!selectMenu?.ignore && selectMenu?.name)
+ if (!selectMenu.ignore && selectMenu.name)
apps/discord-bot/src/interactions/contextMenus/getUsername.ts (1)

9-9: Remove unnecessary type assertion

The type assertion can be avoided by properly typing the interaction parameter in the function signature.

- run: (interaction): void => {
-   interaction = interaction as UserContextMenuCommandInteraction<'cached'>
+ run: (interaction: UserContextMenuCommandInteraction<'cached'>): Promise<void> => {
apps/discord-bot/src/structures/managers/buttonCommands.ts (2)

5-18: Consider creating a base manager class to reduce duplication

The ButtonManager implementation is very similar to SelectMenuManager and other interaction managers. Consider creating a base manager class to reduce code duplication.

Example implementation:

abstract class BaseInteractionManager<T> {
  protected abstract readonly interactionType: string;
  
  async initialize(client: DiscordClient, rootPath: string): Promise<void> {
    const files = fileReader(`${rootPath}/interactions/${this.interactionType}`);
    if (!files.length) return;

    for (const file of files) {
      try {
        const interaction: T = (await import(file))?.export;
        if (!interaction) continue;

        if (!interaction.ignore && interaction.name) {
          this.addToClient(client, interaction);
        }
      } catch (error) {
        console.error(`Failed to load ${this.interactionType} from ${file}:`, error);
      }
    }
  }

  protected abstract addToClient(client: DiscordClient, interaction: T): void;
}

class ButtonManager extends BaseInteractionManager<ButtonCommand> {
  protected readonly interactionType = 'buttons';
  
  protected addToClient(client: DiscordClient, button: ButtonCommand): void {
    client.buttonCommands?.set(button.name, button);
  }
}

15-16: Fix redundant optional chaining

Similar to the SelectMenuManager, the optional chaining is redundant here.

- if (!buttonCommand?.ignore && buttonCommand?.name)
+ if (!buttonCommand.ignore && buttonCommand.name)
apps/discord-bot/src/messageCommands/callSelectMenu.ts (1)

10-26: Consider extracting UI strings to constants

The menu text and options are hardcoded. Consider extracting these to constants or a configuration file for easier maintenance and localization.

Example:

const MENU_STRINGS = {
  content: 'Cookies SelectMenu',
  placeholder: 'Free Cookies!',
  options: [{
    label: 'Click for cookies!',
    description: 'Freeee!',
    value: 'CookieBox',
  }]
} as const;
apps/discord-bot/src/interactions/slashCommands/exampleModal.ts (1)

4-6: Add JSDoc documentation for the slash command

Add documentation to describe the command's purpose, usage, and parameters.

+/**
+ * Test modal command to demonstrate modal functionality
+ * @example
+ * /testmodal
+ */
 export const Slash: SlashCommand = {
   name: 'testmodal',
   description: 'Test Modal',
apps/discord-bot/src/structures/commandOptions/onlyUsers.ts (3)

9-11: Simplify user ID check

The current user ID check is overly complex and could be simplified.

-  if (command.onlyUsers.includes(((message as Interaction<'cached'>).user ?? (message as Message).author)?.id)) {
+  const userId = 'user' in message ? message.user.id : message.author.id;
+  if (command.onlyUsers.includes(userId)) {
     return true
   }

18-29: Enhance error message with allowed users information

The current error message could be more helpful by listing the allowed users.

     message.channel.send({
       embeds: [new EmbedBuilder()
         .setColor('DarkRed')
         .setTimestamp()
         .setAuthor({
           name: message.member?.user.globalName ?? message.member?.user.username ?? '',
           iconURL: message.member?.user.displayAvatarURL(),
         })
         .setThumbnail(client.user.displayAvatarURL())
-        .setDescription('The command you tried to execute couldn\'t be ran as you are not one of the authorized users.'),
+        .setDescription('This command is restricted to specific users.')
+        .addFields({
+          name: 'Authorized Users',
+          value: command.onlyUsers.map(id => `<@${id}>`).join(', ') || 'None specified'
+        }),
       ],
     })

31-31: Remove unnecessary semicolon

The semicolon after the else block is unnecessary and should be removed.

-  };
+  }
🧰 Tools
🪛 Biome (1.9.4)

[error] 31-31: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/structures/commandOptions/onlyRoles.ts (2)

18-29: Enhance error message with role names

Add role names alongside mentions for better clarity.

     message.channel.send({
       embeds: [new EmbedBuilder()
         .setColor('DarkRed')
         .setTimestamp()
         .setAuthor({
           name: message.member?.user.globalName ?? message.member?.user.username ?? '',
           iconURL: message.member?.user.displayAvatarURL(),
         })
         .setThumbnail(client.user.displayAvatarURL())
-        .setDescription(`The command you tried to execute couldn't be executed as you are missing one of these required roles:\n${command.onlyRoles.map((roleID: string) => `↳ <@&${roleID}>`).join('\n')}`),
+        .setDescription('This command requires specific roles to execute.')
+        .addFields({
+          name: 'Required Roles',
+          value: command.onlyRoles.map(roleID => {
+            const role = message.guild?.roles.cache.get(roleID);
+            return `↳ <@&${roleID}>${role ? ` (${role.name})` : ''}`;
+          }).join('\n') || 'None specified'
+        }),
       ],
     })

31-31: Remove unnecessary semicolon

The semicolon after the else block is unnecessary and should be removed.

-  };
+  }
🧰 Tools
🪛 Biome (1.9.4)

[error] 31-32: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/structures/commandOptions/ownerOnly.ts (2)

24-26: Simplify user display name retrieval

The current nested nullish coalescing could be simplified for better readability.

-            name: message.member?.user.globalName ?? message.member?.user.username ?? '',
-            iconURL: message.member?.user.displayAvatarURL(),
+            name: message.member?.user.displayName ?? 'Unknown User',
+            iconURL: message.member?.user.displayAvatarURL() ?? client.user.defaultAvatarURL,

32-32: Remove unnecessary semicolon

The semicolon after the closing brace is unnecessary.

-  };
+  }
🧰 Tools
🪛 Biome (1.9.4)

[error] 32-32: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/events/messageCreate.ts (1)

8-8: Consider adding rate limiting

The message handler should implement rate limiting to prevent spam and abuse.

Would you like me to provide an implementation for a rate limiting mechanism?

apps/discord-bot/src/structures/commandOptions/anyUserPermissions.ts (1)

1-31: Add unit tests for permission checks

The PR description mentions that tests are missing. This security-critical functionality should be thoroughly tested.

Would you like me to help create unit tests for the permission checking functionality?

apps/discord-bot/src/structures/commandOptions/anyClientPermissions.ts (2)

17-28: Add error handling for message sending

The message sending operation could fail silently. Consider adding error handling and returning appropriate feedback.

-    message.channel.send({
+    await message.channel.send({
       embeds: [new EmbedBuilder()
         // ... embed configuration ...
       ],
-    })
+    }).catch(error => {
+      console.error('Failed to send permission error message:', error);
+    });

1-31: Consider extracting common embed creation logic

The embed creation logic is duplicated across multiple permission check files. Consider extracting it into a shared utility function.

Would you like me to help create a shared utility for creating error embeds?

apps/discord-bot/src/structures/commandOptions/allUserPermissions.ts (1)

1-34: Standardize permission types across files

The permission types are inconsistent across files (bigint vs PermissionsString). Consider standardizing the approach.

Consider using PermissionsString consistently across all permission-related files for better type safety and maintainability.

🧰 Tools
🪛 Biome (1.9.4)

[error] 33-34: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/structures/commandOptions/allClientPermissions.ts (2)

1-34: Consider creating a shared permission checking module

There's significant code duplication across the permission checking files. Consider creating a shared module for permission checking logic.

I recommend creating a PermissionChecker class that encapsulates the common permission checking logic and provides methods for both client and user permission checks. This would:

  1. Reduce code duplication
  2. Centralize permission handling logic
  3. Make it easier to maintain and modify permission checking behavior

Would you like me to help design this shared module?

🧰 Tools
🪛 Biome (1.9.4)

[error] 33-34: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)


1-34: Add unit tests for permission checking

Given that permission checking is critical for command execution, comprehensive unit tests should be added.

Would you like me to help create unit tests for the permission checking functionality? The tests should cover:

  1. Various permission combinations
  2. Edge cases with null/undefined values
  3. Error message generation
  4. Integration with command processing
🧰 Tools
🪛 Biome (1.9.4)

[error] 33-34: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/structures/commandOptions/processor.ts (1)

17-17: Add explicit return type and function name for better maintainability

The default export uses an anonymous arrow function without an explicit return type.

Consider this improvement:

-export default async (client: DiscordClient, message: Message | Interaction<'cached'>, command: AnyCommand, interactionType: InteractionTypeOptions) => {
+export default async function processCommandOptions(
+  client: DiscordClient,
+  message: Message | Interaction<'cached'>,
+  command: AnyCommand,
+  interactionType: InteractionTypeOptions
+): Promise<boolean> {
apps/discord-bot/src/structures/commandOptions/globalCooldown.ts (1)

32-32: Simplify timestamp calculation and improve readability

The double Math.floor is unnecessary and makes the code harder to read.

-        .setDescription(`You are currently at cooldown. Please try again in <t:${Math.floor(Math.floor(storedTime + command.globalCooldown) / 1000)}:R>.`),
+        const nextAvailableTime = Math.floor((storedTime + command.globalCooldown) / 1000);
+        .setDescription(`You are currently at cooldown. Please try again in <t:${nextAvailableTime}:R>.`),
apps/discord-bot/README.md (1)

18-20: Fix grammar and style in changelog

Minor grammar issues in the changelog text.

- Added dependency of `simple-json-db` for the cooldown system as i rage quit and can't do it with `fs` myself.
+ Added dependency on `simple-json-db` for the cooldown system as I couldn't implement it with `fs`.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~19-~19: Did you mean “I”?
Context: ...ple-json-dbfor the cooldown system as i rage quit and can't do it withfs` mys...

(I_LOWERCASE_PREMIUM)


[misspelling] ~19-~19: This word is normally spelled with a hyphen.
Context: ...e-json-dbfor the cooldown system as i rage quit and can't do it withfs` myself. - Fix...

(EN_COMPOUNDS_RAGE_QUIT)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8ca8adf and 5484fe2.

📒 Files selected for processing (45)
  • apps/discord-bot/.gitignore (1 hunks)
  • apps/discord-bot/LICENSE (1 hunks)
  • apps/discord-bot/README.md (1 hunks)
  • apps/discord-bot/nodemon.json (1 hunks)
  • apps/discord-bot/package.json (1 hunks)
  • apps/discord-bot/src/bot.ts (1 hunks)
  • apps/discord-bot/src/config.ts (1 hunks)
  • apps/discord-bot/src/events/errorManager.ts (1 hunks)
  • apps/discord-bot/src/events/interactionCreate.ts (1 hunks)
  • apps/discord-bot/src/events/messageCreate.ts (1 hunks)
  • apps/discord-bot/src/events/ready.ts (1 hunks)
  • apps/discord-bot/src/interactions/buttons/deleteOutput.ts (1 hunks)
  • apps/discord-bot/src/interactions/contextMenus/getUsername.ts (1 hunks)
  • apps/discord-bot/src/interactions/modalForms/exampleModal.ts (1 hunks)
  • apps/discord-bot/src/interactions/selectMenus/selectMenuExample.ts (1 hunks)
  • apps/discord-bot/src/interactions/slashCommands/exampleModal.ts (1 hunks)
  • apps/discord-bot/src/interactions/slashCommands/ping.ts (1 hunks)
  • apps/discord-bot/src/messageCommands/callSelectMenu.ts (1 hunks)
  • apps/discord-bot/src/messageCommands/eval.ts (1 hunks)
  • apps/discord-bot/src/messageCommands/ping.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/allClientPermissions.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/allUserPermissions.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/anyClientPermissions.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/anyUserPermissions.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/channelCooldown.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/globalCooldown.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/guildCooldown.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/onlyChannels.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/onlyGuilds.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/onlyRoles.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/onlyUsers.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/ownerOnly.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/processor.ts (1 hunks)
  • apps/discord-bot/src/structures/managers/buttonCommands.ts (1 hunks)
  • apps/discord-bot/src/structures/managers/events.ts (1 hunks)
  • apps/discord-bot/src/structures/managers/messageCommands.ts (1 hunks)
  • apps/discord-bot/src/structures/managers/modalForms.ts (1 hunks)
  • apps/discord-bot/src/structures/managers/selectMenus.ts (1 hunks)
  • apps/discord-bot/src/structures/managers/slashCommands.ts (1 hunks)
  • apps/discord-bot/src/types.ts (1 hunks)
  • apps/discord-bot/src/utils/fileReader.ts (1 hunks)
  • apps/discord-bot/tsconfig.json (1 hunks)
  • libs/external/aceternity/tailwind.config.ts (1 hunks)
  • libs/website/feature/faq/index.ts (1 hunks)
  • libs/website/feature/stats/index.ts (1 hunks)
✅ Files skipped from review due to trivial changes (8)
  • libs/website/feature/faq/index.ts
  • apps/discord-bot/LICENSE
  • apps/discord-bot/.gitignore
  • libs/website/feature/stats/index.ts
  • libs/external/aceternity/tailwind.config.ts
  • apps/discord-bot/nodemon.json
  • apps/discord-bot/tsconfig.json
  • apps/discord-bot/package.json
🧰 Additional context used
🪛 Biome (1.9.4)
apps/discord-bot/src/structures/commandOptions/onlyRoles.ts

[error] 31-32: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/structures/commandOptions/allUserPermissions.ts

[error] 33-34: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/structures/commandOptions/ownerOnly.ts

[error] 32-32: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/structures/commandOptions/onlyGuilds.ts

[error] 31-32: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/structures/commandOptions/allClientPermissions.ts

[error] 33-34: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/structures/commandOptions/onlyChannels.ts

[error] 31-32: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/structures/commandOptions/onlyUsers.ts

[error] 31-31: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

🪛 LanguageTool
apps/discord-bot/README.md

[uncategorized] ~19-~19: Did you mean “I”?
Context: ...ple-json-dbfor the cooldown system as i rage quit and can't do it withfs` mys...

(I_LOWERCASE_PREMIUM)


[misspelling] ~19-~19: This word is normally spelled with a hyphen.
Context: ...e-json-dbfor the cooldown system as i rage quit and can't do it withfs` myself. - Fix...

(EN_COMPOUNDS_RAGE_QUIT)


[style] ~78-~78: The phrase “a variety of” may be wordy. To make your writing clearer, consider replacing it.
Context: ...text menus, and modal forms. - Includes a variety of commonly used command options (not appl...

(A_VARIETY_OF)


[style] ~86-~86: Specify a number, remove phrase, or simply use “many” or “numerous”
Context: ...s may take time to refresh if there are a large number of different guild commands. - Collections...

(LARGE_NUMBER_OF)

🪛 Markdownlint (0.37.0)
apps/discord-bot/README.md

2-2: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


3-3: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


4-4: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


5-5: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


6-6: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


7-7: null
Images should have alternate text (alt text)

(MD045, no-alt-text)

🔇 Additional comments (12)
apps/discord-bot/src/structures/commandOptions/onlyGuilds.ts (2)

1-4: LGTM! Imports are well-organized and properly typed.


9-17: Remove redundant optional chaining

The optional chaining on message.guild?.id is unnecessary since we've already checked for !message.guild in the early return.

-  if (command.onlyGuilds.includes(message.guild?.id)) {
+  if (command.onlyGuilds.includes(message.guild.id)) {

Verify guild ID validation

Ensure that the guild IDs in command.onlyGuilds are properly validated when commands are registered.

✅ Verification successful

Based on the context gathered, I can now generate the final response:

Optional chaining can be safely removed

The optional chaining on message.guild?.id can indeed be safely removed since:

  1. The onlyGuilds validation is processed through a dedicated processor
  2. The processor (processor.ts) runs all validations in sequence, including the guild check
  3. The type definition in types.ts shows onlyGuilds is a proper command option that's handled systematically

The suggested refactor is correct:

-  if (command.onlyGuilds.includes(message.guild?.id)) {
+  if (command.onlyGuilds.includes(message.guild.id)) {

As for guild ID validation, the command registration system uses TypeScript interfaces that enforce onlyGuilds to be an array of strings, and the validation is handled through the command options processor, which provides sufficient validation guarantees.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for command registration and guild ID validation
ast-grep --pattern 'command.onlyGuilds = $_'

Length of output: 46


Script:

#!/bin/bash
# Let's try a different approach to find guild ID validation patterns
# First, let's look for files that mention onlyGuilds
rg -l "onlyGuilds"

# Then search for any validation patterns related to guild IDs
rg "guild.*id" -A 2 -B 2

# Also search for command registration patterns
ast-grep --pattern 'new Command({
  $$$
})'

Length of output: 2486


Script:

#!/bin/bash
# Let's check the Command type definition and onlyGuilds property
cat apps/discord-bot/src/types.ts

# Also check the README for any documentation about onlyGuilds
cat apps/discord-bot/README.md

# And check the command processor to understand the validation flow
cat apps/discord-bot/src/structures/commandOptions/processor.ts

Length of output: 13173

apps/discord-bot/src/structures/commandOptions/guildCooldown.ts (1)

1-4: LGTM! Well-organized imports with proper type declarations.

The imports are clean and properly structured, with clear separation between type imports and concrete implementations.

apps/discord-bot/src/messageCommands/eval.ts (1)

41-47: Ensure partial token leaks are protected
Currently, only exact occurrences of the client’s token are replaced with a warning. Attackers could attempt partial matches. Consider hashing or pattern matching partial tokens.

apps/discord-bot/src/bot.ts (1)

14-15: Validate __dirname usage under ESM
Using import.meta.url with dirname is an ESM pattern. Just ensure this usage is stable across all runtime environments (like bundlers or specialized hosting).

apps/discord-bot/src/events/interactionCreate.ts (1)

28-36: Context menus
No user feedback or logging indicates if a context menu is missing or misconfigured. A small warning log or ephemeral reply can help debugging.

apps/discord-bot/src/types.ts (6)

1-3: Imports look solid.
All imported types appear organized with descriptive names. Ensure that all referenced dependencies (e.g., 'discord.js', 'simple-json-db') are installed and compatible versions are pinned in your package configuration.


5-17: Module augmentation is well-structured.
Augmenting 'discord.js' with a custom 'DiscordClient' is a clean way to expand the base client. However, confirm that these optional properties are used consistently to avoid undefined at runtime when referencing them elsewhere.


65-69: ButtonCommand, SelectMenu, and ModalForm all follow consistent design.
All three interfaces align well with the CommandOptions structure. Ensure that you handle “ownerOnly” or permissions logic consistently in your handlers if you intend to restrict usage.

Also applies to: 71-75, 77-81


83-94: SlashCommandOptions are comprehensive.
Nested options, autocomplete, and custom choices are supported. Be mindful of command complexity when nesting many sub-options.


107-113: ContextMenu support is neatly integrated.
Combining message and user context menus under a single interface is a great approach. Looks good as is.


115-116: Type aliases effectively unify all command interfaces.
This helps keep the registration logic generic and simpler.

Comment on lines +8 to +13
// process.on('unhandledRejection', (error: Error) => {
// console.log(error)
// })
// process.on('uncaughtException', (error: Error) => {
// console.log(error)
// })
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Uncomment and enhance error handling implementation

The error handlers are currently commented out, leaving the bot without any error management. This could lead to silent failures or unexpected crashes in production.

Consider this enhanced implementation:

-    // process.on('unhandledRejection', (error: Error) => {
-    //   console.log(error)
-    // })
-    // process.on('uncaughtException', (error: Error) => {
-    //   console.log(error)
-    // })
+    process.on('unhandledRejection', (error: Error) => {
+      console.error('[Unhandled Rejection]', {
+        name: error.name,
+        message: error.message,
+        stack: error.stack
+      });
+      // TODO: Add error reporting service integration
+    });
+
+    process.on('uncaughtException', (error: Error) => {
+      console.error('[Uncaught Exception]', {
+        name: error.name,
+        message: error.message,
+        stack: error.stack
+      });
+      // TODO: Add error reporting service integration
+      
+      // Gracefully shutdown after uncaught exception
+      process.exit(1);
+    });

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +5 to +32
export function onlyChannelsFN(client: DiscordClient, message: Message | Interaction<'cached'>, command: AnyCommand) {
if (!command.onlyChannels || !Array.isArray(command.onlyChannels) || !message.guild)
return true

if (command.onlyChannels.includes(message.channel?.id)) {
return true
}
else {
if (command.returnErrors === false || command.returnOnlyChannelsError === false)
return false
if (!message.channel || message.channel.type !== ChannelType.GuildText)
return false

message.channel.send({
embeds: [new EmbedBuilder()
.setColor('DarkRed')
.setTimestamp()
.setAuthor({
name: message.member?.user.globalName ?? message.member?.user.username ?? '',
iconURL: message.member?.user.displayAvatarURL(),
})
.setThumbnail(client.user.displayAvatarURL())
.setDescription(`The command you tried to execute cannot be ran in the current channel. Please execute the command in of these authorized channels:\n${command.onlyChannels.map(channelId => `↳ <#${channelId}>`).join('\n')}`),
],
})
return false
};
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add comprehensive tests for permission checks

This function handles command permissions and channel restrictions, which are critical for security. Please add tests covering:

  • Invalid channel IDs
  • Various permission configurations
  • Edge cases (null/undefined values)
  • Different message/interaction types

Would you like me to help create a test suite for this function?

🧰 Tools
🪛 Biome (1.9.4)

[error] 31-32: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

],
})
return false
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove unnecessary semicolon

The semicolon after the closing brace is unnecessary and flagged by the linter as causing unreachable code.

-  };
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
};
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 31-32: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

Comment on lines +9 to +17
if (command.onlyChannels.includes(message.channel?.id)) {
return true
}
else {
if (command.returnErrors === false || command.returnOnlyChannelsError === false)
return false
if (!message.channel || message.channel.type !== ChannelType.GuildText)
return false

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Simplify control flow by removing unnecessary else block

The code structure can be simplified by removing the else block and reorganizing the checks.

-  if (command.onlyChannels.includes(message.channel?.id)) {
-    return true
-  }
-  else {
-    if (command.returnErrors === false || command.returnOnlyChannelsError === false)
-      return false
-    if (!message.channel || message.channel.type !== ChannelType.GuildText)
-      return false
+  if (command.onlyChannels.includes(message.channel?.id)) return true
+  
+  if (command.returnErrors === false || command.returnOnlyChannelsError === false) return false
+  if (!message.channel || message.channel.type !== ChannelType.GuildText) return false
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (command.onlyChannels.includes(message.channel?.id)) {
return true
}
else {
if (command.returnErrors === false || command.returnOnlyChannelsError === false)
return false
if (!message.channel || message.channel.type !== ChannelType.GuildText)
return false
if (command.onlyChannels.includes(message.channel?.id)) return true
if (command.returnErrors === false || command.returnOnlyChannelsError === false) return false
if (!message.channel || message.channel.type !== ChannelType.GuildText) return false

Comment on lines +18 to +32
message.channel.send({
embeds: [new EmbedBuilder()
.setColor('DarkRed')
.setTimestamp()
.setAuthor({
name: message.member?.user.globalName ?? message.member?.user.username ?? '',
iconURL: message.member?.user.displayAvatarURL(),
})
.setThumbnail(client.user.displayAvatarURL())
.setDescription(`The command you tried to execute cannot be ran in the current guild. Please execute the command in of these authorized guilds:\n${command.onlyGuilds.map((guildID: string) => `↳ <#${guildID}>`).join('\n')}`),
],
})
return false
};
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical: Fix incorrect channel mentions in error message

The error message uses channel mentions (<#${guildID}>) for guild IDs, which will not render correctly. This should use guild names instead.

-        .setDescription(`The command you tried to execute cannot be ran in the current guild. Please execute the command in of these authorized guilds:\n${command.onlyGuilds.map((guildID: string) => `↳ <#${guildID}>`).join('\n')}`),
+        .setDescription(`The command you tried to execute cannot be ran in the current guild. Please execute the command in one of these authorized guilds:\n${command.onlyGuilds.map((guildID: string) => `↳ ${client.guilds.cache.get(guildID)?.name ?? guildID}`).join('\n')}`),

Remove unreachable semicolon

The semicolon after the return statement is unreachable and should be removed.

   return false
-  };
+  }

Grammar fix in error message

The error message contains a grammatical error: "cannot be ran" should be "cannot be run".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
message.channel.send({
embeds: [new EmbedBuilder()
.setColor('DarkRed')
.setTimestamp()
.setAuthor({
name: message.member?.user.globalName ?? message.member?.user.username ?? '',
iconURL: message.member?.user.displayAvatarURL(),
})
.setThumbnail(client.user.displayAvatarURL())
.setDescription(`The command you tried to execute cannot be ran in the current guild. Please execute the command in of these authorized guilds:\n${command.onlyGuilds.map((guildID: string) => `↳ <#${guildID}>`).join('\n')}`),
],
})
return false
};
}
message.channel.send({
embeds: [new EmbedBuilder()
.setColor('DarkRed')
.setTimestamp()
.setAuthor({
name: message.member?.user.globalName ?? message.member?.user.username ?? '',
iconURL: message.member?.user.displayAvatarURL(),
})
.setThumbnail(client.user.displayAvatarURL())
.setDescription(`The command you tried to execute cannot be ran in the current guild. Please execute the command in one of these authorized guilds:\n${command.onlyGuilds.map((guildID: string) => `↳ ${client.guilds.cache.get(guildID)?.name ?? guildID}`).join('\n')}`),
],
})
return false
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 31-32: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

Comment on lines +16 to +23
let allSlashCommands: Array<string> = fileReader(`${rootPath}/interactions/slashCommands`)
allSlashCommands = await allSlashCommands.reduce(async (array: any, slash: string): Promise<Array<string>> => {
const command: SlashCommand | undefined = (await import(`file:///${slash}`))?.Slash

if (command?.ignore || !command?.name)
return array
else return (await array).concat(slash)
}, [])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve error handling and type safety in slash commands loading

The slash commands loading logic has several potential issues:

  1. Using any type for the accumulator weakens type safety
  2. No error handling for failed imports
  3. Potential file path issues with file:/// prefix

Consider this safer implementation:

-    allSlashCommands = await allSlashCommands.reduce(async (array: any, slash: string): Promise<Array<string>> => {
-      const command: SlashCommand | undefined = (await import(`file:///${slash}`))?.Slash
+    allSlashCommands = await allSlashCommands.reduce(async (array: Promise<string[]>, slash: string): Promise<string[]> => {
+      const acc = await array;
+      try {
+        const command: SlashCommand | undefined = (await import(slash))?.Slash
+        if (command?.ignore || !command?.name) return acc;
+        return acc.concat(slash);
+      } catch (error) {
+        console.error(`Failed to load slash command from ${slash}:`, error);
+        return acc;
+      }
+    }, Promise.resolve([]))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let allSlashCommands: Array<string> = fileReader(`${rootPath}/interactions/slashCommands`)
allSlashCommands = await allSlashCommands.reduce(async (array: any, slash: string): Promise<Array<string>> => {
const command: SlashCommand | undefined = (await import(`file:///${slash}`))?.Slash
if (command?.ignore || !command?.name)
return array
else return (await array).concat(slash)
}, [])
let allSlashCommands: Array<string> = fileReader(`${rootPath}/interactions/slashCommands`)
allSlashCommands = await allSlashCommands.reduce(async (array: Promise<string[]>, slash: string): Promise<string[]> => {
const acc = await array;
try {
const command: SlashCommand | undefined = (await import(slash))?.Slash
if (command?.ignore || !command?.name) return acc;
return acc.concat(slash);
} catch (error) {
console.error(`Failed to load slash command from ${slash}:`, error);
return acc;
}
}, Promise.resolve([]))

Comment on lines +25 to +32
let allContextMenus: Array<string> = fileReader(`${rootPath}/interactions/contextMenus`)
allContextMenus = await allContextMenus.reduce(async (array: any, context: string): Promise<Array<string>> => {
const command: ContextMenu | undefined = (await import(`file:///${context}`))?.Context

if (command?.ignore || !command?.name || !command?.type)
return array
else return (await array).concat(context)
}, [])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Apply consistent error handling pattern to context menus loading

The context menus loading has the same issues as the slash commands loading.

Apply similar improvements:

-    allContextMenus = await allContextMenus.reduce(async (array: any, context: string): Promise<Array<string>> => {
-      const command: ContextMenu | undefined = (await import(`file:///${context}`))?.Context
+    allContextMenus = await allContextMenus.reduce(async (array: Promise<string[]>, context: string): Promise<string[]> => {
+      const acc = await array;
+      try {
+        const command: ContextMenu | undefined = (await import(context))?.Context
+        if (command?.ignore || !command?.name || !command?.type) return acc;
+        return acc.concat(context);
+      } catch (error) {
+        console.error(`Failed to load context menu from ${context}:`, error);
+        return acc;
+      }
+    }, Promise.resolve([]))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let allContextMenus: Array<string> = fileReader(`${rootPath}/interactions/contextMenus`)
allContextMenus = await allContextMenus.reduce(async (array: any, context: string): Promise<Array<string>> => {
const command: ContextMenu | undefined = (await import(`file:///${context}`))?.Context
if (command?.ignore || !command?.name || !command?.type)
return array
else return (await array).concat(context)
}, [])
let allContextMenus: Array<string> = fileReader(`${rootPath}/interactions/contextMenus`)
allContextMenus = await allContextMenus.reduce(async (array: Promise<string[]>, context: string): Promise<string[]> => {
const acc = await array;
try {
const command: ContextMenu | undefined = (await import(context))?.Context
if (command?.ignore || !command?.name || !command?.type) return acc;
return acc.concat(context);
} catch (error) {
console.error(`Failed to load context menu from ${context}:`, error);
return acc;
}
}, Promise.resolve([]))

Comment on lines +39 to +59
if ((client.messageCommands?.size ?? 0) > 0) {
// console.log(t.bold.red.toFunction()('[MessageCommands] ') + t.bold.cyan.toFunction()(`Loaded ${(client.messageCommands?.size ?? 0)} MessageCommands with ${t.bold.white.toFunction()(`${client.messageCommands_Aliases?.size} Aliases`)}.`))
if ((client.events?.size ?? 0) > 0) {
// console.log(t.bold.yellow.toFunction()('[Events] ') + t.bold.magenta.toFunction()(`Loaded ${(client.events?.size ?? 0)} Events.`))
if ((client.buttonCommands?.size ?? 0) > 0) {
// console.log(t.bold.brightGreen.toFunction()('[ButtonCommands] ') + t.bold.brightYellow.toFunction()(`Loaded ${(client.buttonCommands?.size ?? 0)} Buttons.`))
if ((client.selectMenus?.size ?? 0) > 0) {
// console.log(t.bold.red.toFunction()('[SelectMenus] ') + t.bold.brightBlue.toFunction()(`Loaded ${(client.selectMenus?.size ?? 0)} SelectMenus.`))
if ((client.modalForms?.size ?? 0) > 0) {
// console.log(t.bold.brightCyan.toFunction()('[ModalForms] ') + t.bold.brightYellow.toFunction()(`Loaded ${(client.modalForms?.size ?? 0)} Modals.`))
if (allSlashCommands?.length > 0) {
// console.log(t.bold.magenta.toFunction()('[SlashCommands] ') + t.bold.white.toFunction()(`Loaded ${allSlashCommands.length} SlashCommands.`))
if (allContextMenus?.length > 0) {
// console.log(t.bold.magenta.toFunction()('[ContextMenus] ') + t.bold.white.toFunction()(`Loaded ${allContextMenus.length} ContextMenus.`))
}
}
}
}
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Simplify deeply nested conditional logging

The current nested if-statements make the code hard to maintain and understand.

Consider flattening the structure:

-    if ((client.messageCommands?.size ?? 0) > 0) {
-      // console.log...
-      if ((client.events?.size ?? 0) > 0) {
-        // console.log...
-        if ((client.buttonCommands?.size ?? 0) > 0) {
-          // ...more nesting
-        }
-      }
-    }
+    const stats = {
+      messageCommands: client.messageCommands?.size ?? 0,
+      events: client.events?.size ?? 0,
+      buttonCommands: client.buttonCommands?.size ?? 0,
+      selectMenus: client.selectMenus?.size ?? 0,
+      modalForms: client.modalForms?.size ?? 0,
+      slashCommands: allSlashCommands?.length ?? 0,
+      contextMenus: allContextMenus?.length ?? 0
+    };
+
+    Object.entries(stats).forEach(([type, count]) => {
+      if (count > 0) {
+        // console.log(`[${type}] Loaded ${count} ${type}`);
+      }
+    });

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +82 to +96
## Notes

- Recommended Node.js version: 16 and above.
- Global slash commands and context menus may take time to refresh as it is controlled by Discord.
- Guild commands may take time to refresh if there are a large number of different guild commands.
- Collections where command and event data is stored and used:
- `<Client>.messageCommands`: Message commands cache
- `<Client>.messageCommands_Aliases`: Message command aliases cache
- `<Client>.events`: Client events cache
- `<Client>.buttonCommands`: Button interactions cache
- `<Client>.selectMenus`: Select menu interactions cache
- `<Client>.modalForms`: Modal form interactions cache
- `<Client>.slashCommands`: Slash commands cache
- `<Client>.contextMenus`: ContextMenus commands cache

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add documentation for community server integration

The README lacks information about the specific purpose of this bot in the community server context.

Consider adding a new section:

## Community Server Integration

This bot serves as a foundation for linking server roles to third-party applications. Key features include:
- Role management integration capabilities
- Third-party application linking support
- Portal integration (see issue #217)

For development and contribution guidelines specific to the community server features, see [CONTRIBUTING.md].
🧰 Tools
🪛 LanguageTool

[style] ~86-~86: Specify a number, remove phrase, or simply use “many” or “numerous”
Context: ...s may take time to refresh if there are a large number of different guild commands. - Collections...

(LARGE_NUMBER_OF)

Comment on lines +1 to +8
<p align="center">
<img src="https://media.discordapp.net/attachments/774290264764055582/1093484780525469757/A_banner_for_a_discord_bots_template_made_using_discord.js.png" height="200" width="400"><br>
<img src="https://img.shields.io/badge/version-1.0.7-05122A?style=for-the-badge">
<a href="https://discord.gg/VStdRr8nP2"><img src="https://img.shields.io/badge/discord-invite-5865f2?style=for-the-badge&logo=discord&logoColor=white"></a>
<img src="https://img.shields.io/github/issues/RileCraft/DiscordBot-Template-ts.svg?style=for-the-badge">
<img src="https://img.shields.io/github/forks/RileCraft/DiscordBot-Template-ts.svg?style=for-the-badge">
<img src="https://img.shields.io/github/stars/RileCraft/DiscordBot-Template-ts.svg?style=for-the-badge">
</p>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add alt text to images for accessibility

Images should have descriptive alt text for accessibility.

Update the image tags:

-  <img src="https://media.discordapp.net/attachments/774290264764055582/1093484780525469757/A_banner_for_a_discord_bots_template_made_using_discord.js.png" height="200" width="400"><br>
-  <img src="https://img.shields.io/badge/version-1.0.7-05122A?style=for-the-badge">
+  <img src="https://media.discordapp.net/attachments/774290264764055582/1093484780525469757/A_banner_for_a_discord_bots_template_made_using_discord.js.png" alt="Discord Bot Template Banner" height="200" width="400"><br>
+  <img src="https://img.shields.io/badge/version-1.0.7-05122A?style=for-the-badge" alt="Version 1.0.7">

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Markdownlint (0.37.0)

2-2: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


3-3: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


4-4: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


5-5: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


6-6: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


7-7: null
Images should have alternate text (alt text)

(MD045, no-alt-text)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (8)
apps/discord-bot/template-server/src/storage.ts (1)

1-1: Consider using persistent storage instead of in-memory Map

Using an in-memory Map for token storage has several limitations:

  • Data is lost when the server restarts
  • Doesn't scale across multiple instances
  • No persistence guarantees

Consider using a persistent storage solution like Redis or a database for token storage in production. This would provide:

  • Data persistence across restarts
  • Horizontal scalability
  • Better security features
  • Built-in encryption capabilities

Would you like me to provide an example implementation using Redis or a database?

apps/discord-bot/template-server/src/discord.ts (2)

36-60: Add clearer error-handling strategies.
The error is thrown when the response is not okay, but consider adding fallback logic or retry attempts to mitigate transient failures. This can be helpful if Discord’s API has intermittent issues.


119-126: Avoid hardcoding platform_name.
"Example Linked Role Discord Bot" is hardcoded. If you intend to change the bot branding or re-purpose the same code for multiple bots, consider placing this value in configuration.

apps/discord-bot/template-server/src/register.ts (1)

7-54: Check numeric type definitions for metadata.
The numeric type integers (e.g., 2, 7) have domain-specific meanings in Discord’s role-connection metadata schema. To improve maintainability, define constants or an enum referencing official documentation, making the code self-documenting and less prone to mistakes if these values change.

apps/discord-bot/template-server/src/server.ts (2)

59-62: Confirm state verification logic and error responses.
The 403 status code is appropriate for invalid states, but consider returning an explanatory message or a redirect to clarify for a user who might accidentally end up here.


64-65: Handle null or malformed OAuth code.
If the user arrives without a valid OAuth code, the request to get tokens will fail with a less specific error. Consider explicitly checking for a missing or invalid code before calling getOAuthTokens.

apps/discord-bot/template-server/README.md (2)

35-104: Add language specifiers to code blocks

For better syntax highlighting and documentation consistency, add language specifiers to the code blocks. For example:

-```
+```bash
 git clone https://github.com/discord/linked-roles-sample.git

This applies to all code blocks in the setup and running sections.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Markdownlint (0.37.0)</summary>

74-74: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)

---

80-80: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)

---

90-90: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)

---

35-35: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)

---

41-41: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)

---

50-50: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)

---

60-60: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)

---

73-73: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)

---

79-79: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)

---

89-89: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)

---

95-95: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

---

`114-118`: **Format URL as markdown link and add internal documentation references**

Please format the bare URL as a proper markdown link and consider adding references to internal documentation:

```diff
 - Read **[the tutorial](https://discord.com/developers/docs/tutorials/configuring-app-metadata-for-linked-roles)** for in-depth information.
-- Browse https://github.com/JustinBeckwith/fitbit-discord-bot/ for a more in-depth example using the Fitbit API
+- Browse [Fitbit Discord Bot Example](https://github.com/JustinBeckwith/fitbit-discord-bot/) for a more in-depth example using the Fitbit API
 - Join the **[Discord Developers server](https://discord.gg/discord-developers)** to ask questions about the API, attend events hosted by the Discord API team, and interact with other devs.
+- Check our [Internal Bot Development Guide](../docs/bot-development.md) for team-specific guidelines and best practices
🧰 Tools
🪛 Markdownlint (0.37.0)

117-117: null
Bare URL used

(MD034, no-bare-urls)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5484fe2 and ee33f55.

⛔ Files ignored due to path filters (1)
  • apps/discord-bot/template-server/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • apps/discord-bot/template-server/.gitignore (1 hunks)
  • apps/discord-bot/template-server/README.md (1 hunks)
  • apps/discord-bot/template-server/package.json (1 hunks)
  • apps/discord-bot/template-server/src/config.ts (1 hunks)
  • apps/discord-bot/template-server/src/discord.ts (1 hunks)
  • apps/discord-bot/template-server/src/register.ts (1 hunks)
  • apps/discord-bot/template-server/src/server.ts (1 hunks)
  • apps/discord-bot/template-server/src/storage.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • apps/discord-bot/template-server/.gitignore
  • apps/discord-bot/template-server/package.json
🧰 Additional context used
🪛 LanguageTool
apps/discord-bot/template-server/README.md

[style] ~8-~8: Consider removing “of” to be more concise
Context: ...ole-discord-bot) ## Project structure All of the files for the project are on the left-h...

(ALL_OF_THE)

🪛 Markdownlint (0.37.0)
apps/discord-bot/template-server/README.md

74-74: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


80-80: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


90-90: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


117-117: null
Bare URL used

(MD034, no-bare-urls)


11-11: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


35-35: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


41-41: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


50-50: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


60-60: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


73-73: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


79-79: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


89-89: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


95-95: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (4)
apps/discord-bot/template-server/src/storage.ts (1)

1-9: ⚠️ Potential issue

Add security measures for token storage

The current implementation stores sensitive tokens without any security measures.

Consider implementing these security measures:

  1. Encrypt tokens before storage
  2. Implement token rotation
  3. Add token expiration checks
  4. Implement proper logging for security events
  5. Add rate limiting for token operations

Would you like me to provide an example implementation with these security measures?

Let's check if there are any security-related configurations or encryption utilities in the codebase:

apps/discord-bot/template-server/src/discord.ts (1)

19-30: Consider validating or white-listing OAuth state.
While using a randomly generated state is good practice for preventing CSRF attacks, make sure you have a clear process to validate or track legitimate states beyond just storing them in cookies (e.g., storing in a database if there's high security risk).

apps/discord-bot/template-server/src/server.ts (1)

107-134: Implement robust error handling for external data fetch failures.
When external data fetch (e.g., from cookie data or a third-party service) fails, the current approach sets metadata to an empty object. This is functional but might need logging or user-friendly error notifications if the user’s data is crucial.

apps/discord-bot/template-server/README.md (1)

36-36: ⚠️ Potential issue

Update repository URL to match the current project

The clone URL currently points to discord/linked-roles-sample. This should be updated to point to your organization's repository:

-git clone https://github.com/discord/linked-roles-sample.git
+git clone https://github.com/cuhacking/2025.git

Likely invalid or redundant comment.

Comment on lines +3 to +5
export async function storeDiscordTokens(userId: string, tokens: any) {
await store.set(`discord-${userId}`, tokens)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve type safety and error handling for token storage

Several improvements needed for production readiness:

  1. any type should be replaced with a proper interface
  2. Unnecessary async/await usage
  3. Missing error handling
  4. No validation for userId or tokens

Consider this improved implementation:

+ interface DiscordTokens {
+   access_token: string;
+   refresh_token: string;
+   expires_at: number;
+ }
+
- export async function storeDiscordTokens(userId: string, tokens: any) {
-   await store.set(`discord-${userId}`, tokens)
+ export function storeDiscordTokens(userId: string, tokens: DiscordTokens): void {
+   if (!userId?.trim()) {
+     throw new Error('Invalid userId provided');
+   }
+   if (!tokens?.access_token || !tokens?.refresh_token) {
+     throw new Error('Invalid tokens object');
+   }
+   store.set(`discord-${userId}`, tokens);
+ }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function storeDiscordTokens(userId: string, tokens: any) {
await store.set(`discord-${userId}`, tokens)
}
interface DiscordTokens {
access_token: string;
refresh_token: string;
expires_at: number;
}
export function storeDiscordTokens(userId: string, tokens: DiscordTokens): void {
if (!userId?.trim()) {
throw new Error('Invalid userId provided');
}
if (!tokens?.access_token || !tokens?.refresh_token) {
throw new Error('Invalid tokens object');
}
store.set(`discord-${userId}`, tokens);
}

Comment on lines +1 to +9
const store = new Map()

export async function storeDiscordTokens(userId: string, tokens: any) {
await store.set(`discord-${userId}`, tokens)
}

export async function getDiscordTokens(userId: string) {
return store.get(`discord-${userId}`)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Based on the package.json files and previous test file searches, I can now generate the final response:

Add unit tests for token storage operations

The Discord bot app currently lacks a testing setup, while the project has Vitest available in the root dependencies. You'll need to:

  • Set up a test directory in the Discord bot app
  • Configure Vitest for the Discord bot
  • Create storage test file covering:
    • Token storage/retrieval operations
    • Input validation
    • Error handling
    • Security measures

Example test file location: apps/discord-bot/src/__tests__/storage.test.ts

🔗 Analysis chain

Add unit tests for token storage operations

Based on the PR objectives, tests haven't been added yet. Token storage is critical functionality that requires thorough testing.

Would you like me to generate comprehensive unit tests covering:

  • Token storage success/failure cases
  • Token retrieval success/failure cases
  • Input validation
  • Error handling
  • Security measures

Let's check the testing setup in the project:


Let me check if there are any existing tests specifically for the Discord bot functionality.


Let me check if there's a testing framework setup in the discord-bot app or its parent directory.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Look for test files and testing utilities
fd -e test.ts -e spec.ts

# Check for testing framework configurations
fd -e jest.config -e vitest.config

Length of output: 309


Script:

#!/bin/bash
# Look for Discord-related test files that might have been missed
rg -l "discord" --type ts $(fd -e test.ts -e spec.ts)

# Check if there's a test directory in the discord-bot app
fd . apps/discord-bot -t d | grep -i "test"

# Look for any testing utilities or mocks related to storage
rg -l "storage" --type ts $(fd -e test.ts -e spec.ts)

Length of output: 674


Script:

#!/bin/bash
# Look for package.json in discord-bot app to check testing dependencies
cat apps/discord-bot/package.json

# Check for any test scripts or testing configurations in the root package.json
cat package.json

Length of output: 6297

* token. Check if the access token has expired, and if it has, use the
* refresh token to acquire a new, fresh access token.
*/
export async function getAccessToken(userId: any, tokens: any) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use a more specific type instead of 'any' for userId.
The parameter 'userId: any' can introduce type safety issues. Consider defining a more specific type, such as string or a custom user type, to avoid potential runtime errors.

Comment on lines +10 to +16
const config = {
DISCORD_TOKEN: process.env.DISCORD_TOKEN,
DISCORD_CLIENT_ID: process.env.DISCORD_CLIENT_ID,
DISCORD_CLIENT_SECRET: process.env.DISCORD_CLIENT_SECRET,
DISCORD_REDIRECT_URI: process.env.DISCORD_REDIRECT_URI,
COOKIE_SECRET: process.env.COOKIE_SECRET,
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add runtime validation of environment variables.
While loading them is straightforward, missing or invalid environment variables can cause subtler bugs downstream. A small validation step here to confirm values aren’t undefined or empty strings can save time diagnosing errors.

Comment on lines +73 to +75
```
$ node server.js
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add TypeScript-specific running instructions

Since this is a TypeScript project, please add compilation and development mode instructions:

+### Development
+
+To run in development mode with automatic recompilation:
+```bash
+npm run dev
+```
+
+### Production
+
+To build and run for production:
+```bash
+npm run build
+npm start
🧰 Tools
🪛 Markdownlint (0.37.0)

74-74: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


73-73: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


> ❇️ A version of this code is also hosted [on Glitch 🎏](https://glitch.com/edit/#!/linked-role-discord-bot)

## Project structure
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add testing documentation section

As mentioned in the PR objectives, testing documentation is required. Please add a new section that covers:

  • Test setup instructions
  • How to run tests
  • How to write new tests
  • Test coverage requirements

Example structure:

## Testing

### Setup
Instructions for setting up the test environment...

### Running Tests
```bash
npm test
npm run test:watch  # for development
npm run test:coverage  # to generate coverage report

Writing Tests

Guidelines for writing new tests...

Coverage Requirements

Minimum coverage thresholds and requirements...


<!-- This is an auto-generated comment by CodeRabbit -->

Comment on lines +12 to +22
├── assets -> Images used in this tutorial
├── src
│ ├── config.js -> Parsing of local configuration
│ ├── discord.js -> Discord specific auth & API wrapper
│ ├── register.js -> Tool to register the metadata schema
│ ├── server.js -> Main entry point for the application
│ ├── storage.js -> Provider for storing OAuth2 tokens
├── .env -> your credentials and IDs
├── .gitignore
├── package.json
└── README.md
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Update project structure to reflect TypeScript implementation

The project structure shows .js files, but according to the PR objectives, this is a TypeScript project. Please update the file extensions to .ts for accuracy:

 ├── assets          -> Images used in this tutorial
 ├── src
-│   ├── config.js   -> Parsing of local configuration
-│   ├── discord.js  -> Discord specific auth & API wrapper
-│   ├── register.js -> Tool to register the metadata schema
-│   ├── server.js   -> Main entry point for the application
-│   ├── storage.js  -> Provider for storing OAuth2 tokens
+│   ├── config.ts   -> Parsing of local configuration
+│   ├── discord.ts  -> Discord specific auth & API wrapper
+│   ├── register.ts -> Tool to register the metadata schema
+│   ├── server.ts   -> Main entry point for the application
+│   ├── storage.ts  -> Provider for storing OAuth2 tokens
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
├── assets -> Images used in this tutorial
├── src
│ ├── config.js -> Parsing of local configuration
│ ├── discord.js -> Discord specific auth & API wrapper
│ ├── register.js -> Tool to register the metadata schema
│ ├── server.js -> Main entry point for the application
│ ├── storage.js -> Provider for storing OAuth2 tokens
├── .env -> your credentials and IDs
├── .gitignore
├── package.json
└── README.md
├── assets -> Images used in this tutorial
├── src
│ ├── config.ts -> Parsing of local configuration
│ ├── discord.ts -> Discord specific auth & API wrapper
│ ├── register.ts -> Tool to register the metadata schema
│ ├── server.ts -> Main entry point for the application
│ ├── storage.ts -> Provider for storing OAuth2 tokens
├── .env -> your credentials and IDs
├── .gitignore
├── package.json
└── README.md

- git clone https://github.com/RileCraft/DiscordBot-Template-TS.git
apps/discord-bot
- delete .git & .github
- tidy up package.json
- pnpm lint:fix
- acts as the 3rd party app for the bot to communicate with
@JowiAoun JowiAoun force-pushed the jowi/chore/216-scaffold-community-server-discord-bot branch from ee33f55 to d63677a Compare December 24, 2024 20:56
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 32

♻️ Duplicate comments (8)
apps/discord-bot/src/structures/commandOptions/onlyGuilds.ts (1)

18-32: ⚠️ Potential issue

Fix error message formatting and grammar issues

The error handling has several issues that need to be addressed:

         .setDescription(
-          `The command you tried to execute cannot be ran in the current guild. Please execute the command in of these authorized guilds:\n${command.onlyGuilds.map((guildID: string) => `↳ <#${guildID}>`).join('\n')}`,
+          `The command you tried to execute cannot be run in the current guild. Please execute the command in one of these authorized guilds:\n${command.onlyGuilds.map((guildID: string) => `↳ ${client.guilds.cache.get(guildID)?.name ?? guildID}`).join('\n')}`,
         )
       ],
     })
-    return false
-  };
+    return false
+  }

Changes:

  1. Fixed grammar: "cannot be ran" → "cannot be run"
  2. Fixed typo: "in of these" → "in one of these"
  3. Replaced channel mentions with guild names
  4. Removed unreachable semicolon
🧰 Tools
🪛 Biome (1.9.4)

[error] 31-32: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/events/errorManager.ts (1)

8-13: ⚠️ Potential issue

Uncomment and enhance error handling implementation

The error handlers are currently commented out, leaving the bot without any error management. This could lead to silent failures or unexpected crashes in production.

Additional enhancement: Consider implementing a custom error handling class to centralize error management and add structured logging:

class ErrorManager {
  private static formatError(error: Error) {
    return {
      name: error.name,
      message: error.message,
      stack: error.stack,
      timestamp: new Date().toISOString()
    };
  }

  static handleUnhandledRejection(error: Error) {
    console.error('[Unhandled Rejection]', this.formatError(error));
    // TODO: Add error reporting service integration
  }

  static handleUncaughtException(error: Error) {
    console.error('[Uncaught Exception]', this.formatError(error));
    // TODO: Add error reporting service integration
    
    // Gracefully shutdown after uncaught exception
    process.exit(1);
  }
}

process.on('unhandledRejection', ErrorManager.handleUnhandledRejection);
process.on('uncaughtException', ErrorManager.handleUncaughtException);
apps/discord-bot/src/structures/managers/events.ts (1)

10-23: ⚠️ Potential issue

Enhance event registration with proper error handling and type safety

Combining previous review suggestions with additional improvements for robust event handling.

-  for (const event of eventFiles) {
+  for (const eventFile of eventFiles) {
+    try {
+      // Validate event file path
+      if (!eventFile.endsWith('.js') && !eventFile.endsWith('.ts')) {
+        console.warn(`Skipping invalid event file: ${eventFile}`);
+        continue;
+      }
+
-      const clientEvent: ClientEvent = (await import(`file:///${event}`))?.Event
+      const clientEvent: ClientEvent = (await import(eventFile))?.Event
+
+      // Validate event structure
+      if (!clientEvent || typeof clientEvent !== 'object') {
+        console.warn(`Invalid event export in ${eventFile}`);
+        continue;
+      }
+
       if (clientEvent.ignore)
         continue
 
       client.events?.set(clientEvent.name, clientEvent)
 
       if (clientEvent.customEvent)
-        clientEvent.run(client)
+        await clientEvent.run(client)
       else if (clientEvent.name && clientEvent.runOnce)
-        client.once(clientEvent.name, (...args: unknown[]) => clientEvent.run(...args, client))
+        client.once(clientEvent.name, async (...args: Parameters<typeof clientEvent.run>) => {
+          try {
+            await clientEvent.run(...args, client);
+          } catch (error) {
+            console.error(`Error in event ${clientEvent.name}:`, error);
+          }
+        })
       else if (clientEvent.name)
-        client.on(clientEvent.name, (...args: unknown[]) => clientEvent.run(...args, client))
+        client.on(clientEvent.name, async (...args: Parameters<typeof clientEvent.run>) => {
+          try {
+            await clientEvent.run(...args, client);
+          } catch (error) {
+            console.error(`Error in event ${clientEvent.name}:`, error);
+          }
+        })
+    } catch (error) {
+      console.error(`Failed to load event from ${eventFile}:`, error);
+    }
   };
apps/discord-bot/src/events/messageCreate.ts (2)

11-28: ⚠️ Potential issue

Replace forEach with for...of for async operations

Using forEach with async operations can lead to race conditions and doesn't properly handle promise rejections.

-    PREFIX.forEach(async (botPrefix: string) => {
+    for (const botPrefix of PREFIX) {
+      try {
         if (!message.content.startsWith(botPrefix))
-          return
+          continue;
         // ... rest of the code ...
         if (processedCommandOptionsChecker)
           await command.run(client, message, args)
+      } catch (error) {
+        console.error('Error processing command:', error);
+        // Consider sending an error message to the channel
+      }
-    })
+    }

18-18: ⚠️ Potential issue

Add input validation for command arguments

The current implementation splits arguments without validation, which could lead to security issues.

apps/discord-bot/src/structures/commandOptions/anyUserPermissions.ts (2)

8-10: ⚠️ Potential issue

Improve permission check error handling

The permission check could fail silently if message.member is null.


26-26: 🛠️ Refactor suggestion

Enhance permission display in error message

The current implementation shows raw permission bits, which are not user-friendly.

apps/discord-bot/src/structures/commandOptions/anyClientPermissions.ts (1)

8-10: 🛠️ Refactor suggestion

Improve permission check robustness

The permission check could be simplified and made more robust by using optional chaining consistently.

🧹 Nitpick comments (33)
apps/discord-bot/template-server/src/storage.ts (1)

1-1: Consider a more robust storage solution for production use

Using an in-memory Map for token storage has several limitations:

  1. Tokens are lost when the server restarts
  2. Not scalable across multiple instances
  3. No persistence

Consider implementing a storage interface that can be backed by different implementations:

interface TokenStorage {
  set(key: string, value: DiscordTokens): Promise<void>;
  get(key: string): Promise<DiscordTokens | null>;
}

class InMemoryStorage implements TokenStorage {
  private store: Map<string, DiscordTokens> = new Map();
  
  async set(key: string, value: DiscordTokens): Promise<void> {
    this.store.set(key, value);
  }
  
  async get(key: string): Promise<DiscordTokens | null> {
    return this.store.get(key) || null;
  }
}

This allows for easy switching between storage backends (Redis, PostgreSQL, etc.) while maintaining type safety.

apps/discord-bot/src/structures/commandOptions/onlyChannels.ts (2)

5-8: Add additional parameter validation

While the current validation is good, consider adding these safety checks:

  • Validate that client is not null
  • Ensure command object has required properties
 export function onlyChannelsFN(client: DiscordClient, message: Message | Interaction<'cached'>, command: AnyCommand) {
+  if (!client || !message || !command) return false;
   if (!command.onlyChannels || !Array.isArray(command.onlyChannels) || !message.guild)
     return true

18-29: Consider limiting the channel list length

The channel list in the error message could become very long if there are many authorized channels. Consider:

  • Limiting the number of channels shown
  • Adding a "and X more..." suffix if truncated
-        .setDescription(`The command you tried to execute cannot be ran in the current channel. Please execute the command in of these authorized channels:\n${command.onlyChannels.map(channelId => `↳ <#${channelId}>`).join('\n')}`)
+        .setDescription(`The command you tried to execute cannot be ran in the current channel. Please execute the command in of these authorized channels:\n${command.onlyChannels.slice(0, 5).map(channelId => `↳ <#${channelId}>`).join('\n')}${command.onlyChannels.length > 5 ? `\n...and ${command.onlyChannels.length - 5} more channels` : ''}`)
apps/discord-bot/src/structures/commandOptions/onlyGuilds.ts (3)

5-8: Consider improving readability of the validation condition

The compound condition could be more readable if split into multiple lines.

-  if (!command.onlyGuilds || !Array.isArray(command.onlyGuilds) || !message.guild)
+  if (
+    !command.onlyGuilds ||
+    !Array.isArray(command.onlyGuilds) ||
+    !message.guild
+  )

9-12: Remove redundant optional chaining and unnecessary else block

The code can be simplified:

  1. Optional chaining on message.guild?.id is redundant since we already checked !message.guild
  2. The else block is unnecessary since the previous block returns
-  if (command.onlyGuilds.includes(message.guild?.id)) {
+  if (command.onlyGuilds.includes(message.guild.id)) {
     return true
-  }
-  else {

1-32: Consider architectural improvements

  1. The error messages should be externalized for localization support.
  2. As mentioned in the PR objectives, unit tests should be added to verify the behavior of this function.

Would you like me to:

  1. Propose a structure for implementing localization?
  2. Generate unit tests for this function?
🧰 Tools
🪛 Biome (1.9.4)

[error] 31-32: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/tsconfig.json (3)

3-3: Consider enabling incremental compilation for faster builds

Setting "incremental": true can significantly improve build times in development by reusing results from previous compilations.

-    "incremental": false,
+    "incremental": true,

21-21: Enable unused parameters checking

Having noUnusedParameters disabled can lead to confusing code with unused parameters. Consider enabling it to maintain cleaner function signatures.

-    "noUnusedParameters": false,
+    "noUnusedParameters": true,

1-33: Add source map support for better debugging

Consider adding source map generation for better debugging experience, especially useful when running the Discord bot in development.

Add the following option to your compiler options:

   "compilerOptions": {
+    "sourceMap": true,
     // ... other options
   }
apps/discord-bot/src/structures/commandOptions/ownerOnly.ts (3)

6-8: Improve readability of the initial command check

The initial check is correct but could be more explicit about its intent.

-  if (!command.ownerOnly || typeof command.ownerOnly !== 'boolean')
+  if (!command.ownerOnly || typeof command.ownerOnly !== 'boolean') {
+    // Command is not restricted to owners
     return true
+  }

18-30: Add error handling for embed creation

The embed creation could fail if user data is inaccessible.

-    message.channel.send({
+    try {
+      await message.channel.send({
       embeds: [
         new EmbedBuilder()
           .setColor('DarkRed')
           .setTimestamp()
           .setAuthor({
             name: message.member?.user.globalName ?? message.member?.user.username ?? '',
             iconURL: message.member?.user.displayAvatarURL(),
           })
           .setThumbnail(client.user.displayAvatarURL())
           .setDescription('The command you tried to run is __restricted__ for the developers of this bot and thus the command failed to execute.'),
       ],
-    })
+      })
+    } catch (error) {
+      console.error('Failed to send owner-only error message:', error)
+    }

31-33: Remove unnecessary semicolon and fix code structure

The trailing semicolon is unnecessary, and the code structure can be improved.

-    return false
-  };
+  return false
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 32-32: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/README.md (1)

82-86: Improve clarity in technical notes

-Guild commands may take time to refresh if there are a large number of different guild commands.
+Guild commands may take time to refresh if there are many different guild commands.
🧰 Tools
🪛 LanguageTool

[style] ~86-~86: Specify a number, remove phrase, or simply use “many” or “numerous”
Context: ...s may take time to refresh if there are a large number of different guild commands. - Collections...

(LARGE_NUMBER_OF)

apps/discord-bot/src/structures/commandOptions/onlyUsers.ts (3)

5-8: Consider adding explicit type guard for onlyUsers array.

While the current check works, it could be more explicit about the expected type.

-  if (!command.onlyUsers || !Array.isArray(command.onlyUsers))
+  if (!Array.isArray(command.onlyUsers) || command.onlyUsers.length === 0)

18-31: Enhance error message and add null checks.

A few suggestions to improve the error handling:

  1. Add null check for client.user
  2. Provide more specific error message
  3. Remove unnecessary semicolon
     message.channel.send({
       embeds: [new EmbedBuilder()
         .setColor('DarkRed')
         .setTimestamp()
         .setAuthor({
           name: message.member?.user.globalName ?? message.member?.user.username ?? '',
           iconURL: message.member?.user.displayAvatarURL(),
         })
-        .setThumbnail(client.user.displayAvatarURL())
+        .setThumbnail(client.user?.displayAvatarURL())
         .setDescription('The command you tried to execute couldn\'t be ran as you are not one of the authorized users.'),
       ],
     })
-    return false
-  };
+    return false;

Also consider enhancing the error message to be more specific:

-        .setDescription('The command you tried to execute couldn\'t be ran as you are not one of the authorized users.'),
+        .setDescription(`You don't have permission to use the \`${command.name}\` command. This command is restricted to specific users.`),
🧰 Tools
🪛 Biome (1.9.4)

[error] 31-31: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)


1-32: Consider enhancing the permission system architecture.

A few architectural suggestions:

  1. Consider integrating with a broader permission system that can handle role-based access control (RBAC) alongside user-specific permissions
  2. Add validation for user ID format to prevent invalid IDs in the onlyUsers array
  3. Consider adding an interface for command options to better document the expected structure of onlyUsers, returnErrors, and returnOnlyUsersError

Would you like me to provide an example implementation of these architectural improvements?

🧰 Tools
🪛 Biome (1.9.4)

[error] 31-31: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/structures/commandOptions/onlyRoles.ts (1)

9-12: Optimize the role checking logic and remove unnecessary else block.

The role checking logic is correct but can be simplified by removing the unnecessary else block.

  if (command.onlyRoles.some((roleID: string) => message.member?.roles.cache.has(roleID))) {
    return true
  }
- else {
apps/discord-bot/src/events/ready.ts (2)

8-15: Add error handling for activity setup.

While the activity setup is correct, it should handle potential errors when setting the activity status.

-    client.user?.setActivity('Humans.', {
-      type: ActivityType.Watching,
-    })
+    try {
+      await client.user?.setActivity('Humans.', {
+        type: ActivityType.Watching,
+      })
+    } catch (error) {
+      console.error('Failed to set activity status:', error)
+    }

8-61: Consider architectural improvements for better maintainability.

  1. Extract command loading logic into a separate service class for better separation of concerns and testability.
  2. Consider implementing dependency injection for the client and configuration.
  3. Add interfaces for command loaders to make them pluggable and easier to test.

Example service structure:

interface CommandLoader {
  loadCommands(): Promise<string[]>;
}

class SlashCommandLoader implements CommandLoader {
  constructor(private rootPath: string) {}
  async loadCommands(): Promise<string[]> {
    // Implementation
  }
}

class ReadyEventHandler {
  constructor(
    private client: DiscordClient,
    private slashCommandLoader: CommandLoader,
    private contextMenuLoader: CommandLoader
  ) {}
  
  async handle(): Promise<void> {
    // Implementation
  }
}
apps/discord-bot/template-server/src/register.ts (1)

3-6: Documentation needs enhancement

While the documentation explains the purpose, it should also mention:

  • The expected format of the metadata
  • The significance of the metadata types
  • The impact of re-running this registration
 /**
  * Register the metadata to be stored by Discord. This should be a one time action.
  * Note: uses a Bot token for authentication, not a user token.
+ * 
+ * @see https://discord.com/developers/docs/resources/application-role-connection-metadata
+ * 
+ * @remarks
+ * - This updates the metadata schema for all users
+ * - Re-running will override existing metadata configuration
+ * - Types: number_lt(1), number_gt(2), number_eq(3), number_neq(4),
+ *         datetime_lt(5), datetime_gt(6), boolean_eq(7), boolean_neq(8)
  */
apps/discord-bot/template-server/README.md (1)

117-117: Fix markdown formatting issues

Several markdown formatting issues need to be addressed:

  1. Use proper markdown link syntax for bare URLs
  2. Add language identifiers to code blocks
  3. Remove $ from command examples
-Browse https://github.com/JustinBeckwith/fitbit-discord-bot/ for a more in-depth example
+Browse [fitbit-discord-bot](https://github.com/JustinBeckwith/fitbit-discord-bot/) for a more in-depth example

Also update code blocks to include language identifiers:

-```
+```bash
 $ node server.js

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Markdownlint (0.37.0)</summary>

117-117: null
Bare URL used

(MD034, no-bare-urls)

</details>

</details>

</blockquote></details>
<details>
<summary>apps/discord-bot/src/structures/managers/modalForms.ts (1)</summary><blockquote>

`5-5`: **Add JSDoc documentation for the ModalManager function**

Consider adding comprehensive JSDoc documentation to describe the function's purpose, parameters, and usage.

```diff
+/**
+ * Initializes and loads modal form handlers for the Discord bot
+ * @param client - The Discord client instance to register modal forms with
+ * @param rootPath - The root directory path where modal form files are located
+ * @throws {Error} When modal form files cannot be loaded or are invalid
+ */
 export async function ModalManager(client: DiscordClient, rootPath: string): Promise<void> {
apps/discord-bot/src/structures/commandOptions/anyClientPermissions.ts (1)

1-31: Consider extracting shared permission checking logic

This file shares significant code with anyUserPermissions.ts. Consider extracting common functionality into a shared utility.

Create a new utility file permissionUtils.ts:

import { DiscordClient, Interaction, Message, EmbedBuilder } from 'discord.js'
import { AnyCommand } from '../../types.js'

export function createPermissionErrorEmbed(
  client: DiscordClient,
  message: Message | Interaction<'cached'>,
  permissions: bigint[],
  errorMessage: string
): EmbedBuilder {
  return new EmbedBuilder()
    .setColor('DarkRed')
    .setTimestamp()
    .setAuthor({
      name: message.member?.user.globalName ?? message.member?.user.username ?? '',
      iconURL: message.member?.user.displayAvatarURL(),
    })
    .setThumbnail(client.user.displayAvatarURL())
    .setDescription(errorMessage)
}

This would reduce code duplication and make maintenance easier.

apps/discord-bot/src/structures/commandOptions/allUserPermissions.ts (2)

8-8: Simplify permission check with optional chaining

The permission check can be simplified while maintaining type safety.

-  const missingPermissions: Array<PermissionsString> | undefined = message.member?.permissions.missing(command.allUserPermissions)
+  const missingPermissions = message.member?.permissions?.missing(command.allUserPermissions)

24-27: Enhance user identification robustness

The user identification logic could be more robust by adding a fallback for the display name.

          .setAuthor({
-            name: message.member?.user.globalName ?? message.member?.user.username ?? '',
+            name: message.member?.user.globalName ?? message.member?.user.username ?? message.member?.user.tag ?? 'Unknown User',
            iconURL: message.member?.user.displayAvatarURL(),
          })
apps/discord-bot/src/events/interactionCreate.ts (1)

66-66: Remove unnecessary semicolon

The semicolon after the closing brace of the modal submit block is unnecessary.

    };
-  };
+  }
apps/discord-bot/src/types.ts (5)

5-17: Enhance module augmentation documentation

The comment could be more descriptive about the purpose of extending the Client interface.

Consider replacing the existing comment with:

-}; // Extends Discord.js Client to allow for the Map caches.
+}; // Extends Discord.js Client to add command registries and cooldown management capabilities

19-47: Enhance type safety and organization of CommandOptions

The interface could benefit from the following improvements:

  1. Use more specific types for permissions
  2. Group related properties using nested interfaces

Consider applying these changes:

import { PermissionsBitField } from 'discord.js';

interface CooldownOptions {
  channelCooldown?: number;
  globalCooldown?: number;
  guildCooldown?: number;
}

interface PermissionOptions {
  allClientPermissions?: Array<keyof typeof PermissionsBitField.Flags>;
  allUserPermissions?: Array<keyof typeof PermissionsBitField.Flags>;
  anyClientPermissions?: Array<keyof typeof PermissionsBitField.Flags>;
  anyUserPermissions?: Array<keyof typeof PermissionsBitField.Flags>;
}

interface TargetingOptions {
  allowInDms?: boolean;
  onlyChannels?: Array<string>;
  onlyGuilds?: Array<string>;
  onlyRoles?: Array<string>;
  onlyUsers?: Array<string>;
  ownerOnly?: boolean;
}

export interface CommandOptions extends 
  CooldownOptions,
  PermissionOptions,
  TargetingOptions {
  // Error handling options remain the same
  returnErrors?: boolean;
  // ...
}

49-114: Add JSDoc documentation for command interfaces

The command interfaces would benefit from detailed documentation explaining their purpose and usage.

Consider adding JSDoc comments like this:

/**
 * Represents a message-based command that is triggered by a prefix.
 * @example
 * const pingCommand: MessageCommand = {
 *   name: 'ping',
 *   run: (client, message) => {
 *     message.reply('Pong!');
 *   }
 * };
 */
export interface MessageCommand extends CommandOptions {
  // ... existing properties
}

115-116: Derive InteractionTypeOptions from AnyCommand type

To maintain consistency and reduce potential errors, consider deriving the interaction type options from the AnyCommand type.

Apply this change:

export type AnyCommand = MessageCommand | ButtonCommand | SelectMenu | ModalForm | SlashCommand | ContextMenu;

// Derive from AnyCommand to ensure consistency
export type InteractionTypeOptions = {
  [K in AnyCommand as K extends any ? K['constructor']['name'] : never]: K;
}[keyof AnyCommand];

1-116: Well-structured type system for Discord bot

The overall architecture provides a robust foundation for type-safe Discord bot development. The interfaces are well-designed and extensible, allowing for easy addition of new command types in the future.

Consider creating a separate documentation file (e.g., ARCHITECTURE.md) explaining the type system and providing examples of how to implement each command type.

apps/discord-bot/src/bot.ts (1)

5-5: Consider using a more robust database for cooldown management

Using simple-json-db for cooldown management might lead to scalability issues in production. Consider using Redis or a similar in-memory database for better performance and reliability with cooldowns.

Also applies to: 35-35

apps/discord-bot/src/structures/managers/slashCommands.ts (1)

11-27: Add JSDoc documentation for interfaces

The interfaces are well-structured but lack documentation. Consider adding JSDoc comments to improve maintainability:

+/**
+ * Represents a global Discord application command
+ * @interface GlobalCommandArray
+ */
 interface GlobalCommandArray {
   name: string
   nsfw?: boolean
   type: ApplicationCommandType
   description?: string
   options?: Array<SlashCommandOptions>
 };

+/**
+ * Represents guild-specific Discord application commands
+ * @interface GuildCommandObjects
+ */
 interface GuildCommandObjects {
   [key: string]: Array<{
     name: string
     nsfw?: boolean
     description?: string
     type: ApplicationCommandType
     options?: Array<SlashCommandOptions>
   }>
 };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee33f55 and d63677a.

⛔ Files ignored due to path filters (1)
  • apps/discord-bot/template-server/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (53)
  • apps/discord-bot/.gitignore (1 hunks)
  • apps/discord-bot/LICENSE (1 hunks)
  • apps/discord-bot/README.md (1 hunks)
  • apps/discord-bot/nodemon.json (1 hunks)
  • apps/discord-bot/package.json (1 hunks)
  • apps/discord-bot/src/bot.ts (1 hunks)
  • apps/discord-bot/src/config.ts (1 hunks)
  • apps/discord-bot/src/events/errorManager.ts (1 hunks)
  • apps/discord-bot/src/events/interactionCreate.ts (1 hunks)
  • apps/discord-bot/src/events/messageCreate.ts (1 hunks)
  • apps/discord-bot/src/events/ready.ts (1 hunks)
  • apps/discord-bot/src/interactions/buttons/deleteOutput.ts (1 hunks)
  • apps/discord-bot/src/interactions/contextMenus/getUsername.ts (1 hunks)
  • apps/discord-bot/src/interactions/modalForms/exampleModal.ts (1 hunks)
  • apps/discord-bot/src/interactions/selectMenus/selectMenuExample.ts (1 hunks)
  • apps/discord-bot/src/interactions/slashCommands/exampleModal.ts (1 hunks)
  • apps/discord-bot/src/interactions/slashCommands/ping.ts (1 hunks)
  • apps/discord-bot/src/messageCommands/callSelectMenu.ts (1 hunks)
  • apps/discord-bot/src/messageCommands/eval.ts (1 hunks)
  • apps/discord-bot/src/messageCommands/ping.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/allClientPermissions.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/allUserPermissions.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/anyClientPermissions.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/anyUserPermissions.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/channelCooldown.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/globalCooldown.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/guildCooldown.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/onlyChannels.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/onlyGuilds.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/onlyRoles.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/onlyUsers.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/ownerOnly.ts (1 hunks)
  • apps/discord-bot/src/structures/commandOptions/processor.ts (1 hunks)
  • apps/discord-bot/src/structures/managers/buttonCommands.ts (1 hunks)
  • apps/discord-bot/src/structures/managers/events.ts (1 hunks)
  • apps/discord-bot/src/structures/managers/messageCommands.ts (1 hunks)
  • apps/discord-bot/src/structures/managers/modalForms.ts (1 hunks)
  • apps/discord-bot/src/structures/managers/selectMenus.ts (1 hunks)
  • apps/discord-bot/src/structures/managers/slashCommands.ts (1 hunks)
  • apps/discord-bot/src/types.ts (1 hunks)
  • apps/discord-bot/src/utils/fileReader.ts (1 hunks)
  • apps/discord-bot/template-server/.gitignore (1 hunks)
  • apps/discord-bot/template-server/README.md (1 hunks)
  • apps/discord-bot/template-server/package.json (1 hunks)
  • apps/discord-bot/template-server/src/config.ts (1 hunks)
  • apps/discord-bot/template-server/src/discord.ts (1 hunks)
  • apps/discord-bot/template-server/src/register.ts (1 hunks)
  • apps/discord-bot/template-server/src/server.ts (1 hunks)
  • apps/discord-bot/template-server/src/storage.ts (1 hunks)
  • apps/discord-bot/tsconfig.json (1 hunks)
  • libs/external/aceternity/tailwind.config.ts (1 hunks)
  • libs/website/feature/faq/index.ts (1 hunks)
  • libs/website/feature/stats/index.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (26)
  • apps/discord-bot/template-server/.gitignore
  • apps/discord-bot/src/interactions/modalForms/exampleModal.ts
  • apps/discord-bot/LICENSE
  • apps/discord-bot/.gitignore
  • libs/website/feature/faq/index.ts
  • apps/discord-bot/src/interactions/selectMenus/selectMenuExample.ts
  • apps/discord-bot/src/interactions/slashCommands/ping.ts
  • apps/discord-bot/template-server/src/config.ts
  • apps/discord-bot/src/interactions/buttons/deleteOutput.ts
  • apps/discord-bot/src/interactions/contextMenus/getUsername.ts
  • libs/website/feature/stats/index.ts
  • libs/external/aceternity/tailwind.config.ts
  • apps/discord-bot/src/config.ts
  • apps/discord-bot/src/interactions/slashCommands/exampleModal.ts
  • apps/discord-bot/nodemon.json
  • apps/discord-bot/src/utils/fileReader.ts
  • apps/discord-bot/src/messageCommands/callSelectMenu.ts
  • apps/discord-bot/src/structures/managers/selectMenus.ts
  • apps/discord-bot/template-server/package.json
  • apps/discord-bot/src/structures/commandOptions/processor.ts
  • apps/discord-bot/src/messageCommands/ping.ts
  • apps/discord-bot/template-server/src/server.ts
  • apps/discord-bot/src/structures/managers/messageCommands.ts
  • apps/discord-bot/src/messageCommands/eval.ts
  • apps/discord-bot/package.json
  • apps/discord-bot/template-server/src/discord.ts
🧰 Additional context used
🪛 Biome (1.9.4)
apps/discord-bot/src/structures/commandOptions/onlyRoles.ts

[error] 31-32: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/structures/commandOptions/allClientPermissions.ts

[error] 33-34: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/structures/commandOptions/onlyGuilds.ts

[error] 31-32: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/structures/commandOptions/ownerOnly.ts

[error] 32-32: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/structures/commandOptions/allUserPermissions.ts

[error] 33-34: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/structures/commandOptions/onlyChannels.ts

[error] 31-32: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/structures/commandOptions/onlyUsers.ts

[error] 31-31: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

🪛 LanguageTool
apps/discord-bot/template-server/README.md

[style] ~8-~8: Consider removing “of” to be more concise
Context: ...ole-discord-bot) ## Project structure All of the files for the project are on the left-h...

(ALL_OF_THE)

apps/discord-bot/README.md

[uncategorized] ~19-~19: Did you mean “I”?
Context: ...ple-json-dbfor the cooldown system as i rage quit and can't do it withfs` mys...

(I_LOWERCASE_PREMIUM)


[misspelling] ~19-~19: This word is normally spelled with a hyphen.
Context: ...e-json-dbfor the cooldown system as i rage quit and can't do it withfs` myself. - Fix...

(EN_COMPOUNDS_RAGE_QUIT)


[style] ~78-~78: The phrase “a variety of” may be wordy. To make your writing clearer, consider replacing it.
Context: ...text menus, and modal forms. - Includes a variety of commonly used command options (not appl...

(A_VARIETY_OF)


[style] ~86-~86: Specify a number, remove phrase, or simply use “many” or “numerous”
Context: ...s may take time to refresh if there are a large number of different guild commands. - Collections...

(LARGE_NUMBER_OF)

🪛 Markdownlint (0.37.0)
apps/discord-bot/template-server/README.md

74-74: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


80-80: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


90-90: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


117-117: null
Bare URL used

(MD034, no-bare-urls)


11-11: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


35-35: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


41-41: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


50-50: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


60-60: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


73-73: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


79-79: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


89-89: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


95-95: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)

apps/discord-bot/README.md

2-2: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


3-3: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


4-4: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


5-5: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


6-6: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


7-7: null
Images should have alternate text (alt text)

(MD045, no-alt-text)

🔇 Additional comments (37)
apps/discord-bot/template-server/src/storage.ts (1)

3-5: Previous feedback about type safety and error handling still applies

The implementation needs the improvements suggested in the previous review regarding:

  1. Replacing any with a proper interface
  2. Adding error handling
  3. Removing unnecessary async/await
  4. Adding input validation
apps/discord-bot/src/structures/commandOptions/guildCooldown.ts (3)

9-9: Sanitize database key to prevent potential injection issues.

This code concatenates user-controlled inputs (guild ID, interaction type, command name, member ID) into the DB key without sanitizing or limiting special characters (e.g., dots). Consider replacing dots or other special characters in user input to avoid key collisions or injection vulnerabilities.


13-14: Add error handling on cooldown DB write.

Code silently fails if the database write operation throws an error, which may lead to inconsistent cooldown states. Wrap the DB access in a try/catch block and log or handle errors gracefully.


5-8: Consider adding test coverage and verifying time precision.

  1. Test Coverage: These lines handle guild-specific cooldown logic and user notification, but they have no test coverage. You could create unit tests to validate command eligibility, cooldown expiration, and error message logic.
  2. Time Precision: Using Math.floor for time difference is fine, but confirm that small differences in milliseconds are acceptable for your application. If higher precision is required, you might store or compare times at full precision.

Also applies to: 23-35

apps/discord-bot/src/structures/commandOptions/globalCooldown.ts (3)

11-11: Check for uninitialized database.

Accessing client.cooldownDB?.get is safe in principle, but you should confirm that cooldownDB is always defined before usage. If it's undefined, the cooldown data might never be stored, causing unintended command execution behavior.


9-9: Sanitize the DB key and add error handling.

  1. DB Key Construction: The concatenation of user inputs into the DB key may lead to collisions or injection if certain characters (e.g., dots) are used.
  2. Error Handling: Wrap the DB write in try/catch so the application can log or recover from database errors.

Also applies to: 13-14


5-8: Add tests for global cooldown flow.

Ensure coverage for:

  • Normal vs. expired cooldown scenarios.
  • Missing or invalid cooldown values.
  • Error/notification behavior.

This helps verify logic correctness and user-facing cooldown messages.

Also applies to: 23-35

apps/discord-bot/src/structures/commandOptions/channelCooldown.ts (3)

9-9: Fix the typo in the database key.

There is a spelling error "channelCoolown" instead of "channelCooldown", which can break cooldown tracking.

-  const dbData = `channelCoolown.${message.channel?.id}.${interactionType}.${command.name}.${message.member?.id}`
+  const dbData = `channelCooldown.${message.channel?.id}.${interactionType}.${command.name}.${message.member?.id}`

5-8: Extract common cooldown logic to a shared utility.

The logic here is nearly identical to the guild/global cooldown approach. Refactor to reduce code duplication by creating a reusable helper function to handle cooldown checks and notification for all scopes (channel, guild, global).

Also applies to: 13-14


23-35: Add channel-specific cooldown tests.

Consider testing:

  1. Whether the channel cooldown is enforced as expected in different channels or guild contexts.
  2. How errors or messages are displayed when a cooldown is active.
apps/discord-bot/src/structures/commandOptions/onlyChannels.ts (4)

1-4: LGTM! Clean and well-organized imports

The imports are properly organized with type imports separated from regular imports.


9-17: Simplify control flow structure

The nested if-else blocks make the code harder to follow. Consider flattening the structure as suggested in the previous review.


31-31: Remove unnecessary semicolon causing unreachable code

The semicolon after the closing brace is unnecessary and causes a linting error.

🧰 Tools
🪛 Biome (1.9.4)

[error] 31-32: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)


5-32: Add comprehensive tests for permission checks

This function handles critical channel access control. Comprehensive testing is essential as mentioned in the previous review.

🧰 Tools
🪛 Biome (1.9.4)

[error] 31-32: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/structures/commandOptions/onlyGuilds.ts (1)

1-4: LGTM! Clean import organization

The imports are well-organized, separating type imports from value imports.

apps/discord-bot/tsconfig.json (3)

4-8: LGTM: Basic compilation settings are well configured

The basic compilation settings are appropriate for a modern Node.js application:

  • Using ESNext target allows for modern JavaScript features
  • Node16 module system is suitable for current LTS versions
  • JSON module resolution is correctly enabled

9-13: LGTM: Strong type checking configuration

The strict type checking options are well configured to catch potential issues early in development:

  • Strict mode and its related flags are enabled
  • No implicit any types
  • Proper handling of this context
  • Override checking enabled

Also applies to: 15-17, 19-20, 22-22


23-31: LGTM: Output and compatibility settings are appropriate

The output and compatibility settings are well configured:

  • Declaration files generation is enabled with source maps
  • Proper error handling with noEmitOnError
  • Good module interop settings
  • Consistent file casing enforced
apps/discord-bot/src/structures/commandOptions/ownerOnly.ts (2)

1-5: LGTM! Imports are well-organized and complete.

All necessary types and utilities are properly imported.


9-11: Improve type safety in user ID extraction

The nested nullish coalescing and type casting could be simplified and made more type-safe.

apps/discord-bot/src/structures/commandOptions/onlyUsers.ts (1)

1-4: LGTM! Imports are well-organized.

The imports are properly typed and logically ordered, with type imports preceding concrete imports.

apps/discord-bot/src/structures/commandOptions/onlyRoles.ts (4)

1-4: LGTM! Imports are well-organized and properly typed.


5-8: LGTM! Well-structured function signature with proper type guards.

The early returns effectively handle edge cases and null checks.


27-27: Fix incorrect role mention format

The role mention format should use @& instead of #.

-        .setDescription(`The command you tried to execute couldn't be executed as you are missing one of these required roles:\n${command.onlyRoles.map((roleID: string) => `↳ <#${roleID}>`).join('\n')}`),
+        .setDescription(`The command you tried to execute couldn't be executed as you are missing one of these required roles:\n${command.onlyRoles.map((roleID: string) => `↳ <@&${roleID}>`).join('\n')}`),

5-31: Consider enhancing role validation and error message customization

A few architectural improvements to consider:

  1. Add role resolution validation to ensure the specified role IDs exist in the guild
  2. Allow customization of error messages through command options
  3. Consider implementing role hierarchies or role combinations for more complex permission scenarios

Let's verify if the roles are properly validated before use:

apps/discord-bot/src/events/ready.ts (3)

1-7: LGTM! Clean and well-organized imports.

The imports are properly organized with type imports separated, and all necessary dependencies are included.


16-23: Previous review comments about slash commands loading still apply.

The implementation still has type safety and error handling issues that were identified in the previous review.


25-32: Previous review comments about context menus loading still apply.

The implementation has the same issues as the slash commands loading that were identified in the previous review.

apps/discord-bot/template-server/src/register.ts (1)

32-54: LGTM: Error handling implementation

The error handling is well-implemented with:

  • Proper HTTP response status checking
  • Detailed error messages
  • Error propagation with context
apps/discord-bot/template-server/README.md (3)

11-22: Update project structure to reflect TypeScript implementation

The project structure shows .js files, but this is a TypeScript project.

🧰 Tools
🪛 Markdownlint (0.37.0)

11-11: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


73-75: Add TypeScript-specific running instructions

Since this is a TypeScript project, please add compilation and development mode instructions.

🧰 Tools
🪛 Markdownlint (0.37.0)

74-74: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


73-73: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


7-7: Add testing documentation section

As mentioned in the PR objectives, testing documentation is required.

apps/discord-bot/src/structures/managers/modalForms.ts (1)

1-3: LGTM! Well-structured imports

The imports are properly typed and follow a logical organization pattern.

apps/discord-bot/src/structures/commandOptions/allUserPermissions.ts (2)

19-20: Remove redundant optional chaining

The optional chaining on message?.channel is unnecessary since the channel existence is already checked in the if condition above.

-    message?.channel.send({
+    message.channel.send({

33-34: Remove unreachable semicolon

The semicolon after the closing brace is unreachable and should be removed.

    return false
-  };
+  }
🧰 Tools
🪛 Biome (1.9.4)

[error] 33-34: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

apps/discord-bot/src/structures/commandOptions/allClientPermissions.ts (1)

8-8: Simplify guild member access chain

The optional chaining in the guild members access is redundant since we already check for guild existence.

-  const missingPermissions: Array<PermissionsString> | undefined = message.guild?.members?.me?.permissions.missing(command.allClientPermissions)
+  const missingPermissions: Array<PermissionsString> | undefined = message.guild.members.me?.permissions.missing(command.allClientPermissions)
apps/discord-bot/src/bot.ts (1)

37-44: LGTM! Well-structured command storage

Good use of TypeScript Maps for different command types, maintaining type safety and clear separation of concerns.

Comment on lines +7 to +9
export async function getDiscordTokens(userId: string) {
return store.get(`discord-${userId}`)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add token expiration checks

The function should verify token expiration before returning stored tokens. This prevents usage of expired tokens and ensures timely refresh.

Add expiration checks:

interface DiscordTokens {
  access_token: string;
  refresh_token: string;
  expires_at: number;
}

export async function getDiscordTokens(userId: string): Promise<DiscordTokens | null> {
  const tokens = store.get(`discord-${userId}`) as DiscordTokens | null;
  
  if (!tokens) return null;
  
  // Check if token is expired (with 5 minute buffer)
  if (Date.now() >= tokens.expires_at - 300000) {
    // Token is expired or expiring soon
    // TODO: Implement token refresh logic
    return null;
  }
  
  return tokens;
}

"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitOverride": true,
"noImplicitReturns": false,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enable implicit returns checking for better type safety

Having noImplicitReturns disabled means functions might have code paths that don't return a value, potentially leading to runtime errors.

-    "noImplicitReturns": false,
+    "noImplicitReturns": true,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"noImplicitReturns": false,
"noImplicitReturns": true,

"strictNullChecks": true,
"strictPropertyInitialization": true,
"allowUnreachableCode": false,
"allowUnusedLabels": true,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Disable unused labels for cleaner code

Having allowUnusedLabels enabled can lead to messy code with redundant labels. Consider disabling it to enforce cleaner code practices.

-    "allowUnusedLabels": true,
+    "allowUnusedLabels": false,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"allowUnusedLabels": true,
"allowUnusedLabels": false,

Comment on lines +12 to +17
else {
if (command.returnErrors === false || command.returnOwnerOnlyError === false)
return false
if (!message.channel || message.channel.type !== ChannelType.GuildText)
return false

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Simplify error handling logic

The nested conditions can be flattened to improve readability and maintainability.

-  else {
-    if (command.returnErrors === false || command.returnOwnerOnlyError === false)
-      return false
-    if (!message.channel || message.channel.type !== ChannelType.GuildText)
-      return false
+  if (command.returnErrors === false || command.returnOwnerOnlyError === false) {
+    return false
+  }
+  if (!message.channel || message.channel.type !== ChannelType.GuildText) {
+    return false
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
else {
if (command.returnErrors === false || command.returnOwnerOnlyError === false)
return false
if (!message.channel || message.channel.type !== ChannelType.GuildText)
return false
if (command.returnErrors === false || command.returnOwnerOnlyError === false) {
return false
}
if (!message.channel || message.channel.type !== ChannelType.GuildText) {
return false
}

Comment on lines +4 to +7
<a href="https://discord.gg/VStdRr8nP2"><img src="https://img.shields.io/badge/discord-invite-5865f2?style=for-the-badge&logo=discord&logoColor=white"></a>
<img src="https://img.shields.io/github/issues/RileCraft/DiscordBot-Template-ts.svg?style=for-the-badge">
<img src="https://img.shields.io/github/forks/RileCraft/DiscordBot-Template-ts.svg?style=for-the-badge">
<img src="https://img.shields.io/github/stars/RileCraft/DiscordBot-Template-ts.svg?style=for-the-badge">
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Update repository links for this project

The badge links still point to the original template repository (RileCraft/DiscordBot-Template-ts). These should be updated to point to this project's repository.

🧰 Tools
🪛 Markdownlint (0.37.0)

4-4: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


5-5: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


6-6: null
Images should have alternate text (alt text)

(MD045, no-alt-text)


7-7: null
Images should have alternate text (alt text)

(MD045, no-alt-text)

Comment on lines +19 to +31
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.GuildPresences,
GatewayIntentBits.DirectMessages,
GatewayIntentBits.MessageContent, // Only for bots with message content intent access.
GatewayIntentBits.DirectMessageReactions,
GatewayIntentBits.GuildMembers,
GatewayIntentBits.GuildMessageReactions,
GatewayIntentBits.GuildWebhooks,
GatewayIntentBits.GuildVoiceStates,
GatewayIntentBits.GuildInvites,
],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Optimize Discord intents for security

The bot is requesting more intents than might be necessary, including the privileged MessageContent intent. Consider:

  1. Removing unnecessary intents to follow the principle of least privilege
  2. Documenting why each intent is needed
  3. Ensuring the bot has approval for privileged intents
 intents: [
   GatewayIntentBits.Guilds,
-  GatewayIntentBits.GuildMessages,
-  GatewayIntentBits.GuildPresences,
-  GatewayIntentBits.DirectMessages,
-  GatewayIntentBits.MessageContent, // Only for bots with message content intent access.
-  GatewayIntentBits.DirectMessageReactions,
   GatewayIntentBits.GuildMembers,
-  GatewayIntentBits.GuildMessageReactions,
-  GatewayIntentBits.GuildWebhooks,
   GatewayIntentBits.GuildVoiceStates,
-  GatewayIntentBits.GuildInvites,
 ],

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +46 to +53
await MessageCMDManager(client, __dirname)
await EventManager(client, __dirname)
await ButtonManager(client, __dirname)
await SelectMenuManager(client, __dirname)
await ModalManager(client, __dirname)
await client.login(BOT_TOKEN)
await SlashManager(client, __dirname) // Includes context menu handling as they belong to same command type.
})()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling and startup logging

The initialization sequence lacks error handling and logging. Consider:

  1. Adding try-catch blocks for each manager initialization
  2. Adding startup logging for better observability
  3. Implementing a graceful shutdown mechanism
+  try {
     await MessageCMDManager(client, __dirname)
+    console.log('Message commands loaded')
     await EventManager(client, __dirname)
+    console.log('Events loaded')
     await ButtonManager(client, __dirname)
+    console.log('Button commands loaded')
     await SelectMenuManager(client, __dirname)
+    console.log('Select menus loaded')
     await ModalManager(client, __dirname)
+    console.log('Modal forms loaded')
     await client.login(BOT_TOKEN)
+    console.log('Bot logged in successfully')
     await SlashManager(client, __dirname)
+    console.log('Slash commands loaded')
+  } catch (error) {
+    console.error('Failed to initialize bot:', error)
+    process.exit(1)
+  }
+
+  process.on('SIGTERM', () => {
+    console.log('Received SIGTERM, shutting down gracefully')
+    client.destroy()
+    process.exit(0)
+  })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await MessageCMDManager(client, __dirname)
await EventManager(client, __dirname)
await ButtonManager(client, __dirname)
await SelectMenuManager(client, __dirname)
await ModalManager(client, __dirname)
await client.login(BOT_TOKEN)
await SlashManager(client, __dirname) // Includes context menu handling as they belong to same command type.
})()
try {
await MessageCMDManager(client, __dirname)
console.log('Message commands loaded')
await EventManager(client, __dirname)
console.log('Events loaded')
await ButtonManager(client, __dirname)
console.log('Button commands loaded')
await SelectMenuManager(client, __dirname)
console.log('Select menus loaded')
await ModalManager(client, __dirname)
console.log('Modal forms loaded')
await client.login(BOT_TOKEN)
console.log('Bot logged in successfully')
await SlashManager(client, __dirname) // Includes context menu handling as they belong to same command type.
console.log('Slash commands loaded')
} catch (error) {
console.error('Failed to initialize bot:', error)
process.exit(1)
}
process.on('SIGTERM', () => {
console.log('Received SIGTERM, shutting down gracefully')
client.destroy()
process.exit(0)
})
})()


if (allSlashCommandsFiles.length > 0) {
for (const slashCommandFile of allSlashCommandsFiles) {
const slashCommand: SlashCommand | undefined = (await import(`file:///${slashCommandFile}`))?.Slash
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Secure dynamic imports and add validation

The dynamic imports using file:/// protocol could pose security risks. Consider:

  1. Adding path validation
  2. Implementing a whitelist mechanism
  3. Adding error handling for malformed imports
-      const slashCommand: SlashCommand | undefined = (await import(`file:///${slashCommandFile}`))?.Slash
+      const normalizedPath = path.normalize(slashCommandFile)
+      if (!normalizedPath.startsWith(rootPath)) {
+        console.error(`Invalid command path: ${slashCommandFile}`)
+        continue
+      }
+      try {
+        const slashCommand: SlashCommand | undefined = (await import(normalizedPath))?.Slash
+      } catch (error) {
+        console.error(`Failed to import command from ${slashCommandFile}:`, error)
+        continue
+      }

Also applies to: 70-70

export async function SlashManager(client: DiscordClient, rootPath: string): Promise<void> {
const allSlashCommandsFiles = fileReader(`${rootPath}/interactions/slashCommands`)
const allContextMenusFiles = fileReader(`${rootPath}/interactions/contextMenus`)
const rest: REST = new REST({ version: '10' }).setToken(client.token)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Secure the client token handling

The client token is directly used in the REST initialization. Consider:

  1. Adding token validation
  2. Implementing token rotation capability
  3. Adding error handling for invalid tokens
-  const rest: REST = new REST({ version: '10' }).setToken(client.token)
+  if (!client.token) {
+    throw new Error('Bot token is not configured')
+  }
+  const rest: REST = new REST({ version: '10' }).setToken(client.token)

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +99 to +112
// try {
await rest.put(Routes.applicationCommands(client.application.id), {
body: globalCommandsArray,
})

for (const guildObject of Object.entries(guildCommandsObject)) {
await rest.put(Routes.applicationGuildCommands(client.application.id, guildObject[0]), {
body: guildObject[1],
})
};
// }
// catch (error: unknown) {
// console.log(error)
// };
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Implement proper error handling and rate limiting

The command registration lacks error handling and rate limit consideration. Uncomment and improve the error handling:

-  // try {
+  try {
+    console.log(`Registering ${globalCommandsArray.length} global commands`)
     await rest.put(Routes.applicationCommands(client.application.id), {
       body: globalCommandsArray,
     })
 
+    console.log(`Registering guild commands for ${Object.keys(guildCommandsObject).length} guilds`)
     for (const guildObject of Object.entries(guildCommandsObject)) {
+      try {
         await rest.put(Routes.applicationGuildCommands(client.application.id, guildObject[0]), {
           body: guildObject[1],
         })
+      } catch (error) {
+        console.error(`Failed to register commands for guild ${guildObject[0]}:`, error)
+      }
     };
-  // }
-  // catch (error: unknown) {
-  //   console.log(error)
-  // };
+  } catch (error) {
+    console.error('Failed to register commands:', error)
+    throw error
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// try {
await rest.put(Routes.applicationCommands(client.application.id), {
body: globalCommandsArray,
})
for (const guildObject of Object.entries(guildCommandsObject)) {
await rest.put(Routes.applicationGuildCommands(client.application.id, guildObject[0]), {
body: guildObject[1],
})
};
// }
// catch (error: unknown) {
// console.log(error)
// };
try {
console.log(`Registering ${globalCommandsArray.length} global commands`)
await rest.put(Routes.applicationCommands(client.application.id), {
body: globalCommandsArray,
})
console.log(`Registering guild commands for ${Object.keys(guildCommandsObject).length} guilds`)
for (const guildObject of Object.entries(guildCommandsObject)) {
try {
await rest.put(Routes.applicationGuildCommands(client.application.id, guildObject[0]), {
body: guildObject[1],
})
} catch (error) {
console.error(`Failed to register commands for guild ${guildObject[0]}:`, error)
}
};
} catch (error) {
console.error('Failed to register commands:', error)
throw error
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

chore: scaffold community server discord bot
1 participant