bote

package module
v0.0.0-...-1810209 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 4, 2026 License: MIT Imports: 41 Imported by: 0

README

Bote: Interactive Telegram Bot Framework for Go

Bote is a powerful wrapper for Telebot.v4 that simplifies building interactive Telegram bots with smart message management, user state tracking, and advanced keyboard handling.

Go Reference Go Report Card

Features

  • Smart Message Management: Main, head, notification, and history message handling
  • User State Tracking: Track and manage user states across different messages
  • Interactive Keyboards: Easy creation and management of inline keyboards
  • Middleware Support: Add custom middleware functions
  • Internationalization: Built-in support for multiple languages
  • Persistence: Optional user data persistence between bot restarts
  • Context-based API: Clean, context-based API for handlers

Installation

go get -u github.com/maxbolgarin/bote

Concepts

Bote introduces several important concepts that make building interactive bots easier:

Message Types
  • Main Message: The primary interactive message shown to the user
  • Head Message: Optional message displayed above the main message
  • Notification Message: Temporary messages for user notifications
  • Error Message: Special messages for error handling
  • History Messages: Previous main messages that have been replaced
User States

Each message in Bote has an associated state, allowing you to track and control the flow of your bot's interaction with users. States help you manage user interactions across different messages and sessions.

Context

The Context interface provides access to user information, message management, and keyboard creation. All handlers receive a Context object to interact with the bot and user.

Quick Start

package main

import (
	"context"
	"os"
	"os/signal"

	"github.com/maxbolgarin/bote"
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	token := os.Getenv("TELEGRAM_BOT_TOKEN")
	if token == "" {
		panic("TELEGRAM_BOT_TOKEN is not set")
	}

	// Create a new bot with configuration
	cfg := bote.Config{
		DefaultLanguageCode: "en",
		NoPreview:           true,
	}

	b, err := bote.New(ctx, token, bote.WithConfig(cfg))
	if err != nil {
		panic(err)
	}

	// Start the bot with a start handler
	b.Start(ctx, startHandler, nil)

	// Wait for interrupt signal
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt)
	<-ch

	// Stop the bot gracefully
	b.Stop()
}

// Handler for the /start command
func startHandler(ctx bote.Context) error {
	// Create an inline keyboard
	kb := bote.InlineBuilder(3, bote.OneBytePerRune,
		ctx.Btn("Option 1", option1Handler),
		ctx.Btn("Option 2", option2Handler),
		ctx.Btn("Option 3", option3Handler),
	)
	
	// Send a main message with the keyboard
	return ctx.SendMain(bote.NoChange, "Welcome to my bot! Choose an option:", kb)
}

func option1Handler(ctx bote.Context) error {
	return ctx.SendMain(bote.NoChange, "You selected Option 1", nil)
}

func option2Handler(ctx bote.Context) error {
	return ctx.SendMain(bote.NoChange, "You selected Option 2", nil)
}

func option3Handler(ctx bote.Context) error {
	return ctx.SendMain(bote.NoChange, "You selected Option 3", nil)
}

Core Concepts in Detail

Bot Initialization

Create a new bot instance with the New function:

b, err := bote.New(ctx, token, bote.WithConfig(cfg))

Available options:

  • WithConfig(cfg Config): Set bot configuration
  • WithUserDB(db UsersStorage): Use a custom user storage
  • WithLogger(log Logger): Use a custom logger
  • WithMessages(msgs MessageProvider): Use custom message provider
  • WithUpdateLogger(l UpdateLogger): Use custom update logger
States

States in Bote track the user's progress and context. Create custom states like this:

type AppState string

func (s AppState) String() string { return string(s) }
func (s AppState) IsText() bool   { return false }
func (s AppState) NotChanged() bool { return s == "" }

const (
	StateStart    AppState = "start"
	StateMainMenu AppState = "main_menu"
	StateSettings AppState = "settings"
	StateProfile  AppState = "profile"
	// Define text expecting states
	StateAwaitingName AppState = "awaiting_name"
)

// For text-expecting states, override IsText
func (s AppState) IsText() bool {
	return s == StateAwaitingName
}
Message Management

Bote provides several methods for managing messages:

// Send a main message
ctx.SendMain(newState, "Hello, world!", keyboard)

// Send a notification
ctx.SendNotification("Notification message", nil)

// Edit the main message
ctx.EditMain(newState, "Updated message", newKeyboard)

// Send both main and head messages
ctx.Send(newState, "Main message", "Head message", mainKeyboard, headKeyboard)
User Management

Access and manage user data:

// Get the current user
user := ctx.User()

// Access user properties
userID := user.ID()
username := user.Username()
language := user.Language()

// Get user state
currentState := user.StateMain()

// Get all messages
messages := user.Messages()
Keyboard Creation

Create interactive inline keyboards:

// Simple inline keyboard with buttons in one row
keyboard := bote.SingleRow(
    ctx.Btn("Button 1", handler1),
    ctx.Btn("Button 2", handler2),
)

// Multi-row keyboard with automatic layout
keyboard := bote.InlineBuilder(2, bote.TwoBytesPerRune,
    ctx.Btn("Button 1", handler1),
    ctx.Btn("Button 2", handler2),
    ctx.Btn("Button 3", handler3),
    ctx.Btn("Button 4", handler4),
)

// Manual keyboard building
kb := bote.NewKeyboard(2) // 2 buttons per row maximum
kb.Add(ctx.Btn("Button 1", handler1))
kb.Add(ctx.Btn("Button 2", handler2))
kb.StartNewRow() // Force new row
kb.Add(ctx.Btn("Button 3", handler3))
keyboard := kb.CreateInlineMarkup()
Handling Button Callbacks

When creating buttons, you register handlers that will be called when the button is pressed:

ctx.Btn("Settings", func(ctx bote.Context) error {
    // This will be called when the Settings button is pressed
    
    // You can access button data
    data := ctx.Data() // Get complete callback data
    
    // Create a new keyboard for settings
    kb := bote.InlineBuilder(1, bote.OneBytePerRune,
        ctx.Btn("Profile", profileHandler),
        ctx.Btn("Language", languageHandler),
        ctx.Btn("Back", mainMenuHandler),
    )
    
    return ctx.EditMain(StateSettings, "Settings", kb)
})
Handling Text Messages

Handle text messages by setting a text handler and checking the user state:

// Set the text handler
b.SetTextHandler(func(ctx bote.Context) error {
    // Get the user state
    state := ctx.User().StateMain()
    
    // Handle based on state
    switch state {
    case StateAwaitingName:
        name := ctx.Text()
        // Process the name
        return ctx.SendMain(StateMainMenu, "Thank you, " + name + "!", mainMenuKeyboard)
    default:
        return ctx.SendMain(StateMainMenu, "I don't understand that command.", mainMenuKeyboard)
    }
})

// In another handler, set the state to await text
func askNameHandler(ctx bote.Context) error {
    return ctx.SendMain(StateAwaitingName, "Please enter your name:", nil)
}

Advanced Features

Persistence

Implement the UsersStorage interface to persist user data between bot restarts:

type MyStorage struct {
    // Your storage implementation
}

func (s *MyStorage) Insert(ctx context.Context, userModel bote.UserModel) error {
    // Insert user into database
}

func (s *MyStorage) Find(ctx context.Context, id int64) (bote.UserModel, bool, error) {
    // Find user in database
}

func (s *MyStorage) Update(id int64, userModel *bote.UserModelDiff) {
    // Update user in database asynchronously
}

// Use your storage when creating the bot
storage := &MyStorage{}
b, err := bote.New(ctx, token, bote.WithUserDB(storage))
Custom Message Provider

Implement the MessageProvider interface for custom messaging and internationalization:

type MyMessages struct{}

func (m *MyMessages) Messages(languageCode string) bote.Messages {
    switch languageCode {
    case "ru":
        return &MyRussianMessages{}
    default:
        return &MyEnglishMessages{}
    }
}

type MyEnglishMessages struct{}

func (m *MyEnglishMessages) GeneralError() string {
    return "An error occurred"
}

func (m *MyEnglishMessages) FatalError() string {
    return "A fatal error occurred! Please restart with /start"
}

func (m *MyEnglishMessages) PrepareMessage(msg string, u bote.User, newState bote.State, msgID int, isHistorical bool) string {
    // Customize message as needed
    return msg
}

// Use your message provider when creating the bot
msgs := &MyMessages{}
b, err := bote.New(ctx, token, bote.WithMessages(msgs))
Middleware

Add custom middleware to process updates before they reach handlers:

b.AddMiddleware(func(upd *tele.Update, user bote.User) bool {
    // Process update
    // Return false to stop update processing
    return true
})
Bot Restart Recovery

Handle bot restarts by providing a state map to the Start method:

stateMap := map[bote.State]bote.InitBundle{
    StateMainMenu: {
        Handler: mainMenuHandler,
    },
    StateSettings: {
        Handler: settingsHandler,
    },
}

b.Start(ctx, startHandler, stateMap)

Complete Example: Multi-step Form Bot

Here's a more complete example of a bot that guides users through a form:

package main

import (
	"context"
	"os"
	"os/signal"

	"github.com/maxbolgarin/bote"
)

// Define custom states
type AppState string

func (s AppState) String() string { return string(s) }
func (s AppState) IsText() bool   { return s == StateAwaitingName || s == StateAwaitingEmail || s == StateAwaitingAge }
func (s AppState) NotChanged() bool { return s == "" }

const (
	StateStart        AppState = "start"
	StateMainMenu     AppState = "main_menu"
	StateForm         AppState = "form"
	StateFormComplete AppState = "form_complete"
	
	// Text states
	StateAwaitingName  AppState = "awaiting_name"
	StateAwaitingEmail AppState = "awaiting_email"
	StateAwaitingAge   AppState = "awaiting_age"
)

// User data
type UserData struct {
	Name  string
	Email string
	Age   string
}

var userData = make(map[int64]UserData)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	token := os.Getenv("TELEGRAM_BOT_TOKEN")
	if token == "" {
		panic("TELEGRAM_BOT_TOKEN is not set")
	}

	cfg := bote.Config{
		DefaultLanguageCode: "en",
		NoPreview:           true,
	}

	b, err := bote.New(ctx, token, bote.WithConfig(cfg))
	if err != nil {
		panic(err)
	}

	// Set text handler
	b.SetTextHandler(textHandler)

	// Define state map for bot restart
	stateMap := map[bote.State]bote.InitBundle{
		StateMainMenu: {
			Handler: mainMenuHandler,
		},
		StateForm: {
			Handler: formHandler,
		},
		StateAwaitingName: {
			Handler: askNameHandler,
		},
		StateAwaitingEmail: {
			Handler: askEmailHandler,
		},
		StateAwaitingAge: {
			Handler: askAgeHandler,
		},
		StateFormComplete: {
			Handler: formCompleteHandler,
		},
	}

	// Start the bot
	b.Start(ctx, startHandler, stateMap)

	// Wait for interrupt signal
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt)
	<-ch

	// Stop the bot gracefully
	b.Stop()
}

// Start handler - initial command
func startHandler(ctx bote.Context) error {
	kb := bote.InlineBuilder(1, bote.OneBytePerRune,
		ctx.Btn("Main Menu", mainMenuHandler),
	)
	return ctx.SendMain(StateStart, "Welcome to the Form Bot!", kb)
}

// Main menu handler
func mainMenuHandler(ctx bote.Context) error {
	kb := bote.InlineBuilder(1, bote.OneBytePerRune,
		ctx.Btn("Fill Form", formHandler),
	)
	return ctx.SendMain(StateMainMenu, "Main Menu", kb)
}

