From abf21381a01f44c1f9d52b8d16d1f3e708bfa63a Mon Sep 17 00:00:00 2001 From: Mohammad Mahdi Date: Tue, 10 Mar 2026 00:55:22 +0330 Subject: [PATCH] feat: Enhance Telegram service with authentication status management and improve RSS feed generation error handling --- internal/handlers/channel_handler.go | 8 +++ internal/rss/generator.go | 73 +++++++++++++++++++++++----- internal/telegram/provider.go | 7 ++- internal/telegram/service.go | 43 ++++++++++++---- 4 files changed, 110 insertions(+), 21 deletions(-) diff --git a/internal/handlers/channel_handler.go b/internal/handlers/channel_handler.go index 88456c3..999def3 100644 --- a/internal/handlers/channel_handler.go +++ b/internal/handlers/channel_handler.go @@ -55,6 +55,14 @@ func (ch *ChannelHandler) GetMessagesJson(c *gin.Context) { } func (ch *ChannelHandler) GetMessagesRSS(c *gin.Context) { + authStatCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + authStat, err := ch.tgService.AuthStatus(authStatCtx) + if err != nil || !authStat { + c.JSON(500, gin.H{"error": "Telegram client is not initialized"}) + } + channelId := c.Param("id") limit := 5 diff --git a/internal/rss/generator.go b/internal/rss/generator.go index 6d250a4..5e3d5cf 100644 --- a/internal/rss/generator.go +++ b/internal/rss/generator.go @@ -2,6 +2,8 @@ package rss import ( "encoding/xml" + "fmt" + "net/url" "strconv" "time" @@ -40,23 +42,57 @@ type RSSGenerator struct { logger *zap.Logger } -func NewRSSGenerator(logger zap.Logger) *RSSGenerator { - return &RSSGenerator{logger: &logger} +func NewRSSGenerator(logger *zap.Logger) *RSSGenerator { + return &RSSGenerator{logger: logger} } func (r *RSSGenerator) GenerateFeed(items []tg.MessageClass, channelId string) *RSSFeed { + if channelId == "" { + r.logger.Error("GenerateFeed called with empty channelId") + return nil + } + + nowStr := time.Now().Format("Mon, 02 Jan 2006 15:04 MST") + rssChannel := &RSSChannel{ Title: "Recent posts from @" + channelId, Link: "https://t.me/" + channelId, Description: "This feed contains the most recent posts from the Telegram channel @" + channelId + ". " + "Stay updated with the latest news and updates from the channel.", - PubDate: time.Now().Format("Mon, 02 Jan 2006 15:04 MST"), - LastBuildDate: time.Now().Format("Mon, 02 Jan 2006 15:04 MST"), + PubDate: nowStr, + LastBuildDate: nowStr, Generator: "Telegram RSS Generator", } + + errorCount := 0 + for _, m := range items { - rssChannel.Items = append(rssChannel.Items, *r.messageToItem(m, channelId)) + if m == nil { + errorCount++ + continue + } + + item, err := r.messageToItem(m, channelId) + if err != nil { + r.logger.Error("failed to convert message to RSS item", + zap.String("channel", channelId), + zap.Int("message_id", m.GetID()), + zap.Error(err), + ) + errorCount++ + continue + } + + rssChannel.Items = append(rssChannel.Items, *item) } + + if errorCount > 0 { + r.logger.Warn("feed generation completed with errors", + zap.String("channel", channelId), + zap.Int("failed", errorCount), + ) + } + return &RSSFeed{ Version: "2.0", XmlnsAtom: "http://www.w3.org/2005/Atom", @@ -64,12 +100,27 @@ func (r *RSSGenerator) GenerateFeed(items []tg.MessageClass, channelId string) * } } -func (r *RSSGenerator) messageToItem(msg tg.MessageClass, channelId string) *RSSItem { +func (r *RSSGenerator) messageToItem(msg tg.MessageClass, channelId string) (*RSSItem, error) { + message, ok := msg.(*tg.Message) + if !ok { + return nil, fmt.Errorf("unsupported message type: %T", msg) + } + + messageURL, err := url.Parse("https://t.me/" + channelId + "/" + strconv.Itoa(msg.GetID())) + if err != nil { + return nil, fmt.Errorf("failed to parse message URL: %w", err) + } + + description := message.Message + if description == "" { + description = "No content" + } + return &RSSItem{ Title: "Post by @" + channelId + " on Telegram", - Link: "https://t.me/" + channelId + "/" + strconv.Itoa(msg.GetID()), // TODO: URL parse it - PubDate: time.Unix(int64(msg.(*tg.Message).Date), 0).Format("Mon, 02 Jan 2006 15:04 MST"), - Description: msg.(*tg.Message).Message, - Guid: "https://t.me/" + channelId + "/" + strconv.Itoa(msg.GetID()), - } + Link: messageURL.String(), + PubDate: time.Unix(int64(message.Date), 0).Format("Mon, 02 Jan 2006 15:04 MST"), + Description: description, + Guid: messageURL.String(), + }, nil } diff --git a/internal/telegram/provider.go b/internal/telegram/provider.go index 638aefa..323ea84 100644 --- a/internal/telegram/provider.go +++ b/internal/telegram/provider.go @@ -52,7 +52,7 @@ func NewTelegramClient(cfg *config.Config, logger *zap.Logger) *telegram.Client return telegram.NewClient(cfg.TgAppId, cfg.TgAppHash, opts) } -func RunClient(lc fx.Lifecycle, client *telegram.Client, logger *zap.Logger) { +func RunClient(lc fx.Lifecycle, client *telegram.Client, service *Service, logger *zap.Logger) { var stop func() error lc.Append(fx.Hook{ @@ -64,6 +64,11 @@ func RunClient(lc fx.Lifecycle, client *telegram.Client, logger *zap.Logger) { stop = s logger.Info("telegram client connected") + + if err := service.InitAuthStatus(ctx); err != nil { + logger.Warn("Failed to initialize Telegram auth status", zap.Error(err)) + } + return nil }, OnStop: func(ctx context.Context) error { diff --git a/internal/telegram/service.go b/internal/telegram/service.go index fc81b75..cbede6d 100644 --- a/internal/telegram/service.go +++ b/internal/telegram/service.go @@ -4,6 +4,7 @@ import ( "context" "errors" "sync" + "time" "github.com/gotd/td/telegram" "github.com/gotd/td/telegram/auth" @@ -15,24 +16,48 @@ type Service struct { client *telegram.Client log *zap.Logger - mu sync.Mutex - phone string - phoneCodeHash string + mu sync.Mutex + phone string + phoneCodeHash string + authStatus bool + authCheckedAt time.Time + authTTL time.Duration } func NewService(client *telegram.Client, logger *zap.Logger) *Service { return &Service{ - client: client, - log: logger, + client: client, + log: logger, + authTTL: 10 * time.Minute, } } func (s *Service) AuthStatus(ctx context.Context) (bool, error) { - _, err := s.client.Auth().Status(ctx) - if err != nil { - return false, err + s.mu.Lock() + defer s.mu.Unlock() + + if time.Since(s.authCheckedAt) < s.authTTL { + return s.authStatus, nil } - return true, nil + + status, err := s.client.Auth().Status(ctx) + if err != nil { + s.authStatus = false + } else { + s.authStatus = status.Authorized + } + s.authCheckedAt = time.Now() + + return s.authStatus, err +} + +func (s *Service) InitAuthStatus(ctx context.Context) error { + status, err := s.AuthStatus(ctx) + if err != nil { + return err + } + s.log.Info("Telegram auth status initialized", zap.Bool("authenticated", status)) + return nil } func (s *Service) SendCode(ctx context.Context, phone string) error {