package nostr import ( "context" "fmt" "log" "time" "github.com/nbd-wtf/go-nostr" ) // FeedService handles feed operations type FeedService struct { client *Client feedKind int // Feed event kind (from config) } // NewFeedService creates a new feed service func NewFeedService(client *Client, feedKind int) *FeedService { return &FeedService{ client: client, feedKind: feedKind, } } // FetchFeedItems fetches recent feed events from the configured feed relay only, with retries func (fs *FeedService) FetchFeedItems(ctx context.Context, feedRelay string, maxEvents int) ([]FeedItem, error) { if feedRelay == "" { return nil, fmt.Errorf("feed relay not configured") } filter := nostr.Filter{ Kinds: []int{fs.feedKind}, Limit: maxEvents, } logFilter(filter, fmt.Sprintf("feed (kind %d)", fs.feedKind)) const maxRetries = 3 var lastErr error // Retry up to 3 times for the configured feed relay only for attempt := 1; attempt <= maxRetries; attempt++ { if attempt > 1 { log.Printf("Retrying feed relay %s (attempt %d/%d)", feedRelay, attempt, maxRetries) // Wait a bit before retrying (exponential backoff: 1s, 2s) waitTime := time.Duration(attempt-1) * time.Second time.Sleep(waitTime) } // Create a context with timeout for this attempt attemptCtx, cancel := context.WithTimeout(ctx, 10*time.Second) // Connect to the relay relayConn, err := fs.client.ConnectToRelay(attemptCtx, feedRelay) if err != nil { log.Printf("Failed to connect to feed relay %s (attempt %d/%d): %v", feedRelay, attempt, maxRetries, err) lastErr = err cancel() continue // Try next attempt } // Try to fetch events events, err := relayConn.QuerySync(attemptCtx, filter) relayConn.Close() cancel() if err != nil { log.Printf("Failed to fetch feed events from %s (attempt %d/%d): %v", feedRelay, attempt, maxRetries, err) lastErr = err continue // Try next attempt } // Success! Convert events to feed items items := make([]FeedItem, 0, len(events)) for _, event := range events { item := FeedItem{ Author: event.PubKey, Content: event.Content, Time: time.Unix(int64(event.CreatedAt), 0), Link: fmt.Sprintf("https://alexandria.gitcitadel.eu/events?id=nevent1%s", event.ID), } items = append(items, item) } log.Printf("Successfully fetched %d feed items from %s (attempt %d/%d)", len(items), feedRelay, attempt, maxRetries) return items, nil } // All retries failed return nil, fmt.Errorf("failed to fetch feed from %s after %d retries (last error: %w)", feedRelay, maxRetries, lastErr) } // FeedItem represents a feed item type FeedItem struct { Author string Content string Time time.Time Link string }