package storage import ( "context" "database/sql" "errors" "fmt" "strings" "time" ) // GetCooldownMemoNames returns memo names shown within the last cooldownDays. func (s *Storage) GetCooldownMemoNames(ctx context.Context, cooldownDays int) (map[string]struct{}, error) { cutoff := time.Now().Unix() - int64(cooldownDays)*86400 rows, err := s.db.QueryContext(ctx, `SELECT DISTINCT memo_name FROM show_history WHERE shown_at > ?`, cutoff) if err != nil { return nil, fmt.Errorf("query cooldown memos: %w", err) } defer rows.Close() result := make(map[string]struct{}) for rows.Next() { var name string if err := rows.Scan(&name); err != nil { return nil, fmt.Errorf("scan memo_name: %w", err) } result[name] = struct{}{} } return result, rows.Err() } // GetShowCounts returns show_count for each of the given memo names. // Memos not present in history will be absent from the result (count = 0). func (s *Storage) GetShowCounts(ctx context.Context, memoNames []string) (map[string]int, error) { if len(memoNames) == 0 { return nil, nil } placeholders := make([]string, len(memoNames)) args := make([]any, len(memoNames)) for i, name := range memoNames { placeholders[i] = "?" args[i] = name } query := fmt.Sprintf( //nolint:gosec // placeholders are always "?" `SELECT memo_name, COUNT(*) FROM show_history WHERE memo_name IN (%s) GROUP BY memo_name`, strings.Join(placeholders, ",")) rows, err := s.db.QueryContext(ctx, query, args...) if err != nil { return nil, fmt.Errorf("query show counts: %w", err) } defer rows.Close() result := make(map[string]int) for rows.Next() { var name string var count int if err := rows.Scan(&name, &count); err != nil { return nil, fmt.Errorf("scan show count: %w", err) } result[name] = count } return result, rows.Err() } // GetLastShownToday returns the memo_name and tier of the most recently shown memo // within the given time range [from, to). Returns empty string if nothing was shown. func (s *Storage) GetLastShownToday(ctx context.Context, from, to int64) (memoName string, tier int, err error) { err = s.db.QueryRowContext(ctx, `SELECT memo_name, tier FROM show_history WHERE shown_at >= ? AND shown_at < ? ORDER BY shown_at DESC LIMIT 1`, from, to).Scan(&memoName, &tier) if err != nil { if errors.Is(err, sql.ErrNoRows) { return "", 0, nil } return "", 0, fmt.Errorf("get last shown today: %w", err) } return memoName, tier, nil } // RecordShow records that a memo was shown. func (s *Storage) RecordShow(ctx context.Context, memoName string, tier int) error { _, err := s.db.ExecContext(ctx, `INSERT INTO show_history (memo_name, shown_at, tier) VALUES (?, ?, ?)`, memoName, time.Now().Unix(), tier) if err != nil { return fmt.Errorf("insert show history: %w", err) } return nil }