{"id":40698027,"url":"https://github.com/wraithfive/playbot","last_synced_at":"2026-01-21T11:39:57.762Z","repository":{"id":319926309,"uuid":"1079364069","full_name":"wraithfive/playbot","owner":"wraithfive","description":"Discord gacha bot with color roles, D20 roll mechanic, multi-stream QOTD scheduling, and React admin panel (Java/Spring Boot)","archived":false,"fork":false,"pushed_at":"2026-01-12T01:26:47.000Z","size":2929,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-01-12T04:51:29.187Z","etag":null,"topics":["admin-panel","discord","discord-bot","gacha","java","jda","oauth2","qotd","react","spring-boot","websocket"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wraithfive.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-19T16:55:49.000Z","updated_at":"2025-11-08T04:39:30.000Z","dependencies_parsed_at":"2025-10-21T01:22:37.979Z","dependency_job_id":"a49b2ca5-a82e-4ebf-b672-1192eaebd8c5","html_url":"https://github.com/wraithfive/playbot","commit_stats":null,"previous_names":["wraithfive/playbot"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/wraithfive/playbot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wraithfive%2Fplaybot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wraithfive%2Fplaybot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wraithfive%2Fplaybot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wraithfive%2Fplaybot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wraithfive","download_url":"https://codeload.github.com/wraithfive/playbot/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wraithfive%2Fplaybot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28632773,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-21T04:47:28.174Z","status":"ssl_error","status_checked_at":"2026-01-21T04:47:22.943Z","response_time":86,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["admin-panel","discord","discord-bot","gacha","java","jda","oauth2","qotd","react","spring-boot","websocket"],"created_at":"2026-01-21T11:39:57.023Z","updated_at":"2026-01-21T11:39:57.756Z","avatar_url":"https://github.com/wraithfive.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Playbot\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Java](https://img.shields.io/badge/Java-21-orange.svg)](https://openjdk.org/projects/jdk/21/)\n[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.4.1-green.svg)](https://spring.io/projects/spring-boot)\n[![React](https://img.shields.io/badge/React-19.1.1-blue.svg)](https://react.dev/)\n\nA Discord bot that lets users roll for random colored name roles once per day, and manage/schedule Question of the Day (QOTD) prompts—backed by a web-based admin panel.\n\n**[Features](#features) • [Quick Start](#quick-start) • [Documentation](#table-of-contents) • [Contributing](CONTRIBUTING.md) • [License](#license)**\n\n## Features\n\n- **Discord Bot** - Users roll for random colored roles with rarity-based weighting\n- **D20 Roll Mechanic** - Risk/reward system: roll d20 within 60 minutes after daily roll for nat 20 (guaranteed Epic+) or nat 1 (48hr cooldown)\n- **Web Admin Panel** - Manage gacha roles and QOTD streams through an intuitive web interface\n- **OAuth2 Authentication** - Secure Discord login for server administrators\n- **Rarity System** - 5-tier rarity system (Legendary, Epic, Rare, Uncommon, Common)\n- **Daily Rolls** - Users can roll once per day (admins can use `/testroll` for unlimited testing)\n- **Database Migrations** - Schema fully managed by Liquibase with a clean baseline and safe, idempotent changesets\n- **Real-time Updates** - WebSocket/STOMP channel for live server/role/QOTD updates to the admin UI\n- **QOTD (Question of the Day)** - Multi-stream support with independent scheduling per channel\n  - Multiple QOTD streams per channel with custom banners, mentions, and cron schedules\n  - Per-stream timezone configuration (e.g., America/Chicago, UTC)\n  - User submissions with optional stream targeting and autocomplete\n  - Moderate submissions (approve/reject/bulk operations)\n  - Auto-approve mode per stream\n  - Bulk import via CSV with author attribution\n\n## Technology Stack\n\n**Backend:**\n- Java 21 (LTS) with modern features\n- Spring Boot 3.4.1\n- JDA 6.1.0 (Java Discord API)\n- Java Records for immutable DTOs\n- Spring Security OAuth2 (Discord) with JDBC-backed sessions and token storage\n- Spring Session (JDBC) for persistent HTTP sessions\n- Liquibase for database schema versioning\n- STOMP over WebSocket for real-time notifications\n\n**Frontend:**\n- React 19.1.1 + TypeScript\n- Vite 7.1.7 (build tool)\n- React Router DOM (navigation)\n- Axios with CSRF protection\n- TanStack React Query (data fetching)\n\n**Database:**\n- H2 Database (file-based persistence)\n- Spring Data JPA\n- Cooldown data survives restarts\n- Liquibase-managed schema (baseline + changesets). See [Database \u0026 Migrations](#database--migrations).\n\n**Security:**\n- CSRF protection enabled (cookie token for REST)\n- OAuth2 authentication flow (Discord)\n- Secure token handling\n- Session cookie and OAuth client storage via JDBC (Liquibase-managed tables)\n\n## Table of Contents\n\n- [Quick Start](#quick-start)\n- [Discord Bot Setup](#discord-bot-setup)\n- [Web Admin Panel](#web-admin-panel)\n- [Bot Permissions](#bot-permissions)\n- [Creating Color Roles](#creating-color-roles)\n- [Role Management](#role-management)\n- [Commands](#commands)\n- [API Documentation](#api-documentation)\n- [Self-Hosting](#self-hosting)\n- [QOTD (Question of the Day)](#qotd-question-of-the-day)\n- [Database \u0026 Migrations](#database--migrations)\n- [Security](#security)\n- [Troubleshooting](#troubleshooting)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Quick Start\n\n### Prerequisites\n\n- Java 21 (LTS) or higher\n- Maven 3.6+\n- Node.js 18+ (for web admin panel)\n- Discord Bot Token\n- Discord Application with OAuth2 configured\n\n## Discord Bot Setup\n\n1. **Create your Discord Application** in the [Discord Developer Portal](https://discord.com/developers/applications):\n   - Click \"New Application\"\n   - Name your bot and save\n\n2. **Get your Discord credentials** from the [Discord Developer Portal](https://discord.com/developers/applications):\n   - Click \"Reset Token\" to generate a new token\n   - Copy the token (you'll only see it once!)\n\n   **Example:**\n   ```env\n   DISCORD_TOKEN=your_actual_bot_token_from_discord_developer_portal\n   DISCORD_CLIENT_ID=your_actual_client_id_here\n   DISCORD_CLIENT_SECRET=your_actual_client_secret_here\n   ADMIN_PANEL_URL=http://localhost:8080\n   ```\n\n   **Note:** For production, change `ADMIN_PANEL_URL` to your actual domain (e.g., `https://your-domain.com`)\n\n   ⚠️ **Important:** Never commit your `.env` file to version control! It's already in `.gitignore`.\n\n4. Build the project:\n   ```bash\n   # Make the build script executable (first time only)\n   chmod +x build.sh\n\n   # Build both backend and frontend\n   ./build.sh\n\n   # Options:\n   # ./build.sh --skip-tests      # Skip running tests\n   # ./build.sh --clean           # Clean before building\n   # ./build.sh --production      # Production build with optimization\n   ```\n\n   Or build manually:\n   ```bash\n   # Backend only\n   mvn clean package -DskipTests\n\n   # Frontend only\n   cd frontend \u0026\u0026 npm install \u0026\u0026 npm run build\n   ```\n\n### Quick Start - Run Both Services\n\nFor convenience, use the startup script to run both backend and frontend:\n\n```bash\n# Make the script executable (first time only)\nchmod +x start.sh\n\n# Start both services\n./start.sh\n```\n\nThe script will:\n- Start the backend on port 8080\n- Start the frontend on port 3000\n- Display logs from both services\n- Press Ctrl+C to stop both services\n\n### Inviting the Bot\n\n**Option 1: Using the Admin Panel (Recommended)**\n\n1. Start the bot and web admin panel using `./start.sh`\n2. Login to the admin panel at `http://localhost:3000`\n3. Find a server where you're an admin but the bot isn't present\n4. Click the \"Invite Bot\" button\n5. Authorize the bot with the requested permissions\n6. **IMPORTANT:** After the bot joins, go to Server Settings → Roles and drag the Playbot role ABOVE all gacha roles\n\n**Option 2: Manual Invite (Advanced)**\n\n1. Go to the [Discord Developer Portal](https://discord.com/developers/applications)\n2. Select your application\n3. Go to OAuth2 \u003e URL Generator\n4. Select scopes: `bot` and `applications.commands`\n5. Select permissions: `Manage Roles`, `Send Messages`, `View Channels`\n6. Use the generated URL to invite the bot to your server\n7. **IMPORTANT:** After the bot joins, go to Server Settings → Roles and drag the Playbot role ABOVE all gacha roles\n\n### OAuth2 Configuration\n\nFor the web admin panel, configure OAuth2 in the Developer Portal:\n\n1. Go to OAuth2 \u003e General\n2. Add redirect URI:\n   - Development: `http://localhost:8080/login/oauth2/code/discord`\n   - Production: `https://your-domain.com/login/oauth2/code/discord`\n\n**Important:** After OAuth2 login succeeds, the backend will redirect you back to the frontend at `http://localhost:3000`\n\n## Web Admin Panel\n\nThe web admin panel provides a visual interface for managing gacha roles.\n\n### Starting the Frontend\n\n1. Navigate to the frontend directory and install dependencies:\n   ```bash\n   cd frontend\n   npm install\n   ```\n\n2. Start the development server:\n   ```bash\n   npm run dev\n   ```\n\n   The frontend will start on port 3000 (Vite may use 5173 or another port if 3000 is taken).\n\n3. Open your browser to the URL shown in the terminal (typically `http://localhost:3000`)\n\n4. Click \"Login with Discord\" to authenticate with your configured OAuth2 credentials\n\n### Features\n\n- **Server List** - View all servers where you have admin permissions\n- **Role Browser** - Browse all gacha roles organized by rarity\n- **Color Preview** - Visual preview of each role's color\n- **Bot Status** - Check which servers have the bot installed\n- **Live Updates** - Admin UI can subscribe to `/topic/guild-updates` via STOMP for join/leave/role/QOTD changes\n\n### Access Requirements\n\nTo access the admin panel, you need:\n- Administrator or Manage Server permissions on at least one server\n- The bot must be installed on your server\n\n### Customizing Legal Documents\n\nThe admin panel includes Privacy Policy and Terms of Service pages. For self-hosting, you should customize these:\n\n1. Edit the template files in `frontend/src/components/`:\n   - `PrivacyPolicy.template.tsx`\n   - `TermsOfService.template.tsx`\n\n2. Replace placeholders with your information:\n   - `[YOUR NAME/ORGANIZATION]` - Your name or organization\n   - `[YOUR CONTACT EMAIL]` - Your contact email\n   - `[YOUR WEBSITE]` - Your website URL\n\n3. Generate the production files:\n   ```bash\n   cd frontend\n   npm run generate:legal\n   ```\n\n   Or set environment variables for automatic generation during build:\n   ```bash\n   ORGANIZATION_NAME=\"Your Name\" \\\n   CONTACT_EMAIL=\"you@example.com\" \\\n   WEBSITE_URL=\"https://example.com\" \\\n   npm run build\n   ```\n\n**Note:** These templates are provided as-is. Consult with a lawyer to ensure compliance with your jurisdiction's laws.\n\n## Bot Permissions\n\nThe bot requires the following permissions:\n\n- **Manage Roles** - To assign and remove color roles from users\n- **Send Messages** - To respond to commands\n- **View Channels** - To see channels and respond to slash commands\n\n### ⚠️ CRITICAL: Role Hierarchy Setup\n\n**The bot's role MUST be positioned ABOVE all gacha roles in your server's role hierarchy.**\n\nDiscord's permission system prevents bots from managing roles that are higher than their own role in the list. Even if the bot has the \"Manage Roles\" permission, it cannot assign or remove roles that are positioned above it.\n\n**How to fix role hierarchy:**\n\n1. Open Discord and go to your Server Settings\n2. Click on \"Roles\" in the left sidebar\n3. Find the Playbot role (or your bot's role)\n4. **Drag it ABOVE all roles that start with `gacha:`**\n5. The hierarchy should look like this:\n\n```\nAdministrator Roles\n───────────────────\nPlaybot ← BOT ROLE MUST BE HERE\n───────────────────\ngacha:legendary:Rainbow\ngacha:epic:Gold\ngacha:rare:Pink\n... (all other gacha roles)\n───────────────────\n@everyone\n```\n\n**If you get permission errors when using `/roll`:**\n- This is almost always because the bot's role is too low in the hierarchy\n- Move the bot's role higher and try again\n- The bot will tell you exactly what's wrong in the error message\n\n## Creating Color Roles\n\n### Role Naming Format\n\nThe bot reads roles from your server that follow this naming convention:\n\n```\ngacha:rarity:ColorName\n```\n\nor\n\n```\ngacha:ColorName\n```\n\n**Examples:**\n- `gacha:legendary:Rainbow` - Rainbow color with legendary rarity\n- `gacha:epic:Gold` - Gold color with epic rarity\n- `gacha:common:Gray` - Gray color with common rarity\n- `gacha:Custom` - Custom color with no rarity (equal weight)\n\n### Valid Rarities\n\n| Rarity | Emoji | Drop Rate | Weight |\n|--------|-------|-----------|--------|\n| legendary | 🟡 | 0.5% | 0.25 |\n| epic | 🟣 | 2.5% | 1.25 |\n| rare | 🔵 | 7% | 2.33 |\n| uncommon | 🟢 | 20% | 4 |\n| common | ⚪ | 70% | 10 |\n\nRoles without a rarity specification will have equal weight (1.0) in the selection pool.\n\n### Quick Setup with Scripts\n\n#### Option 1: Using Bash Script\n\n1. Edit `create-roles.sh` and set your bot token and guild ID:\n   ```bash\n   BOT_TOKEN=\"your_bot_token_here\"\n   GUILD_ID=\"your_guild_id_here\"\n   ```\n\n2. Run the script:\n   ```bash\n   chmod +x create-roles.sh\n   ./create-roles.sh\n   ```\n\nThis creates 17 default roles:\n- 1 legendary (Rainbow)\n- 2 epic (Gold, Violet)\n- 3 rare (Pink, Cyan, Lime)\n- 5 uncommon (Blue, Red, Green, Purple, Orange)\n- 7 common (Gray, Brown, Olive, Teal, Navy, Maroon, Beige)\n\n#### Option 2: Using Postman\n\n1. Import `discord-gacha-roles.postman_collection.json` into Postman\n2. Set collection variables:\n   - `bot_token`: Your bot token\n   - `guild_id`: Your server/guild ID\n3. Run the collection\n\n#### Option 3: Manual Creation\n\n1. Go to Server Settings \u003e Roles\n2. Create a new role\n3. Name it using the format: `gacha:rarity:ColorName`\n4. Set the color you want\n5. Ensure it's positioned BELOW the bot's role\n6. Repeat for each color you want to add\n\n## Role Management\n\n### Role Hierarchy\n\n```\n[Bot Role] ← MUST BE HERE OR HIGHER\n───────────\ngacha:legendary:Rainbow\ngacha:epic:Gold\ngacha:epic:Violet\ngacha:rare:Pink\n... (other gacha roles)\n───────────\n@everyone\n```\n\n**Important:** If gacha roles are above the bot's role, the bot cannot assign them.\n\n### Adding New Colors\n\nTo add a new color to the gacha pool:\n\n1. Create a role with the naming format: `gacha:rarity:ColorName`\n2. Set the desired color\n3. Position it below the bot's role\n4. Users can immediately start rolling for it (no bot restart needed!)\n\n### Removing Colors\n\nTo remove a color from the gacha pool:\n\n1. Delete or rename the role (remove the `gacha:` prefix)\n2. The bot will stop including it in rolls immediately\n\n### Changing Drop Rates\n\nTo adjust the distribution of rarities, edit the values in `ColorGachaHandler.java`:\n\n```java\nprivate enum Rarity {\n    COMMON(10, \"⚪\", \"70%\"),      // Adjust weight (currently 10)\n    UNCOMMON(4, \"🟢\", \"20%\"),     // Adjust weight (currently 4)\n    RARE(2.33, \"🔵\", \"7%\"),       // Adjust weight (currently 2.33)\n    EPIC(1.25, \"🟣\", \"2.5%\"),     // Adjust weight (currently 1.25)\n    LEGENDARY(0.25, \"🟡\", \"0.5%\"); // Adjust weight (currently 0.25)\n}\n```\n\nThen rebuild and restart the bot.\n\n## Commands\n\nAll commands use Discord's slash command system. Type `/` in Discord to see available commands.\n\n### User Commands\n\n| Command | Description | Restrictions | Visibility |\n|---------|-------------|--------------|------------|\n| `/roll` | Roll for a random color | Once per day | Public (everyone sees result) |\n| `/d20` | Roll d20 for bonus/penalty | Available 60 min after `/roll` | Public (everyone sees result) |\n| `/mycolor` | Check your current color and rarity | None | Private (only you see) |\n| `/colors` | View all available colors and rarities | None | Private (only you see) |\n| `/help` | Show help message and legal links | None | Private (only you see) |\n| `/qotd-submit` | Submit a question suggestion | None | Private (only you see) |\n\n### Admin/Moderator Commands\n\n| Command | Description | Permissions Required | Visibility |\n|---------|-------------|---------------------|------------|\n| `/testroll` | Test roll (unlimited uses) | Administrator, Manage Server, or Moderate Members | Private (only you see) |\n\n**Note:** Most commands use \"ephemeral\" responses, meaning only you can see the bot's reply. This keeps channels clean and makes the experience more private.\n\n## API Documentation\n\nThe web admin communicates with a secured REST API and receives live updates over WebSocket/STOMP. See the full API and WebSocket docs in [WEB_API_README.md](WEB_API_README.md).\n\nAll API endpoints (except `/api/health`) require Discord OAuth2 authentication.\n\nFor QOTD endpoints (configs, questions, submissions, CSV upload) and the `/ws` STOMP topic details, refer to the API doc.\n\n## Self-Hosting\n\nFor deployment, environment variables, and reverse proxy notes, see [SELF_HOSTING.md](SELF_HOSTING.md).\n\n## Troubleshooting\n\n### ❌ \"Failed to assign role!\" Error\n\n**This is the most common issue and is almost always caused by role hierarchy.**\n\n**Solution:**\n1. Open Discord → Server Settings → Roles\n2. Find the Playbot role (or your bot's role name)\n3. **Drag it ABOVE all `gacha:` roles**\n4. Try using `/roll` again\n\nThe bot will tell you the specific issue in the error message:\n- **\"Missing Permissions\"** → Bot doesn't have \"Manage Roles\" permission\n- **\"hierarchy\"** → Bot's role is too low in the role list\n- Other errors will show the specific Discord API error\n\n### Bot isn't changing user name colors\n\n**Cause:** The bot's role is not high enough in the role hierarchy.\n\n**Solution:**\n1. Go to Server Settings \u003e Roles\n2. Drag the bot's role ABOVE all gacha roles\n3. Try rolling again\n\nSee the [Role Hierarchy Setup](#️-critical-role-hierarchy-setup) section for detailed instructions.\n\n### \"No gacha roles found\" error\n\n**Cause:** No roles starting with `gacha:` exist on the server.\n\n**Solution:** Create roles using the naming format `gacha:rarity:ColorName` or run the setup script.\n\n### Rolls seem unbalanced\n\n**Cause:** The distribution might be affected by the number of roles in each rarity tier.\n\n**Example:** If you have 10 common roles and only 1 legendary role, the system works as intended - each individual common role is less likely than the legendary, but commons as a category are more likely.\n\n**Solution:** This is by design. Each individual role is weighted by its rarity, but having more roles in a rarity tier doesn't change the overall probability of that tier.\n\n### Bot not responding to slash commands\n\n**Possible causes:**\n1. Bot is offline - Check if the bot process is running (`./start.sh`)\n2. Bot lacks permissions - Ensure bot has \"Send Messages\" and \"View Channels\" permissions\n3. Commands not registered - Kick and re-invite the bot, or wait up to 1 hour for Discord to sync commands\n\n### Slash commands not appearing\n\n**Solution:**\n1. Make sure you invited the bot with the `applications.commands` scope\n2. If you used the old invite (before slash commands), remove and re-invite the bot\n3. Commands are registered per-server when the bot joins or starts up\n\n## Testing Mode\n\nCurrently, the daily roll limit is **disabled** for testing purposes. This is controlled in `ColorGachaHandler.java` lines 84-95.\n\nTo **enable** the daily limit for production:\n\n1. Open `src/main/java/com/discordbot/ColorGachaHandler.java`\n2. Find the comment `// TESTING MODE: Infinite rolls enabled`\n3. Uncomment the daily check code block\n4. Rebuild and restart the bot\n\n## Finding Your Guild ID\n\n1. Enable Developer Mode in Discord:\n   - User Settings \u003e Advanced \u003e Developer Mode (toggle on)\n2. Right-click your server icon\n3. Click \"Copy Server ID\"\n\n## Support\n\nFor issues or feature requests, please check the bot logs for error messages. The bot logs detailed information about role assignment operations.\n\nCommon log messages:\n- `Removing old role: [role name]` - Bot is removing previous gacha role\n- `Assigning role: [role name] to user: [username]` - Bot is assigning new role\n- `Successfully assigned role!` - Role assignment succeeded\n- `ERROR assigning role: [error]` - Role assignment failed (check permissions/hierarchy)\n\n## Contributing\n\nWe welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on:\n\n- Reporting bugs\n- Suggesting features\n- Submitting pull requests\n- Code standards\n- Development setup\n\n### Quick Contribution Steps\n\n1. Fork the repository\n2. Create a feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\nPlease read our [Code of Conduct](CODE_OF_CONDUCT.md) before contributing.\n\n## Database \u0026 Migrations\n\nThis project uses Liquibase to manage the database schema.\n\n- Migrations live under `src/main/resources/db/changelog/`\n- A documented baseline (`000-initial-schema.xml`) captures tables that previously existed\n- Spring tables (sessions and OAuth2 authorized clients) are managed in `000-spring-framework-tables.xml`\n- Hibernate DDL is set to `validate` only; Liquibase owns schema changes\n- Spring auto-initialization for session and OAuth schemas is disabled; see `application.properties`\n\nCommon tasks:\n\n- Add a migration: create `changes/00X-your-change.xml`, include it in `db.changelog-master.xml`\n- Use preconditions with `onFail=\"CONTINUE\"` to keep changesets idempotent\n- For NOT NULL additions: add nullable → backfill → add not-null constraint\n\nSee [DATABASE_MIGRATIONS.md](DATABASE_MIGRATIONS.md) for templates and full guidance.\n\n## Security\n\nHigh-level security posture:\n\n- OAuth2 login with Discord; only server admins can access secured endpoints\n- \"Staff\" role is treated as admin-equivalent for server management (except bot invites, which remain admin-only); bot must be present to detect Staff membership\n- CSRF protection enabled for REST via cookie token; WebSocket upgrades exempt\n- CORS origin is derived from `ADMIN_PANEL_URL` and restricted accordingly\n- Sessions and OAuth client data are stored via JDBC tables (Liquibase-managed)\n- Reverse-proxy headers (X-Forwarded-*) are respected for correct redirects\n\nFor a hardening checklist (cookie flags, HSTS, rate limiting, logging levels, proxy config), see the Production Hardening section in [SELF_HOSTING.md](SELF_HOSTING.md#production-hardening).\n\n## Privacy \u0026 Legal\n\nIf you're self-hosting Playbot, you are responsible for providing your own Privacy Policy and Terms of Service to your users.\n\n**Templates are provided** in `frontend/src/components/`:\n- `PrivacyPolicy.template.tsx` - Customize with your information\n- `TermsOfService.template.tsx` - Customize with your information\n\nCopy these files to `PrivacyPolicy.tsx` and `TermsOfService.tsx` and replace all `[PLACEHOLDERS]` with your details. Consult with a lawyer to ensure compliance with your jurisdiction.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n### Attribution\n\nThis project uses several open source libraries. See [ATTRIBUTIONS.md](ATTRIBUTIONS.md) for full details and license information.\n\n## Acknowledgments\n\n- [JDA](https://github.com/discord-jda/JDA) for the excellent Discord API wrapper\n- [Spring Boot](https://spring.io/projects/spring-boot) for the robust backend framework\n- [React](https://react.dev/) for the frontend framework\n- All [contributors](https://github.com/wraithfive/playbot/contributors) who have helped improve this project\n\n---\n\n**Built with ❤️ using Java and React**\n\n**Repository:** https://github.com/wraithfive/playbot\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwraithfive%2Fplaybot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwraithfive%2Fplaybot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwraithfive%2Fplaybot/lists"}