Telebot is a Telegram bot framework in Go.

Related tags

Bot Building telebot
Overview

Telebot

"I never knew creating Telegram bots could be so sexy!"

GoDoc Travis codecov.io Discuss on Telegram

go get -u gopkg.in/tucnak/telebot.v2

Overview

Telebot is a bot framework for Telegram Bot API. This package provides the best of its kind API for command routing, inline query requests and keyboards, as well as callbacks. Actually, I went a couple steps further, so instead of making a 1:1 API wrapper I chose to focus on the beauty of API and performance. Some of the strong sides of telebot are:

  • Real concise API
  • Command routing
  • Middleware
  • Transparent File API
  • Effortless bot callbacks

All the methods of telebot API are extremely easy to memorize and get used to. Also, consider Telebot a highload-ready solution. I'll test and benchmark the most popular actions and if necessary, optimize against them without sacrificing API quality.

Getting Started

Let's take a look at the minimal telebot setup:

package main

import (
	"log"
	"time"

	tb "gopkg.in/tucnak/telebot.v2"
)

func main() {
	b, err := tb.NewBot(tb.Settings{
		// You can also set custom API URL.
		// If field is empty it equals to "https://api.telegram.org".
		URL: "http://195.129.111.17:8012",

		Token:  "TOKEN_HERE",
		Poller: &tb.LongPoller{Timeout: 10 * time.Second},
	})

	if err != nil {
		log.Fatal(err)
		return
	}

	b.Handle("/hello", func(m *tb.Message) {
		b.Send(m.Sender, "Hello World!")
	})

	b.Start()
}

Simple, innit? Telebot's routing system takes care of delivering updates to their endpoints, so in order to get to handle any meaningful event, all you got to do is just plug your function to one of the Telebot-provided endpoints. You can find the full list here.

b, _ := tb.NewBot(settings)

b.Handle(tb.OnText, func(m *tb.Message) {
	// all the text messages that weren't
	// captured by existing handlers
})

b.Handle(tb.OnPhoto, func(m *tb.Message) {
	// photos only
})

b.Handle(tb.OnChannelPost, func (m *tb.Message) {
	// channel posts only
})

b.Handle(tb.OnQuery, func (q *tb.Query) {
	// incoming inline queries
})

There's dozens of supported endpoints (see package consts). Let me know if you'd like to see some endpoint or endpoint idea implemented. This system is completely extensible, so I can introduce them without breaking backwards-compatibility.

Poller

Telebot doesn't really care how you provide it with incoming updates, as long as you set it up with a Poller, or call ProcessUpdate for each update (see examples/awslambdaechobot):

// Poller is a provider of Updates.
//
// All pollers must implement Poll(), which accepts bot
// pointer and subscription channel and start polling
// synchronously straight away.
type Poller interface {
	// Poll is supposed to take the bot object
	// subscription channel and start polling
	// for Updates immediately.
	//
	// Poller must listen for stop constantly and close
	// it as soon as it's done polling.
	Poll(b *Bot, updates chan Update, stop chan struct{})
}

Telegram Bot API supports long polling and webhook integration. Poller means you can plug telebot into whatever existing bot infrastructure (load balancers?) you need, if you need to. Another great thing about pollers is that you can chain them, making some sort of middleware:

poller := &tb.LongPoller{Timeout: 15 * time.Second}
spamProtected := tb.NewMiddlewarePoller(poller, func(upd *tb.Update) bool {
	if upd.Message == nil {
		return true
	}

	if strings.Contains(upd.Message.Text, "spam") {
		return false
	}

	return true
})

bot, _ := tb.NewBot(tb.Settings{
	// ...
	Poller: spamProtected,
})

// graceful shutdown
time.AfterFunc(N * time.Second, b.Stop)

// blocks until shutdown
bot.Start()

fmt.Println(poller.LastUpdateID) // 134237

Commands

When handling commands, Telebot supports both direct (/command) and group-like syntax (/[email protected]) and will never deliver messages addressed to some other bot, even if privacy mode is off. For simplified deep-linking, telebot also extracts payload:

// Command: /start <PAYLOAD>
b.Handle("/start", func(m *tb.Message) {
	if !m.Private() {
		return
	}

	fmt.Println(m.Payload) // <PAYLOAD>
})

Files

Telegram allows files up to 20 MB in size.

Telebot allows to both upload (from disk / by URL) and download (from Telegram) and files in bot's scope. Also, sending any kind of media with a File created from disk will upload the file to Telegram automatically:

a := &tb.Audio{File: tb.FromDisk("file.ogg")}

fmt.Println(a.OnDisk()) // true
fmt.Println(a.InCloud()) // false

// Will upload the file from disk and send it to recipient
bot.Send(recipient, a)

// Next time you'll be sending this very *Audio, Telebot won't
// re-upload the same file but rather utilize its Telegram FileID
bot.Send(otherRecipient, a)

fmt.Println(a.OnDisk()) // true
fmt.Println(a.InCloud()) // true
fmt.Println(a.FileID) // <telegram file id: ABC-DEF1234ghIkl-zyx57W2v1u123ew11>

You might want to save certain Files in order to avoid re-uploading. Feel free to marshal them into whatever format, File only contain public fields, so no data will ever be lost.

Sendable

Send is undoubtedly the most important method in Telebot. Send() accepts a Recipient (could be user, group or a channel) and a Sendable. Other types other than the telebot-provided media types (Photo, Audio, Video, etc.) are Sendable. If you create composite types of your own, and they satisfy the Sendable interface, Telebot will be able to send them out.

// Sendable is any object that can send itself.
//
// This is pretty cool, since it lets bots implement
// custom Sendables for complex kinds of media or
// chat objects spanning across multiple messages.
type Sendable interface {
	Send(*Bot, Recipient, *SendOptions) (*Message, error)
}

The only type at the time that doesn't fit Send() is Album and there is a reason for that. Albums were added not so long ago, so they are slightly quirky for backwards compatibilities sake. In fact, an Album can be sent, but never received. Instead, Telegram returns a []Message, one for each media object in the album:

p := &tb.Photo{File: tb.FromDisk("chicken.jpg")}
v := &tb.Video{File: tb.FromURL("http://video.mp4")}

msgs, err := b.SendAlbum(user, tb.Album{p, v})

Send options

Send options are objects and flags you can pass to Send(), Edit() and friends as optional arguments (following the recipient and the text/media). The most important one is called SendOptions, it lets you control all the properties of the message supported by Telegram. The only drawback is that it's rather inconvenient to use at times, so Send() supports multiple shorthands:

// regular send options
b.Send(user, "text", &tb.SendOptions{
	// ...
})

// ReplyMarkup is a part of SendOptions,
// but often it's the only option you need
b.Send(user, "text", &tb.ReplyMarkup{
	// ...
})

// flags: no notification && no web link preview
b.Send(user, "text", tb.Silent, tb.NoPreview)

Full list of supported option-flags you can find here.

Editable

If you want to edit some existing message, you don't really need to store the original *Message object. In fact, upon edit, Telegram only requires chat_id and message_id. So you don't really need the Message as the whole. Also you might want to store references to certain messages in the database, so I thought it made sense for any Go struct to be editable as a Telegram message, to implement Editable:

