pkg/bot: Bot struct with telego long-polling, command handlers (/scan, /verify, /recon, /status, /stats, /providers, /help, /key), /subscribe + /unsubscribe, notification dispatcher. pkg/scheduler: gocron v2 wrapper with SQLite-backed job persistence, Start/Stop/AddJob/RemoveJob lifecycle. cmd/serve.go: keyhunter serve [--telegram] [--port=8080] cmd/schedule.go: keyhunter schedule add/list/remove
105 lines
2.5 KiB
Go
105 lines
2.5 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/salvacybersec/keyhunter/pkg/storage"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var scheduleCmd = &cobra.Command{
|
|
Use: "schedule",
|
|
Short: "Manage scheduled recurring scans",
|
|
}
|
|
|
|
var scheduleAddCmd = &cobra.Command{
|
|
Use: "add",
|
|
Short: "Add a scheduled scan job",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
name, _ := cmd.Flags().GetString("name")
|
|
cron, _ := cmd.Flags().GetString("cron")
|
|
scan, _ := cmd.Flags().GetString("scan")
|
|
|
|
if name == "" || cron == "" || scan == "" {
|
|
return fmt.Errorf("--name, --cron, and --scan are required")
|
|
}
|
|
|
|
db, _, err := openDBWithKey()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer db.Close()
|
|
|
|
job := storage.ScheduledJob{
|
|
Name: name,
|
|
CronExpr: cron,
|
|
ScanPath: scan,
|
|
Enabled: true,
|
|
}
|
|
id, err := db.SaveScheduledJob(job)
|
|
if err != nil {
|
|
return fmt.Errorf("adding job: %w", err)
|
|
}
|
|
fmt.Printf("Scheduled job %q (ID %d) added: %s -> %s\n", name, id, cron, scan)
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var scheduleListCmd = &cobra.Command{
|
|
Use: "list",
|
|
Short: "List scheduled scan jobs",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
db, _, err := openDBWithKey()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer db.Close()
|
|
|
|
jobs, err := db.ListScheduledJobs()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(jobs) == 0 {
|
|
fmt.Println("No scheduled jobs.")
|
|
return nil
|
|
}
|
|
fmt.Printf("%-5s %-20s %-20s %-30s %-8s\n", "ID", "NAME", "CRON", "SCAN", "ENABLED")
|
|
for _, j := range jobs {
|
|
fmt.Printf("%-5d %-20s %-20s %-30s %-8v\n", j.ID, j.Name, j.CronExpr, j.ScanPath, j.Enabled)
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var scheduleRemoveCmd = &cobra.Command{
|
|
Use: "remove <id>",
|
|
Short: "Remove a scheduled scan job by ID",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
db, _, err := openDBWithKey()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer db.Close()
|
|
|
|
var id int64
|
|
if _, err := fmt.Sscanf(args[0], "%d", &id); err != nil {
|
|
return fmt.Errorf("invalid job ID: %s", args[0])
|
|
}
|
|
if _, err := db.DeleteScheduledJob(id); err != nil {
|
|
return fmt.Errorf("removing job: %w", err)
|
|
}
|
|
fmt.Printf("Removed scheduled job #%d\n", id)
|
|
return nil
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
scheduleAddCmd.Flags().String("name", "", "job name")
|
|
scheduleAddCmd.Flags().String("cron", "", "cron expression")
|
|
scheduleAddCmd.Flags().String("scan", "", "scan path/command")
|
|
scheduleCmd.AddCommand(scheduleAddCmd)
|
|
scheduleCmd.AddCommand(scheduleListCmd)
|
|
scheduleCmd.AddCommand(scheduleRemoveCmd)
|
|
}
|