--- phase: 17-telegram-scheduler plan: 01 type: execute wave: 1 depends_on: [] files_modified: - pkg/bot/bot.go - pkg/bot/bot_test.go - go.mod - go.sum autonomous: true requirements: [TELE-01] must_haves: truths: - "Bot struct initializes with telego client given a valid token" - "Bot registers command handlers and starts long polling" - "Bot respects allowed_chats restriction (empty = allow all)" - "Bot gracefully shuts down on context cancellation" artifacts: - path: "pkg/bot/bot.go" provides: "Bot struct, New, Start, Stop, RegisterHandlers, auth middleware" exports: ["Bot", "New", "Config", "Start", "Stop"] - path: "pkg/bot/bot_test.go" provides: "Unit tests for Bot creation and auth filtering" key_links: - from: "pkg/bot/bot.go" to: "github.com/mymmrac/telego" via: "telego.NewBot + long polling" pattern: "telego\\.NewBot" --- Create the pkg/bot/ package foundation: Bot struct wrapping telego v1.8.0, command registration, long-polling lifecycle, and chat ID authorization middleware. Purpose: Establishes the Telegram bot infrastructure that all command handlers (Plan 17-03, 17-04) build on. Output: pkg/bot/bot.go with Bot struct, pkg/bot/bot_test.go with unit tests. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/17-telegram-scheduler/17-CONTEXT.md @cmd/stubs.go @pkg/storage/db.go Task 1: Add telego dependency and create Bot package skeleton go.mod, go.sum, pkg/bot/bot.go 1. Run `go get github.com/mymmrac/telego@v1.8.0` to add telego as a direct dependency. 2. Create pkg/bot/bot.go with: - `Config` struct: - `Token string` (Telegram bot token) - `AllowedChats []int64` (empty = allow all) - `DB *storage.DB` (for subscriber queries, finding lookups) - `ScanEngine *engine.Engine` (for /scan handler) - `ReconEngine *recon.Engine` (for /recon handler) - `ProviderRegistry *providers.Registry` (for /providers, /verify) - `EncKey []byte` (encryption key for finding decryption) - `Bot` struct: - `cfg Config` - `bot *telego.Bot` - `updates <-chan telego.Update` (long polling channel) - `cancel context.CancelFunc` (for shutdown) - `New(cfg Config) (*Bot, error)`: - Create telego.Bot via `telego.NewBot(cfg.Token)` (no options needed for long polling) - Return &Bot with config stored - `Start(ctx context.Context) error`: - Create cancelable context from parent - Call `bot.SetMyCommands` to register command descriptions (scan, verify, recon, status, stats, providers, help, key, subscribe, unsubscribe) - Get updates via `bot.UpdatesViaLongPolling(nil)` which returns a channel - Loop over updates channel, dispatch to handler based on update.Message.Text command prefix - Check authorization via `isAllowed(chatID)` before dispatching any handler - On ctx.Done(), call `bot.StopLongPolling()` and return - `Stop()`: - Call cancel function to trigger shutdown - `isAllowed(chatID int64) bool`: - If cfg.AllowedChats is empty, return true - Otherwise check if chatID is in the list - Handler stubs (will be implemented in Plan 17-03): - `handleScan(bot *telego.Bot, msg telego.Message)` - `handleVerify(bot *telego.Bot, msg telego.Message)` - `handleRecon(bot *telego.Bot, msg telego.Message)` - `handleStatus(bot *telego.Bot, msg telego.Message)` - `handleStats(bot *telego.Bot, msg telego.Message)` - `handleProviders(bot *telego.Bot, msg telego.Message)` - `handleHelp(bot *telego.Bot, msg telego.Message)` - `handleKey(bot *telego.Bot, msg telego.Message)` Each stub sends "Not yet implemented" reply via `bot.SendMessage`. - Use telego's MarkdownV2 parse mode for all replies. Create helper: - `reply(bot *telego.Bot, chatID int64, text string) error` — sends MarkdownV2 message - `replyPlain(bot *telego.Bot, chatID int64, text string) error` — sends plain text (for error messages) - Per-user rate limiting: `rateLimits map[int64]time.Time` with mutex. `checkRateLimit(userID int64, cooldown time.Duration) bool` returns false if user sent a command within cooldown window. Default cooldown 60s for /scan, /verify, /recon; 5s for others. Import paths: github.com/mymmrac/telego, github.com/mymmrac/telego/telegoutil (for SendMessageParams construction). cd /home/salva/Documents/apikey && go build ./pkg/bot/... pkg/bot/bot.go compiles with telego dependency. Bot struct, New, Start, Stop, isAllowed, and all handler stubs exist. Task 2: Unit tests for Bot creation and auth filtering pkg/bot/bot_test.go - Test 1: New() with empty token returns error from telego - Test 2: isAllowed with empty AllowedChats returns true for any chatID - Test 3: isAllowed with AllowedChats=[100,200] returns true for 100, false for 999 - Test 4: checkRateLimit returns true on first call, false on immediate second call, true after cooldown Create pkg/bot/bot_test.go: - TestNew_EmptyToken: Verify New(Config{Token:""}) returns an error. - TestIsAllowed_EmptyList: Create Bot with empty AllowedChats, verify isAllowed(12345) returns true. - TestIsAllowed_RestrictedList: Create Bot with AllowedChats=[100,200], verify isAllowed(100)==true, isAllowed(999)==false. - TestCheckRateLimit: Create Bot, verify checkRateLimit(1, 60s)==true first call, ==false second call. Note: Since telego.NewBot requires a valid token format, for tests that need a Bot struct without a real connection, construct the Bot struct directly (bypassing New) to test isAllowed and rate limit logic independently. cd /home/salva/Documents/apikey && go test ./pkg/bot/... -v -count=1 All 4 test cases pass. Bot auth filtering and rate limiting logic verified. - `go build ./pkg/bot/...` compiles without errors - `go test ./pkg/bot/... -v` passes all tests - `grep telego go.mod` shows direct dependency at v1.8.0 - pkg/bot/bot.go exists with Bot struct, New, Start, Stop, isAllowed, handler stubs - telego v1.8.0 is a direct dependency in go.mod - All unit tests pass After completion, create `.planning/phases/17-telegram-scheduler/17-01-SUMMARY.md`