Skip to content

Conversation

@Scyye
Copy link
Owner

@Scyye Scyye commented Mar 31, 2025

Closes #58

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced enhanced configuration management and asset handling capabilities to streamline resource management.
    • Added an "end" button to page menus for easier message dismissal.
  • Documentation

    • Added comprehensive documentation across commands, menus, caching, configuration, and utilities for clearer user guidance and improved maintainability.
    • Completely revamped README with detailed setup instructions, examples, and framework overviews for commands, config, menus, and caching.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 31, 2025

Walkthrough

This pull request primarily adds and expands JavaDoc documentation across the codebase. Enhancements were made in multiple modules including caching, commands, configuration, menus, and utilities. New annotations, records, and methods have been documented in detail, and minor method signature updates (such as the addition of a non-null annotation) have been introduced. Some new methods (e.g., for updating caches and generating asset paths) were added alongside the documentation improvements, without altering the underlying functionality or overall control flow. Additionally, documentation was added to support user installable apps integration, including explanations of command context usage and detached channel checks, as well as README updates to guide developers on this feature.

Changes

File(s) Change Summary
src/main/java/botcommons/cache/CacheManager.java Added detailed JavaDoc for the class and its methods; updated init to include @NotNull on the JDA parameter; introduced a new update method; and documented new record classes (MessageStructure, GuildStructure, ChannelStructure, MemberStructure).
src/main/java/botcommons/commands/* Added or enhanced JavaDoc comments in several files:
- AutoCompleteHandler.java: Documented the value attribute.
- Command.java & CommandHolder.java: Added JavaDoc for annotations.
- CommandInfo.java: Added class-, method-, and nested class-level documentation.
- CommandManager.java: Added documentation for the class and its command handling methods, including explanation of info.userContext for command context settings.
- GenericCommandEvent.java: Expanded documentation for various methods, deprecated isSlashCommand, and documented detached channel check in listenOnce.
- Param.java: Documented annotation attributes.
- ReplyContext.java: Added a new listenOnce method with documentation including detached channel check, and enhanced the finish method docs.
src/main/java/botcommons/config/* In Config.java, enhanced the documentation for the existing config creation method and added a new overloaded makeConfig(String file) method along with a new write method. In ConfigManager.java, added JavaDoc comments to methods that set or retrieve configuration values and to the file writing routine.
src/main/java/botcommons/menu/MenuManager.java Added JavaDoc comments for the constructor and methods including registerMenu, registerMenuWithId, sendMenu, sendMenuPrivate, and replyMenu, clarifying their roles in menu registration and dispatching.
src/main/java/botcommons/utilities/* In JsonUtils.java, added class- and method-level documentation for JSON serialization and cache file handling. In StringUtilities.java, enhanced documentation for existing methods (stringifyMap and parseMap) and added a new getAssetPath method that generates asset paths, creates necessary directories, and logs directory creation status.
README.md Extensively rewritten and expanded with detailed sections on Commands, Config, Menu, and Cache frameworks. Added new documentation on user installable apps support including usage examples and command context explanations. Installation instructions, examples, and contributing/licensing information were also added or improved.

Poem

I’m a rabbit with a joyful heart,
Hopping through code, a brand new start.
Docs have been polished, methods shine bright,
Comments guide our journey, day and night.
With every line refined, our code sings in delight!
🐇✨

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch documentation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @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.

@Scyye
Copy link
Owner Author

Scyye commented Mar 31, 2025

@coderabbitai summary

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 31, 2025

✅ Actions performed

Summary regeneration triggered.

@Scyye
Copy link
Owner Author

Scyye commented Mar 31, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 31, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Scyye
Copy link
Owner Author

Scyye commented Mar 31, 2025

@coderabbitai is there anything else that should be given documentation? or anything else that needs to be changed in the documentation before this is merged?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 31, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

After reviewing the PR files, I'll need to examine the codebase more thoroughly to answer your question completely. Let me gather more information.

Let me check for XML documentation comments in the codebase to see what's documented and what might be missing.

Let me check the PR diff to understand what documentation was specifically added in this PR.

@Scyye After reviewing the codebase, I found a few areas where documentation could be improved:

  1. Some public methods may be missing XML documentation comments (///). These comments help with IntelliSense and generate proper documentation.

  2. Consider adding a README.md file with:

    • Overview of BotCommons
    • Installation instructions
    • Basic usage examples
    • Link to more detailed documentation
  3. For existing XML comments, ensure they include:

    • <summary> for all methods and properties
    • <param> tags for all parameters
    • <returns> tags where methods return values
    • <exception> tags for any exceptions thrown

Would you like me to provide more specific recommendations about which files need documentation improvements?


🏁 Script executed:

#!/bin/bash
# Get PR details
gh pr view 62 --json title,body,additions,deletions,changedFiles,files

# Get a list of the files changed in this PR
gh pr view 62 --json files --jq '.files[].path'

Length of output: 2211


🏁 Script executed:

#!/bin/bash
# Get details of the changed files
for file in $(gh pr view 62 --json files --jq '.files[].path'); do
  echo "=== File: $file ==="
  cat "$file" 2>/dev/null || echo "Could not access file"
  echo ""
done

# List all .cs files in the repo to find potentially undocumented components
find . -name "*.cs" | sort

Length of output: 70002


🏁 Script executed:

#!/bin/bash
# Look for C# files with and without XML documentation comments
echo "=== Files with XML documentation comments ==="
grep -l "///" $(find . -name "*.cs") | sort

echo -e "\n=== Files without XML documentation comments ==="
for file in $(find . -name "*.cs"); do
  if ! grep -q "///" "$file"; then
    echo "$file"
  fi
done

# Check the content of files that don't have documentation comments
for file in $(find . -name "*.cs"); do
  if ! grep -q "///" "$file"; then
    echo -e "\n=== Content of undocumented file: $file ==="
    cat "$file"
  fi
done

Length of output: 303


🏁 Script executed:

#!/bin/bash
# Get the PR diff
gh pr diff 62

# List all public classes, methods, and properties in CS files
echo "=== Public API surface ==="
for file in $(find . -name "*.cs"); do
  echo -e "\n--- File: $file ---"
  grep -E "public (class|interface|enum|struct|record|delegate|static|void|[A-Za-z0-9_<>]+) [A-Za-z0-9_]+" "$file" || echo "No public API found"
done

Length of output: 41383


@Scyye After reviewing the PR changes, I'm impressed with the comprehensive documentation added across all files. The Javadoc comments for classes, methods, parameters, and return types are well written and informative.

Here are a few suggestions that could further enhance your documentation before merging:

  1. Consider adding a README.md file with:

    • Overview of what BotCommons is and its purpose
    • Installation/setup instructions
    • Basic usage examples for common tasks
    • Link to more detailed documentation if available
  2. Some interfaces like IMenu are referenced but their documentation wasn't visible in the changes. Make sure all public interfaces are documented.

  3. A few methods have documentation that appears incomplete, like ReplyContext.finish() which mentions "including:" but doesn't complete the list.

  4. Consider adding a few usage examples in the documentation for key classes like CommandManager or CacheManager to show how they should be implemented.

Overall, the documentation is in excellent shape and covers the essential information users would need to understand the library.

@Scyye
Copy link
Owner Author

Scyye commented Mar 31, 2025

@coderabbitai could you possible write a readme file?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 31, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if a README already exists and examine project structure
echo "Checking for existing README files..."
find . -name "README*" -o -name "readme*"

# Look at build configuration to understand dependencies
echo -e "\nExamining build files..."
find . -name "pom.xml" -o -name "build.gradle" | xargs cat

# Check the main class or entry point for overview information
echo -e "\nLooking for main entry point..."
find . -path "*/main/java/*.java" -name "*Main.java" -o -name "*Application.java"

# Get a list of all packages to understand the project structure
echo -e "\nExamining package structure..."
find ./src -type d -path "*/main/java/*" | sort

# Look at key files to understand the library's purpose and features
echo -e "\nExamining key files for project description..."
grep -r "This library" --include="*.java" ./src || echo "No 'This library' description found"

Length of output: 3127


🏁 Script executed:

#!/bin/bash
# Check the content of the existing README.md
echo "Reading existing README.md..."
cat README.md

# Look at key class files to understand their purpose
echo -e "\nChecking key class documentation..."
head -30 src/main/java/botcommons/cache/CacheManager.java
head -30 src/main/java/botcommons/commands/CommandManager.java
head -30 src/main/java/botcommons/config/ConfigManager.java
head -30 src/main/java/botcommons/menu/MenuManager.java

# Get version information
echo -e "\nChecking version info..."
grep -r "version" --include="pom.xml" .

Length of output: 8707


@Scyye Here's a comprehensive README.md for the BotCommons project:

# BotCommons

A powerful framework for creating Discord bots using [JDA (Java Discord API)](https://github.com/DV8FromTheWorld/JDA) that simplifies common bot development tasks.

![Version](https://img.shields.io/badge/version-1.11-blue)
![Java](https://img.shields.io/badge/java-21-orange)
![License](https://img.shields.io/badge/license-MIT-green)

## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Commands Framework](#commands-framework)
- [Config Framework](#config-framework)
- [Menu Framework](#menu-framework)
- [Cache Framework](#cache-framework)

## Features

BotCommons provides several key features to simplify Discord bot development:

- **Commands Framework**: Easy creation and management of slash commands with parameter validation
- **Config Framework**: Simple configuration management for global and per-guild settings
- **Menu Framework**: Interactive menu system for user interactions
- **Cache Framework**: Efficient caching of Discord entities like messages, guilds, channels and members

## Installation

### Maven

Add the following to your `pom.xml`:

```xml
<dependency>
    <groupId>dev.scyye</groupId>
    <artifactId>BotCommons</artifactId>
    <version>1.11</version>
</dependency>

Gradle

Add the following to your build.gradle:

dependencies {
    implementation 'dev.scyye:BotCommons:1.11'
}

Commands Framework

The Commands Framework simplifies the creation and management of slash commands.

Creating Commands

  1. Create a class annotated with @CommandHolder
  2. Create methods annotated with @Command
  3. Register the commands with the CommandManager
import botcommons.commands.*;

// You can also specify a group for the commands in the holder
@CommandHolder
public class PingCommand {
    @Command(name = "ping", help = "Pong!")
    public void execute(GenericCommandEvent event,
                        @Param(
                            description = "A user",
                            type = Param.ParamType.USER
                        )
                        // the name of the argument is grabbed from the parameter name        
                        User user) {
        event.replySuccess("Pong! " + user.getAsMention()).finish(message -> {
            // Success consumer
        });
    }
}

Setting Up CommandManager

public class Main {
    public static void main(String[] args) {
        JDA jda = JDABuilder.createDefault("token")
                .addEventListeners(new CommandManager())
                .build();
        
        // Initialize the CommandManager
        CommandManager.init(jda);
        
        // Register commands
        CommandManager.addCommands(PingCommand.class);
    }
}

Parameter Annotations

Use the @Param annotation to define command parameters:

@Param(
    description = "Description of the parameter",
    required = true, // Default is true
    type = Param.ParamType.USER, // Type of parameter (STRING, INTEGER, USER, etc.)
    autocomplete = true // Enable autocomplete (requires implementing AutoCompleteHandler)
)

Config Framework

The Config Framework provides both global and per-guild configuration management.

Global Config

public static void main(String[] args) {
    // Set the bot name for the config file
    Config.botName = "MyBot";
    
    // Create a default config
    Config config = Config.makeConfig(new HashMap<>() {{
        put("prefix", "!");
        put("admins", new String[]{"userId1", "userId2"});
        put("defaultSettings", new HashMap<String, Object>() {{
            put("welcomeMessage", true);
            put("loggingEnabled", false);
        }});
    }});
    
    // Access config values
    String prefix = config.get("prefix", String.class);
    boolean loggingEnabled = ((Map<String, Object>)config.get("defaultSettings")).get("loggingEnabled");
}

Guild Config

public static void main(String[] args) {
    JDA jda = JDABuilder.createDefault("token")
            .addEventListeners(new ConfigManager(new HashMap<>() {{
                put("prefix", "!");
                put("welcomeMessage", true);
                put("loggingEnabled", false);
            }}))
            .build();
            
    // Later, get a guild's config
    Config guildConfig = event.getConfig();
    boolean welcomeMessage = guildConfig.get("welcomeMessage", Boolean.class);
    
    // Update a value
    ConfigManager.getInstance().setValue(guildId, "welcomeMessage", false);
}

Note: This will automatically create the /sql and /config commands.

Menu Framework

The Menu Framework allows you to create interactive menus with buttons and pagination.

Setting Up MenuManager

public static void main(String[] args) {
    JDA jda = JDABuilder.createDefault("token")
            .build();
            
    // Create menu manager instance
    MenuManager menuManager = new MenuManager(jda);
}

Creating and Registering Menus

// Create a simple menu implementation
IMenu helpMenu = new IMenu() {
    @Override
    public String getMenuId() {
        return "help_menu";
    }
    
    @Override
    public MessageCreateAction generateMenu(Object... args) {
        return new MessageCreateAction()
            .setContent("Help Menu")
            .addActionRow(
                Button.primary("prev", "Previous"),
                Button.primary("next", "Next")
            );
    }
    
    @Override
    public void handleButtonInteraction(ButtonInteractionEvent event) {
        String buttonId = event.getComponentId();
        if (buttonId.equals("prev")) {
            // Handle previous button
        } else if (buttonId.equals("next")) {
            // Handle next button
        }
    }
};

// Register the menu
menuManager.registerMenu(helpMenu);

// Send the menu to a channel
menuManager.sendMenu("help_menu", channelId);

// Or reply to a command with a menu
event.replyMenu("help_menu").finish();

Cache Framework

The Cache Framework allows efficient caching of Discord entities for improved performance.

Initializing Cache

public static void main(String[] args) {
    JDA jda = JDABuilder.createDefault("token")
            .build();
    
    // Initialize cache with various options
    CacheManager.init(
        jda,
        true,  // Cache guild members
        true,  // Cache mutual guilds
        true,  // Cache channel messages
        true,  // Cache user messages
        true   // Cache users
    );
}

Accessing Cached Data

// Access cached members for a guild
List<CacheManager.MemberStructure> members = CacheManager.guildMemberCache.get(guildId);

// Access cached messages
List<CacheManager.MessageStructure> messages = CacheManager.channelMessageCache.get(channelId);

// Update the cache to JSON files
CacheManager.update();

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.


Would you like me to make any adjustments to this README?

@Scyye
Copy link
Owner Author

Scyye commented Jul 24, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 24, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Scyye
Copy link
Owner Author

Scyye commented Jul 24, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 24, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@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: 10

🔭 Outside diff range comments (12)
src/main/java/botcommons/commands/ReplyContext.java (1)

138-154: Combined content + embeds are silently ignored
The current dispatch logic never executes when both content and embeds are provided, resulting in no message being sent.

-		} else if (embeds.isEmpty()) {
+		} else if (embeds.isEmpty()) {+		} else { // both content and embeds present
+			if (!interactionEvent.isAcknowledged())
+				action = interactionEvent.reply(content).addEmbeds(getEmbeds());
+			else
+				action2 = interactionEvent.getHook().sendMessage(content).addEmbeds(getEmbeds());
 		}
src/main/java/botcommons/config/Config.java (1)

27-27: Use Path.of() for better path handling.

String concatenation for file paths can cause issues on different operating systems. Consider using Path.of() for cross-platform compatibility:

-		String fileName = botName + "-assets\\config.json";
+		String fileName = Path.of(botName + "-assets", "config.json").toString();
src/main/java/botcommons/utilities/StringUtilities.java (1)

30-45: Excellent documentation but consider brevity.

The method logic is sound, but the JavaDoc could be more concise while maintaining clarity. The generic type parameters are well explained.

src/main/java/botcommons/commands/CommandInfo.java (2)

24-43: Avoid double-brace initialization for optionTypeParams

The anonymous subclass created by the double-brace initializer keeps an implicit reference to the outer CommandInfo class and may increase memory usage. It also makes the map unmodifiable only by convention.

-private static final HashMap<Class<?>, OptionType> optionTypeParams = new HashMap<>() {{
-    put(String.class, OptionType.STRING);
-    ...
-}};
+private static final Map<Class<?>, OptionType> OPTION_TYPE_PARAMS;
+static {
+    Map<Class<?>, OptionType> map = new HashMap<>();
+    map.put(String.class, OptionType.STRING);
+    ...
+    OPTION_TYPE_PARAMS = Collections.unmodifiableMap(map);
+}

66-74: JavaDoc promises non-null but implementation can return null

from(@Nullable Method) returns null when the argument is null, yet the JavaDoc states it “returns a CommandInfo instance”. Update the JavaDoc (or remove @Nullable and throw) to avoid misleading users.

src/main/java/botcommons/cache/CacheManager.java (2)

30-35: Use thread-safe collections for shared caches

guildMemberCache, mutualGuildsCache, channelMessageCache, userMessageCache, and userCache are written from multiple JDA event threads without synchronization. Replace HashMap/ArrayList with ConcurrentHashMap / thread-safe lists or introduce proper locks to prevent data races and ConcurrentModificationExceptions.


119-123: Potential NPE when a user leaves their last mutual guild

mutualGuildsCache.get(event.getUser().getId()) can be null if the user was never cached (e.g., joins & immediately leaves).
Guard the call:

-List<GuildStructure> guilds = mutualGuildsCache.get(event.getUser().getId());
-if (guilds != null) {
-    guilds.remove(GuildStructure.fromGuild(event.getGuild()));
-}
+mutualGuildsCache.computeIfPresent(
+        event.getUser().getId(),
+        (_, guilds) -> {
+            guilds.remove(GuildStructure.fromGuild(event.getGuild()));
+            return guilds.isEmpty() ? null : guilds;
+        });
src/main/java/botcommons/commands/CommandManager.java (3)

64-88: Commands from non-group holders are added twice

When meta.group() equals "n/a" you first iterate once (lines 65-77) and then immediately iterate again (lines 82-87), inserting duplicates in commands.
Early-return after the first loop or merge the loops to avoid duplicate registrations.


254-267: Prefer EnumMap over raw HashMap for typeMap

OptionType is an enum; EnumMap offers faster lookups and lower memory footprint.

-private static final HashMap<OptionType, Class<?>> typeMap = new HashMap<>() {{
+private static final EnumMap<OptionType, Class<?>> typeMap = new EnumMap<>(OptionType.class) {{

275-330: getCommand has quadratic scans and subtle alias edge-cases

  1. Two nested linear searches for every lookup → O(n²) worst-case on aliases.
  2. For subcommands, matching is string-split based and ignores alias groups.

Consider indexing once into two Map<String, Method> (one for root commands, one for full subcommand paths) during registration for O(1) lookups and simplified alias handling.

src/main/java/botcommons/commands/GenericCommandEvent.java (2)

274-278: replyMenu mutates the menu ID and logs to stdout

Appending "-fake" to the provided id breaks caller expectations and makes it impossible to retrieve the same menu later.
Also, prefer SLF4J logger over System.out.

-MenuManager.registerMenuWithId(id+"-fake", menu);
-System.out.println("Registered menu with id: " + id);
-return this.replyContext.menu(id+"-fake", args);
+MenuManager.registerMenuWithId(id, menu);
+logger.debug("Registered menu with id: {}", id);
+return this.replyContext.menu(id, args);

162-181: Repeated new Gson() on every getArg call

Instantiate Gson once as a static field to avoid unnecessary object creation on hot paths.

private static final Gson GSON = new Gson();
...
return GSON.fromJson(option.getDefaultValue(), type);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9dacfde and 7ce19f2.

📒 Files selected for processing (14)
  • src/main/java/botcommons/cache/CacheManager.java (4 hunks)
  • src/main/java/botcommons/commands/AutoCompleteHandler.java (1 hunks)
  • src/main/java/botcommons/commands/Command.java (1 hunks)
  • src/main/java/botcommons/commands/CommandHolder.java (1 hunks)
  • src/main/java/botcommons/commands/CommandInfo.java (4 hunks)
  • src/main/java/botcommons/commands/CommandManager.java (4 hunks)
  • src/main/java/botcommons/commands/GenericCommandEvent.java (5 hunks)
  • src/main/java/botcommons/commands/Param.java (1 hunks)
  • src/main/java/botcommons/commands/ReplyContext.java (2 hunks)
  • src/main/java/botcommons/config/Config.java (3 hunks)
  • src/main/java/botcommons/config/ConfigManager.java (1 hunks)
  • src/main/java/botcommons/menu/MenuManager.java (5 hunks)
  • src/main/java/botcommons/utilities/JsonUtils.java (3 hunks)
  • src/main/java/botcommons/utilities/StringUtilities.java (2 hunks)
🔇 Additional comments (18)
src/main/java/botcommons/commands/CommandHolder.java (1)

5-7: Documentation looks good
No functional changes and the Javadoc accurately describes the annotation purpose.

src/main/java/botcommons/config/Config.java (1)

54-71: LGTM! Good error handling and validation.

The JavaDoc is comprehensive and the method properly validates required configuration values before returning the config instance.

src/main/java/botcommons/utilities/StringUtilities.java (1)

47-65: Well-implemented asset path utility with good error handling.

The method properly handles directory creation with appropriate logging. The implementation is robust and follows good practices.

src/main/java/botcommons/config/ConfigManager.java (5)

37-43: LGTM! Clear and concise documentation.

The JavaDoc appropriately describes the method's purpose and parameter usage.


45-53: Good documentation with clear parameter explanations.

The JavaDoc effectively explains both the server-specific configuration setting and the file persistence aspect.


55-65: Well-documented method with comprehensive parameter descriptions.

The documentation clearly explains the method's behavior and parameter requirements.


67-78: Excellent generic method documentation.

The JavaDoc properly explains the generic type parameter and return behavior, including null handling.


80-121: Well-documented private method with robust implementation.

The documentation clearly explains the file writing process, and the implementation includes proper retry logic with exponential backoff and comprehensive error handling.

src/main/java/botcommons/utilities/JsonUtils.java (4)

19-21: LGTM! Appropriate class-level documentation.

The class documentation clearly explains the utility's purpose.


23-41: Good documentation for the GSON instance.

The documentation explains the static GSON instance's role in the utility class.


44-60: Well-documented cache creation method.

The JavaDoc clearly explains the method's behavior and parameters, including the conditional file creation logic.


62-97: Excellent documentation and robust implementation.

The method documentation is clear, and the implementation includes proper error handling, data merging, and thread safety considerations.

src/main/java/botcommons/menu/MenuManager.java (6)

25-33: LGTM! Clear constructor documentation.

The JavaDoc appropriately explains the constructor's purpose and JDA integration.


35-45: Good documentation for menu registration.

The JavaDoc clearly explains the registration process and annotation requirement.


47-60: Excellent documentation with proper validation.

The method includes good parameter validation and clear documentation explaining the dynamic registration capability.


62-81: Well-documented menu sending method.

The documentation clearly explains the channel-based menu sending functionality.


83-98: Good documentation for private menu sending.

The JavaDoc appropriately explains the private channel menu delivery process.


126-137: Clear documentation for menu reply functionality.

The JavaDoc effectively explains the interaction hook-based menu reply system with dynamic argument support.

Comment on lines +58 to 63
/**
* Updates all caches to their respective JSON files.
*/
public static void update() {
JsonUtils.updateCache(guildMemberCache, "guild_member_cache");
JsonUtils.updateCache(mutualGuildsCache, "mutual_guilds_cache");
Copy link
Contributor

@coderabbitai coderabbitai bot Jul 24, 2025

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

update() is IO-heavy; call frequency is very high

update() writes every cache to disk on every message receive/delete/member update.
Consider debouncing (scheduled executor) or batching updates to avoid disk thrashing, especially in large guilds.

🤖 Prompt for AI Agents
In src/main/java/botcommons/cache/CacheManager.java around lines 58 to 63, the
update() method writes all caches to disk on every call, causing excessive IO
operations. To fix this, implement a debouncing mechanism using a scheduled
executor or a similar scheduler to batch multiple update requests and write to
disk less frequently, reducing disk thrashing and improving performance.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Could you provide an example of implementation for that

Copy link
Contributor

Choose a reason for hiding this comment

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

@Scyye Here's a complete implementation example that adds debouncing to reduce excessive disk IO:

package botcommons.cache;

import botcommons.utilities.JsonUtils;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
import net.dv8tion.jda.api.events.guild.GuildReadyEvent;
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent;
import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent;
import net.dv8tion.jda.api.events.message.MessageDeleteEvent;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * CacheManager is a utility class to manage various caches for JDA entities.
 */
@SuppressWarnings("unused")
public class CacheManager extends ListenerAdapter {
    private CacheManager() {}

    public static final HashMap<String, List<MemberStructure>> guildMemberCache = new HashMap<>();
    public static final HashMap<String, List<GuildStructure>> mutualGuildsCache = new HashMap<>();
    public static final HashMap<String, List<MessageStructure>> channelMessageCache = new HashMap<>();
    public static final HashMap<String, List<MessageStructure>> userMessageCache = new HashMap<>();
    public static final HashMap<String, User> userCache = new HashMap<>();

    // Debouncing mechanism
    private static final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(r -> {
        Thread t = new Thread(r, "CacheManager-Writer");
        t.setDaemon(true);
        return t;
    });
    private static final AtomicBoolean updatesPending = new AtomicBoolean(false);
    private static final long DEBOUNCE_DELAY_SECONDS = 5; // Write to disk every 5 seconds max

    /**
     * Initializes the CacheManager with the provided JDA instance and cache options.
     * @param jda The JDA instance to fetch data from. This cannot be null.
     * @param guildMembers Whether to enable the guild member cache.
     * @param mutualGuilds Whether to enable the mutual guilds cache.
     * @param channelMessages Whether to enable the channel messages cache.
     * @param userMessages Whether to enable the user messages cache.
     * @param users Whether to enable the user cache.
     */
    public static void init(@NotNull JDA jda, boolean guildMembers, boolean mutualGuilds, boolean channelMessages, boolean userMessages, boolean users) {
        if (!guildMembers && !mutualGuilds && !channelMessages && !userMessages && !users) 
            throw new IllegalArgumentException("At least one cache must be enabled");
        
        jda.addEventListener(new CacheManager());

        if (guildMembers) JsonUtils.createCache(guildMemberCache, "guild_member_cache");
        if (mutualGuilds) JsonUtils.createCache(mutualGuildsCache, "mutual_guilds_cache");
        if (channelMessages) JsonUtils.createCache(channelMessageCache, "channel_message_cache");
        if (userMessages) JsonUtils.createCache(userMessageCache, "user_message_cache");
        if (users) JsonUtils.createCache(userCache, "user_cache");

        // Schedule periodic cleanup to ensure updates are written even if no new events occur
        scheduler.scheduleWithFixedDelay(() -> {
            if (updatesPending.get()) {
                forceUpdate();
            }
        }, DEBOUNCE_DELAY_SECONDS, DEBOUNCE_DELAY_SECONDS, TimeUnit.SECONDS);
    }

    /**
     * Schedules cache updates to be written to disk after a short delay.
     * This reduces IO operations by batching multiple rapid updates together.
     */
    public static void update() {
        if (updatesPending.compareAndSet(false, true)) {
            scheduler.schedule(() -> {
                forceUpdate();
            }, DEBOUNCE_DELAY_SECONDS, TimeUnit.SECONDS);
        }
    }

    /**
     * Immediately writes all caches to their respective JSON files.
     * Use sparingly - prefer update() for better performance.
     */
    public static void forceUpdate() {
        updatesPending.set(false);
        JsonUtils.updateCache(guildMemberCache, "guild_member_cache");
        JsonUtils.updateCache(mutualGuildsCache, "mutual_guilds_cache");
        JsonUtils.updateCache(channelMessageCache, "channel_message_cache");
        JsonUtils.updateCache(userMessageCache, "user_message_cache");
        JsonUtils.updateCache(userCache, "user_cache");
    }

    /**
     * Shuts down the cache manager and ensures all pending updates are written.
     * Call this when your application is shutting down.
     */
    public static void shutdown() {
        forceUpdate(); // Write any pending updates
        scheduler.shutdown();
        try {
            if (!scheduler.awaitTermination(10, TimeUnit.SECONDS)) {
                scheduler.shutdownNow();
            }
        } catch (InterruptedException e) {
            scheduler.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }

    @Override
    public void onMessageReceived(@NotNull MessageReceivedEvent event) {
        if (!event.isFromGuild()) return;
        
        channelMessageCache.putIfAbsent(event.getChannel().getId(), new ArrayList<>());
        channelMessageCache.get(event.getChannel().getId()).add(MessageStructure.fromMessage(event.getMessage()));

        if (event.getMember() == null) return;

        userMessageCache.putIfAbsent(event.getAuthor().getId(), new ArrayList<>());
        userMessageCache.get(event.getAuthor().getId()).add(MessageStructure.fromMessage(event.getMessage()));
        update(); // Now schedules instead of immediately writing
    }

    @Override
    public void onMessageDelete(@NotNull MessageDeleteEvent event) {
        if (!event.isFromGuild()) return;
        
        channelMessageCache.get(event.getChannel().getId()).removeIf(messageStructure -> 
            messageStructure.id().equals(event.getMessageId()));
        userMessageCache.forEach((member, messageStructures) -> 
            messageStructures.removeIf(messageStructure -> messageStructure.id().equals(event.getMessageId())));
        update(); // Now schedules instead of immediately writing
    }

    @Override
    public void onGuildReady(@NotNull GuildReadyEvent event) {
        event.getGuild().loadMembers().onSuccess(members -> {
            System.out.printf("Loaded %d members for guild %s%n", members.size(), event.getGuild().getName());
            guildMemberCache.put(event.getGuild().getId(), members.stream().map(MemberStructure::fromMember).toList());

            members.forEach(member -> {
                mutualGuildsCache.putIfAbsent(member.getUser().getId(), new ArrayList<>());
                mutualGuildsCache.get(member.getUser().getId()).add(GuildStructure.fromGuild(event.getGuild()));
                userCache.putIfAbsent(member.getUser().getId(), member.getUser());
            });
            forceUpdate(); // Guild ready is infrequent, write immediately
        }).onError(Throwable::printStackTrace);
    }

    @Override
    public void onGuildMemberJoin(@NotNull GuildMemberJoinEvent event) {
        guildMemberCache.get(event.getGuild().getId()).add(MemberStructure.fromMember(event.getMember()));
        mutualGuildsCache.putIfAbsent(event.getUser().getId(), new ArrayList<>());
        mutualGuildsCache.get(event.getUser().getId()).add(GuildStructure.fromGuild(event.getGuild()));
        update(); // Schedule update
    }

    @Override
    public void onGuildMemberRemove(@NotNull GuildMemberRemoveEvent event) {
        guildMemberCache.get(event.getGuild().getId()).removeIf(memberStructure -> 
            memberStructure.id().equals(event.getUser().getId()));
        mutualGuildsCache.get(event.getUser().getId()).remove(GuildStructure.fromGuild(event.getGuild()));
        update(); // Schedule update
    }

    // ... rest of the record classes remain the same
}

Key improvements:

  1. ScheduledExecutorService: Manages delayed writes using a dedicated daemon thread
  2. AtomicBoolean flag: Tracks if updates are pending to avoid scheduling multiple writes
  3. Debounced update(): Schedules writes after 5 seconds, only if no write is already scheduled
  4. forceUpdate(): Immediately writes to disk (used for infrequent but important events like guild ready)
  5. Periodic cleanup: Ensures updates are eventually written even if no new events occur
  6. Proper shutdown: shutdown() method to gracefully close the executor and flush pending updates

Usage notes:

  • Call CacheManager.shutdown() when your bot shuts down to ensure all data is saved
  • Adjust DEBOUNCE_DELAY_SECONDS based on your needs (5 seconds is a good balance)
  • Critical events like onGuildReady use forceUpdate() for immediate persistence

This reduces disk IO from potentially hundreds of writes per minute to at most one write every 5 seconds, dramatically improving performance in active guilds! 🐰

Thoughtful rabbit implementing efficient caching patterns
(\ /)
( .. ) 💾
o
(")(")

Comment on lines +8 to +11
/**
* The name of the command that this auto complete handler is for.
* @return value
*/
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Clarify the @return description
The tag currently returns a generic “value”, which does not add information beyond the method name. Consider making it explicit, e.g. “an array with the names of the command(s) this handler applies to”, to improve readability for API consumers.

-	 * @return value
+	 * @return the names of the command(s) this auto-complete handler supports
📝 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
/**
* The name of the command that this auto complete handler is for.
* @return value
*/
/**
* The name of the command that this auto complete handler is for.
* @return the names of the command(s) this auto-complete handler supports
*/
🤖 Prompt for AI Agents
In src/main/java/botcommons/commands/AutoCompleteHandler.java lines 8 to 11, the
Javadoc @return tag description is too generic and uninformative. Update the
@return description to explicitly state that it returns an array with the names
of the command(s) this handler applies to, improving clarity for API users.

Comment on lines +11 to +17
/**
* This annotation is used to define a command for the bot.
* <p>
* The name, help, aliases, usage, category and permission can be specified.
* <p>
* The userContext defines where this command can be used (e.g. GUILD, PRIVATE_CHANNEL)
*/
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Mismatch between Javadoc and default userContext values
The comment mentions PRIVATE_CHANNEL, but the default array contains BOT_DM. Either update the docs or the default to avoid confusion.

- * The userContext defines where this command can be used (e.g. GUILD, PRIVATE_CHANNEL)
+ * The userContext defines where this command can be used (e.g. GUILD, BOT_DM)
📝 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
/**
* This annotation is used to define a command for the bot.
* <p>
* The name, help, aliases, usage, category and permission can be specified.
* <p>
* The userContext defines where this command can be used (e.g. GUILD, PRIVATE_CHANNEL)
*/
/**
* This annotation is used to define a command for the bot.
* <p>
* The name, help, aliases, usage, category and permission can be specified.
* <p>
* The userContext defines where this command can be used (e.g. GUILD, BOT_DM)
*/
🤖 Prompt for AI Agents
In src/main/java/botcommons/commands/Command.java around lines 11 to 17, the
Javadoc mentions the userContext value "PRIVATE_CHANNEL" but the default
userContext array uses "BOT_DM". To fix this mismatch, either update the Javadoc
to mention "BOT_DM" instead of "PRIVATE_CHANNEL" or change the default
userContext array to include "PRIVATE_CHANNEL" to keep the documentation and
code consistent.

Comment on lines 154 to 156
boolean isSlashCommand() {
return true;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Deprecated isSlashCommand() is still package-private

If the method is truly obsolete, mark it private or remove it entirely; keeping it accessible clutters the API surface and encourages accidental use.

🤖 Prompt for AI Agents
In src/main/java/botcommons/commands/GenericCommandEvent.java around lines 154
to 156, the method isSlashCommand() is deprecated but still package-private,
which unnecessarily exposes it. To fix this, either change the method's access
modifier to private if it must remain for internal use, or remove the method
entirely if it is no longer needed, thereby reducing the API surface and
preventing accidental usage.

Comment on lines +13 to 16
/**
* The name of the parameter.
*/
String description();
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Incorrect description of the description attribute
The attribute stores the description of the parameter, not its name.

-	 * The name of the parameter.
+	 * A human-readable description of the parameter shown in the command UI.
📝 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
/**
* The name of the parameter.
*/
String description();
/**
* A human-readable description of the parameter shown in the command UI.
*/
String description();
🤖 Prompt for AI Agents
In src/main/java/botcommons/commands/Param.java around lines 13 to 16, the
Javadoc comment for the description() method incorrectly states it stores the
name of the parameter. Update the comment to correctly indicate that this
attribute stores the description of the parameter.

Comment on lines +80 to +87
/**
* Adds a listener for a specific event type that will only be called once.
* @param eventType The type of event to listen for. This should be a subclass of {@link GenericEvent}.
* @param filter A predicate to filter the events. This allows you to specify conditions for the events that should trigger the listener.
* @param listener The listener function that will be called when the event is received. This should be a function that takes an instance of the event type and returns Void.
* @return The current instance of {@link ReplyContext} for chaining.
* @param <T> The type of the event to listen for. This should be a subclass of {@link GenericEvent}.
*/
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Minor Javadoc inconsistency
The listener parameter is described as “returns Void”, but the functional interface already enforces that. Removing that clause keeps the wording tight.

🤖 Prompt for AI Agents
In src/main/java/botcommons/commands/ReplyContext.java around lines 80 to 87,
the Javadoc for the listener parameter redundantly states that the listener
function "returns Void." Since the functional interface already enforces this,
remove the phrase "and returns Void" from the listener parameter description to
keep the documentation concise and clear.

Comment on lines +106 to +110
/**
* This method finalizes the reply context and sends the message to Discord. It will handle all the cases for sending a message, including:
* @param consumer A consumer that will be called with the resulting message after it has been sent. This allows you to perform additional actions on the message, such as logging or further processing.
* @return true if the reply was successfully sent, false otherwise. This method will also mark the reply context as finished.
*/
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Improve Javadoc structure
The list marker “including:” is left open without bullet points. Either add bullets or remove the word to avoid dangling wording.

🤖 Prompt for AI Agents
In src/main/java/botcommons/commands/ReplyContext.java around lines 106 to 110,
the Javadoc comment uses the phrase "including:" but does not provide any bullet
points, leaving the list incomplete. To fix this, either remove the word
"including:" to avoid dangling wording or add proper bullet points detailing the
cases handled for sending a message, ensuring the Javadoc is clear and
well-structured.

Comment on lines +17 to 21
* Creates a new Config instance from the provided old values and bot name.
* @param oldValues A map containing old values to be used in the configuration. This can include key-value pairs that will be added to the new config.
* @param botName The name of the bot for which the configuration is being created. This will be used to set the "bot-name" key in the configuration.
* @return Returns a new instance of the Config class, which is a HashMap containing the provided old values and additional default values.
*/
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Consider more concise JavaDoc documentation.

The JavaDoc is overly verbose and repetitive. Consider simplifying while maintaining clarity:

-	 * Creates a new Config instance from the provided old values and bot name.
-	 * @param oldValues A map containing old values to be used in the configuration. This can include key-value pairs that will be added to the new config.
-	 * @param botName The name of the bot for which the configuration is being created. This will be used to set the "bot-name" key in the configuration.
-	 * @return Returns a new instance of the Config class, which is a HashMap containing the provided old values and additional default values.
+	 * Creates a new Config instance from old values and bot name.
+	 * @param oldValues Map of existing configuration values to preserve
+	 * @param botName Bot name used for directory creation and config identification
+	 * @return New Config instance with merged values and defaults
📝 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
* Creates a new Config instance from the provided old values and bot name.
* @param oldValues A map containing old values to be used in the configuration. This can include key-value pairs that will be added to the new config.
* @param botName The name of the bot for which the configuration is being created. This will be used to set the "bot-name" key in the configuration.
* @return Returns a new instance of the Config class, which is a HashMap containing the provided old values and additional default values.
*/
* Creates a new Config instance from old values and bot name.
* @param oldValues Map of existing configuration values to preserve
* @param botName Bot name used for directory creation and config identification
* @return New Config instance with merged values and defaults
*/
🤖 Prompt for AI Agents
In src/main/java/botcommons/config/Config.java around lines 17 to 21, the
JavaDoc for the method is too verbose and repetitive. Simplify the documentation
by summarizing the purpose of the method clearly and concisely, focusing on key
points such as creating a new Config instance using old values and setting the
bot name, without redundant explanations of parameters or return values.

Comment on lines +96 to 102
/**
* Writes the current configuration to a JSON file.
* @throws IOException If an I/O error occurs while writing to the file. This can happen if the file is not writable, the directory does not exist, or there are permission issues.
*/
public void write() throws IOException {
Files.writeString(Path.of(Config.instance.get("bot-name") + "-assets", "config.json"), toString());
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add null check for Config.instance.

The write() method assumes Config.instance is not null, but this could lead to a NullPointerException if called before instance initialization:

 public void write() throws IOException {
+	if (Config.instance == null) {
+		throw new IllegalStateException("Config instance not initialized");
+	}
 	Files.writeString(Path.of(Config.instance.get("bot-name") + "-assets", "config.json"), toString());
 }
📝 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
/**
* Writes the current configuration to a JSON file.
* @throws IOException If an I/O error occurs while writing to the file. This can happen if the file is not writable, the directory does not exist, or there are permission issues.
*/
public void write() throws IOException {
Files.writeString(Path.of(Config.instance.get("bot-name") + "-assets", "config.json"), toString());
}
/**
* Writes the current configuration to a JSON file.
* @throws IOException If an I/O error occurs while writing to the file. This can happen if the file is not writable, the directory does not exist, or there are permission issues.
*/
public void write() throws IOException {
if (Config.instance == null) {
throw new IllegalStateException("Config instance not initialized");
}
Files.writeString(Path.of(Config.instance.get("bot-name") + "-assets", "config.json"), toString());
}
🤖 Prompt for AI Agents
In src/main/java/botcommons/config/Config.java around lines 96 to 102, the
write() method uses Config.instance without checking if it is null, which can
cause a NullPointerException if the instance is not initialized. Add a null
check for Config.instance at the start of the write() method and handle the case
appropriately, such as throwing an informative exception or returning early, to
prevent the method from proceeding when Config.instance is null.

Comment on lines +18 to 28
/**
* This method converts a HashMap with generic keys and values to a HashMap with String keys and String values by serializing each key and value to JSON format.
* @param map The input HashMap with generic keys and values. This map can contain any type of objects as keys and values.
* @return A new {@link HashMap} where each key and value from the input map has been serialized to a JSON string. This allows for easy storage or transmission of the map's contents in a standardized format.
*/
public static HashMap<String, String> stringifyMap(HashMap<?, ?> map) {
HashMap<String, String> stringMap = new HashMap<>();
Gson gson = new GsonBuilder().setPrettyPrinting().create();
map.forEach((key, value) -> stringMap.put(gson.toJson(key), gson.toJson(value)));
return stringMap;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Simplify JavaDoc for better readability.

The documentation is overly verbose. Consider a more concise approach:

-	 * This method converts a HashMap with generic keys and values to a HashMap with String keys and String values by serializing each key and value to JSON format.
-	 * @param map The input HashMap with generic keys and values. This map can contain any type of objects as keys and values.
-	 * @return A new {@link HashMap} where each key and value from the input map has been serialized to a JSON string. This allows for easy storage or transmission of the map's contents in a standardized format.
+	 * Converts a HashMap to String keys and values by JSON serialization.
+	 * @param map Input map with any key/value types
+	 * @return HashMap with JSON-serialized keys and values
📝 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
/**
* This method converts a HashMap with generic keys and values to a HashMap with String keys and String values by serializing each key and value to JSON format.
* @param map The input HashMap with generic keys and values. This map can contain any type of objects as keys and values.
* @return A new {@link HashMap} where each key and value from the input map has been serialized to a JSON string. This allows for easy storage or transmission of the map's contents in a standardized format.
*/
public static HashMap<String, String> stringifyMap(HashMap<?, ?> map) {
HashMap<String, String> stringMap = new HashMap<>();
Gson gson = new GsonBuilder().setPrettyPrinting().create();
map.forEach((key, value) -> stringMap.put(gson.toJson(key), gson.toJson(value)));
return stringMap;
}
/**
* Converts a HashMap to String keys and values by JSON serialization.
* @param map Input map with any key/value types
* @return HashMap with JSON-serialized keys and values
*/
public static HashMap<String, String> stringifyMap(HashMap<?, ?> map) {
HashMap<String, String> stringMap = new HashMap<>();
Gson gson = new GsonBuilder().setPrettyPrinting().create();
map.forEach((key, value) -> stringMap.put(gson.toJson(key), gson.toJson(value)));
return stringMap;
}
🤖 Prompt for AI Agents
In src/main/java/botcommons/utilities/StringUtilities.java around lines 18 to
28, the JavaDoc for the stringifyMap method is too verbose. Simplify it by
briefly stating that the method converts a map with any key and value types into
a map with JSON string keys and values, suitable for standardized storage or
transmission, without excessive detail.

Copy link
Contributor

@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: 2

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7ce19f2 and 8370479.

📒 Files selected for processing (4)
  • README.md (1 hunks)
  • src/main/java/botcommons/commands/CommandInfo.java (4 hunks)
  • src/main/java/botcommons/config/ConfigManager.java (2 hunks)
  • src/main/java/botcommons/menu/types/PageMenu.java (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
README.md (3)

Learnt from: Scyye
PR: #41
File: src/main/java/botcommons/commands/Command.java:20-20
Timestamp: 2025-03-20T23:21:31.217Z
Learning: In the BotCommons project, the replacement of custom Scope enum with JDA's InteractionContextType enum in the Command interface was about utilizing JDA's built-in functionality rather than expanding context types.

Learnt from: Scyye
PR: #41
File: src/main/java/botcommons/commands/Command.java:20-20
Timestamp: 2025-03-20T23:21:31.218Z
Learning: In the BotCommons project, the change from custom Scope enum to JDA's InteractionContextType is about aligning with Discord API standards rather than expanding command usage contexts.

Learnt from: Scyye
PR: #41
File: src/main/java/botcommons/commands/Command.java:20-20
Timestamp: 2025-03-20T23:21:31.218Z
Learning: In the BotCommons project, the replacement of custom Scope enum with JDA's InteractionContextType enum is about leveraging JDA's built-in functionality rather than maintaining a custom solution for defining where commands can be used.

🪛 LanguageTool
README.md

[style] ~258-~258: Consider using a less common alternative to make your writing sound more unique and professional.
Context: ...ontributing Contributions are welcome! Please feel free to submit a Pull Request. ## License Thi...

(FEEL_FREE_TO_STYLE_ME)

🔇 Additional comments (6)
src/main/java/botcommons/menu/types/PageMenu.java (1)

64-65: Excellent UI improvement!

Adding the "end" button makes the interface more intuitive by providing users with a visible way to close the menu. This change properly aligns the button array with the existing logic in the handle() method that already supports the "end" button functionality.

src/main/java/botcommons/config/ConfigManager.java (1)

37-84: Excellent comprehensive documentation!

The JavaDoc comments are thorough and well-structured, providing clear explanations of parameters, return types, and method behavior. The generic type documentation for getValue() is particularly helpful for developers.

src/main/java/botcommons/commands/CommandInfo.java (3)

19-21: Good class-level documentation.

Clear and concise class documentation that explains the purpose of the CommandInfo class.


27-39: Valuable enhancement: primitive type support.

Adding primitive type mappings to the optionTypeParams HashMap is excellent. This prevents potential issues when command methods use primitive parameters instead of their wrapper classes, ensuring consistent OptionType mapping.


64-77: Comprehensive method documentation.

The JavaDoc comments for getOption(), from() methods, and the Option inner class are well-written and provide clear explanations of parameters, return values, and functionality.

Also applies to: 119-123, 132-134

README.md (1)

1-263: Outstanding comprehensive documentation!

This README provides excellent coverage of all framework components with clear, practical examples. The structure with badges, table of contents, and detailed usage guides makes it highly accessible for developers. The code examples are well-structured and demonstrate proper usage patterns that align with the JavaDoc enhancements throughout the codebase.


## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Minor style improvement suggestion.

Consider replacing "feel free to" with a more direct phrase for a more professional tone.

-Contributions are welcome! Please feel free to submit a Pull Request.
+Contributions are welcome! Please submit a Pull Request.
📝 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
Contributions are welcome! Please feel free to submit a Pull Request.
Contributions are welcome! Please submit a Pull Request.
🧰 Tools
🪛 LanguageTool

[style] ~258-~258: Consider using a less common alternative to make your writing sound more unique and professional.
Context: ...ontributing Contributions are welcome! Please feel free to submit a Pull Request. ## License Thi...

(FEEL_FREE_TO_STYLE_ME)

🤖 Prompt for AI Agents
In README.md at line 258, replace the phrase "feel free to" with a more direct
and professional alternative such as "submit" or "welcome to submit" to improve
the tone of the contribution invitation.

Comment on lines +139 to +140

writeConfigToFile(serverId, configs.get(serverId));
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify the immediate config persistence doesn't cause performance issues.

Adding writeConfigToFile() after loading ensures immediate persistence, which is good for data integrity. However, ensure this doesn't cause performance issues during bot startup when multiple guilds become ready simultaneously.


🏁 Script executed:

#!/bin/bash
# Check if there are any rate limiting or concurrent access protections in the writeConfigToFile method
ast-grep --pattern $'writeConfigToFile($_, $_) {
  $$$
}'

Length of output: 58


Let’s locate the actual implementation of writeConfigToFile(...) to inspect for any throttling or synchronization logic.


🏁 Script executed:

#!/bin/bash
# Find all occurrences and surrounding context of writeConfigToFile in Java sources
rg -n 'writeConfigToFile' -C 5 --glob '*.java'

Length of output: 4519


Let’s inspect the full body of writeConfigToFile to confirm its retry logic and any synchronization or throttling:


🏁 Script executed:

#!/bin/bash
# Dump lines 85–120 of ConfigManager.java to review writeConfigToFile implementation
sed -n '85,120p' src/main/java/botcommons/config/ConfigManager.java

Length of output: 1456


Ensure config writes don’t block bot startup
The writeConfigToFile(...) method runs blocking I/O (creating directories + writing JSON) with up to 3 retries and exponential backoff on the caller’s thread. When dozens of guilds become ready simultaneously, this can introduce significant startup latency.

Suggested fixes:

  • Offload all file writes to a dedicated executor (e.g. a single-threaded ExecutorService) so the main event threads aren’t blocked.
  • Batch or debounce rapid changes per server to coalesce multiple writes into one.
  • Consider deferring persistence on startup (load all configs, then flush them once) rather than writing each immediately during readiness events.
🤖 Prompt for AI Agents
In src/main/java/botcommons/config/ConfigManager.java around lines 139-140, the
call to writeConfigToFile(serverId, configs.get(serverId)) performs blocking I/O
on the main thread, causing startup delays when many guilds are ready
simultaneously. To fix this, refactor the code to offload all file writes to a
dedicated single-threaded ExecutorService to avoid blocking event threads.
Additionally, implement batching or debouncing logic to combine multiple rapid
writes per server into a single write operation. Optionally, defer persistence
during startup by loading all configs first and then flushing them once after
readiness events complete.

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.

Add documentation

2 participants