// Package main demonstrates multi-page inline keyboard navigation. // // /list shows page 1 of a sample item list with [« Prev] [Next »] buttons. // Tapping a button edits the message in-place to show the next/previous page. // Page state is encoded directly in callback data ("page:") — no server-side // session needed. // // TELEGRAM_BOT_TOKEN=xxx go run ./examples/pagination package main import ( "context" "fmt" "log" "os" "os/signal" "strconv" "strings" "syscall" "github.com/lukaszraczylo/go-telegram/api" "github.com/lukaszraczylo/go-telegram/client" "github.com/lukaszraczylo/go-telegram/dispatch" "github.com/lukaszraczylo/go-telegram/transport" ) const itemsPerPage = 5 // sampleItems is the list being paginated. var sampleItems = []string{ "Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo", "Sierra", "Tango", } // renderPage builds the message text and keyboard for the given page number. func renderPage(items []string, page int) (string, *api.InlineKeyboardMarkup) { start := page * itemsPerPage if start >= len(items) { start = 0 page = 0 } end := start + itemsPerPage if end > len(items) { end = len(items) } var sb strings.Builder fmt.Fprintf(&sb, "Items (page %d of %d):\n\n", page+1, totalPages(len(items))) for i := start; i < end; i++ { fmt.Fprintf(&sb, "%d. %s\n", i+1, items[i]) } var btns []api.InlineKeyboardButton if page > 0 { btns = append(btns, api.InlineKeyboardButton{ Text: "« Prev", CallbackData: fmt.Sprintf("page:%d", page-1), }) } if end < len(items) { btns = append(btns, api.InlineKeyboardButton{ Text: "Next »", CallbackData: fmt.Sprintf("page:%d", page+1), }) } var markup *api.InlineKeyboardMarkup if len(btns) > 0 { markup = &api.InlineKeyboardMarkup{ InlineKeyboard: [][]api.InlineKeyboardButton{btns}, } } return sb.String(), markup } func totalPages(total int) int { pages := total / itemsPerPage if total%itemsPerPage != 0 { pages++ } return pages } func main() { token := os.Getenv("TELEGRAM_BOT_TOKEN") if token == "" { log.Fatal("TELEGRAM_BOT_TOKEN required") } ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() bot := client.New(token, client.WithHTTPClient(client.NewRetryDoer(client.NewDefaultHTTPDoer())), ) router := dispatch.New(bot) // /list — send page 0. router.OnCommand("/list", func(c *dispatch.Context, m *api.Message) error { text, markup := renderPage(sampleItems, 0) _, err := api.SendMessage(c.Ctx, c.Bot, &api.SendMessageParams{ ChatID: api.ChatIDFromInt(m.Chat.ID), Text: text, ReplyMarkup: markup, }) return err }) // page: callbacks — edit message in-place. router.OnCallback(`^page:(\d+)$`, func(c *dispatch.Context, q *api.CallbackQuery) error { page, _ := strconv.Atoi(c.RegexMatch[1]) // Acknowledge the tap first. _, _ = api.AnswerCallbackQuery(c.Ctx, c.Bot, &api.AnswerCallbackQueryParams{ CallbackQueryID: q.ID, }) if q.Message == nil { return nil } msg, ok := q.Message.(*api.Message) if !ok { return nil } text, markup := renderPage(sampleItems, page) chatID := api.ChatIDFromInt(msg.Chat.ID) mid := msg.MessageID _, err := api.EditMessageText(c.Ctx, c.Bot, &api.EditMessageTextParams{ ChatID: &chatID, MessageID: &mid, Text: text, ReplyMarkup: markup, }) return err }) poller := transport.NewLongPoller(bot) if err := router.Run(ctx, poller); err != nil && err != context.Canceled { log.Printf("router exited: %v", err) } }