Discord bot
The Discord bot lets you ingest images from Discord channels into Pindeck using emoji reactions, with a built-in moderation queue.
Overview
The bot and media gateway are deployed from a separate repository (discord-bot). The Pindeck app consumes their services via Convex HTTP actions.
Setup
1. Create a Discord application
- Go to the Discord Developer Portal
- Create a new application and bot
- Note the Client ID and Bot Token
- Enable the Message Content Intent in the bot settings
2. Invite the bot to your server
Use the OAuth2 URL generator with the bot scope and permissions:
- Read Messages
- Read Message History
- Add Reactions
- Manage Messages (for moderation)
Set these in the Discord bot’s .env.local:
| Variable | Description |
|---|
DISCORD_TOKEN | Bot token from the Developer Portal |
DISCORD_CLIENT_ID | Application client ID |
DISCORD_GUILD_ID | Server ID where the bot operates |
DISCORD_INGEST_EMOJIS | Emoji triggers for image ingest (e.g., :pushpin:) |
INGEST_API_KEY | API key matching Convex’s INGEST_API_KEY |
PINDECK_INGEST_URL | Optional override for ingest endpoint URL |
PINDECK_DISCORD_QUEUE_URL | Optional override for queue endpoint URL |
PINDECK_DISCORD_MODERATION_URL | Optional override for moderation endpoint URL |
MEDIA_GATEWAY_URL | RustFS-backed media API URL |
MEDIA_GATEWAY_TOKEN | Bearer token for media API writes |
MEDIA_GATEWAY_BUCKET | RustFS bucket, normally pindeck |
4. Run the bot
cd ~/Documents/Github/discord-bot
bun install
bun run dev
How ingest works
- A user adds a configured emoji reaction to a message with an image attachment
- The bot sends the image to Pindeck’s
/ingestExternal HTTP endpoint
- Pindeck stores the image in RustFS, including preview/derivative URLs and five sampled colors
- Pindeck creates a record with
sourceType = "discord"
- The image appears in the moderation queue
Moderation queue
New Discord-sourced images enter the moderation queue:
- Images land in a pending state via the
/discordQueue endpoint
- Moderators review and approve or reject via the
/discordModerate endpoint
- Approved images move into the same
Review & Finalize draft lane as local uploads
- Draft Discord images use the same sampled colors, colored tags, metadata fields, and variation controls as local uploads
- Rejected images are removed
Approved status webhook messages also trigger the Discord bot to post the six variation buttons when DISCORD_APPROVED_VARIATION_BUTTONS is enabled: Shot, Action, Coverage, B-Roll, Style, and Subtle.
Hosting
The Discord bot runs on a Hostinger VPS:
- Hostname:
srv1353991
- Public IP:
187.77.8.227
- Tailscale IP:
100.105.199.93
- User:
root
Connect via SSH:
Tailscale SSH can still be used as a fallback operator path, but the normal deploy workflow now uses direct SSH to the public IP.
Do not treat services/discord-bot in the Pindeck repo as the deployment source. Always use the separate discord-bot repository.
API endpoints
| Endpoint | Method | Description |
|---|
/ingestExternal | POST | Accept an external image for ingest |
/discordQueue | POST | Add an image to the moderation queue |
/discordModerate | POST | Approve or reject a queued image |
See the API reference for full endpoint documentation.
Hostinger DNS note
The Hostinger bot container runs on the same public host that serves the Convex HTTP domains. If the container resolves convex-site.serving.cloud or convex.serving.cloud back to 127.0.1.1, Bun fetches fail with connection errors. The deployed Compose file pins those names to the Docker host gateway (172.18.0.1) using extra_hosts.