9.8 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 17-telegram-scheduler | 02 | execute | 1 |
|
true |
|
|
Purpose: Establishes cron-based recurring scan infrastructure and the persistence layer for subscriptions and jobs. Independent of pkg/bot/ (Wave 1 parallel). Output: pkg/scheduler/, pkg/storage/subscribers.go, pkg/storage/scheduled_jobs.go, updated schema.sql.
<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/17-telegram-scheduler/17-CONTEXT.md @pkg/storage/db.go @pkg/storage/schema.sql @pkg/engine/engine.goFrom pkg/storage/db.go:
type DB struct { sql *sql.DB }
func Open(path string) (*DB, error)
func (db *DB) Close() error
func (db *DB) SQL() *sql.DB
From pkg/engine/engine.go:
type ScanConfig struct { Workers int; Verify bool; Unmask bool }
func (e *Engine) Scan(ctx context.Context, src sources.Source, cfg ScanConfig) (<-chan Finding, error)
- Append to pkg/storage/schema.sql (after existing custom_dorks table):
-- Phase 17: Telegram bot subscribers for auto-notifications.
CREATE TABLE IF NOT EXISTS subscribers (
chat_id INTEGER PRIMARY KEY,
username TEXT,
subscribed_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Phase 17: Cron-based scheduled scan jobs.
CREATE TABLE IF NOT EXISTS scheduled_jobs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
cron_expr TEXT NOT NULL,
scan_command TEXT NOT NULL,
notify_telegram BOOLEAN DEFAULT FALSE,
enabled BOOLEAN DEFAULT TRUE,
last_run DATETIME,
next_run DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
- Create pkg/storage/subscribers.go:
Subscriberstruct:ChatID int64,Username string,SubscribedAt time.Time(db *DB) AddSubscriber(chatID int64, username string) error— INSERT OR REPLACE(db *DB) RemoveSubscriber(chatID int64) (int64, error)— DELETE, return rows affected(db *DB) ListSubscribers() ([]Subscriber, error)— SELECT all(db *DB) IsSubscribed(chatID int64) (bool, error)— SELECT count
- Create pkg/storage/scheduled_jobs.go:
ScheduledJobstruct:ID int64,Name string,CronExpr string,ScanCommand string,NotifyTelegram bool,Enabled bool,LastRun *time.Time,NextRun *time.Time,CreatedAt time.Time(db *DB) SaveScheduledJob(j ScheduledJob) (int64, error)— INSERT(db *DB) ListScheduledJobs() ([]ScheduledJob, error)— SELECT all(db *DB) GetScheduledJob(name string) (*ScheduledJob, error)— SELECT by name(db *DB) DeleteScheduledJob(name string) (int64, error)— DELETE by name, return rows affected(db *DB) UpdateJobLastRun(name string, lastRun time.Time, nextRun *time.Time) error— UPDATE last_run and next_run(db *DB) SetJobEnabled(name string, enabled bool) error— UPDATE enabled flag cd /home/salva/Documents/apikey && go build ./pkg/storage/... schema.sql has subscribers and scheduled_jobs tables. Storage CRUD methods compile.
- Create pkg/scheduler/scheduler.go:
-
Configstruct:DB *storage.DBScanFunc func(ctx context.Context, scanCommand string) (int, error)— abstracted scan executor (avoids tight coupling to engine)OnComplete func(result JobResult)— callback for notification bridge (Plan 17-04 wires this)
-
Schedulerstruct:cfg Configsched gocron.Scheduler(gocron scheduler instance)jobs map[string]gocron.Job(gocron job handles keyed by name)mu sync.Mutex
-
New(cfg Config) (*Scheduler, error):- Create gocron scheduler via
gocron.NewScheduler() - Return Scheduler
- Create gocron scheduler via
-
Start(ctx context.Context) error:- Load all enabled jobs from DB via
cfg.DB.ListScheduledJobs() - For each, call internal
registerJob(job)which creates a gocron.CronJob and stores handle - Call
sched.Start()to begin scheduling
- Load all enabled jobs from DB via
-
Stop() error:- Call
sched.Shutdown()to stop all jobs
- Call
-
AddJob(name, cronExpr, scanCommand string, notifyTelegram bool) error:- Save to DB via
cfg.DB.SaveScheduledJob - Register with gocron via
registerJob
- Save to DB via
-
RemoveJob(name string) error:- Remove gocron job handle from
jobsmap and callsched.RemoveJob - Delete from DB via
cfg.DB.DeleteScheduledJob
- Remove gocron job handle from
-
ListJobs() ([]storage.ScheduledJob, error):- Delegate to
cfg.DB.ListScheduledJobs()
- Delegate to
-
RunJob(ctx context.Context, name string) (JobResult, error):- Manual trigger — look up job in DB, call ScanFunc directly, call OnComplete callback
-
Internal
registerJob(sj storage.ScheduledJob):- Create gocron job:
sched.NewJob(gocron.CronJob(sj.CronExpr, false), gocron.NewTask(func() { ... })) - The task function: call
cfg.ScanFunc(ctx, sj.ScanCommand), update last_run/next_run via DB, callcfg.OnCompleteif sj.NotifyTelegram
- Create gocron job:
- Create pkg/scheduler/scheduler_test.go:
- Use storage.Open(":memory:") for all tests
- TestStorageRoundTrip: Save job, list, verify fields match
- TestSubscriberRoundTrip: Add subscriber, list, verify; remove, verify empty
- TestSchedulerStartLoadsJobs: Save 2 enabled jobs to DB, create Scheduler with mock ScanFunc, call Start, verify gocron has 2 jobs registered (check len(s.jobs)==2)
- TestSchedulerAddRemoveJob: Add via Scheduler.AddJob, verify in DB; Remove, verify gone from DB
- TestSchedulerRunJob: Manual trigger via RunJob, verify ScanFunc called with correct scanCommand, verify OnComplete called with result cd /home/salva/Documents/apikey && go test ./pkg/scheduler/... ./pkg/storage/... -v -count=1 -run "TestStorage|TestSubscriber|TestScheduler" Scheduler starts, loads jobs from DB, registers with gocron. AddJob/RemoveJob/RunJob work end-to-end. All tests pass.
<success_criteria>
- pkg/scheduler/ exists with Scheduler struct, gocron wrapper, job loading from DB
- pkg/storage/subscribers.go and pkg/storage/scheduled_jobs.go exist with full CRUD
- schema.sql has both new tables
- gocron v2.19.1 is a direct dependency in go.mod
- All tests pass </success_criteria>