Skip to content

weblab-technology/tacobot

ย 
ย 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

127 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Tacobot ๐ŸŒฎ

Internal Slack recognition program for the wlt-and-shaman workspace. Inspired by HeyTaco.

Give ๐ŸŒฎ reactions to teammates in #taqueria. Every employee has a daily allowance (default 5). Tacos accumulate as a balance that can be redeemed in the shop โ€” HR-mediated.

What it does

  • Give by reaction: react to a teammate's message with ๐ŸŒฎ โ†’ they get 1 taco.
  • Give by mention: post <@teammate> :taco: :taco: in an allowlisted channel โ†’ 2 tacos to that teammate. Multi-recipient gives split the count per recipient.
  • Daily allowance: each user gets TACO_DAILY_ALLOWANCE tacos to give per day, auto-reset at 00:00 UTC by a Vercel cron.
  • Two counters per user: lifetime received_total (for the leaderboard) and current balance (redeemable).
  • DM commands: score, balance, left, shop, help โ€” English and French aliases.
  • Public shop: /shop lists active items with prices and descriptions. When SHOP_SELF_SERVE_ENABLED=true, signed-in users can Buy directly โ€” the purchase atomically debits balance + decrements stock + creates a pending order, and the bot posts an announcement to #tacoshop with Mark fulfilled / Cancel & refund buttons that admins click without leaving Slack. Buyers see their own pending orders on /shop and can self-cancel.
  • Admin console: /admin/items (catalog CRUD with image upload), /admin/users (redemption form), /admin/activity (chronological give feed with reversal status, channel filter, and Slack deep-links), and /admin/leaderboard (ranked list with metric/period/channel filters). All gated by Sign in with Slack against ADMIN_SLACK_IDS.
  • Append-only audit log: every give, redemption, and reversal is a row in transactions with the channel, message timestamp, admin, item, and reason.
  • Reversible gives: deleting your :taco: message or removing your ๐ŸŒฎ reaction writes a compensating type='reversal' row, decrements the recipient's balance, restores your daily allowance (capped at the daily cap), and DMs both parties.
  • Slack-retry idempotent: each individual taco has a unique slack_event_id; retries are no-ops, not duplicates. Reversals additionally key on reversed_transaction_id so a single give can be reversed at most once.
  • Concurrent-safe: gives and redemptions use atomic UPDATE โ€ฆ WHERE balance/daily_remaining >= N so concurrent attempts can't overdraw.

Quick links

Stack

Next.js 15 + TypeScript + Tailwind on Vercel Pro ยท Bolt for JS (Slack Events API) ยท Vercel Postgres + Drizzle ORM ยท Auth.js v5 + Slack OIDC for the admin pages ยท Vitest with pglite for in-process integration tests.

Prerequisites

  • Vercel Pro (60s function timeout, unlimited cron jobs).
  • Slack workspace admin to create the app.
  • Vercel Postgres / Neon database attached to the project.

One-time setup

1. Create the Slack app

See docs/slack-setup.md for the full checklist (scopes, event subscriptions, OAuth redirect, OIDC scopes).

2. Configure environment variables

Copy .env.example to .env.local (for dev) or set them in Vercel project settings.

Variable Purpose
SLACK_BOT_TOKEN xoxb-โ€ฆ from app install
SLACK_SIGNING_SECRET App's signing secret
SLACK_BOT_USER_ID Optional; cached from auth.test if absent
SLACK_CLIENT_ID / SLACK_CLIENT_SECRET For Sign in with Slack (admin)
TACO_CHANNELS Comma-separated channel IDs where typed/reaction gives count
TACO_DAILY_ALLOWANCE Defaults to 5
TACO_ALT_EMOJI_NAME Optional; custom emoji name (no colons) accepted as currency alongside :taco:. When set, the bot's confirmation reaction (if enabled) uses this emoji instead of :taco:.
TACO_REACT_ON_GIVE Optional; if true, the bot adds a :taco: (or alt-emoji) reaction to the giver's message as a visual ack. Defaults to false โ€” recommended off, since the bot's reaction is easily mistaken for an extra give.
ADMIN_SLACK_IDS Comma-separated Slack user IDs allowed into /admin
HR_SLACK_ID Legacy /shop HR contact link (retires alongside self-serve flag)
HR_SLACK_HANDLE Legacy display handle (without @) for HR contact
SHOP_SELF_SERVE_ENABLED When true, /shop shows Buy buttons + a pending-orders panel and posts to #tacoshop. Defaults to false.
TACOSHOP_CHANNEL_ID Required when self-serve is on: Slack channel ID where order announcements + admin Fulfill / Cancel buttons live
AUTH_SECRET openssl rand -base64 32
AUTH_URL Auto-set on Vercel via VERCEL_URL
POSTGRES_URL Provided by the Vercel Postgres integration
NEXT_PUBLIC_SHOP_URL Public URL of /shop
NEXT_PUBLIC_COMPANY_NAME Appears in the page <title>; defaults to "WLT"
CRON_SECRET Auto-injected by Vercel for cron requests

