tg

module
v0.0.0-...-015408d Latest Latest
Warning

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

Go to latest
Published: Aug 22, 2025 License: MIT

README ยถ

image

TG - Telegram Bot Framework for Go

Go Reference Go Report Card Coverage Status Go Ask DeepWiki Telegram Bot API Version

Modern and elegant wrapper around gotgbot with convenient API and functional programming style support.

Video Demo

TG Framework Demo

Features

  • ๐Ÿš€ Simple and intuitive API with method chaining support
  • ๐ŸŽฏ Type-safe event handlers for all Telegram update types
  • ๐Ÿ”ง Flexible bot configuration through Builder pattern
  • ๐Ÿ“ Rich functionality for messages, media, keyboards and payments
  • ๐ŸŽฎ Built-in FSM support (finite state machines) for complex dialogues
  • ๐Ÿ’ฐ Telegram Payments and Stars support with refunds
  • ๐ŸŽฒ Full support for all Telegram content types and features
  • ๐Ÿ”ง Middleware system for request filtering and processing
  • ๐Ÿ“‚ Advanced file handling with metadata and thumbnails
  • ๐ŸŒ Webhook support with security features

Quick Start

Installation
go mod init your-bot
go get github.com/enetx/tg
Simple Echo Bot
package main

import (
    "github.com/enetx/g"
    "github.com/enetx/tg/bot"
    "github.com/enetx/tg/ctx"
)

func main() {
    token := "token"
    b := bot.New(token).Build().Unwrap()

    // Handle text messages
    b.On.Message.Text(func(ctx *ctx.Context) error {
        return ctx.Reply("Echo: " + g.String(ctx.EffectiveMessage.Text)).Send().Err()
    })

    // Handle /start command
    b.Command("start", func(ctx *ctx.Context) error {
        return ctx.Reply("Welcome to the bot!").Send().Err()
    })

    b.Polling().Start()
}

Message Handlers

Handle different types of Telegram messages:

// Text messages
b.On.Message.Text(func(ctx *ctx.Context) error {
    return ctx.Reply("Text received").Send().Err()
})

// Media messages
b.On.Message.Photo(func(ctx *ctx.Context) error {
    return ctx.Reply("Photo received").Send().Err()
})

b.On.Message.Voice(func(ctx *ctx.Context) error {
    return ctx.Reply("Voice received").Send().Err()
})

b.On.Message.Video(func(ctx *ctx.Context) error {
    return ctx.Reply("Video received").Send().Err()
})

b.On.Message.Document(func(ctx *ctx.Context) error {
    return ctx.Reply("Document received").Send().Err()
})

// Contact and location
b.On.Message.Contact(func(ctx *ctx.Context) error {
    contact := ctx.EffectiveMessage.Contact
    return ctx.Reply("Thanks for sharing your contact!").Send().Err()
})

b.On.Message.Location(func(ctx *ctx.Context) error {
    location := ctx.EffectiveMessage.Location
    return ctx.Reply("Location received").Send().Err()
})

Commands

Register commands with advanced options:

// Basic command
b.Command("start", func(ctx *ctx.Context) error {
    return ctx.SendMessage("Start command triggered!").Send().Err()
})

// Command with custom triggers and options
b.Command("help", func(ctx *ctx.Context) error {
    return ctx.Reply("Help message").Send().Err()
}).
    Triggers('!', '.').  // Allow !help and .help
    AllowEdited().       // Handle edited messages
    AllowChannel().      // Work in channels
    Register()

// Commands work automatically, but you can customize them further

Inline Keyboards

Create interactive inline keyboards:

// Basic inline keyboard
b.Command("menu", func(ctx *ctx.Context) error {
    markup := keyboard.Inline().
        Row().
        Text("Option 1", "opt1").
        Text("Option 2", "opt2").
        Row().
        URL("Visit Site", "https://example.com").
        WebApp("Open App", "https://webapp.com")

    return ctx.Reply("Choose an option:").Markup(markup).Send().Err()
})

