Thank you for your interest in contributing! This document explains how to get started, what to expect during the review process, and the conventions this project follows.
- Getting Started
- Making Changes
- Submitting a Pull Request
- Reporting Issues
- Project Architecture
- Legal
| Requirement | Version | Notes |
|---|---|---|
| JDK | 21+ | Required |
| Git | 2.x+ | For cloning and contributing |
| IntelliJ IDEA | Latest | Recommended IDE |
| Discord bot token | - | Create one at the Discord Developer Portal |
Required environment variables:
| Variable | Description |
|---|---|
DISCORD_TOKEN |
Discord bot token |
DEVELOPER_ERROR_LOG_CHANNEL_ID |
Discord channel ID for error logging |
-
Fork and clone the repository
Fork the repository, then clone your fork:
git clone https://github.com/<your-username>/discord-api.git cd discord-api
-
Build the project
cd discord-api ./gradlew build -
Open in IntelliJ IDEA
Open the project root as a Gradle project. Ensure the Lombok plugin is installed and annotation processing is enabled.
-
Verify the setup
./gradlew test
A DebugBot class in src/test/ allows testing commands in isolation without
starting the full bot. Set the required environment variables and run it
directly from IntelliJ or via Gradle:
./gradlew test --tests "*.debug.DebugBot"- Create a feature branch from
masterfor your work. - Use a descriptive branch name:
fix/modal-submit-handler,feat/media-gallery-component,docs/response-examples.
git checkout -b feat/my-feature master- Reactive - All command and listener handlers return
Mono<Void>using Project Reactor. Never block the event loop. - Collections - Always use
Concurrent.newList(),Concurrent.newMap(), etc. instead of standard Java collections. - Annotations - Use
@NotNull/@Nullablefromorg.jetbrains.annotationson all public method parameters and return types. - Lombok - Use
@Getter,@RequiredArgsConstructor,@Log4j2, etc. The logger field is non-static (lombok.log.fieldIsStatic = false). - Builder pattern - Use
ClassBuilder<T>with@BuildFlagvalidation. Follow the existing pattern inResponse.builder(),Page.builder(),Button.builder(), etc.
- Class level - Noun phrase describing what the type is.
- Method level - Active verb, third person singular.
- Tags -
@param,@return,@throwson public methods. Lowercase sentence fragments, no trailing period. Single space after param name. - Punctuation - Only single hyphens (
-) as separators. - Never use
@authoror@since.
- Every
DiscordCommandsubclass must have a@Structureannotation with a uniquename. - Implement
process(C context)and returnMono<Void>. - Use
getParameters()to define slash command options.
Write clear, concise commit messages that describe what changed and why.
Add Container component for Discord Components V2
Implements the new container layout component that wraps other
components with optional accent color and spoiler support.
- Use the imperative mood ("Add", "Fix", "Update").
- Keep the subject line under 72 characters.
- Add a body when the why isn't obvious from the subject.
Tests use JUnit 5 (Jupiter):
./gradlew test- The
DebugBotinsrc/test/is the primary way to test commands interactively against a live Discord gateway. - Unit tests for component builders, context logic, and handler state don't require a live connection.
-
Push your branch to your fork.
git push origin feat/my-feature
-
Open a Pull Request against the
masterbranch of SkyBlock-Simplified/discord-api. -
In the PR description, include:
- A summary of the changes and the motivation behind them.
- Steps to test or verify the changes.
- Screenshots or recordings of Discord interactions if applicable.
-
Respond to review feedback. PRs may go through one or more rounds of review before being merged.
- Correctness of reactive chains (no blocking calls, proper error handling).
- Adherence to the builder pattern and component type system.
- Impact on downstream modules (
simplified-bot). - Compatibility with Discord's API and Components V2 flag behavior.
Use GitHub Issues to report bugs or request features.
When reporting a bug, include:
- Java version (
java --version) - Discord4J version (check
gradle/libs.versions.toml) - Operating system
- Full error stacktrace (if applicable)
- Steps to reproduce
- Expected vs. actual behavior
A brief overview to help you find your way around the codebase:
src/main/java/dev/sbs/discordapi/
├── DiscordBot.java # Abstract entry point (configure -> login -> connect)
├── command/
│ ├── DiscordCommand.java # Base command class with @Structure annotation
│ ├── exception/ # CommandException, PermissionException, InputException, etc.
│ └── parameter/ # Parameter, Argument
├── component/
│ ├── Component.java # Root component interface
│ ├── TextDisplay.java # Text display component (V2)
│ ├── interaction/ # Button, SelectMenu, TextInput, Modal,
│ │ # RadioGroup, Checkbox, CheckboxGroup
│ ├── layout/ # ActionRow, Container, Section, Separator, Label
│ ├── media/ # Attachment, FileUpload, MediaGallery, Thumbnail
│ ├── capability/ # EventInteractable, Toggleable, ModalUpdatable,
│ │ # UserInteractable
│ └── scope/ # AccessoryComponent, ContainerComponent,
│ # SectionComponent, TopLevelMessageComponent, etc.
├── context/
│ ├── EventContext.java # Root context interface
│ ├── command/ # CommandContext, SlashCommandContext, AutoCompleteContext, etc.
│ ├── component/ # ComponentContext, ButtonContext, SelectMenuContext,
│ │ # ModalContext, CheckboxContext, RadioGroupContext, etc.
│ └── message/ # MessageContext, ReactionContext
├── exception/ # DiscordException, DiscordUserException, etc.
├── handler/
│ ├── DiscordConfig.java # Builder-pattern bot configuration
│ ├── CommandHandler.java # Command registration and routing
│ ├── EmojiHandler.java # Custom emoji upload/lookup
│ ├── DiscordLocale.java # BCP 47 locale enum
│ ├── exception/ # ExceptionHandler, DiscordExceptionHandler,
│ │ # SentryExceptionHandler, CompositeExceptionHandler
│ ├── response/ # ResponseHandler, CachedResponse, ResponseEntry,
│ │ # ResponseFollowup
│ └── shard/ # ShardHandler, Shard
├── listener/
│ ├── command/ # Slash, user, message command listeners
│ ├── component/ # Button, select menu, modal, checkbox,
│ │ # radio group listeners
│ ├── message/ # Message create/delete, reaction listeners
│ └── lifecycle/ # Disconnect, guild create listeners
├── response/
│ ├── Response.java # Response interface + TreeResponse/FormResponse
│ ├── Emoji.java # Emoji representation
│ ├── embed/ # Embed, Author, Field, Footer
│ ├── handler/ # HistoryHandler, PaginationHandler, OutputHandler,
│ │ │ # FilterHandler, SortHandler, SearchHandler
│ │ └── item/ # ItemHandler, EmbedItemHandler, ComponentItemHandler
│ └── page/ # Page, TreePage, FormPage, Paging, Summary, Subpages
│ └── item/ # Item, AuthorItem, TitleItem, DescriptionItem, etc.
│ └── field/ # FieldItem, StringItem, NumberItem, ToggleItem, etc.
└── util/ # DiscordReference, DiscordDate, DiscordProtocol, ProgressBar
- New command - Extend
DiscordCommand<SlashCommandContext>(or other context type) and annotate with@Structure. - New component - Implement the relevant
Componentinterface and add a builder following the existing pattern. - New listener - Extend
DiscordListener<T extends Event>in thelistener/package. It will be discovered automatically via classpath scanning. - New response type - Implement the
Responseinterface with a customHistoryHandler. - New exception handler - Extend
ExceptionHandlerand register it viaDiscordConfigor wrap it in aCompositeExceptionHandler.
By submitting a pull request, you agree that your contributions are licensed under the Apache License 2.0, the same license that covers this project.