feat: add TitlesChannel (0xFFF9) for per-channel display names

- Add dedicated TitlesChannel (0xFFF9) following the VersionChannel pattern
- Server encodes name→title map via EncodeTitlesData/DecodeTitlesData
- Metadata wire format unchanged for backward compatibility with old clients
- All three fetchers (public Telegram, MTProto, X/Nitter) extract and store display names
- Client fetches TitlesChannel with a 10s deadline; falls back to channel handles gracefully on old servers
- Old clients are unaffected — they never query 0xFFF9
This commit is contained in:
Sepehr
2026-04-19 14:45:58 -04:00
parent dd77610f18
commit 85558074b4
7 changed files with 189 additions and 46 deletions
+38
View File
@@ -682,6 +682,44 @@ func (f *Fetcher) FetchLatestVersion(ctx context.Context) (string, error) {
return protocol.DecodeVersionData(data)
}
// FetchTitles fetches and decodes the channel display name map from TitlesChannel.
// Returns an empty map (not an error) when the server does not yet have any display names
// or when the server is an older version that does not support TitlesChannel.
// Uses a short deadline so that old servers (which return NXDOMAIN immediately) do not
// cause the 20-retry backoff in FetchBlock to stall the caller for ~95 seconds.
func (f *Fetcher) FetchTitles(ctx context.Context) (map[string]string, error) {
fetchCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
data, err := f.FetchBlock(fetchCtx, protocol.TitlesChannel, 0)
if err != nil {
return map[string]string{}, nil
}
titles, parseErr := protocol.DecodeTitlesData(data)
if parseErr == nil {
return titles, nil
}
// Titles may span multiple blocks — concatenate and retry.
allData := make([]byte, len(data))
copy(allData, data)
for blk := uint16(1); blk < 10; blk++ {
if fetchCtx.Err() != nil {
return map[string]string{}, nil
}
block, fetchErr := f.FetchBlock(fetchCtx, protocol.TitlesChannel, blk)
if fetchErr != nil {
break
}
allData = append(allData, block...)
if titles, parseErr = protocol.DecodeTitlesData(allData); parseErr == nil {
return titles, nil
}
}
return map[string]string{}, nil
}
// ErrContentHashMismatch is returned when the fetched messages do not match
// the expected content hash from metadata. This typically means the server
// regenerated its blocks between the metadata fetch and the block fetch