// Handle button presses
b.On.Callback.Equal("opt1", func(ctx *ctx.Context) error {
    return ctx.AnswerCallbackQuery("You chose option 1!").Send().Err()
})

b.On.Callback.Prefix("opt", func(ctx *ctx.Context) error {
    data := ctx.Update.CallbackQuery.Data
    return ctx.AnswerCallbackQuery("You clicked: " + g.String(data)).Alert().Send().Err()
})
Dynamic Keyboard Editing
b.On.Callback.Equal("edit", func(ctx *ctx.Context) error {
    // Edit existing keyboard
    markup := keyboard.Inline(ctx.EffectiveMessage.ReplyMarkup).
        Edit(func(btn *keyboard.Button) {
            switch btn.Get.Callback() {
            case "opt1":
                btn.Text("Modified Option 1")
            case "remove":
                btn.Delete()
            }
        })

    return ctx.EditMessageReplyMarkup(markup).Send().Err()
})

Reply Keyboards

Create custom reply keyboards:

b.Command("keyboard", func(ctx *ctx.Context) error {
    markup := keyboard.Reply().
        Row().
        Text("Regular Button").
        Contact("๐Ÿ“ž Share Phone").
        Row().
        Location("๐Ÿ“ Send Location").
        WebApp("๐ŸŒ Web App", "https://webapp.com").
        Row().
        Poll("๐Ÿ“Š Create Poll", "regular")

    return ctx.Reply("Use the keyboard below:").Markup(markup).Send().Err()
})

File Handling

Send various types of media files:

// Photo
b.Command("photo", func(ctx *ctx.Context) error {
    return ctx.SendPhoto("photo.png").
        Caption("Beautiful photo").
        Send().Err()
})

// Document with advanced options
b.Command("doc", func(ctx *ctx.Context) error {
    return ctx.SendDocument("document.pdf").
        Caption("Important document").
        Reply(reply.New(ctx.EffectiveMessage.MessageId)).
        Send().Err()
})

// Video with metadata
b.Command("video", func(ctx *ctx.Context) error {
    return ctx.SendVideo("video.mp4").
        Caption("Cool video").
        Spoiler().
        Timeout(3 * time.Minute). // Custom timeout
        ApplyMetadata().          // Extract video info (ffprobe)
        GenerateThumbnail().      // Auto-generate thumbnail (ffmpeg)
        Send().Err()
})

// Audio with metadata
b.Command("audio", func(ctx *ctx.Context) error {
    return ctx.SendAudio("song.mp3").
        Title("Song Title").
        Performer("Artist Name").
        Duration(180 * time.Second).
        Send().Err()
})

Finite State Machine (FSM)

Create complex multi-step conversations:

import (
	"github.com/enetx/fsm"
	"github.com/enetx/g"
)

// Define states
const (
    StateGetEmail = "get_email"
    StateGetName  = "get_name"
    StateSummary  = "summary"
)

// Store FSM instances per user
var fsmStore = g.NewMapSafe[int64, *fsm.SyncFSM]()

