docs(17): create phase plan — Telegram bot + scheduled scanning
This commit is contained in:
180
.planning/phases/17-telegram-scheduler/17-04-PLAN.md
Normal file
180
.planning/phases/17-telegram-scheduler/17-04-PLAN.md
Normal file
@@ -0,0 +1,180 @@
|
||||
---
|
||||
phase: 17-telegram-scheduler
|
||||
plan: 04
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on: ["17-01", "17-02"]
|
||||
files_modified:
|
||||
- pkg/bot/subscribe.go
|
||||
- pkg/bot/notify.go
|
||||
- pkg/bot/subscribe_test.go
|
||||
autonomous: true
|
||||
requirements: [TELE-05, TELE-07, SCHED-03]
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "/subscribe adds user to subscribers table"
|
||||
- "/unsubscribe removes user from subscribers table"
|
||||
- "New key findings trigger Telegram notification to all subscribers"
|
||||
- "Scheduled scan completion with findings triggers auto-notify"
|
||||
artifacts:
|
||||
- path: "pkg/bot/subscribe.go"
|
||||
provides: "/subscribe and /unsubscribe handler implementations"
|
||||
exports: ["handleSubscribe", "handleUnsubscribe"]
|
||||
- path: "pkg/bot/notify.go"
|
||||
provides: "Notification dispatcher sending findings to all subscribers"
|
||||
exports: ["NotifyNewFindings"]
|
||||
- path: "pkg/bot/subscribe_test.go"
|
||||
provides: "Tests for subscribe/unsubscribe and notification"
|
||||
key_links:
|
||||
- from: "pkg/bot/notify.go"
|
||||
to: "pkg/storage"
|
||||
via: "db.ListSubscribers to get all chat IDs"
|
||||
pattern: "db\\.ListSubscribers"
|
||||
- from: "pkg/bot/notify.go"
|
||||
to: "telego"
|
||||
via: "bot.SendMessage to each subscriber"
|
||||
pattern: "bot\\.SendMessage"
|
||||
- from: "pkg/scheduler/scheduler.go"
|
||||
to: "pkg/bot/notify.go"
|
||||
via: "OnComplete callback calls NotifyNewFindings"
|
||||
pattern: "NotifyNewFindings"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Implement /subscribe, /unsubscribe handlers and the notification dispatcher that bridges scheduler job completions to Telegram messages.
|
||||
|
||||
Purpose: Completes the auto-notification pipeline (TELE-05, TELE-07, SCHED-03). When scheduled scans find new keys, all subscribers get notified automatically.
|
||||
Output: pkg/bot/subscribe.go, pkg/bot/notify.go, pkg/bot/subscribe_test.go.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@$HOME/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/phases/17-telegram-scheduler/17-CONTEXT.md
|
||||
@.planning/phases/17-telegram-scheduler/17-01-SUMMARY.md
|
||||
@.planning/phases/17-telegram-scheduler/17-02-SUMMARY.md
|
||||
@pkg/storage/subscribers.go
|
||||
@pkg/bot/bot.go
|
||||
</context>
|
||||
|
||||
<interfaces>
|
||||
<!-- From Plan 17-02 storage layer -->
|
||||
From pkg/storage/subscribers.go:
|
||||
```go
|
||||
type Subscriber struct { ChatID int64; Username string; SubscribedAt time.Time }
|
||||
func (db *DB) AddSubscriber(chatID int64, username string) error
|
||||
func (db *DB) RemoveSubscriber(chatID int64) (int64, error)
|
||||
func (db *DB) ListSubscribers() ([]Subscriber, error)
|
||||
func (db *DB) IsSubscribed(chatID int64) (bool, error)
|
||||
```
|
||||
|
||||
From pkg/scheduler/scheduler.go:
|
||||
```go
|
||||
type JobResult struct { JobName string; FindingCount int; Duration time.Duration; Error error }
|
||||
type Config struct { ...; OnComplete func(result JobResult) }
|
||||
```
|
||||
</interfaces>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Implement /subscribe, /unsubscribe handlers</name>
|
||||
<files>pkg/bot/subscribe.go</files>
|
||||
<action>
|
||||
Create pkg/bot/subscribe.go with methods on *Bot:
|
||||
|
||||
**handleSubscribe(bot *telego.Bot, msg telego.Message):**
|
||||
- Check if already subscribed via b.cfg.DB.IsSubscribed(msg.Chat.ID)
|
||||
- If already subscribed, reply "You are already subscribed to notifications."
|
||||
- Otherwise call b.cfg.DB.AddSubscriber(msg.Chat.ID, msg.From.Username)
|
||||
- Reply "Subscribed! You will receive notifications when new API keys are found."
|
||||
|
||||
**handleUnsubscribe(bot *telego.Bot, msg telego.Message):**
|
||||
- Call b.cfg.DB.RemoveSubscriber(msg.Chat.ID)
|
||||
- If rows affected == 0, reply "You are not subscribed."
|
||||
- Otherwise reply "Unsubscribed. You will no longer receive notifications."
|
||||
|
||||
Both handlers have no rate limit (instant operations).
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd /home/salva/Documents/apikey && go build ./pkg/bot/...</automated>
|
||||
</verify>
|
||||
<done>/subscribe and /unsubscribe handlers compile and use storage layer.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 2: Notification dispatcher and tests</name>
|
||||
<files>pkg/bot/notify.go, pkg/bot/subscribe_test.go</files>
|
||||
<behavior>
|
||||
- Test 1: NotifyNewFindings with 0 subscribers sends no messages
|
||||
- Test 2: NotifyNewFindings with 2 subscribers formats and sends to both
|
||||
- Test 3: Subscribe/unsubscribe updates DB correctly
|
||||
- Test 4: Notification message contains job name, finding count, and duration
|
||||
</behavior>
|
||||
<action>
|
||||
1. Create pkg/bot/notify.go:
|
||||
|
||||
**NotifyNewFindings(result scheduler.JobResult) method on *Bot:**
|
||||
- If result.FindingCount == 0, do nothing (no notification for empty scans)
|
||||
- If result.Error != nil, notify with error message instead
|
||||
- Load all subscribers via b.cfg.DB.ListSubscribers()
|
||||
- If no subscribers, return (no-op)
|
||||
- Format message:
|
||||
```
|
||||
New findings from scheduled scan!
|
||||
|
||||
Job: {result.JobName}
|
||||
New keys found: {result.FindingCount}
|
||||
Duration: {result.Duration}
|
||||
|
||||
Use /stats for details.
|
||||
```
|
||||
- Send to each subscriber's chat ID via b.bot.SendMessage
|
||||
- Log errors for individual send failures but continue to next subscriber (don't fail on one bad chat ID)
|
||||
- Return total sent count and any errors
|
||||
|
||||
**NotifyFinding(finding engine.Finding) method on *Bot:**
|
||||
- Simpler variant for real-time notification of individual findings (called from scan pipeline if notification enabled)
|
||||
- Format: "New key detected!\nProvider: {provider}\nKey: {masked}\nSource: {source_path}:{line}\nConfidence: {confidence}"
|
||||
- Send to all subscribers
|
||||
- Always use masked key
|
||||
|
||||
2. Create pkg/bot/subscribe_test.go:
|
||||
- TestSubscribeUnsubscribe: Open :memory: DB, add subscriber, verify IsSubscribed==true, remove, verify IsSubscribed==false
|
||||
- TestNotifyNewFindings_NoSubscribers: Create Bot with :memory: DB (no subscribers), call NotifyNewFindings, verify no panic and returns 0 sent
|
||||
- TestNotifyMessage_Format: Verify the formatted notification string contains job name, finding count, duration text
|
||||
- TestNotifyNewFindings_ZeroFindings: Verify no notification sent when FindingCount==0
|
||||
|
||||
For tests that need to verify SendMessage calls, create a `mockTelegoBot` interface or use the Bot struct with a nil telego.Bot and verify the notification message format via a helper function (separate formatting from sending).
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd /home/salva/Documents/apikey && go test ./pkg/bot/... -v -count=1 -run "Subscribe|Notify"</automated>
|
||||
</verify>
|
||||
<done>Notification dispatcher sends to all subscribers on new findings. Subscribe/unsubscribe persists to DB. All tests pass.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
- `go build ./pkg/bot/...` compiles
|
||||
- `go test ./pkg/bot/... -v -run "Subscribe|Notify"` passes
|
||||
- NotifyNewFindings sends to all subscribers in DB
|
||||
- /subscribe and /unsubscribe modify subscribers table
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- /subscribe adds chat to subscribers table, /unsubscribe removes it
|
||||
- NotifyNewFindings sends formatted message to all subscribers
|
||||
- Zero findings produces no notification
|
||||
- Notification always uses masked keys
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/17-telegram-scheduler/17-04-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user