Dill is a Slack bot for managing team rotations and scheduled task queues. It provides a simple, interactive interface within Slack to create, manage, and automate fair user distribution across any channel β no engineering knowledge required.
Whether you're running on-call rotations, meeting facilitators, or any recurring team responsibility, Dill keeps things fair and hands-off.
GenAI project β Dill was built with generative AI tooling and has been validated and in active use by a team for over a year.
- Create, edit and delete rotations β manage multiple independent rotations within any Slack channel
- Fair distribution β the queue is ordered by date of last accepted pick, with the least recently active member always at the front
- Flexible scheduling β weekly, fortnightly or monthly, on any day(s) of the week, at any 30-minute interval, in any timezone (UTC-12 to UTC+14)
- Accept or skip β each pick posts a message with Accept/Skip buttons; skipped members move to the back of the queue until the next day
- In-place message updates β accepted picks update the original message rather than posting a new one, keeping channels tidy
- Silent skips β when someone skips, the next person is picked silently; a record of skips is logged in the thread
- Mark members as on leave β remove a member from upcoming picks without removing them from the rotation
- Automatic reinstatement β members return to the queue automatically when leave ends
- Rotation settings β manually set a member's last accepted date to reorder the queue (useful for bootstrapping a new rotation fairly)
- Daily skip reset β skip status is automatically cleared each day at 00:01 UTC
- Fortnightly and monthly β supports every-2-week and every-4-week frequencies, not just weekly
- Slack-channel backup β back up all rotation data to a private Slack channel; the bot restores from this backup on startup
- Local JSON fallback β data is also written to local JSON files for fast reads and startup
- Central management modal β
/dillopens a modal listing all rotations with upcoming pick previews - Live updates β saving a rotation immediately refreshes the modal
- Easy navigation β Save only appears when there is something to save; otherwise modals show Close
| Command | Description |
|---|---|
/dill |
Opens the main management modal |
/dill help |
Shows all available commands (private, ephemeral) |
/dill status |
Shows bot uptime, memory and active job counts |
/dill pick [name] |
Manually triggers a pick for a rotation |
/dill reset [name] |
Randomises a rotation's queue |
/dill restore-backup |
Restores data from the most recent Slack backup |
/dill delete-backup |
Deletes all backup messages from the backup channel |
/dill kill-kill-kill confirm |
Wipes all data and backups β irreversible |
- Node.js v20.14.0 or higher (or Docker)
- A Slack workspace where you can install apps
- A way to host the bot 24/7 (see Hosting)
Go to api.slack.com/apps and create a new app from scratch.
Enable Socket Mode under Settings β Socket Mode. Generate an App-Level Token with the connections:write scope β this becomes your SLACK_APP_TOKEN.
Add a slash command under Features β Slash Commands:
- Command:
/dill - Description: Manage team rotations
- Usage hint:
[help | pick | reset | status | ...]
Add OAuth scopes under Features β OAuth & Permissions β Bot Token Scopes:
| Scope | Purpose |
|---|---|
chat:write |
Post and update rotation messages |
chat:delete |
Remove old backup messages |
commands |
Register the /dill slash command |
users:read |
Resolve member display names |
channels:read |
Read public channel info |
groups:read |
Read private channel info |
channels:history |
Read backup channel history |
groups:history |
Read backup channel history (private channels) |
channels:join |
Join public channels when a pick is triggered |
Install the app to your workspace under Settings β Install App. Copy the Bot Token (xoxb-β¦) β this becomes your SLACK_BOT_TOKEN.
Copy the Signing Secret from Settings β Basic Information β this becomes your SLACK_SIGNING_SECRET.
git clone https://github.com/cookie-monster1649/dill.git
cd dill
npm installCopy the example file and fill in your values:
cp .env.example .env# Required
SLACK_BOT_TOKEN=xoxb-your-bot-token
SLACK_APP_TOKEN=xapp-your-app-token
SLACK_SIGNING_SECRET=your-signing-secret
# Optional β enables Slack-channel backup (recommended)
DILL_STORAGE_CHANNEL_ID=C1234567890
# Optional β AES-256 encryption key for backups (64 hex chars)
DILL_BACKUP_ENCRYPTION_KEY=
# Optional
NODE_ENV=production
LOG_LEVEL=INFO
PORT=3000See Persistent Storage for details on DILL_STORAGE_CHANNEL_ID.
npm startYou should see β‘οΈ Dill Bot is running! in the console.
Add the bot to a channel:
- Public channels: run
/dillβ the bot joins automatically - Private channels: run
/invite @dillbotfirst, then/dill
Dill requires a persistent process that stays online 24/7. It uses Slack's Socket Mode, so no public inbound URL is needed.
A pre-built image is published to the GitHub Container Registry on every release.
Run with Docker:
docker run -d \
--name dill-bot \
--restart unless-stopped \
-e SLACK_BOT_TOKEN=xoxb-... \
-e SLACK_APP_TOKEN=xapp-... \
-e SLACK_SIGNING_SECRET=... \
-e DILL_STORAGE_CHANNEL_ID=C... \
-e NODE_ENV=production \
ghcr.io/cookie-monster1649/dill:latestRun with Docker Compose:
services:
dill-bot:
image: ghcr.io/cookie-monster1649/dill:latest
restart: unless-stopped
environment:
SLACK_BOT_TOKEN: xoxb-...
SLACK_APP_TOKEN: xapp-...
SLACK_SIGNING_SECRET: ...
DILL_STORAGE_CHANNEL_ID: C...
NODE_ENV: productionBuild from source:
docker build -t dill-bot .
docker run -d --name dill-bot --restart unless-stopped --env-file .env dill-botNote on data persistence with Docker: By default, local JSON files are written inside the container and lost on restart. Use
DILL_STORAGE_CHANNEL_ID(Slack-channel backup) for persistence without volume mounts β the bot restores from Slack on every startup.
- Fork this repo to your own GitHub account
- Create a new project at railway.app and connect your fork
- Add environment variables in the Railway dashboard
- Railway deploys automatically on every push to
main
Railway's free tier has sleep behaviour β upgrade to a paid plan for always-on operation.
fly launch # creates fly.toml
fly secrets set \
SLACK_BOT_TOKEN=xoxb-... \
SLACK_APP_TOKEN=xapp-... \
SLACK_SIGNING_SECRET=... \
DILL_STORAGE_CHANNEL_ID=C...
fly deployFly's smallest machine (shared-cpu-1x, 256MB RAM) is sufficient. Set min_machines_running = 1 in fly.toml to prevent sleeping.
heroku create your-dill-bot
heroku config:set \
SLACK_BOT_TOKEN=xoxb-... \
SLACK_APP_TOKEN=xapp-... \
SLACK_SIGNING_SECRET=... \
DILL_STORAGE_CHANNEL_ID=C... \
NODE_ENV=production
git push heroku main
heroku ps:scale web=1Heroku's Eco dynos sleep after 30 minutes of inactivity. Use a Basic dyno or higher for always-on operation.
Dill can back up all rotation data to a private Slack channel. On startup the bot restores from the most recent backup, so data survives restarts and redeployments.
Setup:
- Create a private Slack channel (e.g.
#dill-storage) and invite your bot to it - Get the channel ID β right-click the channel name β Copy link, then extract the
Cβ¦segment from the URL - Add it to your environment:
DILL_STORAGE_CHANNEL_ID=C1234567890
How it works:
- One backup message is kept in the channel at all times (previous messages are deleted on each write)
- Backups are triggered automatically after any data change
- Large backups are split into multiple messages and automatically reassembled on restore
- Optionally set
DILL_BACKUP_ENCRYPTION_KEY(64 hex characters) to encrypt backups with AES-256 before posting
For each rotation, a βοΈ settings button lets you manually set each member's last accepted date (YYYY-MM-DD). This controls queue order and is useful for:
- Bootstrapping a fair starting order for a new rotation
- Correcting order after members are added or removed
- Re-balancing after extended absences
Members with no date set appear at the front of the queue (they go first).
src/
βββ index.js # Entry point β starts the app
βββ app.js # Orchestrator β wires up Slack handlers and services
βββ bot/ # Slack lifecycle and modal building
βββ commands/ # Slash command handlers
βββ handlers/ # Slack action (button/modal) handlers
βββ services/ # Business logic β scheduling, analytics, storage
βββ stores/ # Data layer β JSON file persistence
βββ utils/ # Helpers β dates, rotations, Slack API wrappers
config.js # All tunable constants
Data is stored in JSON files (configs.json, rotations.json, activestate.json, analytics.json, leave.json) in the project root. These are created automatically on first run and excluded from version control.
Pull requests are welcome. For significant changes, open an issue first to discuss what you'd like to change.
npm run dev # Start with auto-restart on file changes
npm test # Run the test suiteLog verbosity can be increased with LOG_LEVEL=DEBUG in your .env.