5bca435146
allMemVectorMetas (memory.go) replaces the identical query-then-scan block ReembedAll and RepairFactVectors each had for reading id+meta out of memory_vectors — same query, same json.Unmarshal, different structs built from the result. Two RowsAffected() errors were silently dropped with `_`, inconsistent with every other call site in the same files: AcceptProposedRoutine now wraps the error instead of treating it as zero rows, and MarkAcked had it stranded behind a dead branch (both arms returned nil) removed along with the swallowed error. No behavior change; internal/store and internal/memory pass with -race.
70 lines
2.4 KiB
Go
70 lines
2.4 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// WasAcked returns true when the rule has no pending (un-acked) telegram
|
|
// nudges. A resolved nudge (acted/snoozed/ignored) means the user has seen
|
|
// and dealt with it — the alarm is considered acked.
|
|
func (s *Store) WasAcked(ctx context.Context, key string) (bool, error) {
|
|
var n int
|
|
err := s.db.QueryRowContext(ctx,
|
|
`SELECT COUNT(*) FROM nudges
|
|
WHERE rule = ? AND channel = 'telegram' AND outcome = 'pending'`, key).Scan(&n)
|
|
if err != nil {
|
|
return false, fmt.Errorf("was acked %s: %w", key, err)
|
|
}
|
|
return n == 0, nil
|
|
}
|
|
|
|
// MarkSent records that a sev4 telegram nudge was sent (or re-sent) for the
|
|
// given rule at the given time. Used by the repeat-til-ack loop to clock the
|
|
// repeat interval.
|
|
func (s *Store) MarkSent(ctx context.Context, key string, ts time.Time) error {
|
|
_, err := s.db.ExecContext(ctx,
|
|
`INSERT INTO ack_sends (rule, sent_at) VALUES (?, ?)`,
|
|
key, ts.UnixMilli())
|
|
if err != nil {
|
|
return fmt.Errorf("mark sent %s: %w", key, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// LastSent returns the most recent send timestamp for the given rule's
|
|
// telegram nudge. Returns zero time if nothing has been sent yet (the initial
|
|
// send goes through RecordNudge, not MarkSent, so the first MarkSent comes on
|
|
// the repeat path — LastSent may legitimately be zero until then).
|
|
func (s *Store) LastSent(ctx context.Context, key string) (time.Time, error) {
|
|
var millis int64
|
|
err := s.db.QueryRowContext(ctx,
|
|
`SELECT MAX(sent_at) FROM ack_sends WHERE rule = ?`, key).Scan(&millis)
|
|
if err != nil {
|
|
return time.Time{}, fmt.Errorf("last sent %s: %w", key, err)
|
|
}
|
|
if millis == 0 {
|
|
return time.Time{}, nil
|
|
}
|
|
return time.UnixMilli(millis).UTC(), nil
|
|
}
|
|
|
|
// MarkAcked marks ALL pending telegram nudges for the rule as "acted" —
|
|
// stopping the repeat-til-ack loop. Called when the user acknowledges the
|
|
// alarm (voice acknowledgment, Telegram callback, etc.).
|
|
func (s *Store) MarkAcked(ctx context.Context, key string) error {
|
|
now := time.Now()
|
|
// No pending nudges is not an error — already acked or never sent — so the
|
|
// rows-affected count is not read at all: every outcome below this line is
|
|
// the same nil.
|
|
_, err := s.db.ExecContext(ctx,
|
|
`UPDATE nudges SET outcome = 'acted', outcome_ts = ?
|
|
WHERE rule = ? AND channel = 'telegram' AND outcome = 'pending'`,
|
|
now.UnixMilli(), key)
|
|
if err != nil {
|
|
return fmt.Errorf("mark acked %s: %w", key, err)
|
|
}
|
|
return nil
|
|
}
|