# ShbDiscordBot contributor guide ## Purpose and invariants This repository builds one Paper plugin which embeds a JDA Discord bot. Discord is the entry point for issuing short-lived link codes, Minecraft is the only place where a code can be consumed, and ShbUtils Core API is the source of truth for persistent Discord-to-Minecraft links. - `/auth` is a global Discord slash command intended for bot DMs. - `/link ` is a player-only Paper command. - `/sdb reload` is the operator-only full runtime/config reload command. Do not override Paper's built-in `/reload` command. - Link codes are process-local, single-use, active for ten minutes by default, and must never be logged. - Existing Discord or Minecraft links must not be overwritten. - Discord roles only grant LuckPerms nodes. This project deliberately does not revoke them. ## Build and verification - Use Java 25 and the checked-in Gradle wrapper. - `./gradlew test` runs the automated test suite. - `./gradlew shadowJar` creates the deployable plugin JAR; the plain JAR does not include JDA/Jackson. - `./gradlew build` must pass before handing work off. - LuckPerms and Paper are compile-only/server-provided dependencies. Do not shade either of them. ## Architecture rules - Keep configuration parsing, Discord/JDA integration, ShbUtils HTTP transport, link orchestration, Paper commands, and LuckPerms synchronization in separate classes. - Do not perform network requests, JDA waits, or LuckPerms user loads on the Paper main thread. - Only touch Bukkit player/server state on the Paper main thread. Capture UUIDs and immutable values before crossing thread boundaries. - Make scheduled role scans non-overlapping and bound API concurrency. - Treat API `401` and validation/conflict responses as permanent failures; treat I/O, timeouts, `429`, and `5xx` as retryable. - On disable, cancel tasks and close JDA plus plugin-owned executors. - A runtime reload must validate the new config before stopping the current runtime, invalidate pending codes, await JDA shutdown off the Paper thread, recreate all config-bound services, and immediately run role synchronization. ## Security and configuration - Never commit real Discord tokens or `X-Internal-Secret` values. The resource `config.yml` contains empty placeholders only. - Never include secrets, bearer tokens, full HTTP headers, or active link codes in logs or exceptions shown to players. - Parse Discord snowflakes as strings/unsigned IDs and Minecraft identifiers as UUIDs. - Generate codes with `SecureRandom`; preserve leading zeroes and enforce rate limiting when consuming invalid codes. ## API contract and documentation - The local API reference is `docs/API.md`, based on `https://api.shlakoblock.com/api.json`. - Runtime code uses only internal status and Discord-link endpoints. Do not use the API verification-code endpoints for the Discord-to-Minecraft flow: those endpoints implement the reverse direction. - When the upstream OpenAPI version changes, compare every path/schema and update `docs/API.md`, DTOs, tests, and the recorded snapshot date together. ## Testing expectations - Cover code uniqueness, expiry, replacement, rate limiting, reservation/release, and concurrent single-use behavior. - Test HTTP paths, JSON fields, internal-secret headers, status handling, and timeout/error classification without contacting production. - Test role-to-permission union, idempotent grants, unlinked users, and grant-only behavior. - A feature is incomplete if it can block the Paper main thread or leak a secret/code to logs.