Skip to main content

Pagination and iterators

Many Telegram methods return results in pages, expecting you to track offsets and re-request until exhausted. The query package wraps the common ones in iterators that handle paging for you.

Each iterator offers two styles:

  • ForEach(ctx, func(ctx, elem) error) — a callback per element (most convenient).
  • Iter() / Next(ctx) / Value() / Err() — a manual loop when you need full control.

Dialogs

List every dialog (chat) on the account:

import (
"github.com/gotd/td/telegram/query"
"github.com/gotd/td/telegram/query/dialogs"
)

err := query.GetDialogs(client.API()).ForEach(ctx,
func(ctx context.Context, elem dialogs.Elem) error {
if elem.Deleted() {
return nil
}
fmt.Println(elem.Peer)
return nil
},
)

Message history

Walk a chat's history newest-first:

import "github.com/gotd/td/telegram/query/messages"

err := query.Messages(client.API()).
GetHistory(&tg.InputPeerSelf{}). // Saved Messages
ForEach(ctx, func(ctx context.Context, elem messages.Elem) error {
if msg, ok := elem.Msg.(*tg.Message); ok {
fmt.Printf("[%d] %s\n", msg.ID, msg.Message)
}
return nil
})

Elem also has elem.File() to extract downloadable media — see Downloading files.

Stopping early

Return a sentinel error from the callback and ignore it after the loop:

var errDone = errors.New("done")

err := query.Messages(client.API()).GetHistory(peer).
ForEach(ctx, func(ctx context.Context, elem messages.Elem) error {
seen++
if seen >= limit {
return errDone
}
return nil
})
if err != nil && !errors.Is(err, errDone) {
return err
}

Channel participants

List the members of a channel or supergroup. Resolve the channel first (see Peers), then iterate — this is the get-participants example:

import "github.com/gotd/td/telegram/query/channels/participants"

q := participants.NewQueryBuilder(client.API()).
GetParticipants(channel.InputChannel()).
Recent()

// A cheap total without iterating.
if total, err := q.Count(ctx); err == nil {
fmt.Println("members:", total)
}

err := q.ForEach(ctx, func(ctx context.Context, p participants.Elem) error {
user, ok := p.User()
if !ok {
return nil
}
fmt.Printf("%d %s %s\n", user.ID, user.FirstName, user.LastName)
return nil
})

Other iterators

query.NewQuery(api) is the umbrella builder; alongside dialogs, messages and participants it covers blocked users, profile photos and featured sticker sets. See the reference for the full set.

For low-level pagination that the package doesn't wrap (for example messages.getSavedGifs), the query/hasher package computes Telegram's pagination hashes for you, as in the gif-download example.