go-telegram/ui

repository·main·Indexed 42 days ago

https://github.com/go-telegram/ui

A Go package providing high-level interactive UI components for Telegram bots. It includes tools for creating paginators, datepickers, progress bars, text-based dialog flows, and both inline and reply keyboards to improve user engagement and experience.

Tokens
6K
Snippets
17
Records
22
Agent score
53%

What's inside go-telegram/ui

  1. Overview of available UI components

    main

    The go-telegram/ui package provides several specialized UI controls for Telegram bots:

    • DatePicker: Supports custom localizations and defining included/excluded dates.
    • Inline Keyboard: A helper for building inline keyboard markups.
    • Reply Keyboard: A helper for building reply keyboard markups.
    • Paginator: Handles slices of strings with configurable perPage values and custom line separators.
    • Slider: Displays slides containing images and text.
    • Progress: A progress bar component for visualizing long-running tasks.
    • Dialog: Provides simple dialog interfaces.
    • TimePicker: (Planned/Todo)
  2. Create text dialogs with the Dialog component

    main

    The dialog component allows you to create simple text-based dialog flows, such as interactive menus. You define a sequence of dialog.Node objects, where each node contains text and an optional keyboard.

    To navigate between nodes, buttons in the keyboard can be configured with either a NodeID (to transition to another node in your defined list) or a URL (to open an external link).

    To avoid sending new messages for every interaction, use the dialog.Inline() option to update the existing message text instead.

    var dialogNodes = []dialog.Node{
    	{ID: "start", Text: "Start Node", Keyboard: [][]dialog.Button{{{Text: "Go to node 2", NodeID: "2"}, {Text: "Go to node 3", NodeID: "3"}}, {{Text: "Go Telegram UI", URL: "https://github.com/go-telegram/ui"}}}},
    	{ID: "2", Text: "node 2 without keyboard"},
    	{ID: "3", Text: "node 3", Keyboard: [][]dialog.Button{{{Text: "Go to start", NodeID: "start"}, {Text: "Go to node 4", NodeID: "4"}}}},
    	{ID: "4", Text: "node 4", Keyboard: [][]dialog.Button{{{Text: "Back to 3", NodeID: "3"}}}},
    }
  3. Prevent broken buttons after bot restarts using WithPrefix

    main

    By default, UI components register their own bot handlers on initialization. If you restart your bot instance, inline buttons in components that were already opened to users will stop working because the handlers are no longer active.

    To solve this, use the WithPrefix option when creating a widget. This allows you to define a specific prefix for the component's callback data, making it more resilient to restarts.

    picker := datepicker.New(
        b, 
        onDatepickerSimpleSelect, 
        datepicker.WithPrefix("datepicker-simple"),
    )
  4. Create a Reply Keyboard

    main

    Use reply.New() to initialize a new Reply Keyboard. You can configure the keyboard using functional options like reply.WithPrefix(string), reply.IsSelective(), and reply.IsOneTimeKeyboard().

    To add interactive buttons, use the .Button() method on the keyboard instance. This method requires:

    • The button text (string)
    • The bot instance (*bot.Bot)
    • A match type (bot.MatchType)
    • A handler function to execute when the button is pressed.

    You can organize buttons into rows using the .Row() method.

    demoReplyKeyboard = reply.New(
    	reply.WithPrefix("reply_keyboard"),
    	reply.IsSelective(),
    	reply.IsOneTimeKeyboard(),
    ).
    	Button("Button", b, bot.MatchTypeExact, onReplyKeyboardSelect).
    	Row().
    	Button("Cancel", b, bot.MatchTypeExact, onReplyKeyboardSelect)
  5. Create a Paginator slider

    main

    The slider package allows you to create interactive paginators (sliders) for Telegram bots. You define a collection of slider.Slide objects and register interaction handlers using slider.Options. To display the slider, use the Show method on the slider instance.

    package main
    
    import (
    	"context"
    	"strconv"
    
    	"github.com/go-telegram/bot"
    	"github.com/go-telegram/bot/models"
    	"github.com/go-telegram/ui/slider"
    )
    
    func defaultHandler(ctx context.Context, b *bot.Bot, update *models.Update) {
    	slides := []slider.Slide{
    		{
    			Text:  "Slide 1 content",
    			Photo: "photo_id_or_url",
    		},
    		{
    			Text:  "Slide 2 content",
    			Photo: "photo_id_or_url",
    		},
    	}
    
    	opts := []slider.Option{
    		slider.OnSelect("Select", true, sliderOnSelect),
    		slider.OnCancel("Cancel", true, sliderOnCancel),
    	}
    
    	sl := slider.New(slides, opts...)
    
    	sl.Show(ctx, b, strconv.Itoa(update.Message.Chat.ID))
    }
    
    func sliderOnSelect(ctx context.Context, b *bot.Bot, message *models.Message, item int) {
    	// Handle selection of item index
    }
    
    func sliderOnCancel(ctx context.Context, b *bot.Bot, message *models.Message) {
    	// Handle cancellation
    }
  6. Install the Go Telegram Bot UI library

    main

    To use the UI components, you must install both the core go-telegram/bot library and the go-telegram/ui library using go get.

    go get github.com/go-telegram/bot
    go get github.com/go-telegram/ui
  7. Run the UI components live demo

    main

    You can run the provided examples to see the UI components in action. You must set the EXAMPLE_TELEGRAM_BOT_TOKEN environment variable with your valid Telegram Bot Token before running the command.

    export EXAMPLE_TELEGRAM_BOT_TOKEN="your_token_here"
    go run examples/*.go
  8. Use the Progress component to show task status

    main

    The progress package provides a UI component to display progress updates to users in Telegram. You can initialize a progress instance with progress.New(), display it using p.Show(), and update the progress value using p.SetValue().

    To clean up the progress message once a task is finished, use p.Delete() followed by p.Done() to ensure the UI state is correctly finalized in the chat.

    func defaultHandler(ctx context.Context, b *bot.Bot, update *models.Update) {
    	p := progress.New()
    
    	// Display the progress message in the chat
    	p.Show(ctx, b, update.Message.Chat.ID)
    
    	// Run the task in a goroutine to avoid blocking the handler
    	go doSomeLongTaskSimple(ctx, b, p, update.Message.Chat.ID)
    }
    
    func doSomeLongTaskSimple(ctx context.Context, b *bot.Bot, p *progress.Progress, chatID int) {
    	v := 0.0
    	for {
    		time.Sleep(time.Second)
    		if v == 100 {
    			p.Delete(ctx, b)
    			p.Done(ctx, b)
    			return
    		}
    
    		v += 10.0 // simulate work
    		p.SetValue(ctx, b, v)
    	}
    }
  9. Implement a Dialog flow in a Telegram bot

    main

    To use the dialog component, follow these steps:

    1. Define your dialog structure as a slice of dialog.Node objects. Each node must have a unique ID.
    2. In your bot's handler, initialize the dialog using dialog.New(nodes).
    3. Trigger the dialog by calling d.Show(ctx, b, chatID, startNodeID).

    Note: chatID must be passed as a string.

    package main
    
    import (
    	"context"
    	"os"
    	"os/signal"
    	"strconv"
    
    	"github.com/go-telegram/bot"
    	"github.com/go-telegram/bot/models"
    	"github.com/go-telegram/ui/dialog"
    )
    
    func main() {
    	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
    	defer cancel()
    
    	telegramBotToken := os.Getenv("EXAMPLE_TELEGRAM_BOT_TOKEN")
    
    	opts := []bot.Option{
    		bot.WithDefaultHandler(defaultHandler),
    	}
    
    	b := bot.New(telegramBotToken, opts...)
    
    	b.Start(ctx)
    }
    
    var (
    	dialogNodes = []dialog.Node{
    		{ID: "start", Text: "Start Node", Keyboard: [][]dialog.Button{{{Text: "Go to node 2", NodeID: "2"}, {Text: "Go to node 3", NodeID: "3"}}, {{Text: "Go Telegram UI", URL: "https://github.com/go-telegram/ui"}}}},
    		{ID: "2", Text: "node 2 without keyboard"},
    		{ID: "3", Text: "node 3", Keyboard: [][]dialog.Button{{{Text: "Go to start", NodeID: "start"}, {Text: "Go to node 4", NodeID: "4"}}}},
    		{ID: "4", Text: "node 4", Keyboard: [][]dialog.Button{{{Text: "Back to 3", NodeID: "3"}}}},
    	},
    )
    
    func defaultHandler(ctx context.Context, b *bot.Bot, update *models.Update) {
    	d := dialog.New(dialogNodes)
    
    	d.Show(ctx, b, strconv.Itoa(update.Message.Chat.ID), "start")
    }
  10. Configure Datepicker language

    main

    The Datepicker defaults to English (en). You can change the language using the Language(lang string) option.

    To use a custom language, you must first provide a language map to the bot using bot.Languages(langsData) and then set the active language with bot.Language("your_lang_id").

    langsData := map[string]map[string]string{
        "mylang": map[string]string{
            "Monday":    "Mo",
            "Tuesday":   "Tu",
            "Wednesday": "We",
            "Thursday":  "Th",
            "Friday":    "Fr",
            "Saturday":  "Sa",
            "Sunday":    "Su",
            "January":   "January",
            "February":  "February",
            "March":     "March",
            "April":     "April",
            "May":       "May",
            "June":      "June",
            "July":      "July",
            "August":    "August",
            "September": "September",
            "October":   "October",
            "November":  "November",
            "December":  "December",
            "Cancel":    "Cancel",
            "Prev":      "Prev",
            "Next":      "Next",
            "Back":      "Back",
        },
    }
    
    opts := []bot.Option{
        bot.Languages(langsData),
        bot.Language("mylang"),
    }
    
    b := bot.New(telegramBotToken, opts...)
  11. Get started with the Paginator

    main

    The paginator package allows you to display large datasets in a paginated format within a Telegram chat. To use it, create a new paginator instance using paginator.New() with your data and configuration options, then call .Show() to render it to a specific chat.

    Basic workflow:

    1. Prepare your data (e.g., a slice of strings).
    2. Define paginator.Options such as PerPage or WithCloseButton.
    3. Initialize the paginator with paginator.New(data, opts...).
    4. Call p.Show(ctx, bot, chatID) where chatID is the string representation of the Telegram Chat ID.
    package main
    
    import (
    	"context"
    	"os"
    	"os/signal"
    	"strconv"
    
    	"github.com/go-telegram/bot"
    	"github.com/go-telegram/bot/models"
    	"github.com/go-telegram/ui/paginator"
    )
    
    var (
    	data = []string{
    		"*1* Item one",
    		"*2* Item two",
    		// ... more items
    	}
    )
    
    func main() {
    	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
    	defer cancel()
    
    	telegramBotToken := os.Getenv("EXAMPLE_TELEGRAM_BOT_TOKEN")
    
    	opts := []bot.Option{
    		bot.WithDefaultHandler(defaultHandler),
    	}
    
    	b := bot.New(telegramBotToken, opts...)
    	b.Start(ctx)
    }
    
    func defaultHandler(ctx context.Context, b *bot.Bot, update *models.Update) {
    	opts := []paginator.Option{
    		paginator.PerPage(3),
    		paginator.WithCloseButton("Close"),
    	}
    
    	p := paginator.New(data, opts...)
    	p.Show(ctx, b, strconv.Itoa(update.Message.Chat.ID))
    }
  12. Get started with the Datepicker

    main

    To use the Datepicker, initialize it using datepicker.New(bot, callback). The callback is a function that will be executed when a user selects a date. The Datepicker is used as the ReplyMarkup in a SendMessageParams call.

    Callback Signature: func(ctx context.Context, b *bot.Bot, mes *models.Message, date time.Time)

    func defaultHandler(ctx context.Context, b *bot.Bot, update *models.Update) {
    	// Create the datepicker keyboard
    	kb := datepicker.New(b, onDatepickerSimpleSelect)
    
    	b.SendMessage(ctx, &bot.SendMessageParams{
    		ChatID:      update.Message.Chat.ID,
    		Text:        "Select any date",
    		ReplyMarkup: kb,
    	})
    }
    
    // Callback triggered when a date is selected
    func onDatepickerSimpleSelect(ctx context.Context, b *bot.Bot, mes *models.Message, date time.Time) {
    	b.SendMessage(ctx, &bot.SendMessageParams{
    		ChatID: mes.Chat.ID,
    		Text:   "You select " + date.Format("2006-01-02"),
    	})
    }