// Form handler - starts the form process
func formHandler(ctx bote.Context) error {
	// Initialize user data
	userData[ctx.User().ID()] = UserData{}
	
	// Start with name
	return askNameHandler(ctx)
}

// Ask for name
func askNameHandler(ctx bote.Context) error {
	return ctx.SendMain(StateAwaitingName, "Please enter your name:", nil)
}

// Ask for email
func askEmailHandler(ctx bote.Context) error {
	return ctx.SendMain(StateAwaitingEmail, "Please enter your email:", nil)
}

// Ask for age
func askAgeHandler(ctx bote.Context) error {
	return ctx.SendMain(StateAwaitingAge, "Please enter your age:", nil)
}

// Form complete
func formCompleteHandler(ctx bote.Context) error {
	user := userData[ctx.User().ID()]
	
	message := bote.NewBuilder()
	message.Writeln("Form Complete!")
	message.Writeln("")
	message.Writeln("Your information:")
	message.Writeln("Name: " + user.Name)
	message.Writeln("Email: " + user.Email)
	message.Writeln("Age: " + user.Age)
	
	kb := bote.InlineBuilder(1, bote.OneBytePerRune,
		ctx.Btn("Main Menu", mainMenuHandler),
		ctx.Btn("Fill Again", formHandler),
	)
	
	return ctx.SendMain(StateFormComplete, message.String(), kb)
}

// Handle text messages based on state
func textHandler(ctx bote.Context) error {
	state := ctx.User().StateMain()
	text := ctx.Text()
	userID := ctx.User().ID()
	
	switch state {
	case StateAwaitingName:
		userData[userID] = UserData{Name: text}
		return askEmailHandler(ctx)
		
	case StateAwaitingEmail:
		user := userData[userID]
		user.Email = text
		userData[userID] = user
		return askAgeHandler(ctx)
		
	case StateAwaitingAge:
		user := userData[userID]
		user.Age = text
		userData[userID] = user
		return formCompleteHandler(ctx)
		
	default:
		return ctx.SendNotification("I don't understand that command.", nil)
	}
}

Best Practices

  1. Organize states: Keep your states organized and well-defined
  2. Use context methods: Rely on Context methods for message management
  3. Plan your message flow: Design how messages will flow and states will change
  4. Implement persistence: Use a database to store user data between restarts
  5. Use middlewares: Add middlewares for logging, analytics, or rate limiting
  6. Error handling: Always handle errors in your handlers

License

MIT

Documentation

Overview

Package bote provides a comprehensive metrics system for Telegram bot monitoring. It includes metrics for updates, handlers, messages, users, webhooks, and errors.

Index

Constants

View Source
const (
	// Error types for different failure scenarios
	MetricsErrorBotBlocked       = "bot_blocked"        // Bot is blocked by user
	MetricsErrorHandler          = "handler"            // Handler execution error
	MetricsErrorInternal         = "internal"           // Internal bote package error
	MetricsErrorTelegramAPI      = "telegram_api"       // Telegram API error
	MetricsErrorInvalidUserState = "invalid_user_state" // Invalid user state error
	MetricsErrorBadUsage         = "bad_usage"          // Package usage error
	MetricsErrorConnectionError  = "connection_error"   // Connection error

	// Error severity levels
	MetricsErrorSeverityLow = "low"  // Low severity error
	MetricsErrorSeveritHigh = "high" // High severity error

	// Time window constants for active user metrics
	MetricsWindow1h  = "1h"  // 1 hour window
	MetricsWindow24h = "24h" // 24 hour window

)

Error type constants for metrics categorization

View Source
const (
	LogLevelDebug = "debug"
	LogLevelInfo  = "info"
	LogLevelWarn  = "warn"
	LogLevelError = "error"
)
View Source
const (
	// UserIDDBFieldName is a field name for plain user ID in DB.
	UserIDDBFieldName = "id.id_plain"
	// IDEncDBFieldName is a field name for encrypted user ID in DB.
	UserIDEncDBFieldName = "id.id_enc"
	// EncKeyVersionDBFieldName is a field name for encryption key version in DB.
	UserIDEncKeyVersionDBFieldName = "id.enc_key_version"
	// UserIDHMACDBFieldName is a field name for HMAC of user ID in DB.
	UserIDHMACDBFieldName = "id.id_hmac"
	// UserIDHMACKeyVersionDBFieldName is a field name for HMAC key version in DB.
	UserIDHMACKeyVersionDBFieldName = "id.hmac_key_version"
)
View Source
const (
	// MaxTextLenInLogs is the maximum length of the text in message logs.
	MaxTextLenInLogs = 64
)

Variables

View Source
var (
	EmptyBtn      tele.Btn
	EmptyKeyboard = Inline(maxButtonsInRow)
)
View Source
var (
	// Handler duration buckets for all handlers (50ms to 5s)
	BotHandlerDurationBuckets = []float64{0.05, 0.1, 0.25, 0.5, 1, 2, 5}
	// Webhook response time buckets for all webhooks (5ms to 5s)
	WebhookHistogramBuckets = []float64{0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 2.5}
	// Session length buckets for active users (10s to 1h)
	SessionLengthBucketsSeconds = []float64{10, 30, 60, 120, 300, 600, 1800}
)

Predefined histogram buckets for different metric types

View Source
var EmptyHandler = func(Context) error { return nil }

EmptyHandler is a handler that does nothing.

Functions

func AllowWithoutReply

func AllowWithoutReply() any

func CreateBtnData

func CreateBtnData(dataList ...string) string

CreateBtnData creates data string from dataList, that should be passed as data to callback button. This method can be useful when creating InitBundle with providing [InitBundle.Data].

func F

func F(msg string, formats ...Format) string

F returns a formatted string.

func FB

func FB(msg string) string

FB returns a string with bold formatting.

func FBC

func FBC(msg string) string

FBC is an alias for FBoldCode.

func FBI

func FBI(msg string) string

FBI is an alias for FBoldItalic.

func FBU

func FBU(msg string) string

FBU is an alias for FBoldUnderline.

func FBf

func FBf(msg string, args ...any) string

FBf returns a string with bold formatting. If args are provided, it uses fmt.Sprintf to format the string.

func FBold

func FBold(msg string) string

FBold returns a string with bold formatting.

func FBoldCode

func FBoldCode(msg string) string

FBoldCode returns a string with bold and code formatting.

func FBoldItalic

func FBoldItalic(msg string) string

FBoldItalic returns a string with bold and italic formatting.

func FBoldUnderline

func FBoldUnderline(msg string) string

FBoldUnderline returns a string with bold and underline formatting.

func FBoldf

func FBoldf(msg string, args ...any) string

FBoldf returns a string with bold formatting. If args are provided, it uses fmt.Sprintf to format the string.

func FC

func FC(msg string) string

FC returns a string with code formatting.

func FCf

func FCf(msg string, args ...any) string

FCf returns a string with code formatting. If args are provided, it uses fmt.Sprintf to format the string.

func FCode

func FCode(msg string) string

FCode returns a string with code formatting.

func FCodef

func FCodef(msg string, args ...any) string

FCodef returns a string with code formatting. If args are provided, it uses fmt.Sprintf to format the string.

func FI

func FI(msg string) string

FI returns a string with italic formatting.

func FIf

func FIf(msg string, args ...any) string

FI returns a string with italic formatting. If args are provided, it uses fmt.Sprintf to format the string.

func FItalic

func FItalic(msg string) string

FItalic returns a string with italic formatting.

func FItalicf

func FItalicf(msg string, args ...any) string

FItalicf returns a string with italic formatting. If args are provided, it uses fmt.Sprintf to format the string.

func FP

func FP(msg string) string

FP returns a string with pre formatting.

func FPf

func FPf(msg string, args ...any) string

FPf returns a string with pre formatting. If args are provided, it uses fmt.Sprintf to format the string.

func FPre

func FPre(msg string) string

FPre returns a string with pre formatting.

func FPref

func FPref(msg string, args ...any) string

FPref returns a string with pre formatting. If args are provided, it uses fmt.Sprintf to format the string.

func FS

func FS(msg string) string

FS returns a string with strike formatting.

func FSf

func FSf(msg string, args ...any) string

FSf returns a string with strike formatting. If args are provided, it uses fmt.Sprintf to format the string.

func FStrike

func FStrike(msg string) string

FStrike returns a string with strike formatting.

func FStrikef

func FStrikef(msg string, args ...any) string

FStrikef returns a string with strike formatting. If args are provided, it uses fmt.Sprintf to format the string.

func FU

func FU(msg string) string

FU returns a string with underline formatting.

func FUf

func FUf(msg string, args ...any) string

FUf returns a string with underline formatting. If args are provided, it uses fmt.Sprintf to format the string.

func FUnderline

func FUnderline(msg string) string

FUnderline returns a string with underline formatting.

func FUnderlinef

func FUnderlinef(msg string, args ...any) string

FUnderlinef returns a string with underline formatting. If args are provided, it uses fmt.Sprintf to format the string.

func Ff

func Ff(msg string, args ...any) string

Ff returns a formatted string, just like fmt.Sprintf.

func ForceReply

func ForceReply() any

func GetFilledMessage

func GetFilledMessage(left, right, sep, fill string, maxLeft, maxRight, maxLen int) string

GetFilledMessage returns a formatted string with aligned left and right parts.

func HTML

func HTML() any

func Inline

func Inline(rowLength int, btns ...tele.Btn) *tele.ReplyMarkup

Inline creates inline keyboard from provided rows of buttons.

func InlineBuilder

func InlineBuilder(columns int, runesTypes RuneSizeType, btns ...tele.Btn) *tele.ReplyMarkup

InlineBuilder creates inline keyboard from provided buttons and columns count.

func Markdown

func Markdown() any

func MarkdownV2

func MarkdownV2() any

func NewHMAC

func NewHMAC(id int64, hmacKey *EncryptionKey) string

NewHMAC creates a new HMAC of provided ID using provided HMAC key.

func NoPreview

func NoPreview() any

func OneTimeKeyboard

func OneTimeKeyboard() any

func Protected

func Protected() any

func RegisterTextStates

func RegisterTextStates(state ...State) bool

RegisterTextState registers a state that expects text input from the user. It is used to handle text input from the user in a correct order.

func RemoveKeyboard

func RemoveKeyboard() *tele.ReplyMarkup

RemoveKeyboard creates remove keyboard request.

func Silent

func Silent() any

func SingleRow

func SingleRow(btn ...tele.Btn) *tele.ReplyMarkup

SingleRow creates inline keyboard from provided buttons with a single row.

func WithAllowedUpdates

func WithAllowedUpdates(updates ...string) func(opts *Options)

WithAllowedUpdates returns an option that sets the allowed updates.

func WithBotConfig

func WithBotConfig(cfg BotConfig) func(opts *Options)

WithBotConfig returns an option that sets the bot configuration.

func WithConfig

func WithConfig(cfg Config) func(opts *Options)

WithConfig returns an option that sets the bot configuration.

func WithCustomPoller

func WithCustomPoller(poller tele.Poller) func(opts *Options)

WithCustomPoller returns an option that sets the custom poller.

func WithDebugIncomingUpdates

func WithDebugIncomingUpdates() func(opts *Options)

WithDebugIncomingUpdates returns an option that sets the debug incoming updates.

func WithDefaultLanguage

func WithDefaultLanguage(lang Language) func(opts *Options)

WithDefaultLanguage returns an option that sets the default language.

func WithLogLevel

func WithLogLevel(level string) func(opts *Options)

WithDebug returns an option that sets the debug mode.

func WithLogger

