# disbot-a4 — Simple, robust role selector bot **Author:** Lari Natri, 2025 **License:** MIT A simple, robust Discord role-picker bot built with **py-cord** and packaged for Docker Compose. - **Language/Libs:** Python 3.12, [py-cord](https://docs.pycord.dev/en/stable/) only - **Persistence:** SQLite (stdlib), bind-mounted `./data` - **Daily inactivity cleanup** removes all pickable roles from users inactive for N days (default 14). - **Auto-refresh** updates all status panels and DM panels at a configurable interval (default 5 minutes). - **Restart-safe:** DB + tasks resume; re-create UI via `/role-admin-post-status` (or auto-post on startup if configured) - **Public status panels:** list *pickable* roles with member counts and "(inactive)/(full)" hints from configurable min/max values. - **UI: Personal role picker** via `/roles` (sent to your DMs) with buttons to toggle your roles. - **UI: User slash commands** for listing and toggling roles. - **UI: Admin slash commands** to configure pickable roles, limits, inactivity timeout, refresh cadence, list members, manage users, and post status panels. --- ## 1) Quick start 1. **Discord Developer Portal setup** [Discord Developer Portal](https://discord.com/developers/applications) 1. **Create Application -> Bot**: copy the **token** to `secrets/discord_token.txt`. 2. **Privileged Gateway Intents** (Bot settings -> Privileged Gateway Intents): - Enable **Server Members Intent** - *(Optional)* Enable **Message Content Intent** if you want the bot to track activity from messages for inactivity cleanup. Otherwise the bot still works; you can set activity via role interactions only. 1. **Invite** the bot with scopes: - `bot`, `applications.commands` 2. **Bot Permissions**: at minimum grant - **Manage Roles** - **View Channel** - **Send Messages** - **Read Message History** - **Manage Messages** 3. Copy the generated **Invite URL** and invite the bot to your server. 4. Grab your **Guild ID** (enable Developer Mode in Discord client -> right-click server icon -> **Copy Server ID**) and set it in `.env` as `GUILD_ID`. > **Role hierarchy**: Move the bot's highest role **above** every pickable role you want it to manage. The bot cannot add/remove roles equal to or above its top role. Managed/integration roles cannot be assigned by bots. 1. **Create secrets & env** ```bash mkdir -p secrets data sudo chown -R 10001:10001 data echo "YOUR_BOT_TOKEN_HERE" > secrets/discord_token.txt cp .env.example .env # Edit .env: set GUILD_ID and (optionally) ROLE_PANEL_CHANNEL_ID, etc. ``` 2. **Build & run** ```bash docker compose up -d --build --remove-orphans ``` 3. **Add selectable roles for the server** - On Discord server, add selectable roles with slash command: `/role-admin-add-pickable:@Role` (optional `min_users`, `max_users`) - See all commands with `/role-help` 4. **Post the role panel** - If you set `ROLE_PANEL_CHANNEL_ID` in `.env`, the bot will auto-post the panel on startup. - Otherwise, in any channel, run (as an admin): `/role-admin-post-status` --- ## 2) Environment variables | Name | Required | Default | Notes | |------|----------|---------|-------| | `GUILD_ID` | **Yes** | — | Guild (server) ID where the bot operates & registers commands (fast sync). | | `ROLE_PANEL_CHANNEL_ID` | No | — | If set, bot auto-posts the panel on startup to this channel. | | `INACTIVITY_DAYS_DEFAULT` | No | `14` | Initial value for inactivity (can be changed with slash command). | | `REFRESH_MINUTES_DEFAULT` | No | '5' | Initial value for refreshing role status on posts (can be changed with slash command). | | `DATABASE_PATH` | No | `/app/data/bot.db` | SQLite DB path (persisted via volume `./data:/app/data`). | | `LOG_LEVEL` | No | `INFO` | `DEBUG`, `INFO`, `WARNING`, `ERROR`. | | `TZ` | No | `Europe/Helsinki` | Provided via Docker environment in `docker-compose.yml`. | **Secret** (via Docker secrets): - `./secrets/discord_token.txt` — put your bot token here (no quotes, single line). --- ## 3) What the bot does ### Public status panels - Posted with **/role-admin-post-status** (admin-only; posts a message visible to everyone). - Lists each **pickable** role, current **member count**, plus **(inactive)** if count < min and **(full)** if count ≥ max (when configured). - Contains a **Refresh** button. - The bot stores all posted panel message IDs and **auto-refreshes** them (see below). ### Personal DM role picker - Users run **/roles** to receive a **DM** with a personalized panel. - Buttons show primary/secondary style depending on whether the user currently has each role. - If DMs are closed, the bot shows a one-off ephemeral panel in the channel instead (not tracked for auto-refresh). ### Auto-refresh - Refresh cadence is configurable globally per guild via **/role-admin-refresh** (minutes). - The bot updates: - **All public status panels** it has posted in that guild. - **All tracked DM panels** it sent with `/roles`. - Also refreshes promptly after admin config changes and user role toggles. ### Inactivity cleanup - Daily, removes **all pickable roles** from members whose last activity timestamp is older than **N** days (configurable). - "Activity" is updated on any message or role interaction handled by the bot. - Default inactivity days come from `INACTIVITY_DAYS_DEFAULT` and can be changed live using **/role-admin-set-inactivity-days**. #### What counts as **activity**? - Any **message** a user sends in the guild updates their `last_active` timestamp. - The daily job removes **selectable** roles from users with no messages for longer than `inactivity_days`. > You can change `inactivity_days` anytime using `/role-admin-set-inactivity-days`. > Default is taken from `INACTIVITY_DAYS_DEFAULT` when the bot first sees the > guild. --- ## 4) Commands ### User commands - **/roles** Open your personal role picker (DM). Shows the server name & ID in the message. > NOTE: The UI shows at most **25** role buttons in a single panel message > due to Discord limits. - **/role-list** List the pickable roles you currently have (ephemeral reply). - **/role-pick \** Add yourself to a pickable role. - **/role-unpick \** Remove yourself from a pickable role. - **/role-toggle \** Toggle a pickable role on/off. - **/role-help** Show all user commands. If you're an admin, admin commands are listed as well. ### Admin commands - **/role-admin-post-status** Post a public role **status panel** in the current channel (anyone can see it). Panel lines include min/max-based labels: "(inactive)" if count < min and "(full)" if count ≥ max. - **/role-admin-add-pickable \ [min] [max]** Add or update a pickable role and optional min/max hints. - **/role-admin-remove-pickable \** Remove a role from the pickable list. - **/role-admin-set-role-limits \ [min] [max]** Adjust min/max hints for a pickable role. - **/role-admin-set-inactivity-days \** Set inactivity timeout in days (default: 14). - **/role-admin-set-refresh-interval \** Set auto-refresh interval in minutes (default: 5). - **/role-admin-refresh-now** Trigger an immediate refresh - **/role-admin-list-users** List all pickable roles and the users in each (ephemeral to the admin). - **/role-admin-list-config** Show current configuration (inactivity days, refresh minutes, pickable roles with limits). - **/role-admin-add-user-for-role \ \** Add a specific user to a **pickable** role (admin-controlled). - **/role-admin-remove-user-from-role \ \** Remove a specific user from a **pickable** role (admin-controlled). > All admin commands respond **ephemerally** to the invoking admin. Public > status panels are visible to everyone. --- ## 5) Persistence - SQLite database at `DATABASE_PATH` (default `/app/data/bot.db`), bind-mounted to `./data/bot.db` by docker-compose. - Tables: - `settings` (inactivity_days, refresh_minutes) - `roles` (pickable role IDs + optional min/max) - `activity` (last activity per user) - `panels` (public panel messages for auto-refresh) - `dm_panels` (user DM panels for auto-refresh) Back up by copying the `data/` directory (container must be stopped for consistent copy if heavy write load is ongoing). --- ## 6) Troubleshooting - **Buttons say "I cannot manage that role due to role hierarchy/permissions."** - Grant the bot **Manage Roles** permission. - Move the bot's highest role **above** all pickable roles. - Don't use managed/integration roles as pickables. - **"Missing Access (50001)" when posting a status panel** Verify the bot has **View Channel** and **Send Messages** in that channel. Avoid forum roots; use a text channel or a thread. - **Commands don't show up** If `GUILD_ID` is set, commands register immediately for that guild. Without it, global registration can take a while. Set `GUILD_ID` to your server ID in `.env` for instant registration during setup. - **Inactivity cleanup appears to skip** Ensure `GUILD_ID` is set to your target guild. The daily task only runs for `GUILD_ID` (by design). - **DM panel not received** User may have DMs closed. The bot will fallback with an ephemeral panel (not tracked for auto-refresh).