func main() {
    b := bot.New(token).Build().Unwrap()

    // Create FSM template
    template := fsm.New(StateGetEmail).
        Transition(StateGetEmail, "next", StateGetName).
        Transition(StateGetName, "next", StateSummary)

    // Define state handlers
    template.OnEnter(StateGetEmail, func(fctx *fsm.Context) error {
        tgctx := fctx.Meta.Get("tgctx").Some().(*ctx.Context)
        return tgctx.Reply("Enter your email:").Send().Err()
    })

    template.OnEnter(StateGetName, func(fctx *fsm.Context) error {
        email := fctx.Input.(string)
        fctx.Data.Set("email", email)

        tgctx := fctx.Meta.Get("tgctx").Some().(*ctx.Context)
        return tgctx.Reply("Enter your name:").Send().Err()
    })

    template.OnEnter(StateSummary, func(fctx *fsm.Context) error {
        name := fctx.Input.(string)
        email := fctx.Data.Get("email").UnwrapOr("<no email>")

        tgctx := fctx.Meta.Get("tgctx").Some().(*ctx.Context)
        defer fsmStore.Delete(tgctx.EffectiveUser.Id)

        return tgctx.Reply(g.Format("Got name: {} and email: {}", name, email)).Send().Err()
    })

    // Start FSM
    b.Command("register", func(ctx *ctx.Context) error {
        entry := fsmStore.Entry(ctx.EffectiveUser.Id)
        entry.OrSetBy(func() *fsm.SyncFSM { return template.Clone().Sync() })
        fsm := entry.Get().Some()

        fsm.SetState(StateGetEmail)
        fsm.Context().Meta.Set("tgctx", ctx)
        return fsm.CallEnter(StateGetEmail)
    })

    // Handle FSM input
    b.On.Message.Text(func(ctx *ctx.Context) error {
        opt := fsmStore.Get(ctx.EffectiveUser.Id)
        if opt.IsNone() {
            return nil // No active FSM
        }

        fsm := opt.Some()
        fsm.Context().Meta.Set("tgctx", ctx)
        return fsm.Trigger("next", ctx.EffectiveMessage.Text)
    })

    b.Polling().Start()
}

Payments with Telegram Stars

Handle payments using Telegram's Stars system:

// Create invoice
b.Command("buy", func(ctx *ctx.Context) error {
    if ctx.EffectiveChat.Type != "private" {
        return nil
    }

    return ctx.Invoice("Premium Access", "Get premium features", "premium_123", "XTR").
        Price("Premium Plan", 100).  // 100 stars
        Protect().                   // Content protection
        Send().Err()
})

// Handle pre-checkout (validation)
b.On.PreCheckout.Any(func(ctx *ctx.Context) error {
    // Validate payment here if needed
    return ctx.PreCheckout().Ok().Send().Err()
})

// Handle successful payment
b.On.Message.SuccessfulPayment(func(ctx *ctx.Context) error {
    user := ctx.EffectiveUser
    payment := ctx.EffectiveMessage.SuccessfulPayment
    chargeID := payment.TelegramPaymentChargeId

    // Grant premium access here
    g.Println("User {1.FirstName} ({1.Id}) paid {2.TotalAmount} {2.Currency} with payload {2.InvoicePayload}",
        user, payment)

    return ctx.SendMessage(g.Format("Payment complete! Thank you, {}!\nChargeID:\n{}", user.FirstName, chargeID)).
        Send().Err()
})

// Handle refunds
b.Command("refund", func(ctx *ctx.Context) error {
    chargeID := ctx.Args().Get(0).Some()

    if result := ctx.RefundStarPayment(chargeID).Send(); result.IsErr() {
        err := g.String(result.Err().Error())
        if err.Contains("CHARGE_ALREADY_REFUNDED") {
            return ctx.Reply("This payment was already refunded.").Send().Err()
        }
        return ctx.Reply("Refund failed.").Send().Err()
    }

    return ctx.Reply("Refund processed successfully.").Send().Err()
})

Middleware

Add middleware for request processing:

// Global middleware
b.Use(func(ctx *ctx.Context) error {
    // Log all updates
    fmt.Println("Update from user:", ctx.EffectiveUser.Id)
    return nil // Continue processing
})

// Admin-only middleware
adminMiddleware := func(ctx *ctx.Context) error {
	admin := ctx.IsAdmin()
	if admin.IsErr() {
		return admin.Err()
	}

	if !admin.Ok() {
		return ctx.Answer("Access restricted to admins only!").Alert().Send().Err()
	}

    return nil // Continue
}

// Apply middleware to specific handlers
b.On.Callback.Prefix("admin_", adminMiddleware)

Webhook Mode

Set up webhook instead of polling:

import (
    "net/http"
    "io"

    "github.com/enetx/tg/bot"
    "github.com/enetx/tg/types/updates"
)