3. Migrate the database

pnpm db:migrate

(Vercel runs this automatically as part of pnpm build.)

4. Bootstrap user list

After deployment, run once to import existing workspace members:

pnpm sync-users

Subsequent joiners are picked up by the team_join event automatically.

5. Invite the bot

In Slack, run /invite @tacobot in #taqueria-beta (or whichever channel(s) you set in TACO_CHANNELS).

Local development

The dev container (or any local machine) needs Node 20 and pnpm. The first install requires the project's .npmrc setting (store-dir=/home/node/.pnpm-store) to avoid a copyfile race with pnpm's default project-local store on bind-mounted filesystems.

pnpm install

Run the dev server

pnpm dev

The dev server starts fine without secrets, but the moment a route reads config.slack.botToken (or any required env var) it throws. Provide the values via .env.local before exercising the Slack webhook or admin pages.

Tests

Integration tests run against an in-process PGlite โ€” no Docker, no real Postgres needed locally:

pnpm test
pnpm test:watch     # watch mode

For the full development workflow (devcontainer, debugging, testing patterns, migration workflow, CI), see docs/development.md.

Slack event delivery during dev

Slack must reach a public URL to deliver events. For local dev:

  1. ngrok http 3000 (or cloudflared tunnel).
  2. Update the Slack app's Event Subscriptions URL to the ngrok URL with path /api/slack/events.
  3. Don't forget to set the Slack app's OAuth redirect URL too if testing admin sign-in locally.

For most development, deploying to a Vercel preview branch is simpler than running locally โ€” Vercel preview deploys are free and reachable from Slack.

Deploying to Vercel

  1. Link the project: vercel link from the repo, or use the Vercel dashboard "Import Git Repository" flow.
  2. In Vercel โ†’ Storage, create a Postgres / Neon database. This auto-injects POSTGRES_URL and friends.
  3. Set the env vars from the table above in the Vercel project settings.
  4. The vercel.json already declares the daily-reset cron at 0 0 * * * (UTC midnight). It appears in Settings โ†’ Cron Jobs after first deploy.
  5. Push to the deploy branch; Vercel runs pnpm build (which includes pnpm db:migrate) and next build.
  6. After the first deploy, run pnpm sync-users once locally with the production POSTGRES_URL set in .env.local, to bulk-import existing workspace members.

Operations (quick reference)

Action How
Add or remove an admin Update ADMIN_SLACK_IDS in Vercel env, redeploy
Change the channel allowlist Update TACO_CHANNELS, redeploy, /invite @tacobot in any new channel
Change the daily allowance Update TACO_DAILY_ALLOWANCE; the next 00:00 UTC reset refills everyone to the new value
Change the daily-reset timezone Edit the cron expression in vercel.json (UTC; env-vars don't interpolate). Default 0 0 * * * is UTC midnight; e.g. 0 8 * * * = 08:00 UTC
Rotate Slack signing secret Regenerate in Slack dashboard โ†’ update SLACK_SIGNING_SECRET in Vercel โ†’ redeploy
Inspect data pnpm db:studio opens Drizzle Studio against the configured database

For runbook-level detail (smoke checklist, audit-query cookbook, monitoring, failure-mode cheatsheet, manual balance correction policy), see docs/operations.md.

Architecture

End-to-end: Slack POSTs to /api/slack/events โ†’ custom AppRouterReceiver verifies the HMAC and short-circuits URL-verification handshakes โ†’ Bolt App dispatches to the message / reaction / command / user-sync handlers in lib/slack/handlers/ โ†’ pure validate/decide logic in lib/slack/give.ts โ†’ atomic transactional execute in lib/slack/execute.ts โ†’ Drizzle โ†’ Postgres. Auth.js v5 with the Slack OIDC provider gates /admin/* (the allowlist check is in the signIn callback, so non-admins never get a session). A daily Vercel cron resets the allowance.

For the full system diagram, data-model rationale, give/redeem flow traces, idempotency and concurrency model, see docs/architecture.md.

License

Internal use, not published.

About

Slack bot that gives ๐ŸŒฎ

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • TypeScript 99.1%
  • Other 0.9%