diff --git a/internal/server/feed.go b/internal/server/feed.go index 7bb4602..614f634 100644 --- a/internal/server/feed.go +++ b/internal/server/feed.go @@ -210,3 +210,20 @@ func (f *Feed) SetChannels(channels []string) { f.channels = channels f.rebuildMetaBlocks() } + +// SetChannelDisplayName updates the display name for a specific channel number (1-indexed). +// This allows replacing the raw handle (e.g. "networkti") with the channel's +// actual title (e.g. "Sarto") after it has been fetched. +func (f *Feed) SetChannelDisplayName(channelNum int, displayName string) { + f.mu.Lock() + defer f.mu.Unlock() + idx := channelNum - 1 + if idx < 0 || idx >= len(f.channels) { + return + } + if displayName == "" || f.channels[idx] == displayName { + return + } + f.channels[idx] = displayName + f.rebuildMetaBlocks() +} diff --git a/internal/server/public.go b/internal/server/public.go index d8901b8..0b79810 100644 --- a/internal/server/public.go +++ b/internal/server/public.go @@ -123,7 +123,7 @@ func (pr *PublicReader) fetchAll(ctx context.Context) { continue } - msgs, err := pr.fetchChannel(ctx, username) + msgs, title, err := pr.fetchChannel(ctx, username) if err != nil { log.Printf("[public] fetch %s: %v", username, err) failed++ @@ -144,34 +144,56 @@ func (pr *PublicReader) fetchAll(ctx context.Context) { pr.feed.UpdateChannel(chNum, msgs) pr.feed.SetChatInfo(chNum, protocol.ChatTypeChannel, false) + if title != "" { + pr.feed.SetChannelDisplayName(chNum, title) + } fetched++ - log.Printf("[public] updated %s: %d messages", username, len(msgs)) + log.Printf("[public] updated %s (%s): %d messages", username, title, len(msgs)) } log.Printf("[public] fetch cycle done in %s: %d fetched, %d failed, %d total", time.Since(start).Round(time.Millisecond), fetched, failed, len(pr.channels)) } -func (pr *PublicReader) fetchChannel(ctx context.Context, username string) ([]protocol.Message, error) { +func (pr *PublicReader) fetchChannel(ctx context.Context, username string) ([]protocol.Message, string, error) { req, err := http.NewRequestWithContext(ctx, http.MethodGet, pr.baseURL+"/"+url.PathEscape(username), nil) if err != nil { - return nil, err + return nil, "", err } req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; thefeed/1.0; +https://github.com/sartoopjj/thefeed)") resp, err := pr.client.Do(req) if err != nil { - return nil, err + return nil, "", err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("unexpected HTTP status: %s", resp.Status) + return nil, "", fmt.Errorf("unexpected HTTP status: %s", resp.Status) } body, err := io.ReadAll(resp.Body) if err != nil { - return nil, err + return nil, "", err } - return parsePublicMessages(body) + msgs, err := parsePublicMessages(body) + if err != nil { + return nil, "", err + } + title := extractChannelTitle(body) + return msgs, title, nil +} + +// extractChannelTitle parses the channel display name from the Telegram public page. +// It looks for