func main() {
    b := bot.New(token).Build().Unwrap()

    // Register webhook
    err := b.Webhook().
        Domain("https://yourdomain.com").
        Path("/webhook").
        SecretToken("your-secret").
        AllowedUpdates(updates.Message, updates.CallbackQuery).
        Register()
    if err != nil {
        panic(err)
    }

    // Setup HTTP server
    http.HandleFunc("/webhook", func(w http.ResponseWriter, r *http.Request) {
        // Verify secret token
        if r.Header.Get("X-Telegram-Bot-Api-Secret-Token") != "your-secret" {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }

        body, _ := io.ReadAll(r.Body)
        b.HandleWebhook(body)
        w.WriteHeader(http.StatusOK)
    })

    http.ListenAndServe(":8080", nil)
}

Business Account API

Handle business account connections and messages:

// Handle business connection updates
b.On.BusinessConnection.Enabled(func(ctx *ctx.Context) error {
    conn := ctx.Update.BusinessConnection

    // Configure business account
    return ctx.Business(g.String(conn.Id)).SetName("My Business").
        LastName("LLC").
        Send().Err()
})

// Handle business messages
b.On.Message.Business(func(ctx *ctx.Context) error {
    return ctx.Reply("Business message received!").Send().Err()
})

// Handle deleted business messages
b.On.DeletedBusinessMessages.Any(func(ctx *ctx.Context) error {
    deleted := ctx.Update.DeletedBusinessMessages
    // Process message deletions
    return nil
})

// Manage business account settings
b.Command("business_setup", func(ctx *ctx.Context) error {
    connectionId := g.String("your_connection_id")

    // Set profile information
    err := ctx.Business(connectionId).
        SetBio("Professional business account").
        Send().Err()

    if err != nil {
        return err
    }

    // Check star balance
    balance := ctx.Business(connectionId).Balance().GetStarBalance().Send()
    if balance.IsOk() {
        return ctx.Reply("Stars balance: " + g.String(balance.Ok().Amount)).Send().Err()
    }

    return ctx.Reply("Business account configured").Send().Err()
})

Text Entities and Formatting

Format text messages with various entities:

import (
    "github.com/enetx/g"
    "github.com/enetx/tg/entities"
)

// Basic text formatting
b.Command("format", func(ctx *ctx.Context) error {
    text := g.String("Hello bold italic code")

    e := entities.New(text).
        Bold("bold").     // Make "bold" bold
        Italic("italic"). // Make "italic" italic
        Code("code")      // Make "code" monospace

    return ctx.Reply(text).
        Entities(e).
        Send().Err()
})

// Links and spoilers
b.Command("links", func(ctx *ctx.Context) error {
    text := g.String("Click here to visit Google")

    e := entities.New(text).
        URL("here", "https://google.com"). // "here" as hyperlink
        Spoiler("Google")                  // "Google" as spoiler

    return ctx.Reply(text).
        Entities(e).
        Send().Err()
})

// Code blocks with syntax highlighting
b.Command("codeblock", func(ctx *ctx.Context) error {
    code := g.String(`func main() {
    fmt.Println("Hello")
}`)
    codeText := g.Format("Check this Go code:\n{}", code)

    e := entities.New(codeText).
        Pre(code, "go") // Go code with syntax highlighting

    return ctx.Reply(codeText).
        Entities(e).
        Send().Err()
})

// Multiple formatting types
b.Command("mixed", func(ctx *ctx.Context) error {
    text := g.String("Bold italic underline strikethrough spoiler")

    e := entities.New(text).
        Bold("Bold").
        Italic("italic").
        Underline("underline").
        Strikethrough("strikethrough").
        Spoiler("spoiler")

    return ctx.Reply(text).
        Entities(e).
        Send().Err()
})

// Blockquotes
b.Command("quotes", func(ctx *ctx.Context) error {
    text := g.String(`Regular text
This is a blockquote
This is expandable quote`)

    e := entities.New(text).
        Blockquote("This is a blockquote").
        ExpandableBlockquote("This is expandable quote")

    return ctx.Reply(text).
        Entities(e).
        Send().Err()
})

