-
Notifications
You must be signed in to change notification settings - Fork 8
Description
Problem
GitHub integration settings (PR_REVIEW_ENABLED, ISSUE_TRIAGE_ENABLED, GITHUB_NOTIFY_CHAT_ID) are global env vars. The webhook processes events with no awareness of which user cares about which repo. All notifications go to a single hardcoded chat ID.
In multi-user, each user has their own GitHub identity, subscribes to different repos, and wants notifications routed to their chosen destination (preserving the PR #182 separation between GitHub notifications and the main Kai chat).
Parent issue: #196 (sub-issue #5)
Proposal
1. Add repo subscriptions to users.yaml
users:
- telegram_id: 2114582497
name: Daniel
github: dcellison
github_repos:
- dcellison/kai
- dcellison/anvil
github_notify_chat_id: -100123456789
pr_review: true
issue_triage: true2. Route webhook events by repo ownership
When a GitHub webhook event arrives for dcellison/kai:
- Extract the repo full name from the payload (
repository.full_name) - Look up which user(s) have that repo in their
github_reposlist - For each matched user, check their
pr_review/issue_triageflags - Route notifications to each user's
github_notify_chat_id(falls back totelegram_idif unset)
3. Telegram commands for managing subscriptions
/github - show current repo subscriptions and GitHub settings
/github token <ghp_...> - store GitHub PAT for webhook management
/github token clear - remove stored token
/github add <owner/repo> - subscribe + register webhook (if token available)
/github remove <owner/repo> - unsubscribe + deregister webhook (if last subscriber)
/github notify <chat_id> - set notification destination chat
/github notify reset - reset to main chat (telegram_id)
/github reviews on|off - toggle PR review notifications
/github triage on|off - toggle issue triage notifications
4. Webhook registration (ingestion side)
Everything above handles routing - which user sees which repo's events. But events only arrive if the repo has a GitHub webhook pointing to Kai's endpoint. Today only dcellison/kai has this configured. A user who runs /github add dcellison/anvil would be subscribed to... silence.
/github add must handle both sides: subscribe the user to notifications AND register the webhook on the repo so events actually flow in.
GitHub token requirement
Registering a webhook requires admin:repo_hook scope on a GitHub personal access token. Each user stores their own token via Telegram:
/github token <ghp_...> - store GitHub PAT (one-time setup)
/github token clear - remove stored token
- Stored in the database per-user (settings table, key
github_token:{chat_id}) - Never echoed back, never logged, never included in error messages
- The token is also used to verify repo existence during
/github add
Registration flow
When a user runs /github add owner/repo:
- Validate format (
owner/repo) - Check if the user has a stored GitHub token. If not, reply with manual setup instructions and the webhook URL/secret
- Use the GitHub API to check if a webhook already exists on that repo pointing to Kai's endpoint
- If no webhook exists, create one via
POST /repos/{owner}/{repo}/hookswith:url: the public webhook URL (fromTELEGRAM_WEBHOOK_URLbase, i.e.,https://api.syrinx.net/webhook/github)secret: the sharedWEBHOOK_SECRETevents:["push", "pull_request", "issues", "issue_comment", "pull_request_review"]content_type:json
- If webhook creation fails (403 - no admin access, 404 - repo not found), reply with the specific error and manual setup instructions
- On success, add the repo to the user's subscription list
Shared secret model
All repos use the same WEBHOOK_SECRET for HMAC signature validation. This is intentional - the endpoint is already behind Cloudflare with signature verification. Per-repo secrets would require identifying the repo before validating the signature (the body must be read to get the repo name, but the signature covers the body), creating a chicken-and-egg problem.
Manual fallback
If the user has no token or lacks admin access on the repo, /github add still subscribes them to notifications but prints:
Subscribed to owner/repo notifications.
I could not register the webhook automatically (no GitHub token / insufficient permissions).
To receive events, add a webhook manually in the repo settings:
URL: https://api.syrinx.net/webhook/github
Content type: application/json
Secret: (ask your Kai admin)
Events: Pushes, Pull requests, Issues, Issue comments, Pull request reviews
Deregistration
/github remove owner/repo unsubscribes the user. If no other users are subscribed to that repo, Kai removes the webhook from GitHub (if the user has a token with access). If other users are still subscribed, the webhook stays.
Display
/github with no arguments shows:
GitHub: dcellison
Notifications: group "Kai Dev" (-100123456789)
PR reviews: on
Issue triage: on
Subscribed repos:
dcellison/kai
dcellison/anvil
Storage
Two layers:
- users.yaml (admin baseline) -
github_repos,github_notify_chat_id,pr_review,issue_triagefields - Database (user overrides via Telegram) - for repos added/removed via
/github add/removeand setting toggles
Database storage options:
github_token:{chat_id}in settings table (PAT for webhook management - never logged or echoed)github_repos:{chat_id}in settings table as JSON array (for user-added repos beyond the yaml baseline)github_notify_chat:{chat_id}in settings tablepr_review:{chat_id}andissue_triage:{chat_id}in settings table
The effective repo list is the union of users.yaml repos and database-added repos, minus database-removed repos.
Webhook routing changes
Current flow (webhook.py):
- GitHub events arrive at
/webhook/github - PR review and issue triage handlers use global config
- Notifications go to
GITHUB_NOTIFY_CHAT_IDor fall back to first allowed user
New flow:
- GitHub events arrive at
/webhook/github - Extract
repository.full_namefrom payload - Look up subscribed users for that repo
- For each user: check their pr_review/issue_triage flags, route to their notification chat
- If no user is subscribed to the repo, log a warning (don't silently drop)
Validation
/github addvalidates the repo format (owner/repo)- Repo existence is verified automatically during webhook registration (when a token is available). Without a token, format validation only.
/github notifyvalidates the chat_id is a valid integer
Backward compatibility
PR_REVIEW_ENABLEDenv var becomes global default for users without apr_reviewfieldISSUE_TRIAGE_ENABLEDenv var becomes global default for users without anissue_triagefieldGITHUB_NOTIFY_CHAT_IDenv var becomes fallback for users withoutgithub_notify_chat_id- Existing single-user installs work unchanged
Context
- GitHub webhook handler: webhook.py
- PR review agent: triggered by pull_request events (opened/reopened/synchronize)
- Issue triage: triggered by issues events
- Current notification routing: uses
GITHUB_NOTIFY_CHAT_IDwith fallback to first allowed user - PR Route GitHub notifications to a separate Telegram group #182 added the separation between GitHub notifications and main chat
- UserConfig dataclass: config.py lines 86-111 (needs new fields:
github_repos,github_notify_chat_id,pr_review,issue_triage)