func WithLogger(logger Logger, level ...string) func(opts *Options)

WithLogger returns an option that sets the logger.

func WithLongPolling

func WithLongPolling(timeout ...time.Duration) func(opts *Options)

WithLongPolling returns an option that sets the long polling configuration.

func WithLongPollingConfig

func WithLongPollingConfig(cfg LongPollingConfig) func(opts *Options)

WithLongPolling returns an option that sets the long polling configuration.

func WithLowPrivacyMode

func WithLowPrivacyMode() func(opts *Options)

WithLowPrivacyMode returns an option that sets the low privacy mode.

func WithMetricsConfig

func WithMetricsConfig(metrics MetricsConfig) func(opts *Options)

func WithMode

func WithMode(mode PollingMode) func(opts *Options)

WithMode returns an option that sets the polling mode.

func WithMsgsProvider

func WithMsgsProvider(msgs MessageProvider) func(opts *Options)

WithMsgsProvider returns an option that sets the message provider.

func WithOffline

func WithOffline(poller ...tele.Poller) func(opts *Options)

WithOffline returns an option that sets the offline mode. If poller is provided, it will be used instead of the default poller. It is used to create a bot without network for testing purposes.

func WithStrictPrivacyMode

func WithStrictPrivacyMode(encryptionKey *string, encryptionKeyVersion *int64, hmacKey *string, hmacKeyVersion *int64) func(opts *Options)

WithLowPrivacyMode returns an option that sets the low privacy mode.

func WithStrictPrivacyModeKeyProvider

func WithStrictPrivacyModeKeyProvider(keysProvider KeysProvider) func(opts *Options)

WithStrictPrivacyMode returns an option that sets the strict privacy mode.

func WithUpdateLogger

func WithUpdateLogger(logger UpdateLogger) func(opts *Options)

WithUpdateLogger returns an option that sets the update logger.

func WithUserDB

func WithUserDB(db UsersStorage) func(opts *Options)

WithUserDB returns an option that sets the user storage.

func WithWebhook

func WithWebhook(url string, listen ...string) func(opts *Options)

WithWebhook returns an option that creates a webhook configuration from URL and basic settings. This is a convenience function for simple webhook setups.

func WithWebhookAllowedIPs

func WithWebhookAllowedIPs(allowedIPs ...string) func(opts *Options)

WithWebhookAllowedIPs returns an option that sets the webhook allowed IPs.

func WithWebhookAllowedTelegramIPs

func WithWebhookAllowedTelegramIPs() func(opts *Options)

WithWebhookAllowedTelegramIPs returns an option that sets the webhook allowed Telegram IPs.

func WithWebhookCertificate

func WithWebhookCertificate(certFile, keyFile string, loadCertificateInTelegram bool, startHTTPS bool) func(opts *Options)

WithWebhookTLS returns an option that creates a webhook configuration with TLS certificates.

func WithWebhookConfig

func WithWebhookConfig(cfg WebhookConfig) func(opts *Options)

WithWebhookConfig returns an option that sets the webhook configuration.

func WithWebhookDefaultMetrics

func WithWebhookDefaultMetrics(metricsPath ...string) func(opts *Options)

WithWebhookGenerateCertificate returns an option that generates self-signed certificate and uploads it to Telegram.

func WithWebhookGenerateCertificate

func WithWebhookGenerateCertificate(directory ...string) func(opts *Options)

WithWebhookGenerateCertificate returns an option that generates self-signed certificate and uploads it to Telegram.

func WithWebhookMetrics

func WithWebhookMetrics(metrics MetricsConfig, metricsPath ...string) func(opts *Options)

WithWebhookGenerateCertificate returns an option that generates self-signed certificate and uploads it to Telegram.

func WithWebhookRateLimit

func WithWebhookRateLimit(rps int, burst int) func(opts *Options)

WithWebhookRateLimit returns an option that sets the webhook rate limit.

func WithWebhookSecretToken

func WithWebhookSecretToken(secretToken string) func(opts *Options)

WithWebhookSecretToken returns an option that sets the webhook secret token.

func WithWebhookSecurityHeaders

func WithWebhookSecurityHeaders() func(opts *Options)

WithWebhookSecurityHeaders returns an option that sets the webhook security headers.

func WithWebhookServer

func WithWebhookServer(listen string, isHTTPS bool) func(opts *Options)

WithWebhookServer returns an option that sets the webhook server listen address.

Types

type Bot

type Bot struct {
	// contains filtered or unexported fields
}

Bot is a main struct of this package. It contains all necessary components for working with Telegram bot.

func New

func New(ctx context.Context, token string, optsFuncs ...func(*Options)) (*Bot, error)

New creates the bot with optional options. It starts the bot in a separate goroutine. You should call [Bot.Stop] to gracefully shutdown the bot.

func NewWithOptions

func NewWithOptions(ctx context.Context, token string, opts Options) (*Bot, error)

NewWithOptions starts the bot with options. It starts the bot in a separate goroutine. You should call [Bot.Stop] to gracefully shutdown the bot.

func (*Bot) AddMiddleware

func (b *Bot) AddMiddleware(f MiddlewareFuncTele, chatType ...tele.ChatType)

AddMiddleware adds middleware functions that will be called on each update for a specific chat type.

func (*Bot) AddUserMiddleware

func (b *Bot) AddUserMiddleware(f ...MiddlewareFunc)

AddUserMiddleware adds middleware functions that will be called on each update for a user.

func (*Bot) Bot

func (b *Bot) Bot() *tele.Bot

Bot returns the underlying *tele.Bot.

func (*Bot) CreateUserFromModel

func (b *Bot) CreateUserFromModel(model UserModel, addToCache bool) User

CreateUserFromModel creates a new user from UserModel. If addToCache is true, the user will be added to the cache. It is useful when you want to preinit user in cache before he makes a request. User should have plain ID to be added to the cache.

func (*Bot) DeleteInChat

func (b *Bot) DeleteInChat(chatID int64, msgID int) error

func (*Bot) EditInChat

func (b *Bot) EditInChat(chatID int64, msgID int, msg string, kb *tele.ReplyMarkup, opts ...any) error

func (*Bot) GetAllUsersFromCache

func (b *Bot) GetAllUsersFromCache() []User

GetAllUsersFromCache returns all loaded users.

func (*Bot) GetUserID

func (b *Bot) GetUserID(userID FullUserID) (int64, error)

func (*Bot) Handle

func (b *Bot) Handle(endpoint any, f HandlerFunc)

Handle sets handler for any endpoint. Endpoint can be string or callback button.

func (*Bot) SendInChat

func (b *Bot) SendInChat(chatID int64, threadID int, msg string, kb *tele.ReplyMarkup, opts ...any) (int, error)

SendInChat sends a message to a specific chat ID and thread ID. chatID is the target chat ID, threadID is the target thread ID (0 for no thread). msg is the message to send. kb is the keyboard to send. opts are additional options for sending the message.

func (*Bot) SetMessageProvider

func (b *Bot) SetMessageProvider(msgs MessageProvider)

SetMessageProvider sets message provider.

func (*Bot) SetTextHandler

func (b *Bot) SetTextHandler(handler HandlerFunc)

SetTextHandler sets handler for text messages. You should provide a single handler for all text messages, that will call another handlers based on the state.

func (*Bot) Start

func (b *Bot) Start(ctx context.Context, startHandler HandlerFunc, stateMap map[State]InitBundle) chan struct{}

Start starts the bot in a separate goroutine. StartHandler is a handler for /start command. StateMap is a map for initing users after bot restart. It runs an assigned handler for every active user message when user makes a request by message's state. Inline buttons will trigger onCallback handler if you don't init them after bot restart. You can pass nil map if you don't need to reinit messages.

type BotConfig

type BotConfig struct {
	// ParseMode is the default parse mode for the bot.
	// Default: HTML.
	// Environment variable: BOTE_PARSE_MODE.
	// It can be one of the following:
	// - "HTML"
	// - "Markdown"
	// - "MarkdownV2"
	ParseMode tele.ParseMode `yaml:"mode" json:"mode" env:"BOTE_PARSE_MODE"`

	// Privacy is a configuration for privacy mode.
	Privacy PrivacyConfig `yaml:"privacy" json:"privacy"`

	// DefaultLanguage is the default language code for the bot in ISO 639-1 format.
	// Default: "en".
	// Environment variable: BOTE_DEFAULT_LANGUAGE.
	DefaultLanguage Language `yaml:"default_language" json:"default_language" env:"BOTE_DEFAULT_LANGUAGE"`

	// NoPreview is a flag that disables link preview in bot messages.
	// Default: false.
	// Environment variable: BOTE_NO_PREVIEW.
	NoPreview bool `yaml:"no_preview" json:"no_preview" env:"BOTE_NO_PREVIEW"`

	// DeleteMessages is a flag that enables deleting every user message.
	// Default: true.
	// Environment variable: BOTE_DELETE_MESSAGES.
	DeleteMessages *bool `yaml:"delete_messages" json:"delete_messages" env:"BOTE_DELETE_MESSAGES"`

	// UserCacheCapacity is the capacity of the user cache. Cache will evict users with least activity.
	// Default: 10000.
	// Environment variable: BOTE_USER_CACHE_CAPACITY.
	UserCacheCapacity int `yaml:"user_cache_capacity" json:"user_cache_capacity" env:"BOTE_USER_CACHE_CAPACITY"`

	// UserCacheTTL is the TTL of the user cache.
	// Default: 24 hours.
	// Environment variable: BOTE_USER_CACHE_TTL.
	UserCacheTTL time.Duration `yaml:"user_cache_ttl" json:"user_cache_ttl" env:"BOTE_USER_CACHE_TTL"`
}

type ButtonBuilder

type ButtonBuilder interface {
	Btn(name string, callback HandlerFunc, dataList ...string) tele.Btn
}

ButtonBuilder is an interface for creating buttons. Use this interface to provide Context to handlers without other methods.

type Config

type Config struct {
	// Mode is the polling mode to use.
	// Default: long.
	// Possible values:
	// - "long" - long polling
	// - "webhook" - webhook
	// - "custom" - custom poller (you should provide poller using [WithPoller] option)
	// Environment variable: BOTE_MODE.
	Mode PollingMode `yaml:"mode" json:"mode" env:"BOTE_MODE"`

	// LongPolling contains long polling configuration.
	LongPolling LongPollingConfig `yaml:"long_polling" json:"long_polling"`

	// Webhook contains webhook configuration.
	Webhook WebhookConfig `yaml:"webhook" json:"webhook"`

	// Bot contains bot configuration.
	Bot BotConfig `yaml:"bot" json:"bot"`

	// Log contains log configuration.
	Log LogConfig `yaml:"log" json:"log"`
}

Config contains bote configuration.

type Context