// Editable is an interface for all objects that
// provide "message signature", a pair of 32-bit
// message ID and 64-bit chat ID, both required
// for edit operations.
//
// Use case: DB model struct for messages to-be
// edited with, say two columns: msg_id,chat_id
// could easily implement MessageSig() making
// instances of stored messages editable.
type Editable interface {
	// MessageSig is a "message signature".
	//
	// For inline messages, return chatID = 0.
	MessageSig() (messageID int, chatID int64)
}

For example, Message type is Editable. Here is the implementation of StoredMessage type, provided by telebot:

// StoredMessage is an example struct suitable for being
// stored in the database as-is or being embedded into
// a larger struct, which is often the case (you might
// want to store some metadata alongside, or might not.)
type StoredMessage struct {
	MessageID int   `sql:"message_id" json:"message_id"`
	ChatID    int64 `sql:"chat_id" json:"chat_id"`
}

func (x StoredMessage) MessageSig() (int, int64) {
	return x.MessageID, x.ChatID
}

Why bother at all? Well, it allows you to do things like this:

// just two integer columns in the database
var msgs []tb.StoredMessage
db.Find(&msgs) // gorm syntax

for _, msg := range msgs {
	bot.Edit(&msg, "Updated text")
	// or
	bot.Delete(&msg)
}

I find it incredibly neat. Worth noting, at this point of time there exists another method in the Edit family, EditCaption() which is of a pretty rare use, so I didn't bother including it to Edit(), just like I did with SendAlbum() as it would inevitably lead to unnecessary complications.

var m *Message

// change caption of a photo, audio, etc.
bot.EditCaption(m, "new caption")

Keyboards

Telebot supports both kinds of keyboards Telegram provides: reply and inline keyboards. Any button can also act as an endpoints for Handle().

In v2.2 we're introducing a little more convenient way in building keyboards. The main goal is to avoid a lot of boilerplate and to make code clearer.

func main() {
	b, _ := tb.NewBot(tb.Settings{...})

	var (
		// Universal markup builders.
		menu     = &ReplyMarkup{ResizeReplyKeyboard: true}
		selector = &ReplyMarkup{}

		// Reply buttons.
		btnHelp     = menu.Text("ℹ Help")
		btnSettings = menu.Text("⚙ Settings")

		// Inline buttons.
		//
		// Pressing it will cause the client to
		// send the bot a callback.
		//
		// Make sure Unique stays unique as per button kind,
		// as it has to be for callback routing to work.
		//
		btnPrev = selector.Data("⬅", "prev", ...)
		btnNext = selector.Data("➡", "next", ...)
	)

	menu.Reply(
		menu.Row(btnHelp),
		menu.Row(btnSettings),
	)
	selector.Inline(
		selector.Row(btnPrev, btnNext),
	)

	// Command: /start <PAYLOAD>
	b.Handle("/start", func(m *tb.Message) {
		if !m.Private() {
			return
		}

		b.Send(m.Sender, "Hello!", menu)
	})

	// On reply button pressed (message)
	b.Handle(&btnHelp, func(m *tb.Message) {...})

	// On inline button pressed (callback)
	b.Handle(&btnPrev, func(c *tb.Callback) {
		// ...
		// Always respond!
		b.Respond(c, &tb.CallbackResponse{...})
	})

	b.Start()
}

You can use markup constructor for every type of possible buttons:

r := &ReplyMarkup{}

// Reply buttons:
r.Text("Hello!")
r.Contact("Send phone number")
r.Location("Send location")
r.Poll(tb.PollQuiz)

// Inline buttons:
r.Data("Show help", "help") // data is optional
r.Data("Delete item", "delete", item.ID)
r.URL("Visit", "https://google.com")
r.Query("Search", query)
r.QueryChat("Share", query)
r.Login("Login", &tb.Login{...})

Inline mode

So if you want to handle incoming inline queries you better plug the tb.OnQuery endpoint and then use the Answer() method to send a list of inline queries back. I think at the time of writing, telebot supports all of the provided result types (but not the cached ones). This is how it looks like:

b.Handle(tb.OnQuery, func(q *tb.Query) {
	urls := []string{
		"http://photo.jpg",
		"http://photo2.jpg",
	}

	results := make(tb.Results, len(urls)) // []tb.Result
	for i, url := range urls {
		result := &tb.PhotoResult{
			URL: url,

			// required for photos
			ThumbURL: url,
		}

		results[i] = result
		// needed to set a unique string ID for each result
		results[i].SetResultID(strconv.Itoa(i))
	}

	err := b.Answer(q, &tb.QueryResponse{
		Results:   results,
		CacheTime: 60, // a minute
	})

	if err != nil {
		log.Println(err)
	}
})

There's not much to talk about really. It also supports some form of authentication through deep-linking. For that, use fields SwitchPMText and SwitchPMParameter of QueryResponse.

Contributing

  1. Fork it
  2. Clone develop: git clone -b develop https://github.com/tucnak/telebot
  3. Create your feature branch: git checkout -b new-feature
  4. Make changes and add them: git add .
  5. Commit: git commit -m "Add some feature"
  6. Push: git push origin new-feature
  7. Pull request

Donate

I do coding for fun but I also try to search for interesting solutions and optimize them as much as possible. If you feel like it's a good piece of software, I wouldn't mind a tip!

Bitcoin: 1DkfrFvSRqgBnBuxv9BzAz83dqur5zrdTH

License

Telebot is distributed under MIT.

