Basic moderation Discord bot with a mobile-friendly web admin GUI, designed to run in Docker using env.env.
Invite the bot: Discord OAuth2 Invite
Project wiki files live in wiki/.
wiki/Home.md- wiki index and maintenance workflowwiki/Command-Reference.md- full slash command documentationwiki/Feed-Integrations.md- web-managed Reddit, WordPress, LinkedIn, and YouTube feedswiki/Multi-Guild-and-Env.md- multi-guild behavior and environment variable patternswiki/Web-Admin-Interface.md- web GUI authentication, pages, and security controlswiki/Security-Hardening.md- runtime and verification hardening details
When adding or changing a bot command, update wiki/Command-Reference.md in the same pull request.
Set these in env.env:
DISCORD_TOKEN- your bot tokenGUILD_ID- optional default guild ID (needed for legacy single-guild defaults; can be omitted in multi-guild mode)MANAGED_GUILD_IDS- optional comma-separated guild IDs to manage/sync (defaults to all guilds the bot is in)Bot_Log_Channel- optional default text channel ID for bot action logs (can be overridden per guild in web GUI)WEB_ENABLED- enable web GUI (true/false)WEB_BIND_HOST- web server bind host (use0.0.0.0in Docker)WEB_PORT- web GUI port inside containerWEB_TLS_ENABLED- enable HTTPS for the web GUI (true/false)WEB_TLS_PORT- HTTPS web GUI port (recommended:WEB_PORT + 1)WEB_TLS_CERT_FILE- optional TLS certificate path (requiresWEB_TLS_KEY_FILE)WEB_TLS_KEY_FILE- optional TLS private key path (requiresWEB_TLS_CERT_FILE)ENABLE_MEMBERS_INTENT- settrueonly if you enabled Server Members Intent in Discord Developer PortalCOMMAND_RESPONSES_EPHEMERAL- setfalsefor public command replies,truefor user-only (ephemeral) repliesPUPPY_IMAGE_API_URL- API endpoint used by/happyfor random puppy imagesPUPPY_IMAGE_TIMEOUT_SECONDS- timeout for puppy image API requestsFUN_API_TIMEOUT_SECONDS- timeout for fun command APIs (/cat,/meme,/dadjoke)CAT_IMAGE_API_URL- API endpoint used by/catMEME_API_URL- API endpoint used by/memeDAD_JOKE_API_URL- API endpoint used by/dadjokeSHORTENER_ENABLED- enable Shortipy integration commands (/shorten,/expand)SHORTENER_BASE_URL- Shortipy base URL (example:https://l.twy4.us)SHORTENER_TIMEOUT_SECONDS- timeout for Shortipy requestsYOUTUBE_NOTIFY_ENABLED- enable background YouTube upload notificationsYOUTUBE_POLL_INTERVAL_SECONDS- polling interval for YouTube feed checksYOUTUBE_REQUEST_TIMEOUT_SECONDS- timeout for YouTube URL/feed requestsWORDPRESS_REQUEST_TIMEOUT_SECONDS- timeout for WordPress feed discovery and pollingLINKEDIN_REQUEST_TIMEOUT_SECONDS- timeout for LinkedIn profile activity requestsSPICY_PROMPTS_ENABLED- enable the repo-backed Spicy Prompts featureSPICY_PROMPTS_REPO_URL- GitHub repo URL used as the source of Spicy Prompts contentSPICY_PROMPTS_REPO_BRANCH- branch to read from the Spicy Prompts repoSPICY_PROMPTS_MANIFEST_PATH- manifest file path inside the Spicy Prompts repoSPICY_PROMPTS_REQUEST_TIMEOUT_SECONDS- timeout for Spicy Prompts repo fetchesUPTIME_STATUS_ENABLED- enable uptime status integration command (/uptime)UPTIME_STATUS_PAGE_URL- public Uptime Kuma status page URL (example:https://randy.wickedyoda.com/status/everything)UPTIME_STATUS_TIMEOUT_SECONDS- timeout for uptime API requestsWEB_ADMIN_DEFAULT_USERNAME- web admin login usernameWEB_ADMIN_DEFAULT_PASSWORD- web admin login passwordWEB_ADMIN_DEFAULT_PASSWORD_HASH- optional password hash instead of plaintext passwordWEB_ADMIN_SESSION_SECRET- session signing secret for FlaskWEB_SESSION_COOKIE_SECURE- settruewhen using HTTPSWEB_SESSION_COOKIE_SAMESITE- cookie same-site policy (Lax,Strict,None)WEB_SESSION_TIMEOUT_MINUTES- web session timeout (minutes)WEB_AVATAR_MAX_UPLOAD_BYTES- max avatar upload size for/admin/bot-profile(default2097152)WEB_ENFORCE_CSRF- enforce CSRF token checks on POST routes (true/false)WEB_ENFORCE_SAME_ORIGIN_POSTS- block cross-origin POST requests (true/false)WEB_RESTART_ENABLED- allow admin-triggered container restart from web GUI (true/false)DATA_DIR- when using the shipped Docker Compose example, host-side bind path for persistent bot data (example:/root/docker/wickedyodabot)LOG_DIR- when using the shipped Docker Compose example, host-side bind path for persistent bot logs (example:/root/docker/wickedyodabot/logs)WEB_ENV_FILE- optional path to env file used by web GUI settings editor (default:./env.env)WEB_GITHUB_WIKI_URL- optional external wiki URL button in the web GUI Wiki page
- Minimum required variable is
DISCORD_TOKEN. MANAGED_GUILD_IDSis recommended for controlled multi-guild operation.GUILD_IDis optional; keep it only for legacy/single-guild defaults.Bot_Log_Channelis optional when you configure per-guild log channels in/admin/guild-settings.
Example multi-guild config:
DISCORD_TOKEN=your-token
MANAGED_GUILD_IDS=111111111111111111,222222222222222222
WEB_ENABLED=true
WEB_BIND_HOST=0.0.0.0
WEB_PORT=8080
WEB_TLS_ENABLED=true
WEB_TLS_PORT=8081/ping/sayhi/happy/cat/meme/dadjoke/eightball/coinflip/roll/choose/roastme/compliment/wisdom/gif/poll/questionoftheday/spicy/countdown/birthday set/birthday view/birthday upcoming/birthday remove/leaderboard/trivia/wouldyourather/rps/guess/help/tags/tag/shorten/expand/uptime/logs/stats/kick/ban/timeout/untimeout/purge/unban/addrole/removerole
Detailed command behavior, parameters, and permission requirements are documented in wiki/Command-Reference.md.
All command actions (success/failure) are logged to per-guild configured log channel, or Bot_Log_Channel when set.
All actions are also written to SQLite and visible in the web GUI.
Member message activity is also recorded internally and exposed through:
/statsfor a private per-user activity summary in Discord/admin/member-activityfor guild activity rankings and export in the web GUI
SQLite storage is internal to the container at /app/data/mod_actions.db.
- HTTP URL:
http://localhost:8080 - HTTPS URL:
https://localhost:8081 - Health check (ready):
http://localhost:8080/health(returns200only when Discord bot is ready) - Login:
WEB_ADMIN_DEFAULT_USERNAME/WEB_ADMIN_DEFAULT_PASSWORD - If
WEB_TLS_ENABLED=trueand cert/key files are not set, Flask runs with an adhoc self-signed certificate (requirescryptography, included in this image). - Use the guild dropdown in the top nav to switch the server you are managing.
- Theme switcher (Light/Black) is available in the top nav and persists per browser.
- Login includes optional "Keep me signed in" mode (5-day max), inactivity timeout, and IP-based login attempt throttling.
- Pages:
- Home (
/admin/home) - Servers (
/admin/guilds) - Dashboard (
/admin) - Status (
/admin/status) - Action history (
/admin/actions) - Member activity (
/admin/member-activity)- per-guild message activity leaderboards for
24h,7d,30d, and90d - ZIP export for guild activity data, optionally filtered by role
- per-guild message activity leaderboards for
- Reddit feeds (
/admin/reddit) - WordPress feeds (
/admin/wordpress) - LinkedIn feeds (
/admin/linkedin) - YouTube subscriptions (
/admin/youtube) - Spicy Prompts (
/admin/spicy-prompts)- refreshes prompt packs from the configured GitHub repo without restarting the bot
- stores a guild-specific allowed Discord channel for
/spicy - rejects non-age-restricted channels for Spicy Prompts use
- shows cached pack count, prompt count, last sync status, and prompt preview
- Observability (
/admin/observability, login required) - Bot profile (
/admin/bot-profile, admin only)- Includes bot username/nickname updates and avatar upload
- Guild settings (
/admin/guild-settings, admin only) - Logs viewer (
/admin/logs) - Documentation viewer (
/admin/documentation) - Wiki redirect (
/admin/wiki) - Account self-service (
/admin/account)- change email
- change first name
- change last name
- change password
- User management (
/admin/users, admin only) - Command permissions (
/admin/command-permissions, admin only) - Tag responses (
/admin/tag-responses, admin only) - Runtime settings editor (
/admin/settings, admin only) - Public status page (
/status/everything)
- Home (
The GUI is built with responsive Bootstrap layout for mobile and desktop.
Settings are editable from the GUI and saved back to env.env (or WEB_ENV_FILE), with dropdown selectors for boolean and common numeric options where possible.
- Open
/admin/youtubein the web GUI. - Add a YouTube channel URL and select the Discord channel to notify.
- The bot stores subscriptions in SQLite and polls YouTube feeds.
- On new uploads, it posts a notification embed in the selected Discord channel(s).
The web GUI also manages background notifications for:
- WordPress
- YouTube
Each feed integration supports:
- source/profile/site input
- selected Discord target channel
- schedule selection (
5m,10m,15m,30m,1h,3h,6h) - stored last-seen state in SQLite
LinkedIn support is experimental and depends on public activity being accessible without authentication.
- Open
/admin/spicy-promptsin the web GUI. - Set the repo values in
env.envor through the runtime settings page:SPICY_PROMPTS_REPO_URLSPICY_PROMPTS_REPO_BRANCHSPICY_PROMPTS_MANIFEST_PATH
- Use the
Refresh From Repobutton to pull the latest prompt manifest and pack files without restarting the bot. - The fetched prompt cache is stored in SQLite and visible in the page preview table.
Local verification command:
./scripts/verify.shThis runs:
- Python compile check
- Ruff lint + format check
- Pytest
- Bandit (in CI Python 3.12; skipped locally on Python 3.14+ due upstream tool incompatibility)
- pip-audit dependency vulnerability check
- Docker image build verification
GitHub workflows included:
.github/workflows/ci.yml- lint/test/audit/docker build.github/workflows/security.yml- Gitleaks + Trivy FS/Image scans.github/workflows/codeql.yml- CodeQL static analysis.github/workflows/dependency-review.yml- dependency risk gate for pull requests.github/workflows/python-vulnerability-scan.yml- scheduled + on-changepip-audit.github/workflows/sbom.yml- CycloneDX SBOM generation artifact.github/workflows/scorecards.yml- weekly OSSF Scorecards analysis.github/dependabot.yml- weekly dependency updates
docker compose --env-file env.env up -dThe shipped Compose example bind-mounts ${DATA_DIR:-/root/docker/wickedyodabot} on the host to /app/data inside the container.
It also bind-mounts ${LOG_DIR:-/root/docker/wickedyodabot/log} on the host to /logs inside the container.
docker-compose.yml overrides the bot's in-container DATA_DIR to /app/data and LOG_DIR to /logs.
Workflow: .github/workflows/docker-publish.yml
- Publishes on push to
main, semantic version tags (v*.*.*), or manual run. - Publishes automatically after successful
CIcompletion onmain, on semantic version tags (v*.*.*), or manual run. - Push target:
ghcr.io/<owner>/<repo>:latestghcr.io/<owner>/<repo>:<branch|tag|sha>
- Multi-arch build:
linux/amd64,linux/arm64
To trigger publish, push to main or create a tag:
git tag v0.1.0
git push origin main --tagsIn your Discord Developer Portal bot setup and server role, ensure the bot can:
- View Channels
- Send Messages
- Embed Links
- Kick Members
- Ban Members
- Moderate Members
- Manage Messages
- Read Message History