type Context interface {
	// Tele returns underlying telebot context.
	Tele() tele.Context

	// User returns current User context if chat type is private.
	// WARNING: If chat type is not private, returns public read only user.
	User() User

	// IsPrivate returns true if the current chat type is private.
	IsPrivate() bool

	// ChatID returns the ID of the current chat.
	ChatID() int64

	// ChatType returns the type of the current chat.
	ChatType() tele.ChatType

	// IsMentioned returns true if the bot is mentioned in the current message.
	IsMentioned() bool

	// IsReply returns true if the current message is a reply to the bot's message.
	IsReply() bool

	// MessageID returns an ID of the message.
	// If handler was called from a callback button, message is the one with keyboard.
	// If handler was called from a text message and user's state IsText == true,
	//  message is the one with an active text handler with this state (not sent message!).
	// In other case it is an ID of the message sent to the chat.
	MessageID() int

	// ButtonID returns an ID of the pressed callback button.
	ButtonID() string

	// Data returns callback button data. If there are many items in data, they will be separated by '|'.
	Data() string

	// DataParsed returns all items of button data.
	DataParsed() []string

	// Text returns a text sended by the user.
	Text() string

	// TextWithMessage returns a text sended by the user and the ID of this message (deleted by default).
	TextWithMessage() (string, int)

	// Set sets custom data for the current context.
	Set(key, value string)

	// Get returns custom data from the current context.
	Get(key string) string

	// Btn creates button and registers handler for it. You can provide data for the button.
	// Data items will be separated by '|' in a single data string.
	// Button unique value is generated from hexing button name with 10 random bytes at the end.
	Btn(name string, callback HandlerFunc, dataList ...string) tele.Btn

	// Send sends new main and head messages to the user.
	// Old head message will be deleted. Old main message will becomve historical.
	// newState is a state of the user which will be set after sending message.
	// opts are additional options for sending messages.
	// WARNING: It works only in private chats.
	Send(newState State, mainMsg, headMsg string, mainKb, headKb *tele.ReplyMarkup, opts ...any) (err error)

	// SendMain sends new main message to the user.
	// Old head message will be deleted. Old main message will becomve historical.
	// newState is a state of the user which will be set after sending message.
	// opts are additional options for sending message.
	// WARNING: It works only in private chats.
	SendMain(newState State, msg string, kb *tele.ReplyMarkup, opts ...any) error

	// SendNotification sends a notification message to the user.
	// opts are additional options for sending message.
	// WARNING: It works only in private chats.
	SendNotification(msg string, kb *tele.ReplyMarkup, opts ...any) error

	// SendError sends an error message to the user.
	// opts are additional options for sending message.
	// WARNING: It works only in private chats.
	SendError(msg string, opts ...any) error

	// SendFile sends a file to the user.
	// name is the name of the file to send.
	// file is the file to send.
	// opts are additional options for sending the file.
	// WARNING: It works only in private chats.
	SendFile(name string, file []byte, opts ...any) error

	// SendInChat sends a message to a specific chat ID and thread ID.
	// chatID is the target chat ID, threadID is the target thread ID (0 for no thread).
	// msg is the message to send.
	// kb is the keyboard to send.
	// opts are additional options for sending the message.
	SendInChat(chatID int64, threadID int, msg string, kb *tele.ReplyMarkup, opts ...any) (int, error)

	// Edit edits main and head messages of the user.
	// newState is a state of the user which will be set after editing message.
	// opts are additional options for editing messages.
	// WARNING: It works only in private chats.
	Edit(newState State, mainMsg, headMsg string, mainKb, headKb *tele.ReplyMarkup, opts ...any) (err error)

	// EditMain edits main message of the user.
	// newState is a state of the user which will be set after editing message.
	// opts are additional options for editing message.
	// WARNING: It works only in private chats.
	EditMain(newState State, msg string, kb *tele.ReplyMarkup, opts ...any) error

	// EditMainReplyMarkup edits reply markup of the main message.
	// opts are additional options for editing message.
	// WARNING: It works only in private chats.
	EditMainReplyMarkup(kb *tele.ReplyMarkup, opts ...any) error

	// EditHistory edits message of the user by provided ID.
	// newState is a state of the user which will be set after editing message.
	// msgID is the ID of the message to edit.
	// opts are additional options for editing message.
	// WARNING: It works only in private chats.
	EditHistory(newState State, msgID int, msg string, kb *tele.ReplyMarkup, opts ...any) error

	// EditHistoryReplyMarkup edits reply markup of the history message.
	// opts are additional options for editing message.
	// WARNING: It works only in private chats.
	EditHistoryReplyMarkup(msgID int, kb *tele.ReplyMarkup, opts ...any) error

	// EditHead edits head message of the user.
	// opts are additional options for editing message.
	// WARNING: It works only in private chats.
	EditHead(msg string, kb *tele.ReplyMarkup, opts ...any) error

	// EditHeadReplyMarkup edits reply markup of the head message.
	// opts are additional options for editing message.
	// WARNING: It works only in private chats.
	EditHeadReplyMarkup(kb *tele.ReplyMarkup, opts ...any) error

	// EditInChat edits message in a specific chat ID and thread ID.
	// chatID is the target chat ID, threadID is the target thread ID (0 for no thread).
	// msg is the message to edit.
	// kb is the keyboard to edit.
	// opts are additional options for editing message.
	EditInChat(chatID int64, msgID int, msg string, kb *tele.ReplyMarkup, opts ...any) error

	// DeleteHead deletes head message of the user.
	// WARNING: It works only in private chats.
	DeleteHead() error

	// DeleteHistory deletes provided history message.
	// msgID is the ID of the history message to delete.
	// WARNING: It works only in private chats.
	DeleteHistory(msgID int) error

	// DeleteNotification deletes notification message of the user.
	// WARNING: It works only in private chats.
	DeleteNotification() error

	// DeleteError deletes error message of the user.
	// WARNING: It works only in private chats.
	DeleteError() error

	// DeleteAll deletes all messages of the user from the specified ID.
	// WARNING: It works only in private chats.
	DeleteAll(from int)

	// Delete deletes message by provided chat ID and message ID.
	DeleteInChat(chatID int64, msgID int) error

	// DeleteUser deletes user from the memory and deletes all messages of the user.
	// Returns true if all messages were deleted successfully, false otherwise.
	// It doesn't delete user from the persistent database, so you should make it manually.
	// WARNING: It works only in private chats.
	DeleteUser() bool
}

Context is an interface that provides to every handler.

func NewContext

func NewContext(b *Bot, userID int64, callbackMsgID int, data ...string) Context

NewContext creates a new context for the given user simulating that callback button was pressed. It creates a minimal update to handle all possible methods in Context without panics. It can be useful if you want to start a handler without user action (by some external event). Warning! IT WON'T WORK WITH TEXT HANDLERS. Use NewContextText instead.

func NewContextSecure

func NewContextSecure(b *Bot, userIDSecure FullUserID, callbackMsgID int, data ...string) (Context, error)

NewContextSecure creates a new context for the given user from encrypted ID simulating that callback button was pressed. It creates a minimal update to handle all possible methods in Context without panics. It can be useful if you want to start a handler without user action (by some external event). If strict privacy mode is disabled, it will use NewContext instead. Warning! IT WON'T WORK WITH TEXT HANDLERS. Use NewContextText instead.

func NewContextText

func NewContextText(b *Bot, userID int64, textMsgID int, text string) Context

NewContextText creates a new context for the given user simulating that text message was received. It creates a minimal update to handle all possible methods in Context without panics. It can be useful if you want to start a handler without user action (by some external event). Warning! IT WON'T WORK WITH CALLBACK HANDLERS. Use NewContext instead.

func NewContextTextSecure

func NewContextTextSecure(b *Bot, userIDSecure FullUserID, textMsgID int, text string) (Context, error)

NewContextTextSecure creates a new context for the given user from encrypted ID simulating that text message was received. It creates a minimal update to handle all possible methods in Context without panics. It can be useful if you want to start a handler without user action (by some external event). If strict privacy mode is disabled, it will use NewContextText instead. Warning! IT WON'T WORK WITH CALLBACK HANDLERS. Use NewContext instead.

type EncryptionKey

type EncryptionKey struct {
	// contains filtered or unexported fields
}

EncryptionKey is a type that represents an AES encryption key for private user ID.

func NewEncryptionKey

func NewEncryptionKey(version *int64) *EncryptionKey

NewEncryptionKey creates a new EncryptionKey.

func NewEncryptionKeyFromString

func NewEncryptionKeyFromString(key string, version *int64) (*EncryptionKey, error)

NewEncryptionKeyFromString creates a new EncryptionKey from a string. Key should be a hex encoded string of 32 bytes.

func (*EncryptionKey) Clear

func (k *EncryptionKey) Clear()

Clear clears the encryption key from memory to prevent reading it from memory dump. Use it after using the key to avoid leaking it.

func (*EncryptionKey) Key

func (k *EncryptionKey) Key() *[32]byte

Key returns the encryption key.

func (*EncryptionKey) KeyBytes

func (k *EncryptionKey) KeyBytes() []byte

KeyBytes returns the encryption key as a byte slice.

func (*EncryptionKey) String

func (k *EncryptionKey) String() string

String converts EncryptionKey to a string.

func (*EncryptionKey) Version

func (k *EncryptionKey) Version() *int64

Version returns the version of the encryption key.

type Format

type Format string

Format is a type of message formatting in Telegram in HTML format.

const (
	Bold      Format = "<b>"
	Italic    Format = "<i>"
	Code      Format = "<code>"
	Strike    Format = "<s>"
	Underline Format = "<u>"
	Pre       Format = "<pre>"
)

type FullUserID

type FullUserID struct {
	// IDPlain is Telegram user ID in plain format.
	// It is empty if privacy mode is strict.
	IDPlain *int64 `json:"id_plain,omitempty" bson:"id_plain,omitempty" db:"id_plain,omitempty"`

	// IDHMAC is a HMAC of IDPlain. It is used for searching user in DB by ID with plain ID is disabled.
	// It is used if privacy mode is strict. It has a hex encoded string.
	IDHMAC *string `json:"id_hmac,omitempty" bson:"id_hmac,omitempty" db:"id_hmac,omitempty"`
	// IDEnc is an encrypted Telegram user ID. It is used to get readable IDPlain from IDEnc (HMAC is one way function).
	// It is used instead of IDPlain when privacy mode is strict. It has a hex encoded string.
	IDEnc *string `json:"id_enc,omitempty" bson:"id_enc,omitempty" db:"id_enc,omitempty"`
	// HMACKeyVersion is a version of HMAC key for IDHMAC that used for HMAC of IDHMAC.
	// It is used if privacy mode is strict.
	HMACKeyVersion *int64 `json:"hmac_key_version,omitempty" bson:"hmac_key_version,omitempty" db:"hmac_key_version,omitempty"`
	// EncKeyVersion is a version of encryption key for IDEnc that used for encryption and decryption of IDEnc.
	// It is used if privacy mode is strict.
	EncKeyVersion *int64 `json:"enc_key_version,omitempty" bson:"enc_key_version,omitempty" db:"enc_key_version,omitempty"`
}

FullUserID is a structure that represents secure user ID with encrypted and HMAC parts.

func NewPlainUserID

func NewPlainUserID(id int64) FullUserID

NewPlainUserID creates a new FullUserID with the given ID as a plain text.

func NewPrivateUserID

func NewPrivateUserID(id int64, encKey, hmacKey *EncryptionKey) (FullUserID, error)

NewPrivateUserID creates a new FullUserID with the given ID as an encrypted text. It do not store plain user ID, only encrypted and HMAC parts.

func (FullUserID) ID

func (u FullUserID) ID(encKeys ...*EncryptionKey) (int64, error)

ID returns plain user ID if it is set. Otherwise it tries to decrypt encrypted ID. If encryption keys are provided, it tries to decrypt it with each key and returns plain user ID. If key is not provided, it returns error. If there is an error during decryption, it returns error.

func (FullUserID) IsEmpty

func (u FullUserID) IsEmpty() bool

IsEmpty returns true if all fields are nil.

func (FullUserID) String

func (u FullUserID) String() string

String returns plain user ID as a string if it is set, otherwise returns "[ENCRYPTED]".

type HandlerFunc

type HandlerFunc func(Context) error

HandlerFunc represents a function that is used to handle user actions in bot.

type InitBundle