Comments
  • [Question/Bug?] OnPoll doesn't receive any poll

    [Question/Bug?] OnPoll doesn't receive any poll

    When using telebot.OnPoll on a handler, I don't get any poll information when I send a poll to the bot, I have to stop the poll before any information is received. I want to make something similar to the official telegram quiz bot.

    opened by Clashkid155 2
  • Logging SetWebhook error

    Logging SetWebhook error

    If we fill webhook with some incorrect information and telegram return us an error, error handler will be called only in verbose mode. I think it must have more priority.

    opened by osinniy 0
  • Using bots in the test environment is not possible

    Using bots in the test environment is not possible

        JFYI, tried to use [bot test environment](https://core.telegram.org/bots/webapps#using-bots-in-the-test-environment), it does not work because of the URL construction. Telebot constructs URL as `url := b.URL + "/bot" + b.Token + "/" + method`, but for test server it should be `url := b.URL + "/bot" + b.Token + "/test/" + method` — note the **test** suffix.
    

    Originally posted by @prokher in https://github.com/tucnak/telebot/issues/508#issuecomment-1104446503

    opened by demget 1
  • Add new Hanlder on run time ( after bot has been started)

    Add new Hanlder on run time ( after bot has been started)

    Hi,

    does anybody know how can I update ( add or remove, but mostly add) a handler after the bot has been started? I tried to stop then start the bot again but, but it doesn't help.

    opened by yuseferi 12
  • Middleware

    Middleware

    Telebot currently allows global middleware via MiddlewarePoller, which is a decent way to implement spam–control, rate–limiting and whatnot. Unfortunately, it lacks capability in the sense there's insufficient granularity to handlers.

    This issue explores the idea to have middleware on per-handler basis. There's also case to be made for groups of handlers, and therefore, group middleware.

    This is what we are doing at the moment:

    bot.Handle("/start", start)
    bot.Handle("/hello", hello)
    bot.Handle("/help", help)
    bot.Handle("/action_1", action)
    bot.Handle("/action_2", action)
    bot.Handle(tele.OnPhoto, onPhoto)
    bot.Handle(tele.OnDocument, onDocument)
    

    As you can see, in order to share behaviour, start, hello and others—must manually implement some shared logic.

    //bot.Use(globalMiddleware)
    
    b := bot.Group("")
    b.Use(rateLimiter(60)) // will apply to all handlers in the pack
    b.Handle("/start", start)
    b.Handle("/hello", hello)
    b.Handle("/help", hello)
    
    action := b.Group("/action_") // inherits parent's middleware
    action.Use(adminOnly)
    action.Handle("1", actionOne)
    action.Handle("2", actionTwo)
    
    bot.Handle(tele.OnPhoto, onPhoto) // no middleware
    bot.Handle(tele.OnDocument, maxFsize(5), onDocument) // on-handle basis
    

    You can see that in the code above the nested nature of the middleware. The only question remains: what's the signature of the middleware function and how does it behave? There are handlers for everything: messages, inline queries and inline button callbacks; therefore, there must be middleware for all.

    func middlewareFn func(*Update) error
    
    // Example middleware constructor:
    func rateLimiting(limit int) middlewareFunc {
    	return func(update *Update) error {
    		// return true if message is supposed to be passed further
    	}
    }
    

    Cheers, Ian

    feature 
    opened by tucnak 10
  • about use Markdown and album both

    about use Markdown and album both

    when I use markdown and album like this: var album tb.Album album = append(album, &tb.Photo{File: tb.FromURL(image1), Caption: "test"}) album = append(album, &tb.Photo{File: tb.FromURL(image2)}) bot.SendAlbum(&tb.Chat{ID: "123456"}, album, &tb.SendOptions{ParseMode: tb.ModeMarkdown}) I don't know if this is my fault or a bug. If it is my fault, please tell me the correct usage. Anyway, thanks for your time.

    question 
    opened by zitn 10
  • Handle command with image

    Handle command with image

    I send message /dem Response: "Hello world!" But when send message /dem with image i don't gets response

    package main
    
    import (
    	"log"
    	"os"
    	"time"
    
    	tb "gopkg.in/tucnak/telebot.v2"
    )
    
    func main() {
    	bot, err := tb.NewBot(tb.Settings{
    		Token:  os.Getenv("TGT"),
    		Poller: &tb.LongPoller{Timeout: 10 * time.Second},
    	})
    
    	if err != nil {
    		log.Fatal(err)
    		return
    	}
    
    	bot.Handle("/dem", func(message *tb.Message) {
    		bot.Send(message.Sender, "Hello World!")
    	})
    
    	bot.Start()
    }
    

    2020-12-31_11-46

    opened by rustzz 9
  • Channel messages is empty when there are full messages Telegram queue

    Channel messages is empty when there are full messages Telegram queue

    Hi Tucnak, I use your example to run my bot. when the traffic is risen up, I occasionally encounter no more messages are processed, even I restart the bot.

    messages := make(chan telebot.Message)
        bot.Listen(messages, 1*time.Second)
    
        for message := range messages {
            if message.Text == "/hi" {
                bot.SendMessage(message.Chat,
                    "Hello, "+message.Sender.FirstName+"!", nil)
            }
        }
    

    If I use curl command in terminal curl -X POST [api_url]/getUpdates , then I got 100 messages queuing. Is there any reason why messages channel is not able to pick up these messages.

    opened by aladine 9
  • ReplyButton's Action will not be called?

    ReplyButton's Action will not be called?

    the code below will not work

    replyBtn := tb.ReplyButton{
      Text: "🌕 Button #1",
      Action: func(*Callback) { // may not be called??
        fmt.Println("button clicked")
      },
    }
    
    b.Handle(&replyBtn, replyBtn.Action)
    

    It may work if the codes https://github.com/tucnak/telebot/blob/a67e593e355fd1e6cfcc864b48499bc0e9f1ce5c/options.go#L133

    is replaced with

    Action func(*Message) `json:"-"`
    

    so we may not register like this:

    b.Handle(&replyBtn, func(m *tb.Message) {
    		// on reply button pressed
    	})
    
    opened by guest6379 8
  • V2: Moving towards better API

    V2: Moving towards better API

    I set the goal to release v2 by the end of September (see updated 2.0 milestone). As it's going to be a pretty major release, I suggest re-thinking some of the API. Our biggest competitor, telegram-bot-api, 625 stars, is still superior to Telebot in terms of API, so I believe in order to stand out we must provide additional and moreover, sensible value. Telebot has always positioned itself as the framework, while our competitors were and will remain an API wrapper.

    Here are some of the issues I'd like to address (in the order of significance).

    Memory efficiency

    Message is the fattest type in Telebot, taking as much as 1544 bytes on my system (~1.5KB). Mainly because it stores lots of optional fields by-value. This is clearly borderline stupid and uncomfortable (you can't test optionals against nil) and I can't recall why I'd ever thought it was a good idea.

    I trivially managed (https://github.com/tucnak/telebot/commit/d8b3194888f53bba616c37301d67adb8035944bb) to reduce memory usage of Message down to 504 bytes (~0.5KB), making a 67.4% decrease in memory usage, which is pretty fucking good, considering Message is the most-used object. No more hassle around optionals also.

    Use of reflection for prettier API

    We currently use no reflection at all, which is probably a good thing. That said, I think introducing some light weight interface{}-kind of reflection for "optional" parameters may be a good thing.

    SendMessage() / Send()

    // Definition
    func (b *Bot) SendMessage(r Recipient, message string, options ...interface{}) error {}
    
    // Use
    b.SendMessage(user, "message") // <-- much better, isn't it?
    b.SendMessage(user, "message", &SendOptions{})
    

    In fact, we could go further and generalize it all the way down to Send():

    // Definition
    func (b *Bot) Send(r Recipient, things ...interface{}) error {}
    
    // Use
    b.Send(user, "message")
    b.Send(user, "message", &SendOptions{})
    b.Send(user, photo)
    // Etc..
    

    I wonder whether it'll damage Telebot's performance much, we can't really tell without proper benchmarking. That said, considering there's not much high load in Telegram world, we still must end up the fastest bloke on the block by a huge margin (comparing w/ e.g. Python implementations).

    Pros:

    • Much cleaner API and shorter function names
    • We'll finally get rid of huge piles of code duplicates in bot.go

    Cons:

    • Potential performance penalties (inferace{} casting)
    • We lose some compile-time safety
    • More documentation to compensate for optional arguments!

    Sendable

    In fact, we could avoid losing some of the safety and a fair chunk of casting and still introduce a better API by introducing a Sendable interface for all the sendable objects like Photo, Audio, Sticker, etc:

    type Sendable interface {
        Send(*Bot) error
    }
    

    Each and every corresponding sendable type would implement the necessary actions (which quite differ from type to type). This could also be used as a part of a prettier Send(Recipient, ..interface{}) too.

    Message builders

    I thought about it for some time now and I believe we could benefit from some sort of builder API for sending messages, like this:

    // Use
    bot.V()
        .To(recipient)
        .Text("This is a text message")
    //  .Photo(&photo) or any other sendable
        .Markdown()
        .Keyboard(keyboard)
        .Error(handlerFunc)
        .Send()
    

    It looks so good!

    Other...

    I also thought about implementing things like a state machine (telebot users would need to specify a list of states their bot users can be in and also, specify relations between these states) and a global error handler. Unfortunately, I couldn't come up with a reasonable API for "plots" (a state machine) yet though. I usually panic on bot errors and handle them up the stack anyway, so classic if err != nil error handling is rather redundant. I'd love to do things like:

    bot.ErrorHandler = func(err error) {
        panic(err)
    }
    
    bot.Handle("/start", func(c Context) {
        bot.Send(c.Message.Sender, "Hello") // calls ErrorHandler() -> panic()
    })
    

    /cc @zoni @Ronmi @ahmdrz

    UPD: I also feel like we are ovelooking inline queries and callbacks from the API design perspective... Bot start/listen/serve is confusing also!

    opened by tucnak 8
  • does bot.Start work?

    does bot.Start work?

    The inline example provided does not work although it does not crash. It just is silentt! Also, I cannot get bot.Start to work, as if it were in an infinite loop

    opened by mpirbay 8
  • High-level command handlers

    High-level command handlers

    Current implementation prototype is quite dumb. All it does is primitive routing:

    bot, err := telebot.NewBot(os.Getenv("TELEBOT_SECRET"))
    if err != nil {
        log.Println(err)
        return
    }
    
    bot.Handle(telebot.Default, func(_ telebot.Context) {
        // Default handler
    })
    
    bot.Handle("/fun", func(_ telebot.Context) {
        // Would get executed on /fun <whatever=0>
    })
    
    bot.Handle("/evil", func(_ telebot.Context) {
        // Would get executed on /evil <whatever=0>
    })
    
    bot.Serve()
    

    I still think that we should provide some handy API for "user interactipn scripts". E.g. abstract away from actual command handling and get along with "request-details-answer" sort of scripts.

    opened by tucnak 8
  • Upgrade up to Bot API 6.2

    Upgrade up to Bot API 6.2

    April 16, 2022

    Bot API 6.

    opened by udev-21 7
  • Can't get OnText handle to work

    Can't get OnText handle to work

    Hi, I'm trying to learn go lang by porting my telegram bot from C# to go, and I'm using Telebot because it seems quite easy to use.

    I could manage to get the "/hello" handle to work; after that I added the "OnText" handle because I want to check text messages for spam but, while the "/hello" handle works the OnText handle seems to never get fired. I added the bot to a group and I expect the OnText to get fired each time I write a text message.

    Here is my simple code:

    package main
    
    import (
    	"time"
    	"log"
    
    	tb "github.com/tucnak/telebot"
    )
    
    func main() {
    	log.Println("Starting the bot...")
    	b, err := tb.NewBot(tb.Settings{
    		Token:  "MY TOKEN",
    		Poller: &tb.LongPoller{Timeout: 10 * time.Second},
    	})
    
    	if err != nil {
    		log.Fatal(err)
    		return
    	}
    
    	// This doesn't get fired!!
    	b.Handle(tb.OnText, func(m *tb.Message) {
    		b.Send(m.Chat, "Got a text message")
    	})
    
    	// This works
    	b.Handle("/hello", func(m *tb.Message) {
    		options := new(tb.SendOptions)
    		options.ReplyTo = m
    		b.Send(m.Chat, "Hi "+m.Sender.FirstName+", I'm the Bot!", options)
    	})
    
    	b.Start()
    }
    
    opened by simonerom 7
  • [Question/Bug?] OnPoll doesn't receive any poll

    [Question/Bug?] OnPoll doesn't receive any poll

    When using telebot.OnPoll on a handler, I don't get any poll information when I send a poll to the bot, I have to stop the poll before any information is received. I want to make something similar to the official telegram quiz bot.

    opened by Clashkid155 2
  • Logging SetWebhook error

    Logging SetWebhook error

    If we fill webhook with some incorrect information and telegram return us an error, error handler will be called only in verbose mode. I think it must have more priority.

    opened by osinniy 0
  • Using bots in the test environment is not possible

    Using bots in the test environment is not possible

        JFYI, tried to use [bot test environment](https://core.telegram.org/bots/webapps#using-bots-in-the-test-environment), it does not work because of the URL construction. Telebot constructs URL as `url := b.URL + "/bot" + b.Token + "/" + method`, but for test server it should be `url := b.URL + "/bot" + b.Token + "/test/" + method` — note the **test** suffix.
    

    Originally posted by @prokher in https://github.com/tucnak/telebot/issues/508#issuecomment-1104446503

    opened by demget 1
Releases(v3.1.0)
  • v3.1.0(Oct 6, 2022)

    Changelog

    • Bot API 6.0, 6.1, 6.2
    • telebot/layout improvements
    • Changed panic-recovering behavior
    • Source restructure and refactoring
    • Other minor fixes and improvements

    Web apps support

    Use the new OnWebApp event along with the WebApp buttons field to interact with the new stunning web app functionality.

    webApp := &tele.WebApp{
      URL: "https://mywebapp.com",
    }
    
    b.Handle("/start", func(c tele.Context) error {
      markup := b.NewMarkup()
      markup.Inline(markup.WebApp("Open", webApp))
      return c.Send("Open this app!", markup)
    })
    
    b.Handle(tele.OnWebApp, func(c tele.Context) error {
      webapp := c.Message().WebAppData
    
      // Use the data fields:
      // webapp.Data
      // webapp.Text
    }
    

    New Recover middleware

    Be careful with allowing panics inside your handlers after the v3.0.1 release, since panics' recovering by default was removed. Instead, make sure you've added the Recover middleware in case you really need it:

    b.Use(middleware.Recover())
    b.Use(middleware.Logger())
    // ...
    

    telebot/layout improvements

    • Use new tele.M alias in your layout calls instead of map[string]any declaration:
    lt.Text(c, "start", tele.M{...})
    
    • The new CommandsLocale function for localized commands:
    # bot.yml
    commands:
      /help: '{{ text `cmd_help` }}'
    
    # en.yml
    cmd_help: Show the help message
    
    # fr.yml
    cmd_help: Afficher le message d'aide
    
    for _, locale := range lt.Locales() {
      b.SetCommands(locale, lt.CommandsLocale(locale))
    }
    
    • layout.Config is now using viper under the hood
    config:
      tiers:
        default: standard
        all:
        - name: standard
          limit: 5
          delay: 10m
        - name: premium
          limit: 50
          delay: 1h
    
    // Instead of an old sequential method,
    defaultTier := lt.Get("tiers").String("default")
    // you can use viper's dot notation:
    defaultTier := lt.String("tiers.default")
    
    // Some clumsy methods were removed,
    // so instead of lt.Each and lt.Index use:
    for _, tier := range lt.Slice("tiers.all") {
      dur := tier.Duration("delay")
    }
    
    // You can also unmarshal any key directly:
    var tiers []struct {
      Name string
      Limit int
      Delay time.Duration
    }
    
    lt.UnmarshalKey("tiers.all", &tiers)
    

    Backward incompatible changes

    Unfortunately, some of them had a place in the v3.1.0 release and we can't guarantee compatibility through all the minor versions after this. The main reason for it is the fact that Telegram itself allows the breaking changes to appear in their API, however in some cases you can't help it, and breaking becomes inevitable. We break the contract beforehand so that you're previously ready for the future breaking Bot API update.

    Some of the changes you should be aware of:

    • Bot.OnError field won't be exposed, use Settings.OnError instead once at initialization
    • Aforementioned panic-recovering behavior is optional
    • File.FileSize changed the int type to the int64
    • voice_chat_* fields renamed to video_chat_*
    • *ChatJoinRequest functions renamed to *JoinRequest

    Fixes

    • One minute is the default HTTP client timeout now (#478)
    • Handled video response from sendDocument (#481)
    • Message.Media() includes missed Sticker type (#520)
    • Cancel Raw() execution on bot stop (#532)
    • DeleteCommands function is fixed (#533)
    • Fixed the order in which layout template's Funcs are applied (#546)

    Discussion

    Discuss on Telegram

    Source code(tar.gz)
    Source code(zip)
  • v3.0.1(Oct 1, 2022)

    Changelog

    • Sticker is treated as a Media now
    • DeleteCommands is working properly now
    • is_premium field is added to the Chat
    • Stop() behavior improvements (#532)
    • New tele.M alias type for the map[string]any to be used with layout package
    • middleware.Recover() is added instead of the default panic-recovering behavior
    • Bot.OnError is read-only now, instead pass the error handler once through tele.Settings
    Source code(tar.gz)
    Source code(zip)
  • v3.0.0(Feb 8, 2022)

    We're happy to finally announce V3 of Telebot framework — the best of its kind API for interacting with Telegram bots. There were many changes and innovations based on our and the community's negative experience, which had a place while using V2. We've tried to include all common and most convenient routing, handling, and middleware patterns. Although this branch was widely tested and is production-ready, many planned but unimplemented things are left, so we're going to introduce even more allure features in future major and minor versions.

    Especial thanks to all our contributors who helped through these years in fixing bugs, improving, and maintaining API.

    Changelog

    • gopkg.in/tucnak.v3 is the new import path
    • tele is the new recommended alias
    • Bot API 5.3, 5.4, 5.5, 5.6, 5.7
    • Completed list of API errors
    • Better and clarified naming (#301)
    • Added missed events (cb12b170)
    • Got rid of github.com/pkg/errors package (#462)
    • Tons of other minor fixes and improvements

    Introducing Context

    Let's define a simple handler which responds to the /start command using v2:

    b.Handle("/start", func(m *tele.Message) {
        _, err := b.Send(m.Sender, "Hello!")
        if err != nil {
            log.Println(err)
            return
        }
    })
    

    There'll be lots of similar calls in any advanced stateful Telegram bot, not only with b bot instance but also with databases and other dependencies. The default handler function signature does not imply error handling, which is wrong. So, the first thing that suggests itself is a way to minimize errors check routine. We also need one strictly defined place where all errors come together with a corresponding event.

    Secondly, while developing a complex bot, you often need to perform the same logic for different endpoint types. As an example, to send a help message whether replying to the user's command or responding to the button pressing. In v2 it will be two separate functions: one taking *Message and another with *Callback.

    What if we combine all the possible handler types and encapsulate the different logic for different events? Here comes a Context — an interface type, which implementation wraps an incoming update and provides a number of useful short-hands and helper functions.

    b.Handle("/start", func(c tele.Context) error {
        return c.Send("Hello!")
    })
    

    Isn't it handy? It's a one-line v3 versus 4-5 lines v2. Now, we force a user to return the error, so most likely it will be recorded. For this purpose, we can override a special OnError callback:

    b.OnError = func(err error, c tele.Context) {
        if err != tele.ErrTrueResult {
            log.Println(c.Sender().ID, err)
        }
    }
    

    It also doesn't matter which type the event is — context can handle it and send the message in the current chat:

    b.Handle("/start", sendHello)
    b.Handle(&btnHello, sendHello)
    

    We can go even further and let's say, edit the message if it is editable; otherwise, send it:

    func sendHello(c tele.Context) error {
        return c.EditOrSend("Hello!")
    }
    

    The same applies to all the functions in the Context interface. Check them out here.

    Introducing Middleware

    You might notice that the context approach is somewhat similar to what we experience in most advanced HTTP server frameworks like echo or gin. What we're missing is an easier way to define and use middleware. The only method to do it was to wrap a poller with a custom MiddlewarePoller which performs necessary logic processing incoming updates. When we have piles and piles of extra middleware functionality it becomes clumsy.

    So how do we do it now? To register your own or some pre-defined middlewares from the telebot/middleware package, there's a Use function.

    import "gopkg.in/telebot.v3/middleware"
    
    // Global-scoped middleware:
    b.Use(middleware.Logger()) // logs every incoming handled event
    b.Use(middleware.AutoRespond()) // automatically responds to every callback
    
    // Group-scoped middleware:
    adminOnly := b.Group()
    adminOnly.Use(middleware.Whitelist(adminIDs...))
    adminOnly.Handle("/ban", onBan)
    adminOnly.Handle("/kick", onKick)
    
    // Handler-scoped middleware:
    b.Handle(tele.OnText, onText, middleware.IgnoreVia())
    

    It comes clear how to implement your own middleware by looking at built-in straightforward examples.

    New OnMedia endpoint

    All media types are now associated with a Media interface and all InputMedia types — with an Inputtable interface. Here perfectly fits the OnMedia event, which matches any kind of media that wasn't handled by a specific handler.

    b.Handle(tele.OnMedia, func(c tele.Context) error {
        media := c.Message().Media()
    
        // Now you can store media's kind and file ID:
        kind := media.MediaType()
        file := media.MediaFile()
    
        // ...
    })
    

    Several useful helpers

    A handy and my favorite Split helper splits a list of buttons into the rows of them by a given maximum capacity for each row:

    b.Handle("/things", func(c tele.Context) error {
        var btns []tele.Btn
        for _, thing := range things {
            btns = append(btns, /* prepare a button */)
        }
    
        markup := b.NewMarkup()
        markup.Inline(markup.Split(3, btns)...)
    
        return c.Send("Choose a thing:", markup)
    })
    

    An input_field_placeholder wrapper:

    b.Handle("/name", func(c tele.Context) error {
        return c.Send(
            "Input your new name:",
            tele.Placeholder("John Doe"),
        )
    })
    

    Message's text/caption entities parser via EntityText:

    b.Handle(tele.OnText, func(c tele.Context) error {
        m := c.Message()
    
        var mentions []string
        for _, ent := range m.Entities {
            if ent.Type == tele.EntityMention {
                mentions = append(mentions, m.EntityText(ent))
            }
        }
    
        return c.Send(fmt.Sprintf("Mentions: %s.", strings.Join(mentions, ", ")))
    })
    

    Postponed message deletion:

    b.Handle("/start", func(c tele.Context) error {
        c.DeleteAfter(10 * time.Second)
        return c.Send(...)
    })
    

    Introducing telebot/layout

    This part, I believe, deserves another comprehensive description. It provides a lot of use-cases and allows you to separate the code and the business logic from a bot's content. A godoc documentation is available here.

    Check out some examples

    People often complain about the lack of examples. We've prepared plenty of them: github.com/handybots. These are real and working bots opened for the good of the community.

    Discussion

    Discuss on Telegram

    Source code(tar.gz)
    Source code(zip)
  • v2.5.0(Dec 8, 2021)

    Changelog

    User identifiers can now be bigger than 2^31 - 1 and it is no longer possible to store them in a signed 32-bit integer type. User identifiers have up to 52 significant bits, so a 64-bit integer or double-precision float type is safe for storing them. Please make sure that your code can correctly handle such user identifiers.

    Announcement

    Telebot V2 is no longer maintained — this is the last major version. Currently, we're working on V3, which is much more advanced and has tons of helpful features and continuous support. As there're not that many breaking changes between two APIs, consider migrating to the latest v3 branch as soon as the beta will be released.

    Discussion

    Discuss on Telegram

    Source code(tar.gz)
    Source code(zip)
  • v2.4.0(Aug 13, 2021)

  • v2.3.0(Jun 13, 2020)

    Changelog

    • Bot API 4.9

    • Special ErrTrueResult error for updating methods

    • Extended b.Edit() function:

      b.Edit(m, m.Text, newMarkup)
      b.Edit(m, "new <b>text</b>", tb.ModeHTML)
      b.Edit(m, &tb.ReplyMarkup{...})
      b.Edit(m, &tb.Photo{File: ...})
      b.Edit(m, tb.Location{42.1337, 69.4242})
      
    • Default parse mode:

      pref := tb.Settings{
          Token:     "",
          ParseMode: tb.ModeMarkdown,
      }
      
      b, _ := tb.NewBot(pref)
      b.Send(to, "*text*")                   // default markdown mode
      b.Send(to, "<b>text</b>", tb.ModeHTML) // forced html mode
      
    • Verbose mode for outcoming requests:

      pref := tb.Settings{
          Token:   "",
          Verbose: true,
      }
      
      b, _ := tb.NewBot(pref)
      b.Notify(to, tb.Typing)
      
      [verbose] telebot: sent request
      Method: sendChatAction
      Params: {
          "action": "typing",
          "chat_id": "..."
      }
      Response: {
          "ok": true,
          "result": true
      }
      

    Fixes

    • Updating methods unmarshalling fix (#298)
    • SuccessfulPayment currency type fix (#299)
    • Stickers API minor fixes
    Source code(tar.gz)
    Source code(zip)
  • v2.2.0(Jun 13, 2020)

    For this release, aside from Bot API implementation, we've prepared many new useful features.

    Changelog

    • Completed Webhook, Games, Stickers and Payments APIs
    • Synchronous mode and serverless support (#219)
    • Keyboard constructors (#290)
    • Special ChatID recipient type (#263)

    Serverless

    Now it's possible to use telebot together with serverless providers like AWS, Azure, etc... Thanks to one of the package's contributors, you can check out the exhaustive example in examples/awslambdaechobot.

    pref := tb.Settings{
        Token:       "",
        Synchronous: true,
    }
    b, err := tb.NewBot(pref)
    // ...
    

    Keyboard constructors

    Use special helper functions and types to decrease boilerplate across the code. The new keyboards builder make their definition easier. Look into updated README Keyboards section for details.

    menu := &ReplyMarkup{ResizeReplyKeyboard: true}
    
    var (
        // Reply buttons.
        btnHelp     = menu.Text("ℹ Help")
        btnSettings = menu.Text("⚙ Settings")
    )
    
    menu.Reply(
        menu.Row(btnHelp),
        menu.Row(btnSettings),
    )
    
    b.Handle(&btnHelp, func(m *tb.Message) {...})
    b.Handle(&btnSettings, func(m *tb.Message) {...})
    

    ChatID type

    It was a bit confusing to work with specific chat IDs. You had to wrap integer into &tb.Chat{} every time you wanted to send a message. Imagine you have a config file with a bunch of defined group or channel IDs. Now you can use your int64 values directly with functions which accept Recipient interface.

    type Config struct {
        AdminGroup tb.ChatID `yaml:"admin_group_id"`
        AdsChannel tb.ChatID `yaml:"ads_channel_id"`
    }
    
    b.Send(conf.AdminGroup, "message")
    b.Send(conf.AdsChannel, "message")
    

    Fixes

    • Added OnAnimation endpoint
    • Fixed regular expression for errors handling
    • Action fields were removed from Buttons
    • b.Forward() changed to take Editable instead of *Message
    Source code(tar.gz)
    Source code(zip)
  • v2.1.0(Jun 13, 2020)

    Finally, a new Telebot v2.1 release with a lot of minor fixes, additions and new methods implementations.

    Changelog

    • Practically full implemented Bot API 4.8
    • Started code coverage process (now it's about 30%)
    • A convenient way to handle Telegram errors
    • A lot of bug fixes

    Errors

    From this release, you will be able to easily check for specific Telegram errors. We have a bunch of pre-defined Bot API errors and their descriptions in errors.go file. The work is still in progress, so feel free to contribute and to add missed entries. All errors are prefixed with telebot: if it's telebot-related or with telegram: if it's an API response.

    msg, err := b.Send(user, "Hello World!")
    if err != nil {
        if err == tb.ErrBlockedByUser {
            // remove user from database
        }
        // ...
    }
    

    Fixes

    • Use b.Edit() to invoke editMessageLiveLocation method (#239)
    • OnPinned also fires on channel posts (#244)
    • Exposed Webhook API (#214, #265)
    • LongPoller is now stoppable (#266)
    • Fixed a typo in DeleteGroupPhoto method
    • A little more nil checks to prevent unexpected panics
    Source code(tar.gz)
    Source code(zip)
  • ver2.0(Nov 29, 2017)

    "I never knew creating Telegram bots could be so sexy!"

    GoDoc

    go get -u gopkg.in/tucnak/telebot.v2
    

    Overview

    Telebot is a bot framework for Telegram Bot API. This package provides the best of its kind API for command routing, inline query requests and keyboards, as well as callbacks. Actually, I went a couple steps further, so instead of making a 1:1 API wrapper I chose to focus on the beauty of API and performance. Some of the strong sides of telebot are:

    • Real concise API
    • Command routing
    • Middleware
    • Transparent File API
    • Effortless bot callbacks

    All the methods of telebot API are extremely easy to memorize and get used to. Also, consider Telebot a highload-ready solution. I'll test and benchmark the most popular actions and if necessary, optimize against them without sacrificing API quality.

    Getting Started

    Let's take a look at the minimal telebot setup:

    package main
    
    import (
    	"time"
    
    	tb "gopkg.in/tucnak/telebot.v2"
    )
    
    func main() {
    	b, err := tb.NewBot(tb.Settings{
    		Token:  "TOKEN_HERE",
    		Poller: &tb.LongPoller{Timeout: 10 * time.Second},
    	})
    
    	if err != nil {
    		log.Fatal(err)
    		return
    	}
    
    	b.Handle("/hello", func(m *tb.Message) {
    		b.Send(m.Sender, "hello world")
    	})
    
    	b.Start()
    }
    
    

    Simple, innit? Telebot's routing system takes care of deliviering updates to their endpoints, so in order to get to handle any meaningful event, all you got to do is just plug your function to one of the Telebot-provided endpoints. You can find the full list here.

    b, _ := tb.NewBot(settings)
    
    b.Handle(tb.OnText, func(m *tb.Message) {
    	// all the text messages that weren't
    	// captured by existing handlers
    }
    
    b.Handle(tb.OnPhoto, func(m *tb.Message) {
    	// photos only
    }
    
    b.Handle(tb.OnChannelPost, func (m *tb.Message) {
    	// channel posts only
    })
    
    b.Handle(tb.Query, func (q *tb.Query) {
    	// incoming inline queries
    })
    

    Now there's a dozen of supported endpoints (see package consts). Let me know if you'd like to see some endpoint or endpoint idea implemented. This system is completely extensible, so I can introduce them without braking backwards-compatibity.

    Poller

    Telebot doesn't really care how you provide it with incoming updates, as long as you set it up with a Poller:

    // Poller is a provider of Updates.
    //
    // All pollers must implement Poll(), which accepts bot
    // pointer and subscription channel and start polling
    // synchronously straight away.
    type Poller interface {
    	// Poll is supposed to take the bot object
    	// subscription channel and start polling
    	// for Updates immediately.
    	//
    	// Poller must listen for stop constantly and close
    	// it as soon as it's done polling.
    	Poll(b *Bot, updates chan Update, stop chan struct{})
    }
    

    Telegram Bot API supports long polling and webhook integration. I don't really care about webhooks, so the only concrete Poller you'll find in the library is the LongPoller. Poller means you can plug telebot into whatever existing bot infrastructure (load balancers?) you need, if you need to. Another great thing about pollers is that you can chain them, making some sort of middleware:

    poller := &tb.LongPoller{Timeout: 15 * time.Second}
    spamProtected := tb.NewMiddlewarePoller(poller, func(upd *tb.Update) bool {
    	if upd.Message == nil {
    		return true
    	}
    
    	if strings.Contains(upd.Message.Text, "spam") {
    		return false
    	}
    
    	return true
    })
    
    bot, _ := tb.NewBot(tb.Settings{
    	// ...
    	Poller: spamProtected,
    })
    
    // graceful shutdown
    go func() {
    	<-time.After(N * time.Second)
    	bot.Stop()
    })()
    
    bot.Start() // blocks until shutdown
    
    fmt.Println(poller.LastUpdateID) // 134237
    

    Commands

    When handling commands, Telebot supports both direct (/command) and group-like syntax (/[email protected]) and will never deliver messages addressed to some other bot, even if privacy mode is off. For simplified deep-linking, telebot also extracts payload:

    // Command: /start <PAYLOAD>
    b.Handle("/start", func(m *tb.Message) {
    	if !m.Private() {
    		return
    	}
    
    	fmt.Println(m.Payload) // <PAYLOAD>
    })
    

    Files

    Telegram allows files up to 20 MB in size.

    Telebot allows to both upload (from disk / by URL) and download (from Telegram) and files in bot's scope. Also, sending any kind of media with a File created from disk will upload the file to Telegram automatically:

    a := &tb.Audio{File: tb.FromDisk("file.ogg")}
    
    fmt.Println(a.OnDisk()) // true
    fmt.Println(a.InCloud()) // false
    
    // Will upload the file from disk and send it to recipient
    bot.Send(recipient, a)
    
    // Next time you'll be sending this very *Audio, Telebot won't
    // re-upload the same file but rather utilize its Telegram FileID
    bot.Send(otherRecipient, a)
    
    fmt.Println(a.OnDisk()) // true
    fmt.Println(a.InCloud()) // true
    fmt.Println(a.FileID) // <telegram file id: ABC-DEF1234ghIkl-zyx57W2v1u123ew11>
    

    You might want to save certain Files in order to avoid re-uploading. Feel free to marshal them into whatever format, File only contain public fields, so no data will ever be lost.

    Sendable

    Send is undoubteldy the most important method in Telebot. Send() accepts a Recipient (could be user, group or a channel) and a Sendable. FYI, not only all telebot-provided media types (Photo, Audio, Video, etc.) are Sendable, but you can create composite types of your own. As long as they satisfy Sendable, Telebot will be able to send them out.

    // Sendable is any object that can send itself.
    //
    // This is pretty cool, since it lets bots implement
    // custom Sendables for complex kind of media or
    // chat objects spanning across multiple messages.
    type Sendable interface {
        Send(*Bot, Recipient, *SendOptions) (*Message, error)
    }
    

    The only type at the time that doesn't fit Send() is Album and there is a reason for that. Albums were added not so long ago, so they are slightly quirky for backwards compatibilities sake. In fact, an Album can be sent, but never received. Instead, Telegram returns a []Message, one for each media object in the album:

    p := &tb.Photo{File: tb.FromDisk("chicken.jpg")}
    v := &tb.Video{File: tb.FromURL("http://video.mp4")}
    
    msgs, err := b.SendAlbum(user, tb.Album{p, v})
    

    Send options

    Send options are objects and flags you can pass to Send(), Edit() and friends as optional arguments (following the recipient and the text/media). The most important one is called SendOptions, it lets you control all the properties of the message supported by Telegram. The only drawback is that it's rather inconvenient to use at times, so Send() supports multiple shorthands:

    // regular send options
    b.Send(user, "text", &tb.SendOptions{
    	// ...
    })
    
    // ReplyMarkup is a part of SendOptions,
    // but often it's the only option you need
    b.Send(user, "text", &tb.ReplyMarkup{
    	// ...
    })
    
    // flags: no notification && no web link preview
    b.Send(user, "text", tb.Silent, tb.NoPreview)
    

    Full list of supported option-flags you can find here.

    Editable

    If you want to edit some existing message, you don't really need to store the original *Message object. In fact, upon edit, Telegram only requires two IDs: ChatID and MessageID. And it doesn't really require the whole Message. Also you might want to store references to certain messages in the database, so for me it made sense for any Go struct to be editable as Telegram message, to implement Editable interface:

    // Editable is an interface for all objects that
    // provide "message signature", a pair of 32-bit
    // message ID and 64-bit chat ID, both required
    // for edit operations.
    //
    // Use case: DB model struct for messages to-be
    // edited with, say two collums: msg_id,chat_id
    // could easily implement MessageSig() making
    // instances of stored messages editable.
    type Editable interface {
    	// MessageSig is a "message signature".
    	//
    	// For inline messages, return chatID = 0.
    	MessageSig() (messageID int, chatID int64)
    }
    

    For example, Message type is Editable. Here is an implementation of StoredMessage type, provided by telebot:

    // StoredMessage is an example struct suitable for being
    // stored in the database as-is or being embedded into
    // a larger struct, which is often the case (you might
    // want to store some metadata alongside, or might not.)
    type StoredMessage struct {
    	MessageID int   `sql:"message_id" json:"message_id"`
    	ChatID    int64 `sql:"chat_id" json:"chat_id"`
    }
    
    func (x StoredMessage) MessageSig() (int, int64) {
    	return x.MessageID, x.ChatID
    }
    

    Why bother at all? Well, it allows you to do things like this:

    // just two integer columns in the database
    var msgs []tb.StoredMessage
    db.Find(&msgs) // gorm syntax
    
    for _, msg := range msgs {
    	bot.Edit(&msg, "Updated text.")
    	// or
    	bot.Delete(&msg)
    }
    

    I find it incredibly neat. Worth noting, at this point of time there exists another method in the Edit family, EditCaption() which is of pretty rare use, so I didn't bother including it to Edit(), just like I did with SendAlbum() as it would inevitably lead to unnecessary complications.

    var m *Message
    
    // change caption of a photo, audio, etc.
    bot.EditCaption(m, "new caption")
    

    Keyboards

    Telebot supports both kinds of keyboards Telegram provides: reply and inline keyboards. All buttons can act as endpoints for Handle(): Handle():

    func main() {
    	b, _ := tb.NewBot(tb.Settings{...})
    
    	// This button will be displayed in user's
    	// reply keyboard.
    	replyBtn := tb.ReplyButton{Text: "🌕 Button #1"}
    	replyKeys := [][]tb.ReplyButton{
    		[]tb.ReplyButton{replyBtn},
    		// ...
    	}
    
    	// And this one — just under the message itself.
    	// Pressing it will cause the client to send
    	// the bot a callback.
    	//
    	// Make sure Unique stays unique as it has to be
    	// for callback routing to work.
    	inlineBtn := tb.InlineButton{
    		Unique: "sad_moon",
    		Text: "🌚 Button #2",
    	}
    	inlineKeys := [][]tb.InlineButton{
    		[]tb.InlineButton{inlineBtn},
    		// ...
    	}
    
    	b.Handle(&replyBtn, func(m *tb.Message) {
    		// on reply button pressed
    	})
    
    	b.Handle(&inlineBtn, func(c *tb.Callback) {
    		// on inline button pressed (callback!)
    
    		// always respond!
    		c.Respond(&tb.CallbackResponse{...})
    	})
    
    	// Command: /start <PAYLOAD>
    	b.Handle("/start", func(m *tb.Message) {
    		if !m.Private() {
    			return
    		}
    
    		b.Send(m.Sender, "Hello!", &tb.ReplyMarkup{
    			ReplyKeyboard:  replyKeys,
    			InlineKeyboard: inlineKeys,
    		})
    	})
    
    	b.Start()
    }
    

    Inline mode

    So if you want to handle incoming inline queries you better plug the tb.OnQuery endpoint and then use the Answer() method to send a list of inline queries back. I think at the time of writing, telebot supports all of the provided result types (but not the cached ones). This is how it looks like:

    b.Handle(tb.OnQuery, func(q *tb.Query) {
    	urls := []string{
    		"http://photo.jpg",
    		"http://photo2.jpg",
    	}
    
    	results := make(tb.Results, len(urls)) // []tb.Result
    	for i, url := range urls {
    		result := &tb.PhotoResult{
    			URL: url,
    
    			// required for photos
    			ThumbURL: url,
    		}
    
    		results[i] = result
    	}
    
    	err := b.Answer(q, &tb.QueryResponse{
    		Results: results,
    		CacheTime: 60, // a minute
    	})
    
    	if err != nil {
    		fmt.Println(err)
    	}
    })
    

    There's not much to talk about really. It also support some form of authentication through deep-linking. For that, use fields SwitchPMText and SwitchPMParameter of QueryResponse.

    Contributing

    1. Fork it
    2. Clone it: git clone https://github.com/tucnak/telebot
    3. Create your feature branch: git checkout -b my-new-feature
    4. Make changes and add them: git add .
    5. Commit: git commit -m 'Add some feature'
    6. Push: git push origin my-new-feature
    7. Pull request

    Donate

    I do coding for fun but I also try to search for interesting solutions and optimize them as much as possible. If you feel like it's a good piece of software, I wouldn't mind a tip!

    Bitcoin: 1DkfrFvSRqgBnBuxv9BzAz83dqur5zrdTH

    License

    Telebot is distributed under MIT.

    Source code(tar.gz)
    Source code(zip)
Owner
Ian P Badtrousers
I'm a poet
Ian P Badtrousers
Dlercloud-telegram-bot - A Telegram bot for managing your Dler Cloud account

Dler Cloud Telegram Bot A Telegram bot for managing your Dler Cloud account. Usa

Beta Kuang 1 Dec 30, 2021
Pro-bot - A telegram bot to play around with the community telegram channels

pro-bot ?? Pro Bot A Telegram Bot to Play Around With The Community Telegram Cha

TechProber 1 Jan 24, 2022
A bot based on Telegram Bot API written in Golang allows users to download public Instagram photos, videos, and albums without receiving the user's credentials.

InstagramRobot InstagramRobot is a bot based on Telegram Bot API written in Golang that allows users to download public Instagram photos, videos, and

FTC Team 8 Dec 16, 2021
Bot - Telegram Music Bot in Go

Telegram Music Bot in Go An example bot using gotgcalls. Setup Install the serve

null 9 Jun 28, 2022
Bot-template - A simple bot template for creating a bot which includes a config, postgresql database

bot-template This is a simple bot template for creating a bot which includes a c

Disgo 2 Sep 9, 2022
Telegram Bot Framework for Go

Margelet Telegram Bot Framework for Go is based on telegram-bot-api It uses Redis to store it's states, configs and so on. Any low-level interactions

Gleb Sinyavskiy 75 Nov 16, 2022
Parr(B)ot is a Telegram bot framework based on top of Echotron

Parr(B)ot framework A just born Telegram bot framework in Go based on top of the echotron library. You can call it Parrot, Parr-Bot, Parrot Bot, is up

DazFather 9 Aug 22, 2022
The serverless OTP telegram service use telegram as OTP service, and send OTP through webhook

Setup OTP First thing, you need prepare API(webhook) with POST method, the payload format as below { "first_name": "Nolan", "last_name": "Nguyen",

Dong Nguyen 3 Jul 24, 2022
IRC, Slack, Telegram and RocketChat bot written in go

go-bot IRC, Slack & Telegram bot written in Go using go-ircevent for IRC connectivity, nlopes/slack for Slack and Syfaro/telegram-bot-api for Telegram

null 765 Nov 12, 2022
Golang telegram bot API wrapper, session-based router and middleware

go-tgbot Pure Golang telegram bot API wrapper generated from swagger definition, session-based routing and middlewares. Usage benefits No need to lear

Oleg Lebedev 118 Nov 16, 2022
Client lib for Telegram bot api

Micha Client lib for Telegram bot api. Supports Bot API v2.3.1 (of 4th Dec 2016). Simple echo bot example: package main import ( "log" "git

Andrey 23 Nov 10, 2022
Go library for Telegram Bot API

tbot - Telegram Bot Server Features Full Telegram Bot API 4.7 support Zero dependency Type-safe API client with functional options Capture messages by

Alexey Grachov 331 Nov 11, 2022
Golang bindings for the Telegram Bot API

Golang bindings for the Telegram Bot API All methods are fairly self explanatory, and reading the godoc page should explain everything. If something i

null 4.1k Nov 23, 2022
Golang Based Account Generator Telegram Bot

Account Generator Bot Account Generator Bot, written in GoLang via gotgbot library. Variables Env Vars - BOT_TOKEN - Get it from @BotFather CHANNEL_ID

Anonymous Indian 37 Nov 21, 2022
Bot that polls activity API for Github organisation and pushes updates to Telegram.

git-telegram-bot Telegram bot for notifying org events Requirements (for building) Go version 1.16.x Setup If you don't have a telegram bot token yet,

Skycoin 4 Apr 8, 2022
📱 Hentai bot for Telegram 📱

Random-Good-Hanime Hentai Telegram bot for educational purpose ?? Features ?? Random hentai Lot of different categories ?? Real time logs ?? Config fi

Patrick 7 Jun 20, 2022
A telegram bot that fetches multiple RSS cryptocurrency news feeds for sentiment analysis

Crypto News Telegram Bot A simple telegram bot that will help you stay updated on your latest crypto news This bot will help you keep track of the lat

Cha 4 Aug 22, 2021
A Telegram bot that will run code for you.

piston_bot A Telegram bot that will run code for you. Made using piston. Available as @iruncode_bot on telegram. Examples Basic example Input: /run py

Tushar Sadhwani 30 Sep 11, 2022
Telegram bot to get information on vaccine availabilities.

?? berlin-vaccine-alert Telegram bot to get information on vaccine availabilities. Get the latest appointments directly on telegram. The bot listen to

Erwan Leboucher 17 Jun 23, 2022