package data import ( "context" "database/sql" "time" "github.com/lib/pq" ) type ParlWatcherModel struct { DB *sql.DB } // FilterNew returns only the numbers from the input that are not yet recorded. func (m ParlWatcherModel) FilterNew(numbers []string) ([]string, error) { query := ` SELECT unnest($1::text[]) AS num EXCEPT SELECT number FROM parl_notified_votes` ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() rows, err := m.DB.QueryContext(ctx, query, pq.Array(numbers)) if err != nil { return nil, err } defer rows.Close() var result []string for rows.Next() { var n string if err := rows.Scan(&n); err != nil { return nil, err } result = append(result, n) } return result, rows.Err() } // MarkNotified records the given numbers so they are not notified again. func (m ParlWatcherModel) MarkNotified(numbers []string) error { query := ` INSERT INTO parl_notified_votes (number) SELECT unnest($1::text[]) ON CONFLICT (number) DO NOTHING` ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() _, err := m.DB.ExecContext(ctx, query, pq.Array(numbers)) return err } // GetAllNotifiedNumbers returns every number that has already been notified. // Unused in the hot path but useful for debugging/admin. func (m ParlWatcherModel) GetAllNotifiedNumbers() ([]string, error) { query := `SELECT number FROM parl_notified_votes ORDER BY notified_at DESC` ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() rows, err := m.DB.QueryContext(ctx, query) if err != nil { return nil, err } defer rows.Close() var result []string for rows.Next() { var n string if err := rows.Scan(&n); err != nil { return nil, err } result = append(result, n) } return result, rows.Err() } // Ensure ParlWatcherModel satisfies the interface even if DB is nil (e.g. in tests). var _ = sql.ErrNoRows