type InitBundle struct {
	// Handler is a handler for [State]. It will be called for initing user.
	// It is required.
	Handler HandlerFunc
	// Data is a callback data, that can be obtained from [Context.Data] inside [HandlerFunc].
	// It is optional and shoud be provided if you use [Context.Data] in your [InitBundle.Handler].
	Data string
	// Text is a text of simulating message, that can be obtained from [Context.Text] inside [HandlerFunc].
	// It is optional and shoud be provided if you use [Context.Text] in your [InitBundle.Handler].
	Text string
}

InitBundle is a struct for initing users of the bot. You should provide it to Bot.Start method. You should create a mapping of every State to the corresponding HandlerFunc.

type Keyboard

type Keyboard struct {
	// contains filtered or unexported fields
}

Keyboard is a ReplyMarkup (keyboard) builder.

func NewKeyboard

func NewKeyboard(optionalRowLen ...int) *Keyboard

NewKeyboard creates new keyboard builder.

func NewKeyboardWithLength

func NewKeyboardWithLength(runeType RuneSizeType, optionalRowLen ...int) *Keyboard

NewKeyboardWithLength creates new keyboard builder with max runes in a row. It creates a new row in Add if number of runes is greater than max runes in row for selected rune type.

func (*Keyboard) Add

func (k *Keyboard) Add(btns ...tele.Btn) *Keyboard

Add adds buttons to the current row. It creates a new row in Add if number of buttons is greater than max buttons in row. It creates a new row in Add if number of runes is greater than max runes in row for selected rune type.

func (*Keyboard) AddFooter

func (k *Keyboard) AddFooter(btns ...tele.Btn) *Keyboard

AddFooter adds buttons to the footer row.

func (*Keyboard) AddRow

func (k *Keyboard) AddRow(btns ...tele.Btn) *Keyboard

AddRow adds buttons to the current row. It creates a new row if there is buttons in the current row after Add.

func (*Keyboard) CreateInlineMarkup

func (k *Keyboard) CreateInlineMarkup() *tele.ReplyMarkup

CreateInlineMarkup creates inline keyboard from the current keyboard builder.

func (*Keyboard) CreateReplyMarkup

func (k *Keyboard) CreateReplyMarkup(oneTime bool) *tele.ReplyMarkup

CreateReplyMarkup creates reply keyboard from the current keyboard builder.

func (*Keyboard) StartNewRow

func (k *Keyboard) StartNewRow() *Keyboard

StartNewRow creates a new row.

type KeyboardWithContext

type KeyboardWithContext struct {
	*Keyboard
	// contains filtered or unexported fields
}

func NewKeyboardWithContext

func NewKeyboardWithContext(ctx Context, optionalRowLen ...int) *KeyboardWithContext

NewKeyboardWithContext creates new keyboard builder with context. With context you can create buttons using keyboard.

func (*KeyboardWithContext) AB

func (k *KeyboardWithContext) AB(name string, callback HandlerFunc, dataList ...string) *Keyboard

AB is a shortcut for AddBtn. It creates a button and adds it to the current row. It creates a new row in Add if number of buttons is greater than max buttons in row. It creates a new row in Add if number of runes is greater than max runes in row for selected rune type.

func (*KeyboardWithContext) ABR

func (k *KeyboardWithContext) ABR(name string, callback HandlerFunc, dataList ...string) *Keyboard

ABR is a shortcut for AddBtnRow. It creates a button and adds it to the current row. It creates a new row if there is buttons in the current row after Add.

func (*KeyboardWithContext) AddBtn

func (k *KeyboardWithContext) AddBtn(name string, callback HandlerFunc, dataList ...string) *Keyboard

Add creates and adds button to the current row. It creates a new row in Add if number of buttons is greater than max buttons in row. It creates a new row in Add if number of runes is greater than max runes in row for selected rune type.

func (*KeyboardWithContext) AddBtnRow

func (k *KeyboardWithContext) AddBtnRow(name string, callback HandlerFunc, dataList ...string) *Keyboard

AddRow creates and adds button to the current row. It creates a new row if there is buttons in the current row after Add.

type KeysProvider

type KeysProvider interface {
	// GetEncryptionKey returns the encryption key for the bot.
	// It is used to encrypt and decrypt UserID in strict privacy mode.
	GetEncryptionKey() *EncryptionKey
	// GetHMACKey returns the HMAC key for the bot.
	// It is used to HMAC UserID in strict privacy mode.
	GetHMACKey() *EncryptionKey
}

KeysProvider is an interface for providing encryption and HMAC keys in strict privacy mode.

type Language

type Language string
const (
	LanguageAfrikaans        Language = "af"
	LanguageAlbanian         Language = "sq"
	LanguageAmharic          Language = "am"
	LanguageArabic           Language = "ar"
	LanguageArmenian         Language = "hy"
	LanguageAssamese         Language = "as"
	LanguageAzerbaijani      Language = "az"
	LanguageBashkir          Language = "ba"
	LanguageBasque           Language = "eu"
	LanguageBelarusian       Language = "be"
	LanguageBengali          Language = "bn"
	LanguageBosnian          Language = "bs"
	LanguageBreton           Language = "br"
	LanguageBulgarian        Language = "bg"
	LanguageBurmese          Language = "my"
	LanguageCatalan          Language = "ca"
	LanguageCentralKurdish   Language = "ckb"
	LanguageChinese          Language = "zh"
	LanguageCorsican         Language = "co"
	LanguageCroatian         Language = "hr"
	LanguageCzech            Language = "cs"
	LanguageDanish           Language = "da"
	LanguageDari             Language = "prs"
	LanguageDivehi           Language = "dv"
	LanguageDutch            Language = "nl"
	LanguageEnglish          Language = "en"
	LanguageEstonian         Language = "et"
	LanguageFaroese          Language = "fo"
	LanguageFilipino         Language = "fil"
	LanguageFinnish          Language = "fi"
	LanguageFrench           Language = "fr"
	LanguageFrisian          Language = "fy"
	LanguageGalician         Language = "gl"
	LanguageGeorgian         Language = "ka"
	LanguageGerman           Language = "de"
	LanguageGilbertese       Language = "gil"
	LanguageGreek            Language = "el"
	LanguageGreenlandic      Language = "kl"
	LanguageGujarati         Language = "gu"
	LanguageHausa            Language = "ha"
	LanguageHebrew           Language = "he"
	LanguageHindi            Language = "hi"
	LanguageHungarian        Language = "hu"
	LanguageIcelandic        Language = "is"
	LanguageIgbo             Language = "ig"
	LanguageIndonesian       Language = "id"
	LanguageInuktitut        Language = "iu"
	LanguageIrish            Language = "ga"
	LanguageItalian          Language = "it"
	LanguageJapanese         Language = "ja"
	LanguageKiche            Language = "quc"
	LanguageKannada          Language = "kn"
	LanguageKazakh           Language = "kk"
	LanguageKhmer            Language = "km"
	LanguageKinyarwanda      Language = "rw"
	LanguageKiswahili        Language = "sw"
	LanguageKonkani          Language = "kok"
	LanguageKorean           Language = "ko"
	LanguageKurdish          Language = "ku"
	LanguageKyrgyz           Language = "ky"
	LanguageLao              Language = "lo"
	LanguageLatvian          Language = "lv"
	LanguageLithuanian       Language = "lt"
	LanguageLowerSorbian     Language = "dsb"
	LanguageLuxembourgish    Language = "lb"
	LanguageMacedonian       Language = "mk"
	LanguageMalay            Language = "ms"
	LanguageMalayalam        Language = "ml"
	LanguageMaltese          Language = "mt"
	LanguageMaori            Language = "mi"
	LanguageMapudungun       Language = "arn"
	LanguageMarathi          Language = "mr"
	LanguageMohawk           Language = "moh"
	LanguageMongolian        Language = "mn"
	LanguageMoroccanArabic   Language = "ary"
	LanguageNepali           Language = "ne"
	LanguageNorwegian        Language = "no"
	LanguageNorwegianBokmal  Language = "nb"
	LanguageNorwegianNynorsk Language = "nn"
	LanguageOccitan          Language = "oc"
	LanguageOdia             Language = "or"
	LanguagePapiamento       Language = "pap"
	LanguagePashto           Language = "ps"
	LanguagePersian          Language = "fa"
	LanguagePolish           Language = "pl"
	LanguagePortuguese       Language = "pt"
	LanguagePunjabi          Language = "pa"
	LanguageQuechua          Language = "qu"
	LanguageRomanian         Language = "ro"
	LanguageRomansh          Language = "rm"
	LanguageRussian          Language = "ru"
	LanguageSamiInari        Language = "smn"
	LanguageSamiLule         Language = "smj"
	LanguageSamiNorthern     Language = "se"
	LanguageSamiSkolt        Language = "sms"
	LanguageSamiSouthern     Language = "sma"
	LanguageSanskrit         Language = "sa"
	LanguageScottishGaelic   Language = "gd"
	LanguageSerbian          Language = "sr"
	LanguageSesotho          Language = "st"
	LanguageSinhala          Language = "si"
	LanguageSlovak           Language = "sk"
	LanguageSlovenian        Language = "sl"
	LanguageSpanish          Language = "es"
	LanguageSwedish          Language = "sv"
	LanguageSwissGerman      Language = "gsw"
	LanguageSyriac           Language = "syc"
	LanguageTajik            Language = "tg"
	LanguageTamazight        Language = "tzm"
	LanguageTamil            Language = "ta"
	LanguageTatar            Language = "tt"
	LanguageTelugu           Language = "te"
	LanguageThai             Language = "th"
	LanguageTibetan          Language = "bo"
	LanguageTswana           Language = "tn"
	LanguageTurkish          Language = "tr"
	LanguageTurkmen          Language = "tk"
	LanguageUkrainian        Language = "uk"
	LanguageUpperSorbian     Language = "hsb"
	LanguageUrdu             Language = "ur"
	LanguageUyghur           Language = "ug"
	LanguageUzbek            Language = "uz"
	LanguageVietnamese       Language = "vi"
	LanguageWelsh            Language = "cy"
	LanguageWolof            Language = "wo"
	LanguageXhosa            Language = "xh"
	LanguageYakut            Language = "sah"
	LanguageYi               Language = "ii"
	LanguageYoruba           Language = "yo"
	LanguageZulu             Language = "zu"
)

Language constants based on IETF subtags

const (
	LanguageDefault Language = "en"
)

func MustLanguage

func MustLanguage(code string) Language

MustLanguage parses a language code string and returns the corresponding Language constant. It panics if the language code is not recognized. Use this function when you are certain the language code is valid.

func ParseLanguage

func ParseLanguage(code string) (Language, error)

ParseLanguage parses a language code string and returns the corresponding Language constant. It accepts language codes in any case (e.g., "en", "EN", "En"). Returns an error if the language code is not recognized.

func ParseLanguageOrDefault

func ParseLanguageOrDefault(code string) Language

ParseLanguageOrDefault parses a language code string and returns the corresponding Language constant. If the language code is not recognized, it returns the default language.

func (Language) String

func (l Language) String() string

String returns the string representation of the language.

type LogConfig