Advanced Features

Chat Actions

Show typing indicators and other actions:

b.On.Message.Text(func(ctx *ctx.Context) error {
    // Show typing indicator
    ctx.ChatAction().Typing().Send()

    // Process message...
    time.Sleep(2 * time.Second)

    return ctx.Reply("Processed your message").Send().Err()
})
Dice and Games
// Send dice
b.Command("dice", func(ctx *ctx.Context) error {
    return ctx.SendDice().Send().Err()
})

// Send slot machine
b.Command("slot", func(ctx *ctx.Context) error {
    return ctx.SendDice().Slot().Send().Err()
})
Message Editing and Deletion
b.Command("edit", func(ctx *ctx.Context) error {
    // Send initial message
    msg := ctx.Reply("Original message").Send()

    // Edit it
    return ctx.EditMessageText("Edited message").MessageID(msg.Ok().MessageId).Send().Err()
})

b.Command("delete", func(ctx *ctx.Context) error {
    return ctx.DeleteMessage().Send().Err()
})

Bot Configuration

Configure bot with advanced options:

b := bot.New(token).
    APIURL("https://api.telegram.org").  // Custom API URL
    UseTestEnvironment().                // Use test environment
    DisableTokenCheck().                 // Skip token validation
    Build().
    Unwrap()

Error Handling

All methods follow a consistent error handling pattern:

if err := ctx.Reply("Hello").Send().Err(); err != nil {
    log.Printf("Failed to send message: %v", err)
}

// Or chain with result handling
result := ctx.SendPhoto("image.jpg").Send()
if result.IsErr() {
    log.Printf("Failed to send photo: %v", result.Err())
}

API Documentation

Full API documentation is available at GoDoc.

License

MIT License. See LICENSE file for details.

Support

  • Create GitHub issues for bug reports
  • Use discussions for questions and suggestions
  • Explore examples in the examples/ folder

Directories ยถ

Path Synopsis
ctx
advanced/bot_configuration command
Package main demonstrates comprehensive bot configuration and management.
Package main demonstrates comprehensive bot configuration and management.
advanced/chat_administration_advanced command
Package main demonstrates advanced chat administration features in TG Framework.
Package main demonstrates advanced chat administration features in TG Framework.
advanced/input_builders_advanced command
Package main demonstrates advanced usage of all input builders in TG Framework.
Package main demonstrates advanced usage of all input builders in TG Framework.
advanced/interactive_keyboards_advanced command
Package main demonstrates advanced interactive keyboard features in TG Framework.
Package main demonstrates advanced interactive keyboard features in TG Framework.
advanced/payment_processing_advanced command
Package main demonstrates advanced payment processing features in TG Framework.
Package main demonstrates advanced payment processing features in TG Framework.
buttons command
fsm command
fsm/deep command
fsm/inline command
fsm/simple command
story command
webhook command
internal
types
chat
Package chat defines Telegram chat types.
Package chat defines Telegram chat types.
chataction
Package chataction defines supported chat actions for Telegram bots.
Package chataction defines supported chat actions for Telegram bots.
chatmember
Package chat defines the chat-related types used by Telegram.
Package chat defines the chat-related types used by Telegram.
effects
Package effects defines Telegram message effect identifiers.
Package effects defines Telegram message effect identifiers.
entity
Package entity defines the entry (message-entity) types used by Telegram.
Package entity defines the entry (message-entity) types used by Telegram.
passport
Package passport defines Telegram passport element error types and utilities.
Package passport defines Telegram passport element error types and utilities.
permissions
Package permissions defines Telegram chat permission types and utilities.
Package permissions defines Telegram chat permission types and utilities.
rights
Package rights defines Telegram chat administrator rights types and utilities.
Package rights defines Telegram chat administrator rights types and utilities.
roles
Package roles defines Telegram chat administrator role types and utilities.
Package roles defines Telegram chat administrator role types and utilities.

Jump to

Keyboard shortcuts

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