diff --git a/cmd/server/main.go b/cmd/server/main.go index 916669d..4e1347e 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -31,6 +31,7 @@ func main() { loginOnly := flag.Bool("login-only", false, "Authenticate to Telegram, save session, and exit") sessionPath := flag.String("session", "", "Path to Telegram session file (default: {data-dir}/session.json)") maxPadding := flag.Int("padding", 32, "Max random padding bytes in DNS responses (anti-DPI, 0=disabled)") + msgLimit := flag.Int("msg-limit", 15, "Maximum messages to fetch per Telegram channel") showVersion := flag.Bool("version", false, "Show version and exit") flag.Parse() @@ -107,6 +108,7 @@ func main() { Passphrase: *key, ChannelsFile: *channelsFile, MaxPadding: *maxPadding, + MsgLimit: *msgLimit, Telegram: server.TelegramConfig{ APIID: id, APIHash: *apiHash, diff --git a/internal/client/fetcher.go b/internal/client/fetcher.go index 2a3cc89..82fba8b 100644 --- a/internal/client/fetcher.go +++ b/internal/client/fetcher.go @@ -392,7 +392,7 @@ func (f *Fetcher) FetchChannel(ctx context.Context, channelNum int, blockCount i } ordered[r.idx] = r.data completed++ - f.logProgress(fmt.Sprintf("Channel %d", channelNum), float64(completed), float64(blockCount)) + f.logProgress(fmt.Sprintf("Channel %d (%d/%d)", channelNum, completed, blockCount), float64(completed), float64(blockCount)) } var allData []byte diff --git a/internal/protocol/protocol.go b/internal/protocol/protocol.go index 05364d0..4c74893 100644 --- a/internal/protocol/protocol.go +++ b/internal/protocol/protocol.go @@ -9,11 +9,11 @@ import ( const ( // MinBlockPayload is the minimum decrypted payload per DNS TXT block. - MinBlockPayload = 200 + MinBlockPayload = 400 // MaxBlockPayload is the maximum decrypted payload per DNS TXT block. - // 600 bytes data + 28 GCM overhead + 2 prefix + 32 padding → ~856 base64 chars. + // 700 bytes data + 28 GCM overhead + 2 prefix + 32 padding → ~996 base64 chars. // Well within the 4096-byte EDNS0 UDP buffer the client advertises. - MaxBlockPayload = 600 + MaxBlockPayload = 700 // DefaultBlockPayload is kept for compatibility; equals MaxBlockPayload. DefaultBlockPayload = MaxBlockPayload diff --git a/internal/server/server.go b/internal/server/server.go index 8644a41..dd9cdb2 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -18,6 +18,7 @@ type Config struct { Passphrase string ChannelsFile string MaxPadding int + MsgLimit int // max messages per channel (0 = default 15) Telegram TelegramConfig } @@ -52,12 +53,16 @@ func (s *Server) Run(ctx context.Context) error { // Handle login-only mode if s.cfg.Telegram.LoginOnly { - reader := NewTelegramReader(s.cfg.Telegram, s.feed.ChannelNames(), s.feed) + reader := NewTelegramReader(s.cfg.Telegram, s.feed.ChannelNames(), s.feed, 15) return reader.Run(ctx) } // Start Telegram reader in background - reader := NewTelegramReader(s.cfg.Telegram, s.feed.ChannelNames(), s.feed) + msgLimit := s.cfg.MsgLimit + if msgLimit <= 0 { + msgLimit = 15 + } + reader := NewTelegramReader(s.cfg.Telegram, s.feed.ChannelNames(), s.feed, msgLimit) go func() { if err := reader.Run(ctx); err != nil { log.Printf("[telegram] error: %v", err) diff --git a/internal/server/telegram.go b/internal/server/telegram.go index fb74948..ff7f471 100644 --- a/internal/server/telegram.go +++ b/internal/server/telegram.go @@ -58,6 +58,7 @@ type TelegramReader struct { cfg TelegramConfig channels []string // channel usernames without @ feed *Feed + msgLimit int // max messages to fetch per channel mu sync.RWMutex cache map[string]cachedMessages @@ -70,15 +71,19 @@ type cachedMessages struct { } // NewTelegramReader creates a reader for the given channel usernames. -func NewTelegramReader(cfg TelegramConfig, channelUsernames []string, feed *Feed) *TelegramReader { +func NewTelegramReader(cfg TelegramConfig, channelUsernames []string, feed *Feed, msgLimit int) *TelegramReader { cleaned := make([]string, len(channelUsernames)) for i, u := range channelUsernames { cleaned[i] = strings.TrimPrefix(strings.TrimSpace(u), "@") } + if msgLimit <= 0 { + msgLimit = 15 + } return &TelegramReader{ cfg: cfg, channels: cleaned, feed: feed, + msgLimit: msgLimit, cache: make(map[string]cachedMessages), cacheTTL: 15 * time.Minute, } @@ -211,7 +216,7 @@ func (tr *TelegramReader) fetchChannel(ctx context.Context, api *tg.Client, user hist, err := api.MessagesGetHistory(ctx, &tg.MessagesGetHistoryRequest{ Peer: peer, - Limit: 100, + Limit: tr.msgLimit, }) if err != nil { return nil, fmt.Errorf("get history %s: %w", username, err) diff --git a/internal/web/static/index.html b/internal/web/static/index.html index 8160bf0..c4d610e 100644 --- a/internal/web/static/index.html +++ b/internal/web/static/index.html @@ -496,11 +496,12 @@ } function updateProgressDisplay(line) { - // Parse progress from "Channel N [====> ] 45%" - var match = line.match(/Channel\s+(\d+)/); + // Parse progress from "Channel N (received/total) [====> ] 45%" + var match = line.match(/Channel\s+(\d+)\s*(?:\((\d+\/\d+)\))?/); if (!match) return; var channelNum = match[1]; + var counts = match[2] || ''; var percentMatch = line.match(/(\d+)%/); var percent = percentMatch ? parseInt(percentMatch[1]) : 0; @@ -508,15 +509,18 @@ var itemId = 'progress-current'; var item = document.getElementById(itemId); + var label = 'Channel ' + channelNum; + if (counts) label += ' (' + counts + ')'; + if (!item) { item = document.createElement('div'); item.id = itemId; item.className = 'progress-item'; - item.innerHTML = '
Channel ' + channelNum + '
' + + item.innerHTML = '
' + label + '
' + '
'; panel.appendChild(item); } else { - item.querySelector('.progress-label').textContent = 'Channel ' + channelNum; + item.querySelector('.progress-label').textContent = label; } var fill = item.querySelector('.progress-fill');