The most powerful polling and voting bot.
Supports single-choice polls, multi-choice polls, ranked-choice voting, governance guardrails, starboard, exports, builders, and utility commands.
- Polls
- Prediction markets
- Interactive poll builder
- Governance guardrails for polls
- Persistent poll lookup
- Poll participation analytics
- Configurable poll reminders with optional role pings
- Poll CSV export with optional R2 upload
- Message context-menu poll seeding
- Starboard
- Ping health check
Interactive Poll Builder
|
Live Results Diagram
|
Ranked-Choice Ballot Menu
|
Pass Threshold Configuration
|
CSV Export
|
- Node
22.12.0+ pnpm- Redis and Postgres, ideally running in Docker
Copy .env.example to .env and fill in:
DISCORD_TOKENDISCORD_CLIENT_IDDISCORD_GUILD_IDDISCORD_ADMIN_USER_IDSoptional comma-separated Discord user IDs allowed to edit admin-gated bot config like/search configDISCORD_PRESENCE_STATUSoptional:online,idle,dnd, orinvisibleDISCORD_ACTIVITY_TYPEoptional:playing,listening,watching,competing, orstreamingDISCORD_ACTIVITY_TEXToptional activity text, for examplehelpDISCORD_ACTIVITY_URLoptional stream URL, only used when the activity type isstreamingDATABASE_URLREDIS_URLLOG_LEVELoptionalPOLL_CREATION_LIMIT_PER_HOURoptionalMEOW_LIMIT_PER_HOURoptionalSEARCH_LIMIT_PER_MINUTEMARKET_DEFAULT_TIMEZONEoptionalAPP_REVISIONoptionalR2_ACCOUNT_IDoptionalR2_ACCESS_KEY_IDoptionalR2_SECRET_ACCESS_KEYoptionalR2_BUCKEToptionalR2_PUBLIC_BASE_URLoptional, but if set it must be a full URL likehttps://example.com
For local development on your machine, use localhost in DATABASE_URL and REDIS_URL.
Poll governance guardrails require the Guild Members privileged intent to be enabled for the bot in the Discord Developer Portal.
Exhaustive audit logging works best with the Guild Members, Guild Presences, and relevant moderation / voice intents enabled in the Discord Developer Portal.
docker compose up -d postgres redis
pnpm install
pnpm prisma generate
pnpm prisma migrate deploy
pnpm register-commands
pnpm devdocker compose pull bot
docker compose up -dThe container runs Prisma migrations on startup before launching.
Postgres and Redis stay on the internal Docker network by default and are not exposed on the VPS.
By default the bot service runs ghcr.io/dillon1000/gfh-bot:latest, published from GitHub Actions on pushes to main.
PostgreSQL 18's official Docker image changed PGDATA to a versioned path and moved the declared volume root from /var/lib/postgresql/data to /var/lib/postgresql. That means existing hosts already running postgres:16-* should do an explicit major-version upgrade instead of just pulling the new image over the old volume.
Safest path:
# 1. Back up the current 16.x database before touching the container.
docker compose exec postgres pg_dumpall -U postgres > gfh-bot-postgres16-backup.sql
# 2. Stop the stack.
docker compose down
# 3. Remove the old Postgres data volume after the backup is safely stored.
docker volume ls --format '{{.Name}}' | grep '_postgres_data$'
docker volume rm <your_project>_postgres_data
# 4. Start PostgreSQL 18 with a fresh volume.
docker compose up -d postgres
# 5. Restore the dump into PostgreSQL 18.
cat gfh-bot-postgres16-backup.sql | docker compose exec -T postgres psql -U postgres
# 6. Bring the bot back once the restore completes.
docker compose up -d botIf you need a lower-downtime path for a large database, use PostgreSQL's pg_upgrade flow instead. The official image now mounts /var/lib/postgresql specifically so major-version upgrades can use the faster linked upgrade layout.
To enable automatic updates when a new registry image is published:
docker compose --profile autoupdate up -dwatchtower checks for a newer bot image every 5 minutes by default and restarts only containers labeled for updates.
/ping/market config set channel:#predictions-forum/market config view/market config disable/market create title:... outcomes:... close:24h description:... tags:.../market create title:... outcomes:... close:"April 6 2026 10:00pm CDT" description:... tags:.../market edit query:... title:... outcomes:... close:... description:... tags:.../market add-outcomes query:... outcomes:.../market view query:<message link|message id|market id>/market list status:open creator:@user tag:meta/market trade query:... action:buy outcome:1 amount:50/market trade query:... action:short outcome:1 amount:"10 pts"/market trade query:... action:cover outcome:1 amount:"2.5 shares"/market resolve query:... winning_outcome:1 note:... evidence_url:https://.../market cancel query:... reason:.../market portfolio user:@user/market leaderboard/casino config set channel:#casino/casino config view/casino config disable/casino balance user:@user/casino stats user:@user/casino slots bet:25/casino blackjack bet:25/casino poker bet:25/casino rtd bet:25/audit-log setup channel:#audit-log noisy_channel:#audit-noisy/audit-log status/audit-log disable/search messages query:... channel:#general/search advanced content:... channel_ids:"<#...>, <#...>" public:true/search config action:view/search config action:set channel_ids:"<#...>, <#...>"/search config action:clear/poll question:... choices:... description:... mode:single anonymous:false quorum_percent:60 allowed_roles:"<@&...>" blocked_roles:"<@&...>" eligible_channels:"<#...>" time:24h reminders:"1d,1h,10m" reminder_role:"<@&...>"/poll-builder/poll-results query:<message link|message id|poll id>/poll-export query:<message link|message id|poll id>/poll-analytics channel:#general days:30 limit:5- Message context menu:
Create Poll From Message /starboard setup mode:specific channel:#starboard emoji:"⭐,💎,<:gold_star:123>" threshold:3/starboard setup mode:any channel:#starboard threshold:5/starboard disable/starboard status
pnpm testIf you plan on hosting the bot for some reason, these docs should be enough for you to put in your Privileged Intents application, should you need it.
Part of this codebase was generated with AI. Review it like any other generated or third-party code before running it in production.




