Bot releases are hidden (Show)
Context
helperstelebot/react
package with reaction bindingstelebot/layout
Unfortunately, due to breaking the Stickers API update on the Telegram side, we had to break the backward compatibility with previous versions of Telebot as well. However, this allowed us to rework the API and make it feel better and clearer to use.
// 1. UploadSticker now accepts the StickerSetFormat and File
b.UploadSticker(to, tele.StickerStatic, tele.FromDisk("telebot.png"))
// 2. CreateStickerSet now works with multiple stickers
b.CreateStickerSet(to, &tele.StickerInput{
// ...
Input: []tele.InputSticker{
{File: tele.FromDisk("telebot1.png")},
{File: tele.FromDisk("telebot2.png")},
},
})
// 3. AddSticker is now called AddStickerToSet and accepts name and the InputSticker
b.AddStickerToSet(to, tele.InputSticker{
File: tele.FromDisk("telebot.png"),
})
// 1. Get a list of user boosts for a particular chat
boosts, err := b.UserBoosts(chat, user)
if err != nil {
return err
}
// 2. Use boost fields
for _, boost := range boosts {
boost.ID
boost.AddDate()
boost.ExpirationDate()
...
}
// 3. Handle boost-related events
b.Handle(tele.OnBoost, func(c tele.Context) error {
boost := c.Boost()
boost.Chat
boost.Boost
...
})
import (
tele "gopkg.in/telebot.v3"
"gopkg.in/telebot.v3/react"
)
// 1. Use a telebot/react package for the React shorthand
b.React(to, msg, react.React(react.ThumbUp, react.Fire))
// 2. Use ReactionOptions to add special options
b.React(to, msg, tele.ReactionOptions{
Reactions: []tele.Reaction{react.Heart},
Big: true,
})
b.ForwardMany(to, msgs)
b.CopyMany(to, msgs)
b.DeleteMany(to, msgs)
Check out the whole diff: #658
Trigger
bot function to trigger the specific handler:b.Handle("/start", func (c tele.Context) error {
return c.Send("Hello, World!")
})
b.Handle("/help", func (c tele.Context) error {
return b.Trigger("/start", c)
})
SetCaption
function to add a caption to the first media of the album:var album tele.Album
// Populate album dynamically...
album.SetCaption("Hey, hey, hey!")
msgs, err := b.SendAlbum(user, album)
if err != nil {
return err
}
Context
helpers to remove CallbackResponse
from your code:b.Handle(callback, func(c tele.Context) error {
return c.RespondText("This is a pop-up!")
// or ...
return c.RespondAlert("This is an alert!")
})
telebot/layout
from the embedded FS:import (
"embed"
tele "gopkg.in/telebot.v3"
"gopkg.in/telebot.v3/layout"
)
//go:embed layout
var fs embed.FS
func main() {
lt, err := layout.NewFromFS(fs, "bot.yml")
if err != nil {
panic(err)
}
b, err := tele.NewBot(lt.Settings())
if err != nil {
panic(err)
}
// Done!
}
ErrIs
function to check if the given description matches the error (https://github.com/tucnak/telebot/commit/71ac2995cc5d7379c35fceb3b64ce2c693e79e0c)stop
channel race (#641)Context.Args
parsing (#651)*Chat
arguments changed to Recipient
(#568)middleware.RecoverFunc
now accepts the Context
(https://github.com/tucnak/telebot/commit/a26ba9f0bd194c4e22718db7c17a7717809364c7)Published by demget 11 months ago
// 1. Create a forum topic with specified settings
topic, err := b.CreateTopic(chat, &tele.Topic{Name: "Discussions"})
if err != nil {
return err
}
// 2. Use topic methods
b.CloseTopic(chat, topic)
b.ReopenTopic(chat, topic)
b.DeleteTopic(chat, topic)
b.HideGeneralTopic(chat)
b.UnhideGeneralTopic(chat)
...
// 3. Handle topics-related events
b.Handle(tele.OnTopicCreated, func(c tele.Context) error {
topic := c.Topic()
topic.Name
topic.ThreadID
})
// 1. Use a ReplyRecipient constructor for both
// `request_user` and `request_chat` params
var (
replyUser = &tele.ReplyRecipient{
ID: 1, // unique
Bot: tele.Flag(false),
Premium: tele.Flag(true),
}
replyChat = &tele.ReplyRecipient{
ID: 1, // unique
Channel: false, // required
Username: tele.Flag(true),
}
)
markup := &tele.ReplyMarkup{}
markup.Reply(
markup.Row(markup.User(replyUser)),
markup.Row(markup.Chat(replyChat)),
)
b.Send(to, "Share with me...", markup)
// 2. Handle the shared user or chat
b.Handle(tele.OnUserShared, func(c tele.Context) error {
user := c.Message().UserShared
log.Println("Shared user ID:", user.UserID)
})
Use a Flag
helper that returns a pointer to the given bool:
// Flag returns a pointer to the given bool.
// Useful for passing the three-state flags to a Bot API.
// For example, see ReplyRecipient type.
func Flag(b bool) *bool {
return &b
}
Bot.SetWebhook
error is loggable by default (#559)telebot/layout
fixes (https://github.com/tucnak/telebot/commit/7065335e1dcb16d9c57b0d534157e755f32e0137, https://github.com/tucnak/telebot/commit/4c17ca7dc78b110f12bbe0892d7855c59ad17f0a)is_member
field to the ChatMember
(#615)custom_emoji_id
field to the Sticker
(#579)Results.MarshalJSON
addressing inside for-loop (#589)Published by demget about 2 years ago
telebot/layout
improvementsUse 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
}
Recover
middlewareBe 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
improvementstele.M
alias in your layout calls instead of map[string]any
declaration:lt.Text(c, "start", tele.M{...})
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 hoodconfig:
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)
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 initializationFile.FileSize
changed the int
type to the int64
voice_chat_*
fields renamed to video_chat_*
*ChatJoinRequest
functions renamed to *JoinRequest
sendDocument
(#481)Message.Media()
includes missed Sticker
type (#520)Raw()
execution on bot stop (#532)DeleteCommands
function is fixed (#533)Funcs
are applied (#546)Published by demget about 2 years ago
Sticker
is treated as a Media
nowDeleteCommands
is working properly nowis_premium
field is added to the Chat
Stop()
behavior improvements (#532)tele.M
alias type for the map[string]any
to be used with layout packagemiddleware.Recover()
is added instead of the default panic-recovering behaviorBot.OnError
is read-only now, instead pass the error handler once through tele.Settings
Published by demget over 2 years ago
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.
gopkg.in/tucnak.v3
is the new import pathtele
is the new recommended aliasgithub.com/pkg/errors
package (#462)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.
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.
OnMedia
endpointAll 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()
// ...
})
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(...)
})
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.
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.
Published by demget almost 3 years ago
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.
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.
Published by demget about 3 years ago
SendAlbum
improvements (#398)Published by demget over 4 years ago
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
}
SuccessfulPayment
currency type fix (#299)Published by demget over 4 years ago
For this release, aside from Bot API implementation, we've prepared many new useful features.
ChatID
recipient type (#263)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)
// ...
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
typeIt 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")
OnAnimation
endpointAction
fields were removed from Buttonsb.Forward()
changed to take Editable
instead of *Message
Published by demget over 4 years ago
Finally, a new Telebot v2.1
release with a lot of minor fixes, additions and new methods implementations.
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
}
// ...
}
b.Edit()
to invoke editMessageLiveLocation
method (#239)OnPinned
also fires on channel posts (#244)LongPoller
is now stoppable (#266)DeleteGroupPhoto
methodnil
checks to prevent unexpected panicsPublished by tucnak almost 7 years ago
"I never knew creating Telegram bots could be so sexy!"
go get -u gopkg.in/tucnak/telebot.v2
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:
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.
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.
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
When handling commands, Telebot supports both direct (/command
) and group-like syntax (/command@botname
) 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>
})
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 File
s 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.
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 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.
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")
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()
}
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
.
git clone https://github.com/tucnak/telebot
git checkout -b my-new-feature
git add .
git commit -m 'Add some feature'
git push origin my-new-feature
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
Telebot is distributed under MIT.