Skip to content

nowstackit/standup-bot

Repository files navigation

Superjoin Standup Bot

A Slack bot and interactive dashboard that powers the daily 10:30 IST Google Meet standup:

  1. Pre-standup brief at 10:30 IST — pulls 24-72h of Slack chatter, runs through Gemini, posts grouped digest to #team-schedule.
  2. Attendance DMs at 10:00 IST — every roster member gets a Block Kit DM asking office / WFH / leave, with an ETA picker.
  3. Interactive dashboard at / — live attendance grid, drag-and-drop Kanban board, OpenAI Realtime voice assistant.
  4. Voice assistant during standup — browser WebRTC straight to OpenAI Realtime API, captures action items / blockers / attendance updates while the team talks, writes them into the Kanban board in real time.

Architecture

Vercel
├── api/cron/dm-attendance.ts   ← 10:00 IST: DM every roster member
├── api/cron/standup-brief.ts   ← 10:30 IST: post the Slack digest
├── api/slack/command.ts        ← /standup-brief slash command
├── api/slack/interactions.ts   ← attendance DM button clicks + ETA select
├── api/attendance/index.ts     ← GET/POST attendance
├── api/kanban/board.ts         ← GET board
├── api/kanban/cards.ts         ← POST/PATCH/DELETE cards
├── api/roster/index.ts         ← GET roster, POST ?sync=1
├── api/realtime/token.ts       ← mint ephemeral Realtime session
├── api/realtime/tool.ts        ← bridge function-call → DB
├── api/admin/migrate.ts        ← run DB migrations
└── public/                     ← dashboard HTML + JS + CSS (static)

Postgres (Neon)
  roster, attendance, kanban_card

Setup

1. Create Neon Postgres

  1. Sign up at https://neon.tech, create a project.
  2. Copy the pooled connection string → DATABASE_URL.

2. Slack app

  1. https://api.slack.com/appsFrom manifest → paste slack-manifest.yaml.
  2. Replace YOUR-VERCEL-DOMAIN once you deploy.
  3. Install to workspace, grab:
    • Bot OAuth TokenSLACK_BOT_TOKEN
    • User OAuth TokenSLACK_USER_TOKEN
    • Signing SecretSLACK_SIGNING_SECRET
  4. Bot must be a member of #team-schedule (C05BRT9CZ7G) — /invite @standup-bot.

3. Azure OpenAI

  1. Azure AI Foundry → deploy gpt-realtime model (Global Standard, eastus2 / swedencentral).
  2. Copy: resource endpoint, key, deployment name, region.
  3. Env vars: AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY, AZURE_OPENAI_REALTIME_DEPLOYMENT, AZURE_OPENAI_REGION.

4. Vercel env

vercel link
vercel env add SLACK_BOT_TOKEN
vercel env add SLACK_USER_TOKEN
vercel env add SLACK_SIGNING_SECRET
vercel env add GEMINI_API_KEY
vercel env add AZURE_OPENAI_ENDPOINT
vercel env add AZURE_OPENAI_API_KEY
vercel env add AZURE_OPENAI_REALTIME_DEPLOYMENT
vercel env add AZURE_OPENAI_REGION
vercel env add DATABASE_URL
vercel env add CRON_SECRET            # any long random string
vercel env add DASHBOARD_SECRET       # any long random string
vercel env add STANDUP_CHANNEL_ID     # C05BRT9CZ7G
vercel deploy --prod

5. Migrate + seed

# After first deploy:
curl -X POST "https://YOUR-DOMAIN/api/admin/migrate?secret=$CRON_SECRET"

# Then sync roster (also runs as part of dm-attendance cron):
curl -X POST "https://YOUR-DOMAIN/api/roster?sync=1" \
  -H "x-dashboard-secret: $DASHBOARD_SECRET"

Or locally with .env.local:

npm install
npm run db:migrate -- --seed-roster

6. Open dashboard

https://YOUR-DOMAIN/ → paste DASHBOARD_SECRET to unlock.

How a standup runs

Time What happens
10:00 IST Cron syncs roster, DMs every member with attendance buttons.
10:00–30 Replies trickle in → attendance table updates → dashboard reflects live.
10:30 IST Cron posts the Slack brief (attendance summary + Kanban summary on top).
10:30 IST Team joins Google Meet. One person opens the dashboard → clicks Start voice.
10:30–45 Assistant listens via mic (pipe Meet via BlackHole on Mac to feed all voices). Captures action items / blockers as Kanban cards, marks attendance changes ("I'm leaving early"), nudges the next speaker.

Voice setup (Mac)

OpenAI Realtime only sees the mic of the browser tab. To feed every speaker's audio:

  1. Install BlackHole (free).
  2. Audio MIDI Setup → create a Multi-Output Device combining your headphones + BlackHole.
  3. Google Meet → set output to the Multi-Output Device. Now Meet plays to your ears and BlackHole.
  4. Dashboard tab → in browser site settings, set the microphone to BlackHole 2ch. Mic now hears the entire Meet conversation.

Alternative: just speak into a normal mic — the assistant won't hear remote speakers but it can still capture the local moderator's notes.

Auth model

  • CRON_SECRET — required for /api/cron/* + /api/admin/migrate. Vercel auto-injects it for scheduled crons.
  • DASHBOARD_SECRET — required for any state-mutating endpoint hit from the browser (attendance POST, kanban POST/PATCH/DELETE, realtime token mint, realtime tool bridge). Pasted into the dashboard on first load and cached in localStorage.
  • Slack signing secret — verifies /api/slack/command and /api/slack/interactions payloads.

Tuning

Knob Where What it does
Standup channel STANDUP_CHANNEL_ID env Slack channel used for roster source.
Realtime deployment AZURE_OPENAI_REALTIME_DEPLOYMENT env Azure deployment name (e.g. gpt-realtime-slackbot).
Realtime voice AZURE_OPENAI_REALTIME_VOICE env verse (default), alloy, coral, sage, ...
Realtime region AZURE_OPENAI_REGION env Deployment region — used for WebRTC URL.
DM time vercel.json crons[0] 30 4 * * 1-5 = 10:00 IST.
Brief time vercel.json crons[1] 0 5 * * 1-5 = 10:30 IST.
System prompt api/realtime/token.ts SYSTEM_PROMPT What the voice assistant is told to do.
Tool list api/realtime/token.ts TOOLS Functions the assistant can call.

Costs

  • Slack API: free
  • Gemini 2.0 Flash: ~$0.01-0.05 per brief
  • Azure OpenAI Realtime (gpt-realtime): ~$32/M audio input + $64/M audio output tokens. A 15-min standup ≈ $3-6.
  • Neon free tier: enough until you have multiple workspaces.
  • Vercel free tier: cron + functions fit.

Things to add later

  • Slack OAuth instead of pasted dashboard secret
  • Per-user DMs of action items they own after the standup
  • Slack reactions (:white_check_mark:) close Kanban cards
  • Cross-day continuity for blockers ("still blocked yesterday")
  • Recording transcripts to Postgres for searchable history

Releases

No releases published

Packages

 
 
 

Contributors