party/internal/data/parlwatcher.go
2026-05-10 10:49:20 +02:00

83 lines
1.9 KiB
Go

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