type LogConfig struct {
	// Enable is a flag that enables logging of bot activity (except updates logging).
	// Use default slog to stderr if another logger is not provided using [WithLogger] option.
	// Default: true.
	// Environment variable: BOTE_ENABLE_LOGGING.
	Enable *bool `yaml:"enable" json:"enable" env:"BOTE_LOG_ENABLE"`

	// LogUpdates is a flag that enables logging of bot updates.
	// Use default slog to stderr if another logger is not provided using [WithUpdateLogger] option.
	// Default: true.
	// Environment variable: BOTE_LOG_UPDATES.
	LogUpdates *bool `yaml:"log_updates" json:"log_updates" env:"BOTE_LOG_UPDATES"`

	// Level is the log level. Logger will log messages with level greater than or equal to this level.
	// Default: info.
	// Possible values:
	// - "debug"
	// - "info"
	// - "warn"
	// - "error"
	// Environment variable: BOTE_LOG_LEVEL.
	Level string `yaml:"level" json:"level" env:"BOTE_LOG_LEVEL"`

	// HideUserData is a flag that enables hiding user messages, callback data and pressed buttons from logs.
	// Default: false (even in strict privacy because in strict we use HMAC user_id that prevents from identification)
	// Environment variable: BOTE_LOG_HIDE_USER_DATA.
	HideUserData bool `yaml:"hide_user_data" json:"hide_user_data" env:"BOTE_LOG_HIDE_USER_DATA"`

	// DebugIncomingUpdates is a flag that enables logging of incoming updates.
	// It is not for production use.
	// Default: false.
	// Environment variable: BOTE_LOG_DEBUG_INCOMING_UPDATES.
	DebugIncomingUpdates bool `yaml:"debug_incoming_updates" json:"debug_incoming_updates" env:"BOTE_LOG_DEBUG_INCOMING_UPDATES"`
}

type Logger

type Logger interface {
	Debug(string, ...any)
	Info(string, ...any)
	Warn(string, ...any)
	Error(string, ...any)
}

Logger is an interface for logging messages.

type LongPollingConfig

type LongPollingConfig struct {
	// Timeout is the long polling timeout.
	// Default: 15 seconds.
	// Environment variable: BOTE_LP_TIMEOUT.
	Timeout time.Duration `yaml:"timeout" json:"timeout"  env:"BOTE_LP_TIMEOUT"`

	// Limit is the maximum number of updates to be returned in a single request.
	// Default: 0, no limit.
	// Environment variable: BOTE_LP_LIMIT.
	Limit int `yaml:"limit" json:"limit" env:"BOTE_LP_LIMIT"`

	// LastUpdateID is the last update ID to be returned.
	// It sets an offset for the starting update to return.
	// It is used to continue long polling from the last update ID.
	// Default: 0.
	// Environment variable: BOTE_LP_LAST_UPDATE_ID.
	LastUpdateID int `yaml:"last_update_id" json:"last_update_id" env:"BOTE_LP_LAST_UPDATE_ID"`

	// AllowedUpdates is a list of update types the bot wants to receive.
	// Empty list means all update types.
	// Possible values:
	//		message
	// 		edited_message
	// 		channel_post
	// 		edited_channel_post
	// 		inline_query
	// 		chosen_inline_result
	// 		callback_query
	// 		shipping_query
	// 		pre_checkout_query
	// 		poll
	// 		poll_answer
	//
	// Environment variable: BOTE_ALLOWED_UPDATES (comma-separated).
	AllowedUpdates []string `yaml:"allowed_updates" json:"allowed_updates" env:"BOTE_ALLOWED_UPDATES" envSeparator:","`
}

LongPollingConfig contains configuration for long polling-based bot operation.

type MessageBuilder

type MessageBuilder struct {
	strings.Builder
}

MessageBuilder is a wrapper for strings.Builder with additional methods. You should not copy it. Empty value of MessageBuilder is ready to use.

func NewBuilder

func NewBuilder(cap ...int) *MessageBuilder

NewBuilder creates a new Builder instance.

func (*MessageBuilder) IsEmpty

func (b *MessageBuilder) IsEmpty() bool

IsEmpty returns true if the builder's length is 0.

func (*MessageBuilder) Write

func (b *MessageBuilder) Write(msg ...string)

Write writes a string to the builder. It is an alias for strings.Builder.WriteString.

func (*MessageBuilder) WriteBytes

func (b *MessageBuilder) WriteBytes(data ...[]byte)

WriteBytes writes a byte slice to the builder. It is an alias for strings.Builder.Write.

func (*MessageBuilder) WriteIf

func (b *MessageBuilder) WriteIf(condition bool, writeTrue string, writeFalse ...string)

WriteIf writes either writeTrue or writeFalse depending on the value of first argument.

func (*MessageBuilder) WriteIfF

func (b *MessageBuilder) WriteIfF(condition bool, writeTrue, writeFalse string, args ...any)

WriteIf writes either writeTrue or writeFalse depending on the value of first argument.

func (*MessageBuilder) Writebln

func (b *MessageBuilder) Writebln(msg ...string)

Writebln writes a string to the builder and adds a newline at the beginning and a newline at the end.

func (*MessageBuilder) Writeblnf

func (b *MessageBuilder) Writeblnf(msg string, args ...any)

Writeblnf writes a string to the builder and adds a newline at the beginning and a newline at the end. If args are provided, it uses fmt.Sprintf to format the string.

func (*MessageBuilder) Writebn

func (b *MessageBuilder) Writebn(msg ...string)

Writebn writes a string to the builder and adds a newline at the beginning. It is an alias for strings.Builder.WriteString.

func (*MessageBuilder) Writebnf

func (b *MessageBuilder) Writebnf(msg string, args ...any)

Writebnf writes a string to the builder and adds a newline at the beginning. It is an alias for strings.Builder.WriteString. If args are provided, it uses fmt.Sprintf to format the string.

func (*MessageBuilder) Writef

func (b *MessageBuilder) Writef(msg string, args ...any)

Writef writes a string to the builder. It is an alias for strings.Builder.WriteString. If args are provided, it uses fmt.Sprintf to format the string.

func (*MessageBuilder) Writeln

func (b *MessageBuilder) Writeln(s ...string)

Writeln writes a string to the builder and adds a newline at the end.

func (*MessageBuilder) WritelnIf

func (b *MessageBuilder) WritelnIf(condition bool, writeTrue string, writeFalse ...string)

WritelnIf writes either writeTrue or writeFalse depending on the value of first argument. It is an alias for strings.Builder.WriteString.

func (*MessageBuilder) WritelnIfF

func (b *MessageBuilder) WritelnIfF(condition bool, writeTrue, writeFalse string, args ...any)

WriteIf writes either writeTrue or writeFalse depending on the value of first argument.

func (*MessageBuilder) WritelnIfFf

func (b *MessageBuilder) WritelnIfFf(condition bool, writeTrue string, args ...any)

WriteIf writes either writeTrue or writeFalse depending on the value of first argument.

func (*MessageBuilder) Writelnf

func (b *MessageBuilder) Writelnf(s string, args ...any)

Writeln writes a string to the builder and adds a newline at the end. If args are provided, it uses fmt.Sprintf to format the string.

func (*MessageBuilder) Writelnn

func (b *MessageBuilder) Writelnn(s ...string)

Writelnn writes a string to the builder and adds two newlines at the end.

func (*MessageBuilder) Writelnnf

func (b *MessageBuilder) Writelnnf(s string, args ...any)

Writelnn writes a string to the builder and adds two newlines at the end. If args are provided, it uses fmt.Sprintf to format the string.

type MessageProvider

type MessageProvider interface {
	// Messages returns messages for a specific language.
	Messages(language Language) Messages
}

MessageProvider is an interface for providing messages based on the user language code.

type Messages

type Messages interface {
	// CloseBtn is a message on inline keyboard button that closes the error message.
	// Remain it empty if you don't want to show this button.
	CloseBtn() string

	// GeneralError is a general error message that sends when an unhandled error occurs.
	GeneralError() string

	// PrepareMessage calls before every Send, SendMain, Edit, EditMain or EditHistory.
	// Provide zero msgID in Send and SendMain methods.
	// If isHistorical is true, it means that the message is a history message called by EditHistory.
	PrepareMessage(msg string, u User, newState State, msgID int, isHistorical bool) string
}

Messages is a collection of messages for a specific language.

type MessagesState

type MessagesState struct {
	// Main is the main state of the user, state of the Main message.
	Main UserState `bson:"main" json:"main" db:"main"`
	// MessageStates contains all states of the user for all messages. It is a map of message_id -> state.
	MessageStates map[int]UserState `bson:"message_states" json:"message_states" db:"message_states"`
	// MessagesAwaitingText is a unique stack that contains all messages IDs that awaits text.
	// Every message can produce text state and they should be handled as LIFO.
	MessagesAwaitingText []int `bson:"messages_awaiting_text" json:"messages_awaiting_text" db:"messages_awaiting_text"`
	// contains filtered or unexported fields
}

MessagesState contains current user state and state history. State connects to message, every Main and Info message has its own state.

type MetricsConfig

type MetricsConfig struct {
	Registry    *prometheus.Registry
	Namespace   string
	Subsystem   string
	ConstLabels prometheus.Labels
}

type MiddlewareFunc

type MiddlewareFunc func(*tele.Update, User) bool

MiddlewareFunc represents a function that called on every bot update.

type MiddlewareFuncTele

type MiddlewareFuncTele func(*tele.Update) bool

MiddlewareFuncTele represents a function that called on every bot update in telebot format.

type Options

type Options struct {
	// Config contains bote configuration. It is optional and has default values for all fields.
	// You also can set values using environment variables.
	Config Config

	// UserDB is a storage for users. It uses in-memory storage by default.
	// You should implement it in your application if you want to persist users between applicataion restarts.
	UserDB UsersStorage

	// Msgs is a message provider. It uses default messages by default.
	// You should implement it in your application if you want to use custom messages.
	Msgs MessageProvider

	// Logger is a logger. It uses slog logger by default if EnableLogging == true (by default).
	Logger Logger

	// UpdateLogger is a logger for updates. It uses Logger and logs in debug level by default.
	// It will log updates even if EnableLogging == false.
	// You should set LogUpdates == false to disable updates logging.
	UpdateLogger UpdateLogger

	// Poller is a poller for the bot. It uses default poller by default.
	// You should implement it in your application if you want to use custom poller (e.g. for testing).
	// Note: If WebhookConfig is provided, this will be ignored and webhook poller will be used.
	Poller tele.Poller

	// Metrics is a configuration for prometheus metrics.
	// It registers metrics in the provided registry.
	// It do not register metris if Registry is nil.
	Metrics MetricsConfig

	// Offline is a flag that enables offline mode.
	// It is used to create a bot without network for testing purposes.
	Offline bool

	// KeysProvider is a provider of encryption and HMAC keys for the bot.
	// It is used to provide encryption and HMAC keys for the bot in strict privacy mode.
	KeysProvider KeysProvider
	// contains filtered or unexported fields
}

Options contains bote additional options.

type PollingMode

type PollingMode string

PollingMode is the polling mode to use.

const (
	PollingModeLong    PollingMode = "long"
	PollingModeWebhook PollingMode = "webhook"
	PollingModeCustom  PollingMode = "custom"
)

type PrivacyConfig

