Skip to main content
Commands are the primary way users interact with Telegram bots. Telebot routes them through the same Handle() mechanism used for all other endpoints.

Basic command handling

Register a handler for a command by passing the /command string as the endpoint:
b.Handle("/start", func(c tele.Context) error {
    return c.Send("Welcome! Use /help to see available commands.")
})

b.Handle("/help", func(c tele.Context) error {
    return c.Send("Here is what I can do...")
})
The handler receives a Context that wraps the incoming update. Telebot matches the command text using the regex ^(/\w+)(@(\w+))?(\s|$)(.+)?, so only exact /command prefixes are routed.

Group-addressed commands

In group chats, users can address commands to a specific bot: /command@botname. Telebot handles this automatically — it matches /start and /start@mybot to the same handler, provided the bot username matches b.Me.Username. You do not need to register separate handlers for the @botname variants.
// Handles both "/start" and "/start@mybot" in groups
b.Handle("/start", func(c tele.Context) error {
    return c.Reply("Hello!")
})

Deep linking with /start

Telegram supports deep links via t.me/botname?start=PAYLOAD. When a user follows such a link, the bot receives a /start message with the payload appended. Read the payload via c.Message().Payload:
b.Handle("/start", func(c tele.Context) error {
    payload := c.Message().Payload
    if payload == "" {
        return c.Send("Hello!")
    }
    // e.g. payload == "ref_123" from t.me/mybot?start=ref_123
    return c.Send("You came from referral: " + payload)
})
c.Message().Payload contains everything after the command text and a single space. For /start ref_123, Payload is "ref_123".

Multiple arguments with c.Args()

c.Args() splits the message payload by whitespace and returns a []string. This is useful when a command accepts several arguments:
b.Handle("/echo", func(c tele.Context) error {
    args := c.Args()
    if len(args) == 0 {
        return c.Send("Usage: /echo <text>")
    }
    return c.Send(strings.Join(args, " "))
})
For callback buttons, c.Args() splits by | instead — the same function works across contexts.

Setting bot commands

Register the command list displayed in Telegram’s UI via b.SetCommands(). Commands are passed as a []tele.Command slice along with optional scope and language parameters.
commands := []tele.Command{
    {Text: "start",   Description: "Start the bot"},
    {Text: "help",    Description: "Show help"},
    {Text: "settings", Description: "Open settings"},
}

// Set for all users in all chats (default scope)
if err := b.SetCommands(commands); err != nil {
    log.Fatal(err)
}
You can scope commands to specific contexts using tele.CommandScope:
// Admin-only commands visible only in a specific chat
adminScope := tele.CommandScope{
    Type:   tele.CommandScopeChatAdmin,
    ChatID: -1001234567890,
}

adminCmds := []tele.Command{
    {Text: "ban",  Description: "Ban a user"},
    {Text: "kick", Description: "Kick a user"},
}

b.SetCommands(adminCmds, adminScope)

Available scope types

CommandScopeDefault

All chats — the default fallback scope.

CommandScopeAllPrivateChats

All private conversations with the bot.

CommandScopeAllGroupChats

All group and supergroup chats.

CommandScopeAllChatAdmin

All chat administrators across groups.

CommandScopeChat

A specific chat identified by ChatID.

CommandScopeChatAdmin

Administrators of a specific chat.

CommandScopeChatMember

A specific member in a specific chat (ChatID + UserID).

Retrieving and deleting commands

// Get the current command list
cmds, err := b.Commands()

// Delete commands for the default scope
b.DeleteCommands()

// Delete scoped commands
b.DeleteCommands(tele.CommandScope{Type: tele.CommandScopeAllGroupChats})

Language-specific commands

Pass a BCP-47 language code string to serve localised command menus:
russianCmds := []tele.Command{
    {Text: "start", Description: "Запустить бота"},
}
b.SetCommands(russianCmds, "ru")

Full example

package main

import (
    "log"
    "strings"
    "time"

    tele "gopkg.in/telebot.v4"
)

func main() {
    b, err := tele.NewBot(tele.Settings{
        Token:  "TOKEN",
        Poller: &tele.LongPoller{Timeout: 10 * time.Second},
    })
    if err != nil {
        log.Fatal(err)
    }

    b.Handle("/start", func(c tele.Context) error {
        if payload := c.Message().Payload; payload != "" {
            return c.Send("Deep link payload: " + payload)
        }
        return c.Send("Hello!")
    })

    b.Handle("/echo", func(c tele.Context) error {
        args := c.Args()
        if len(args) == 0 {
            return c.Reply("Send me some text after /echo")
        }
        return c.Reply(strings.Join(args, " "))
    })

    _ = b.SetCommands([]tele.Command{
        {Text: "start", Description: "Start the bot"},
        {Text: "echo",  Description: "Echo your text"},
    })

    b.Start()
}

Build docs developers (and LLMs) love