package store import ( "context" "fmt" "time" ) // DialogueSessionRow — one saved follow-up session. Data is the session // encoded by the dialogue package; the store does not look inside it. type DialogueSessionRow struct { ID string Data []byte Ts time.Time TTL time.Duration Expires time.Time } // SaveDialogueSession — write (or replace) the session for one dialogue id. // One row per id: a newer turn overwrites the older state. func (s *Store) SaveDialogueSession(ctx context.Context, id string, data []byte, ts time.Time, ttl time.Duration) error { expires := ts.Add(ttl) _, err := s.db.ExecContext(ctx, ` INSERT INTO dialogue_sessions (id, data, ts, ttl_ms, expires_ts) VALUES (?, ?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET data = excluded.data, ts = excluded.ts, ttl_ms = excluded.ttl_ms, expires_ts = excluded.expires_ts`, id, data, ts.UnixMilli(), ttl.Milliseconds(), expires.UnixMilli()) if err != nil { return fmt.Errorf("save dialogue session: %w", err) } return nil } // DeleteDialogueSession — drop one session (ended, or expired). func (s *Store) DeleteDialogueSession(ctx context.Context, id string) error { if _, err := s.db.ExecContext(ctx, `DELETE FROM dialogue_sessions WHERE id = ?`, id); err != nil { return fmt.Errorf("delete dialogue session: %w", err) } return nil } // LoadDialogueSessions — return the sessions still alive at `now` and delete // the ones that already ran out. An expired session is dead: it never comes // back after a restart. func (s *Store) LoadDialogueSessions(ctx context.Context, now time.Time) ([]DialogueSessionRow, error) { if _, err := s.db.ExecContext(ctx, `DELETE FROM dialogue_sessions WHERE expires_ts <= ?`, now.UnixMilli()); err != nil { return nil, fmt.Errorf("prune dialogue sessions: %w", err) } rows, err := s.db.QueryContext(ctx, `SELECT id, data, ts, ttl_ms, expires_ts FROM dialogue_sessions ORDER BY id`) if err != nil { return nil, fmt.Errorf("load dialogue sessions: %w", err) } defer rows.Close() var out []DialogueSessionRow for rows.Next() { var r DialogueSessionRow var tsMilli, ttlMilli, expMilli int64 if err := rows.Scan(&r.ID, &r.Data, &tsMilli, &ttlMilli, &expMilli); err != nil { return nil, fmt.Errorf("scan dialogue session: %w", err) } r.Ts = time.UnixMilli(tsMilli).UTC() r.TTL = time.Duration(ttlMilli) * time.Millisecond r.Expires = time.UnixMilli(expMilli).UTC() out = append(out, r) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("load dialogue sessions: %w", err) } return out, nil }