type PrivacyConfig struct {
	// Mode is the privacy mode for the bot.
	// Default: "no".
	// Possible values:
	// - "no" - no privacy mode (all data is stored and logged)
	// - "low" - low privacy mode (store UserID + Username)
	// - "strict" - strict mode (only encrypted and HMACed UserID is stored)
	// Environment variable: BOTE_PRIVACY_MODE.
	Mode PrivacyMode `yaml:"mode" json:"mode" env:"BOTE_PRIVACY_MODE"`

	// EncryptionKey is the encryption key for the bot.
	// It is used to encrypt and decrypt UserID in strict privacy mode.
	// Key should be a hex encoded string of 32 bytes.
	// Default: nil.
	// Environment variable: BOTE_ENCRYPTION_KEY.
	EncryptionKey *string `yaml:"encryption_key" json:"encryption_key" env:"BOTE_ENCRYPTION_KEY"`

	// EncryptionKeyVersion is the version of the encryption key.
	// It is used to identify the version of the encryption key.
	// Default: nil.
	// Environment variable: BOTE_ENCRYPTION_KEY_VERSION.
	EncryptionKeyVersion *int64 `yaml:"enc_key_version" json:"enc_key_version" env:"BOTE_ENCRYPTION_KEY_VERSION"`

	// HMACKey is the HMAC key for the bot.
	// It is used to HMAC UserID in strict privacy mode.
	// Key should be a hex encoded string of 32 bytes.
	// Default: nil.
	// Environment variable: BOTE_HMAC_KEY.
	HMACKey *string `yaml:"hmac_key" json:"hmac_key" env:"BOTE_HMAC_KEY"`

	// HMACKeyVersion is the version of the HMAC key.
	// It is used to identify the version of the HMAC key.
	// Default: nil.
	// Environment variable: BOTE_HMAC_KEY_VERSION.
	HMACKeyVersion *int64 `yaml:"hmac_key_version" json:"hmac_key_version" env:"BOTE_HMAC_KEY_VERSION"`
}

type PrivacyMode

type PrivacyMode string

PrivacyMode is a privacy mode for the bot. It is used to make compliance with GDPR (privacy by design). Possible values: - "low" - low privacy mode (UserID + Username are stored) - "strict" - strict mode (only encrypted and HMACed UserID is stored)

const (
	PrivacyModeLow    PrivacyMode = "low"
	PrivacyModeStrict PrivacyMode = "strict"
)

func (PrivacyMode) IsStrict

func (p PrivacyMode) IsStrict() bool

type RuneSizeType

type RuneSizeType string

RuneSizeType sets type of UTF-8 runes in button text. For example, if you use English language, you should use OneBytePerRune. If you use Russian language, you should use TwoBytesPerRune. If you add a lot of emojis of special symbols, you should use FourBytesPerRune.

const (
	OneBytePerRune   RuneSizeType = "OneBytePerRune"
	TwoBytesPerRune  RuneSizeType = "TwoBytesPerRune"
	FourBytesPerRune RuneSizeType = "FourBytesPerRune"

	MaxDataLengthBytes = 64 // 56
)

type State

type State interface {
	fmt.Stringer
	IsText() bool
	NotChanged() bool
}

State is a user state in Telegram bot builded using this package. User changes states when he makes actions, e.g. sends a message, clicks a button, etc. State is connected to message, every Main and Info (history) message has its own state. State is necessary for understanding user behavior and should be used in user init after bot restarting. You should create states as constants in your application and pass it in Send or Edit methods as first argument. States is generally changes in response to user actions inside handlers, but also can be changed in other places in case of any action.

type UpdateLogger

type UpdateLogger interface {
	Log(UpdateType, ...any)
}

UpdateLogger is an interface for logging updates.

type UpdateType

type UpdateType string

UpdateType is a type of update that is using in update logging.

const (
	MessageUpdate    UpdateType = "message"
	CallbackUpdate   UpdateType = "callback"
	NotPrivateUpdate UpdateType = "not_private"
)

func (UpdateType) String

func (t UpdateType) String() string

type User

type User interface {
	// ID returns plain Telegram user ID if it is called from [Context.User] (in handler)
	ID() int64
	// IDFull returns full user ID with encrypted and HMAC parts if they are set.
	IDFull() FullUserID
	// Username returns Telegram username (without @).
	// It is empty if privacy mode is strict.
	Username() string
	// Language returns Telegram user language code.
	Language() Language
	// Info returns user info.
	// It is empty if privacy mode is strict.
	Info() UserInfo
	// State returns current state for the given message ID.
	State(msgID int) (State, bool)
	// StateMain returns state for the Main message.
	StateMain() State
	// Messages returns all message IDs.
	Messages() UserMessages
	// IsDisabled returns true if user disabled the bot.
	IsDisabled() bool
	// String returns user string representation in format '[@username|id]'.
	String() string

	// Stats returns user stats.
	Stats() UserStat
	// LastSeenTime returns the time when user interacts with provided message.
	// If message ID is not provided, it returns the time when user interacts with bot's any message.
	LastSeenTime(optionalMsgID ...int) time.Time

	// UpdateLanguage updates user language.
	UpdateLanguage(language Language)

	// GetValue returns value from user context.
	GetValue(key string) (any, bool)

	// SetValue sets value in user context (persistent).
	SetValue(key string, value any)

	// DeleteValue deletes value from user context (persistent).
	DeleteValue(key string)

	// ClearCache deletes all values from SetValue function.
	ClearCache()
}

User is an interface that represents user context in the bot.

type UserInfo

type UserInfo struct {
	// Username is Telegram username (without @).
	// It is empty if privacy mode is strict.
	Username string `bson:"username,omitempty" json:"username,omitempty" db:"username,omitempty"`
	// FirstName is Telegram user first name.
	// It is empty if privacy mode is enabled.
	FirstName string `bson:"first_name,omitempty" json:"first_name,omitempty" db:"first_name,omitempty"`
	// LastName is Telegram user last name.
	// It is empty if privacy mode is enabled.
	LastName string `bson:"last_name,omitempty" json:"last_name,omitempty" db:"last_name,omitempty"`
	// IsPremium is true if Telegram user has Telegram Premium.
	// It is empty if privacy mode is strict.
	IsPremium *bool `bson:"is_premium,omitempty" json:"is_premium,omitempty" db:"is_premium,omitempty"`
}

UserInfo contains user info, that can be obtained from Telegram.

type UserInfoDiff

type UserInfoDiff struct {
	FirstName *string `bson:"first_name" json:"first_name" db:"first_name"`
	LastName  *string `bson:"last_name" json:"last_name" db:"last_name"`
	Username  *string `bson:"username" json:"username" db:"username"`
	IsPremium *bool   `bson:"is_premium" json:"is_premium" db:"is_premium"`
}

UserInfoDiff contains changes that should be applied to user info.

type UserMessages

type UserMessages struct {
	// Main message is the last and primary one in the chat.
	MainID int `bson:"main_id" json:"main_id" db:"main_id"`
	// Head message is sent right before main message for making bot more interactive.
	HeadID int `bson:"head_id" json:"head_id" db:"head_id"`
	// Notification message can be sent in any time. Old notification message will be deleted when new one is sent.
	NotificationID int `bson:"notification_id" json:"notification_id" db:"notification_id"`
	// Error message can be sent in any time in case of error and deleted automically after next action.
	ErrorID int `bson:"error_id" json:"error_id" db:"error_id"`
	// History message is the previous main messages. Main message becomes History message after new Main sending.
	HistoryIDs []int `bson:"history_ids" json:"history_ids" db:"history_ids"`
	// LastActions contains time of last interaction of user with every message.
	LastActions map[int]time.Time `bson:"last_actions" json:"last_actions" db:"last_actions"`
}

UserMessages contains IDs of user messages.

func (UserMessages) HasMsgID

func (m UserMessages) HasMsgID(msgID int) bool

type UserMessagesDiff

type UserMessagesDiff struct {
	MainID         *int              `bson:"main_id" json:"main_id" db:"main_id"`
	HeadID         *int              `bson:"head_id" json:"head_id" db:"head_id"`
	NotificationID *int              `bson:"notification_id" json:"notification_id" db:"notification_id"`
	ErrorID        *int              `bson:"error_id" json:"error_id" db:"error_id"`
	HistoryIDs     []int             `bson:"history_ids" json:"history_ids" db:"history_ids"`
	LastActions    map[int]time.Time `bson:"last_actions" json:"last_actions" db:"last_actions"`
}

UserMessagesDiff contains changes that should be applied to user messages.

type UserModel

type UserModel struct {
	// ID is user ID.
	ID FullUserID `bson:"id" json:"id" db:"id"`
	// LanguageCode is Telegram user language code.
	// It is not encrypted even in strict privacy mode because it has low cardinality and cannot be used for identification.
	LanguageCode Language `bson:"language_code" json:"language_code" db:"language_code"`
	// ForceLanguageCode is a custom language code for user that can be set by user actions in bot.
	// It is not encrypted even in strict privacy mode because it has low cardinality and cannot be used for identification.
	ForceLanguageCode Language `bson:"force_language_code" json:"force_language_code" db:"force_language_code"`
	// Info contains user info, that can be obtained from Telegram.
	// It is empty if privacy mode is strict.
	Info UserInfo `bson:"info" json:"info" db:"info"`
	// Messages contains IDs of user messages.
	Messages UserMessages `bson:"messages" json:"messages" db:"messages"`
	// State contains state for every user's message.
	State MessagesState `bson:"state" json:"state" db:"state"`
	// Stats contains user stats.
	Stats UserStat `bson:"stats" json:"stats" db:"stats"`
	// IsBot is true if Telegram user is a bot.
	IsBot bool `bson:"is_bot" json:"is_bot" db:"is_bot"`
	// IsDisabled returns true if user is disabled. Disabled means that user blocks bot.
	IsDisabled bool `bson:"is_disabled" json:"is_disabled" db:"is_disabled"`

	// Values is a map of user values.
	Values map[string]any `bson:"values" json:"values" db:"values"`
}

UserModel is a structure that represents user in DB.

type UserModelDiff

type UserModelDiff struct {
	LanguageCode      *Language         `bson:"language_code" json:"language_code" db:"language_code"`
	ForceLanguageCode *Language         `bson:"force_language_code" json:"force_language_code" db:"force_language_code"`
	Info              *UserInfoDiff     `bson:"info" json:"info" db:"info"`
	Messages          *UserMessagesDiff `bson:"messages" json:"messages" db:"messages"`
	State             *UserStateDiff    `bson:"state" json:"state" db:"state"`
	Stats             *UserStatDiff     `bson:"stats" json:"stats" db:"stats"`
	IsDisabled        *bool             `bson:"is_disabled" json:"is_disabled" db:"is_disabled"`
	IsBot             *bool             `bson:"is_bot" json:"is_bot" db:"is_bot"`
	Values            map[string]any    `bson:"values" json:"values" db:"values"`
}

UserModelDiff contains changes that should be applied to user.

type UserStat

type UserStat struct {
	// NumberOfStateChangesTotal is the total number of actions user made.
	NumberOfStateChangesTotal int `bson:"number_of_state_changes_total" json:"number_of_state_changes_total" db:"number_of_state_changes_total"`
	// LastSeenTime is the last time user interacted with the bot.
	LastSeenTime time.Time `bson:"last_seen_time" json:"last_seen_time" db:"last_seen_time"`
	// CreatedTime is the time when user was created.
	CreatedTime time.Time `bson:"created_time" json:"created_time" db:"created_time"`
	// DisabledTime is the time when user was disabled.
	DisabledTime time.Time `bson:"disabled_time" json:"disabled_time" db:"disabled_time"`
}

type UserStatDiff

type UserStatDiff struct {
	NumberOfStateChanges *int       `bson:"number_of_state_changes_total" json:"number_of_state_changes_total" db:"number_of_state_changes_total"`
	LastSeenTime         *time.Time `bson:"last_seen_time" json:"last_seen_time" db:"last_seen_time"`
	DisabledTime         *time.Time `bson:"disabled_time" json:"disabled_time" db:"disabled_time"`
}

type UserState

type UserState string

UserState is a bote implementation of State interface.

const (
	// NoChange is a state that means that user state should not be changed after Send of Edit.
	NoChange UserState = ""
	// FirstRequest is a state of a user after first request to bot.
	FirstRequest UserState = "first_request"
	// Unknown is a state of a user when hiw real state is not provided after creation.
	Unknown UserState = "unknown"
	// Disabled is a state of a disabled user.
	Disabled UserState = "disabled"
)

func ConvertUserState

func ConvertUserState(state State) UserState

func NewUserState

func NewUserState(state string) UserState

func (UserState) IsText

func (s UserState) IsText() bool

func (UserState) NotChanged

func (s UserState) NotChanged() bool

func (UserState) String

func (s UserState) String() string

type UserStateDiff

type UserStateDiff struct {
	Main                 *UserState        `bson:"main" json:"main" db:"main"`
	MessageStates        map[int]UserState `bson:"message_states" json:"message_states" db:"message_states"`
	MessagesAwaitingText []int             `bson:"messages_awaiting_text" json:"messages_awaiting_text" db:"messages_awaiting_text"`
}

UserStateDiff contains changes that should be applied to user state.

type UsersStorage

type UsersStorage interface {
	// Insert inserts user in storage.
	// It is better to NOT modify document before saving to make everything working properly.
	Insert(ctx context.Context, userModel UserModel) error
	// Find returns user from storage. It returns true as a second argument if user was found without error.
	Find(ctx context.Context, id FullUserID) (UserModel, bool, error)

	// UpdateAsync updates user model in storage. The idea of that method is that it calls on every user action
	// (e.g. for update state), so it should be async to make it faster for user (without IO latency).
	// So this method doesn't accept context and doesn't return error because it should be called in async goroutine.
	//
	// Warning! You can't use simple worker pool, because updates should be ordered. If you don't want to
	// make it async, you may use sync operation in this method and handle error using channels, for example.
	// You may be intersting in https://github.com/maxbolgarin/mongox for async operations in MongoDB or
	// https://github.com/maxbolgarin/gorder for general async queue if you use another db.
	//
	// If you want stable work of bote package, don't update user model by yourself. Bote will do it for you.
	// If you want to expand user model by your additional fields, create an another table/collection in your db.
	UpdateAsync(id FullUserID, userModel *UserModelDiff)
}

UsersStorage is a storage for users. You should implement it in your application if you want to persist users between applicataion restarts.

type WebhookConfig

type WebhookConfig struct {
	// URL is the webhook endpoint URL that Telegram will use to send updates.
	// Must be HTTPS and accessible from the internet.
	// Example: "https://yourdomain.com/bot/webhook"
	URL string `yaml:"url" json:"url" env:"BOTE_WEBHOOK_URL"`

	// Listen is the address to bind the webhook server to.
	// Default: ":8443"
	// Environment variable: BOTE_WEBHOOK_LISTEN.
	Listen string `yaml:"listen" json:"listen" env:"BOTE_WEBHOOK_LISTEN"`

	// ReadTimeout is the maximum duration for reading the entire request.
	// Default: 30 seconds.
	// Environment variable: BOTE_WEBHOOK_READ_TIMEOUT.
	ReadTimeout time.Duration `yaml:"read_timeout" json:"read_timeout" env:"BOTE_WEBHOOK_READ_TIMEOUT"`

	// IdleTimeout is the maximum amount of time to wait for the next request.
	// Default: 120 seconds.
	// Environment variable: BOTE_WEBHOOK_IDLE_TIMEOUT.
	IdleTimeout time.Duration `yaml:"idle_timeout" json:"idle_timeout" env:"BOTE_WEBHOOK_IDLE_TIMEOUT"`

	// MaxConnections is the maximum allowed number of simultaneous HTTPS connections to the webhook.
	// Valid range: 1-100. Default: 40.
	// Environment variable: BOTE_WEBHOOK_MAX_CONNECTIONS.
	MaxConnections int `yaml:"max_connections" json:"max_connections" env:"BOTE_WEBHOOK_MAX_CONNECTIONS"`

	// DropPendingUpdates specifies whether to drop pending updates when setting webhook.
	// Default: false.
	// Environment variable: BOTE_WEBHOOK_DROP_PENDING_UPDATES.
	DropPendingUpdates bool `yaml:"drop_pending_updates" json:"drop_pending_updates" env:"BOTE_WEBHOOK_DROP_PENDING_UPDATES"`

	// MetricsPath is the path to serve metrics.
	// Default: "/metrics".
	// Environment variable: BOTE_WEBHOOK_METRICS_PATH.
	MetricsPath string `yaml:"metrics_path" json:"metrics_path" env:"BOTE_WEBHOOK_METRICS_PATH"`

	// EnableMetrics is a flag that enables metrics serving.
	// It uses provided registry to register metrics or a default one if registry is nil.
	// Default: false.
	// Environment variable: BOTE_WEBHOOK_ENABLE_METRICS.
	EnableMetrics bool `yaml:"enable_metrics" json:"enable_metrics" env:"BOTE_WEBHOOK_ENABLE_METRICS"`

	// Security contains security configuration.
	Security WebhookSecurityConfig `yaml:"security" json:"security"`

	// RateLimit contains rate limiting configuration.
	RateLimit WebhookRateLimitConfig `yaml:"rate_limit" json:"rate_limit"`

	// AllowedUpdates is a list of update types the bot wants to receive.
	// Empty list means all update types.
	// Possible values:
	//		message
	// 		edited_message
	// 		channel_post
	// 		edited_channel_post
	// 		inline_query
	// 		chosen_inline_result
	// 		callback_query
	// 		shipping_query
	// 		pre_checkout_query
	// 		poll
	// 		poll_answer
	//
	// Environment variable: BOTE_ALLOWED_UPDATES (comma-separated).
	AllowedUpdates []string `yaml:"allowed_updates" json:"allowed_updates" env:"BOTE_ALLOWED_UPDATES" envSeparator:","`
	// contains filtered or unexported fields
}

WebhookConfig contains configuration for webhook-based bot operation.

type WebhookRateLimitConfig

type WebhookRateLimitConfig struct {
	// Enabled enables rate limiting. Default: true.
	// Environment variable: BOTE_WEBHOOK_RATE_LIMIT_ENABLED.
	Enabled *bool `yaml:"enabled" json:"enabled" env:"BOTE_WEBHOOK_RATE_LIMIT_ENABLED"`

	// RequestsPerSecond is the maximum requests per second allowed.
	// Default: 30 (Telegram's recommended limit).
	// Environment variable: BOTE_WEBHOOK_RATE_LIMIT_RPS.
	RequestsPerSecond int `yaml:"requests_per_second" json:"requests_per_second" env:"BOTE_WEBHOOK_RATE_LIMIT_RPS"`

	// BurstSize is the burst size for rate limiting.
	// Default: 10.
	// Environment variable: BOTE_WEBHOOK_RATE_LIMIT_BURST.
	BurstSize int `yaml:"burst_size" json:"burst_size" env:"BOTE_WEBHOOK_RATE_LIMIT_BURST"`
}

WebhookRateLimitConfig contains rate limiting configuration.

type WebhookSecurityConfig

type WebhookSecurityConfig struct {
	// SecretToken is used for webhook request verification.
	// Highly recommended for security. Will be generated automatically if not provided.
	// Environment variable: BOTE_WEBHOOK_SECRET_TOKEN.
	SecretToken string `yaml:"secret_token" json:"secret_token" env:"BOTE_WEBHOOK_SECRET_TOKEN"`

	// StartHTTPS indicates if using HTTPS in the current server.
	// If true, it will start current server as HTTPS server with provided certificate and key.
	// If false, it will start current server as HTTP server.
	// Default: false.
	// Environment variable: BOTE_WEBHOOK_START_HTTPS.
	StartHTTPS bool `yaml:"start_https" json:"start_https" env:"BOTE_WEBHOOK_START_HTTPS"`

	// LoadCertInTelegram indicates that current certificate (public part) should be loaded to Telegram.
	// It is required for Telegram webhook if you use self-signed certificate.
	// If true, will upload certificate to Telegram and providing certificate is required.
	// If false, it is expected that you use trusted certificate.
	// Default: false.
	// Environment variable: BOTE_WEBHOOK_LOAD_CERT_IN_TELEGRAM.
	LoadCertInTelegram bool `yaml:"load_cert_in_telegram" json:"load_cert_in_telegram" env:"BOTE_WEBHOOK_LOAD_CERT_IN_TELEGRAM"`

	// CertFile is the path to the TLS certificate file.
	// Required if StartHTTPS is true or LoadCertInTelegram is true.
	// If it is empty it is expected that there is a LB in front of the server that termmintaes TLS.
	// HTTPS is strictly required for Telegram webhook.
	// Environment variable: BOTE_WEBHOOK_CERT_FILE.
	CertFile string `yaml:"cert_file" json:"cert_file" env:"BOTE_WEBHOOK_CERT_FILE"`

	// KeyFile is the path to the TLS private key file.
	// Required if StartHTTPS is true or LoadCertInTelegram is true.
	// If it is empty it is expected that there is a LB in front of the server that termmintaes TLS.
	// HTTPS is strictly required for Telegram webhook.
	// Environment variable: BOTE_WEBHOOK_KEY_FILE.
	KeyFile string `yaml:"key_file" json:"key_file" env:"BOTE_WEBHOOK_KEY_FILE"`

	// CheckTLSInRequest indicates that current server should check TLS in request.
	// If true, it will check TLS in request and return 400 if it is not TLS.
	// It will try to find TLS config in request or https in X-Forwarded-Proto header.
	// If false, it will not check TLS in request.
	// Default: true.
	// Environment variable: BOTE_WEBHOOK_CHECK_TLS_IN_REQUEST.
	CheckTLSInRequest *bool `yaml:"check_tls_in_request" json:"check_tls_in_request" env:"BOTE_WEBHOOK_CHECK_TLS_IN_REQUEST"`

	// SecurityHeaders is a flag that enables security headers in response.
	// Default: true.
	// It will set the following headers:
	// - X-Frame-Options: DENY
	// - X-Content-Type-Options: nosniff
	// - X-XSS-Protection: 1; mode=block
	// - Referrer-Policy: strict-origin-when-cross-origin
	// Environment variable: BOTE_WEBHOOK_SECURITY_HEADERS.
	SecurityHeaders *bool `yaml:"security_headers" json:"security_headers" env:"BOTE_WEBHOOK_SECURITY_HEADERS"`

	// GenerateSelfSignedCert is a flag that enables generation of self-signed certificate and upload it to Telegram.
	// It will generate certificate and key files and upload them to Telegram.
	// It will set StartHTTPS to true and LoadCertInTelegram to true.
	// Default: false.
	// Environment variable: BOTE_WEBHOOK_GENERATE_SELF_SIGNED_CERT.
	GenerateSelfSignedCert *bool `yaml:"generate_self_signed_cert" json:"generate_self_signed_cert" env:"BOTE_WEBHOOK_GENERATE_SELF_SIGNED_CERT"`

	// AllowedTelegramIPs is a flag that adds Telegram IPs to allowed IPs.
	// Default: false.
	// Environment variable: BOTE_WEBHOOK_ALLOW_TELEGRAM_IPS.
	AllowTelegramIPs *bool `yaml:"allow_telegram_ips" json:"allow_telegram_ips" env:"BOTE_WEBHOOK_ALLOW_TELEGRAM_IPS"`

	// AllowedIPs contains allowed IP addresses/CIDR blocks.
	// Only requests from these IPs will be accepted.
	// Default: [] to allow all IPs.
	// Environment variable: BOTE_WEBHOOK_ALLOWED_IPS (comma-separated).
	AllowedIPs []string `yaml:"allowed_ips" json:"allowed_ips" env:"BOTE_WEBHOOK_ALLOWED_IPS" envSeparator:","`
}

Directories

Path Synopsis
examples
basic command
webhook command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL