Git Product home page Git Product logo

bot's Introduction

Golang Telegram Bot

Go Report Card codecov

✅ Present in the list of libraries https://core.telegram.org/bots/samples#go

Telegram Group

Supports Bot API version: 7.9 from August 14, 2024

It's a Go zero-dependencies telegram bot framework

A simple example echo-bot:

package main

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

	"github.com/go-telegram/bot"
	"github.com/go-telegram/bot/models"
)

// Send any text message to the bot after the bot has been started

func main() {
	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
	defer cancel()

	opts := []bot.Option{
		bot.WithDefaultHandler(handler),
	}

	b, err := bot.New("YOUR_BOT_TOKEN_FROM_BOTFATHER", opts...)
	if err != nil {
		panic(err)
	}

	b.Start(ctx)
}

func handler(ctx context.Context, b *bot.Bot, update *models.Update) {
	b.SendMessage(ctx, &bot.SendMessageParams{
		ChatID: update.Message.Chat.ID,
		Text:   update.Message.Text,
	})
}

You can find more examples in the examples folder.

To run the examples, set the EXAMPLE_TELEGRAM_BOT_TOKEN environment variable to your bot token.

Getting started

Go version: 1.18

Install the dependencies:

go get -u github.com/go-telegram/bot

Initialize and run the bot:

b, err := bot.New("YOUR_BOT_TOKEN_FROM_BOTFATHER")

b.Start(context.TODO())

On create bot will call the getMe method (with 5 sec timeout). And returns error on fail. If you want to change this timeout, use option bot.WithCheckInitTimeout

You can define a default handler for the bot:

b, err := bot.New("YOUR_BOT_TOKEN_FROM_BOTFATHER", bot.WithDefaultHandler(handler))

func handler(ctx context.Context, b *bot.Bot, update *models.Update) {
	// this handler will be called for all updates
}

Webhooks

If you want to use webhooks, instead of using bot.Start, you should use the bot.StartWebhook method to start the bot. Also, you should use bot.WebhookHandler() method as HTTP handler for your server.

func main() {
	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
	defer cancel()

	opts := []bot.Option{
		bot.WithDefaultHandler(handler),
		bot.WithWebhookSecretToken(os.Getenv("EXAMPLE_TELEGRAM_WEBHOOK_SECRET_TOKEN"))
	}

	b, _ := bot.New(os.Getenv("EXAMPLE_TELEGRAM_BOT_TOKEN"), opts...)

	// call methods.SetWebhook if needed
	
	go b.StartWebhook(ctx)

	http.ListenAndServe(":2000", b.WebhookHandler())

	// call methods.DeleteWebhook if needed
}

func handler(ctx context.Context, b *bot.Bot, update *models.Update) {
	b.SendMessage(ctx, &bot.SendMessageParams{
		ChatID: update.Message.Chat.ID,
		Text:   update.Message.Text,
	})
}

Demo in examples

Also, you can manually process updates with bot.ProcessUpdate method.

update := models.Update{}

json.NewDecoder(req.Body).Decode(&update)

b.ProcessUpdate(ctx, &update)

Middlewares

You can use middlewares with WithMiddlewares(middlewares ...Middleware) option.

See an example in examples

Available methods

All available methods are listed in the Telegram Bot API documentation

You can use all these methods as bot funcs. All methods have name like in official documentation, but with capital first letter.

bot.SendMessage, bot.GetMe, bot.SendPhoto, etc

All methods have signature (ctx context.Context, params <PARAMS>) (<response>, error). Except GetMe, Close and Logout which are have not params

<PARAMS> is a struct with fields that corresponds to Telegram Bot API parameters. All Params structs have name like for corresponded methods, but with Params suffix.

SendMessageParams for SendMessage method etc.

You should pass params by pointer

bot.SendMessage(ctx, &bot.SendMessageParams{...})

Options

You can use options to customize the bot.

b, err := bot.New("YOUR_BOT_TOKEN_FROM_BOTFATHER", opts...)

Options list (see options.go for more details)

  • WithCheckInitTimeout(timeout time.Duration) - timeout for check init bot
  • WithMiddlewares(middlewares ...Middleware) - add middlewares
  • WithMessageTextHandler(pattern string, matchType MatchType, handler HandlerFunc) - add handler for Message.Text field
  • WithCallbackQueryDataHandler(pattern string, matchType MatchType, handler HandlerFunc) - add handler for CallbackQuery.Data field
  • WithDefaultHandler(handler HandlerFunc) - add default handler
  • WithDebug() - enable debug mode
  • WithErrorsHandler(handler ErrorsHandler) - add errors handler
  • WithDebugHandler(handler DebugHandler) - add debug handler
  • WithHTTPClient(pollTimeout time.Duration, client HttpClient) - set custom http client
  • WithServerURL(serverURL string) - set server url
  • WithSkipGetMe() - skip call GetMe on bot init
  • WithAllowedUpdates(params AllowedUpdates) - set allowed_updates for getUpdates method
  • WithUpdatesChannelCap(cap int) - set updates channel capacity, by default 1024
  • WithWebhookSecretToken(webhookSecretToken string) - set X-Telegram-Bot-Api-Secret-Token header sent from telegram servers to confirm validity of update
  • UseTestEnvironment() - use test environment

Message.Text and CallbackQuery.Data handlers

For your convenience, you can use Message.Text and CallbackQuery.Data handlers.

An example:

b, err := bot.New("YOUR_BOT_TOKEN_FROM_BOTFATHER")

b.RegisterHandler(bot.HandlerTypeMessageText, "/start", bot.MatchTypeExact, myStartHandler)

b.Start(context.TODO())

also you can use bot init options WithMessageTextHandler and WithCallbackQueryDataHandler

In this example, the handler will be called when the user sends /start message. All other messages will be handled by the default handler.

Handler Types:

  • HandlerTypeMessageText - for Update.Message.Text field
  • HandlerTypeCallbackQueryData - for Update.CallbackQuery.Data field

RegisterHandler returns a handler ID string. You can use it to remove the handler later.

b.UnregisterHandler(handlerID)

Match Types:

  • MatchTypeExact
  • MatchTypePrefix
  • MatchTypeContains

You can use RegisterHandlerRegexp to match by regular expression.

re := regexp.MustCompile(`^/start`)

b.RegisterHandlerRegexp(bot.HandlerTypeMessageText, re, myStartHandler)

If you want to use custom handler, use RegisterHandlerMatchFunc

matchFunc := func(update *models.Update) bool {
	// your checks
	return true
}

b.RegisterHandlerMatchFunc(bot.HandlerTypeMessageText, matchFunc, myHandler)

InputFile

For some methods, like SendPhoto, SendAudio etc, you can send file by file path or file contents.

To send a file by URL or FileID, you can use &models.InputFileString{Data: string}:

// file id of uploaded image 
inputFileData := "AgACAgIAAxkDAAIBOWJimnCJHQJiJ4P3aasQCPNyo6mlAALDuzEbcD0YSxzjB-vmkZ6BAQADAgADbQADJAQ"
// or URL image path
// inputFileData := "https://example.com/image.png"

params := &bot.SendPhotoParams{
    ChatID:  chatID,
    Photo:   &models.InputFileString{Data: inputFileData},
}

bot.SendPhoto(ctx, params)

Demo in examples

To send an image file by its contents, you can use &models.InputFileUpload{Filename: string, Data: io.Reader}:

fileContent, _ := os.ReadFile("/path/to/image.png")

params := &bot.SendPhotoParams{
    ChatID:  chatID,
    Photo:   &models.InputFileUpload{Filename: "image.png", Data: bytes.NewReader(fileContent)},
}

bot.SendPhoto(ctx, params)

Demo in examples

InputMedia

For methods like SendMediaGroup or EditMessageMedia you can send media by file path or file contents.

Official documentation InputMedia

field media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name.

If you want to use attach:// format, you should to define MediaAttachment field with file content reader.

fileContent, _ := os.ReadFile("/path/to/image.png")

media1 := &models.InputMediaPhoto{
	Media: "https://telegram.org/img/t_logo.png",
}

media2 := &models.InputMediaPhoto{
	Media: "attach://image.png", 
	Caption: "2",
	MediaAttachment: bytes.NewReader(fileContent),
}

params := &bot.SendMediaGroupParams{
    ChatID: update.Message.Chat.ID,
    Media: []models.InputMedia{
        media1,
        media2,
    },
}

bot.SendMediaGroup(ctx, params)

Demo in examples

Helpers

EscapeMarkdown(s string) string

Escape special symbols for Telegram MarkdownV2 syntax

EscapeMarkdownUnescaped(s string) string

Escape only unescaped special symbols for Telegram MarkdownV2 syntax

RandomString(n int) string

Returns fast random a-zA-Z string with n length

True() bool, False() bool

Allows you to define *bool values for params, which require *bool, like SendPollParams

p := &bot.SendPollParams{
    ChatID: chatID,
    Question: "Question",
    Options: []string{"Option 1", "Option 2"},
    IsAnonymous: bot.False(),
}

b.SendPoll(ctx, p)

ValidateWebappRequest(values url.Values, token string) (user *models.User, ok bool)

Validate request from Telegram Webapp

https://core.telegram.org/bots/webapps#validating-data-received-via-the-mini-app

// get url values from request
values := req.URL.Query()

user, ok := bot.ValidateWebappRequest(values, os.Getenv("TELEGRAM_BOT_TOKEN"))
if !ok {
    http.Error(w, "Unauthorized", http.StatusUnauthorized)
    return
}

FileDownloadLink(f *models.File) string

Return file download link after call method GetFile

See documentation

Errors

This library includes error handling. It provides the following error types:

  • ErrorForbidden (403): This error occurs when the bot has no access to the action, such as when the user has blocked the bot.
  • ErrorBadRequest (400): This error indicates a bad request made to the bot's API.
  • ErrorUnauthorized (401): This error occurs when the bot's access is unauthorized for the requested action.
  • TooManyRequestsError: (429) This error indicates that the bot has received too many requests within a short period. It includes a RetryAfter value indicating when to retry the request.
  • ErrorNotFound (404): This error indicates that the requested resource was not found.
  • ErrorConflict (409): This error indicates a conflict occurred during the request.

Usage:

_, err := b.SendMessage(...)

if errors.Is(err, mybot.ErrorForbidden) {
    // Handle the ErrorForbidden (403) case here
}

if errors.Is(err, mybot.ErrorBadRequest) {
    // Handle the ErrorBadRequest (400) case here
}

if errors.Is(err, mybot.ErrorUnauthorized) {
    // Handle the ErrorUnauthorized (401) case here
}

if mybot.IsTooManyRequestsError(err) {
    // Handle the TooManyRequestsError (429) case here
    fmt.Println("Received TooManyRequestsError with retry_after:", err.(*mybot.TooManyRequestsError).RetryAfter)
}

if errors.Is(err, mybot.ErrorNotFound) {
    // Handle the ErrorNotFound (404) case here
}

if errors.Is(err, mybot.ErrorConflict) {
    // Handle the ErrorConflict (409) case here
}

UI Components

In the repo https://github.com/go-telegram/ui you can find a some UI elements for your bot.

  • datepicker
  • inline_keyboard
  • slider
  • paginator

and more...

Please, check the repo for more information and live demo.

bot's People

Contributors

abdoo9 avatar ad avatar dharsanb avatar dir01 avatar dmitriinov avatar drifteri avatar iamelevich avatar icoder-new avatar negasus avatar renatosaksanni avatar sattellite avatar sevkin avatar srgustafson8 avatar swift1911 avatar tonykoh avatar vasilesk avatar vvok12 avatar xeptore avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bot's Issues

The setChatMenuButton doesn't work as specified in the API.

Code

_, err = b.SetChatMenuButton(ctx, &bot.SetChatMenuButtonParams{
	ChatID: nil,
	MenuButton: &models.MenuButtonCommands{
		Type: "commands",
	},
})

API request

{"chat_id":null,"menu_button":{"type":"commands"}}

API response

{"ok":false,"error_code":400,"description":"Bad Request: invalid chat_id specified"}

API description

Link. In the API description - Unique identifier for the target private chat. If not specified, default bot's menu button will be changed

Getting a URL for the file object

The File struct contains FilePath field.

To access the file, we need to construct URL using the bot token and file path:

https://api.telegram.org/file/bot<token>/<file_path>

Would storing directly in the File struct or somewhere else make sense?

Get access to the getupdates chan

Hello everyone! How i can get access to the getupdates chan.
I want to handle sequence of actions by bot user, after certain action by user. Can you give me advise about it

Cannot set SendPollParams.IsAnonymous to false

You are unable to send a new poll that is non-anonymous. This seems to be because the IsAnonymous field on the SendPollParams type is a non-pointer bool, set to omitempty and defaults to true on the Telegram API. I believe this is why - false boolean is treated as empty and therefore omitted.

To reproduce

func main() {
	b, _ := bot.New(telegram_token)

	poll := &bot.SendPollParams{
		ChatID: chat_id,
		Question: "Test poll",
		Options: []string{"1", "2", "3"},
		AllowsMultipleAnswers: true,
		IsAnonymous: false,
	}

	msg, _ := b.SendPoll(context.TODO(), poll)
	log.Printf("Poll is anonymous?: %t", msg.Poll.IsAnonymous)
}
[stdout]
2023/04/08 20:10:20 Poll is anonymous?: true

I'll open a PR soon to set the type to be a *bool which I believe will fix it in this case.

send<Media>Content via TelegramId

How can I send Photo / Voice / Document / Audio / Video / Animation / VideoNote via TelegramId of this media?

Telegram bot api documentation says, that field can be InputFile or String..

bot.rawRequest() doesn't accessible :(

update.EditedMessage is not work in b.RegisterHandlerMatchFunc

package main

import (
"context"
"fmt"
"github.com/go-telegram/bot"
"github.com/go-telegram/bot/models"
"log"
"os"
"os/signal"
)

// Send any text message to the bot after the bot has been started

func main() {
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()

opts := []bot.Option{
	bot.WithDefaultHandler(defaultHandler),
}

b, err := bot.New("6xxxx", opts...)
if nil != err {
	// panics for the sake of simplicity.
	// you should handle this error properly in your code.
	panic(err)
}

b.RegisterHandlerMatchFunc(matchFunc, helloHandler)
b.RegisterHandlerMatchFunc(matchFunc2, helloHandler2)

b.Start(ctx)

}

func matchFunc(update *models.Update) bool {
if update.Message == nil {
return false
}
//time.Sleep(5 * time.Second)
fmt.Println("✅✅✅hello matchFunc1")
return update.Message.Text == "hello"
}

func matchFunc2(update *models.Update) bool {
fmt.Println("❇️❇️❇️❇️:matchFunc2", update.EditedMessage)
if update.EditedMessage == nil {
return false
}
//time.Sleep(5 * time.Second)
fmt.Println("❇️❇❇️❇hello matchFunc2")
return true
}

func helloHandler(ctx context.Context, b *bot.Bot, update *models.Update) {
//time.Sleep(1 * time.Second)
log.Println("🔥hello handler:" + update.Message.Text)
}
func helloHandler2(ctx context.Context, b *bot.Bot, update *models.Update) {
//time.Sleep(1 * time.Second)
log.Println("🔥🔥hello update.EditedMessage:", update.EditedMessage)
}
func defaultHandler(ctx context.Context, b *bot.Bot, update *models.Update) {
log.Println("🌹 default handler", update.EditedMessage)
}

/////////// it dont work

When I use this to edit a message that has already been sent, the helloHandler2 never triggers. Moving the code to defaultHandler can correctly print out the message, proving that it works. But why doesn't helloHandler2 work?

Multiline text

Hello,
I want to ask about long text/byte array submissions. How to correctly encode it, so it can be sent via your library.
I intend to create a bot that invokes some specific commands defined in the YAML config file. The exec output is a byte array and I'm unable to predict its content (which means not only whitespace, and unprintable signs but some fancy/exotic UTF signs can occur).

Can you help me?

Looks like RegisterHandlerMatchFunc does not work properly

Hi, I have the following function to handle unsubscriptions:

func registerOnMyChatMember(api *bot.Bot, h bot.HandlerFunc) {
	api.RegisterHandlerMatchFunc(func(update *models.Update) bool {
		return update.MyChatMember != nil
	}, h)
}

However, it doesn't work with the active default handler, because this check isn't even called.

	opts := []bot.Option{
		bot.WithDefaultHandler(func(ctx context.Context, api *bot.Bot, update *models.Update) {
			defaultHandler(ctx, update, workers, loc)
		}),
		bot.WithMiddlewares(IgnoreChats, IgnoreBots, AutoRespond),
	}

	api, err := bot.New(token, opts...)

I'm not sure why. Maybe it's because RegisterHandlerMatchFunc does not set the handlerType field. Maybe it's because findHandler is called by bot.ProcessUpdate only for non-empty Message and CallbackQuery, not for MyChatMember. Or maybe I'm doing something wrong.

To reproduce this, create a bot instance with a default handler, register any function with RegisterHandlerMatchFunc, and try to handle "Delete and block".

LinkPreviewOptions problem

In the previous version, I used DisableWebPagePreview: true when creating SendMessageParam. and it worked great.
Now, with the new version this is replaced with LinkPreviewOptions and when I add &models.LinkPreviewOptions{IsDisabled: &linkPreviewDisabled} I got message {"ok":false,"error_code":400,"description":"Bad Request: field \"url\" must be of type String"}.
So I added random text like this LinkPreviewOptions: &models.LinkPreviewOptions{URL: &testURL, IsDisabled: &linkPreviewDisabled}, and it worked.

The problem is why I need to specify some URLs and set them here when I want to disable URL previews. Is there an option to add a check if IsDisabled=true then no URL is required?

Store GetMe Result as Bot struct variable

Hello! Can I request to store GetMe result after every GetMe call to Bot struct Variable?

I think this is a good idea so once GetMe is called, we can get the result like bot.Id or bot.Username rather than creating another variable to store GetMe result.

bot.ForwardMessages must retrun array of messageIDs instead of []model.Messages

core.telegram.org

forwardMessages

Use this method to forward multiple messages of any kind. If some of the specified messages can't be found or forwarded, they are skipped. Service messages and messages with protected content can't be forwarded. Album grouping is kept for forwarded messages. On success, an array of MessageId of the sent messages is returned.

methods.go

// ForwardMessages https://core.telegram.org/bots/api#forwardmessages
func (b *Bot) ForwardMessages(ctx context.Context, params *ForwardMessagesParams) ([]models.Message, error) {
	var result []models.Message
	err := b.rawRequest(ctx, "forwardMessages", params, result)
	return result, err
}

Example for Succes payment

Hi, I am a bit new to golang programming, but I have been able to make a small bot work thanks to this library, but there are some things that I have not been able to implement yet, I wonder if you could make an example of a Succes Payment? with the telegram api, using this library? please?

greetings

Don't know which bot is calling the error handler

I have multiple bots running on go routine. I want handle this error:
Starting Bot Failed: error get updates, unexpected response statusCode 409 for method getUpdates, {"ok":false,"error_code":409,"description":"Conflict: terminated by other getUpdates request; make sure that only one bot instance is running"}

which cause by when you run multiple bot instance. I want it to close the instance when that error happen. But it's seems like the error handler function doesn't pass in any value that I can konw which bot is causing the error so I can stop the instance (Because I'm runing multiple bots with go routine).

I'm thinking maybe you should pass in the bot instance into the error handler function.

sorry for my bad english

Add ChatAction constants for Easier Access

Hi! I think it's good idea to add ChatAction constants like models.ParseModeHTML.

https://core.telegram.org/bots/api#sendchataction

const (
	ChatActionTyping          = "typing"
	ChatActionUploadPhoto     = "upload_photo"
	ChatActionRecordVideo     = "record_video"
	ChatActionUploadVideo     = "upload_video"
	ChatActionRecordVoice     = "record_voice"
	ChatActionUploadVoice     = "upload_voice"
	ChatActionUploadDocument  = "upload_document"
	ChatActionChooseSticker   = "choose_sticker"
	ChatActionFindLocation    = "find_location"
	ChatActionRecordVideoNote = "record_video_note"
	ChatActionUploadVideoNote = "upload_video_note"
)

Error: cannot unmarshal object into Go struct field Message.channel_post.chat_background_set of type models.BackgroundType

When a bot checks for updates, it may run into the error. Then it won't be able to proceed with handling the updates and will stuck.

The error message:
[TGBOT] [ERROR] error get updates, error decode response result for method getUpdates, json: cannot unmarshal object into Go struct field Message.channel_post.chat_background_set of type models.BackgroundType

Looks like the library incorrectly works with updates containing background setting in channels.

Photo from Url

How to return photo from url. Actually, if send file it takes long time if i had bad internet connection.

models.InputFileUpload without pointer compiles but does not work

This code compiles and works fine:

b.SendDocument(ctx, &bot.SendDocumentParams{
	ChatID:   123,
	Document: &models.InputFileUpload{Filename: "my.txt", Data: myReader},
})

The same code but without & before models.InputFileUpload fails with the next error:

unexpected response statusCode 400 for method sendDocument, {"ok":false,"error_code":400,"description":"Bad Request: invalid file HTTP URL specified: Wrong port number specified in the URL"

Expected behaviour: this should also work (or just fail with an obvious error message).

Send foto, send document

Greetings, I noticed here that the example does not work: send_photo. Also I can't send the document.

Cannot Unmarshal Number of Updates Struct

Hi, so I got this error when I updated the lib to the latest.

json: cannot unmarshal number into Go struct field Message.Updates.message.forward_origin of type string

How to reproduce:

  • Use Webhook to receives Updates
  • Put Updates on In-Memory Cache by using JSON Marshall
  • Get cache and unmarshall

Channel posts in webhook ProcessUpdate

Currently channel post are not properly processed in webhook.

func (b *Bot) ProcessUpdate(ctx context.Context, upd *models.Update) {
	h := b.defaultHandlerFunc

	defer func() {
		applyMiddlewares(h, b.middlewares...)(ctx, b, upd)
	}()

	if upd.Message != nil {
		h = b.findHandler(HandlerTypeMessageText, upd)
		return
	}
	if upd.CallbackQuery != nil {
		h = b.findHandler(HandlerTypeCallbackQueryData, upd)
		return
	}
}

This need to be updated as well as this:

        // handlers.go
	switch h.handlerType {
	case HandlerTypeMessageText:
		data = update.Message.Text
	case HandlerTypeCallbackQueryData:
		data = update.CallbackQuery.Data
	}

Filename property of InputFileUpload is redundant

Hello! I think the Filename property on InputFileUpload struct is redundant, it would be good if the lib could detect the filename by from passed parameter path. Like doing split of "/" then get the value of last index, continue by verify it was a correct file by checking the extension.

panic: runtime error: invalid memory address or nil pointer dereference

I'm facing a nil pointer dereference. That it is getting me crazy.

I created an empty project, copy and past echo example, and run it. When I send a message from Telegram. The bot die whith the error below.

go version
go version go1.22.1 linux/amd64

uname -a          
on  main [!×1 ?×2 ]  v1.22.1 v1.76.0
Linux pop-os 6.6.10-76060610-generic #202401051437~1709764300~22.04~379e7a9 SMP PREEMPT_DYNAMIC Thu M x86_64 x86_64 x86_64 GNU/Linux

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x30 pc=0x6735e0]

goroutine 53 [running]:
main.handler({0x77b370, 0xc0000dc1c0}, 0xc0000000c0, 0xc0003480a0)
/home/borba/wks/go/BomBot/main.go:33 +0x40
github.com/go-telegram/bot.(*Bot).ProcessUpdate.func1()
/home/borba/go/pkg/mod/github.com/go-telegram/[email protected]/process_update.go:25 +0x6d
github.com/go-telegram/bot.(*Bot).ProcessUpdate(0xc00002a5a0?, {0x77b370?, 0xc0000dc1c0?}, 0xc00004f758?)
/home/borba/go/pkg/mod/github.com/go-telegram/[email protected]/process_update.go:36 +0xc3
github.com/go-telegram/bot.(*Bot).waitUpdates.func1({0x77b370, 0xc0000dc1c0}, 0xc0000dc1c0?, 0xc0001c6000)
/home/borba/go/pkg/mod/github.com/go-telegram/[email protected]/wait_updates.go:29 +0x93
created by github.com/go-telegram/bot.(*Bot).waitUpdates in goroutine 20
/home/borba/go/pkg/mod/github.com/go-telegram/[email protected]/wait_updates.go:26 +0x228
exit status 2
make: *** [Makefile:20: run] Error 1

Forward Message

Hello How to forward message to Admin?, i'm building telegram bot liveChat

SendMediaGroup does not work when SendMediaGroupParams is an array of mixed InputMediaDocument consisting of both images and videos

I am migrating my Telegram bot written in telegraf.js to Go. This is my old working code using telegraf.js:

const payload = response.data.items[0].carousel_media.map((item) => {
  return {
    type: "document",
    media: {
      url:
        item.media_type === 1
          ? item.image_versions2.candidates[0].url
          : item.video_versions[0].url,
      filename: `${item.id}.${item.media_type === 1 ? "jpg" : "mp4"}`,
    },
  };
});

await ctx.replyWithMediaGroup(payload);

Case 1 [WORKING]:

	image := &models.InputMediaDocument{
		Media: <IMAGE_URL>,
	}
	params := &bot.SendMediaGroupParams{
		ChatID: update.Message.Chat.ID,
		Media: []models.InputMedia{
			image,
		},
	}

Case 2 [WORKING]:

	image1 := &models.InputMediaDocument{
		Media: <IMAGE_URL>,
	}
	image2 := &models.InputMediaDocument{
		Media: <IMAGE_URL>,
	}
	params := &bot.SendMediaGroupParams{
		ChatID: update.Message.Chat.ID,
		Media: []models.InputMedia{
			image1,
			image2,
		},
	}

Case 3 [NOT EXPECTED]:

	video := &models.InputMediaDocument{
		Media: <VIDEO_URL>,
	}
	params := &bot.SendMediaGroupParams{
		ChatID: update.Message.Chat.ID,
		Media: []models.InputMedia{
			video,
		},
	}

In this case, it works but not the way I want, the bot responds as a video, not a document, while I want the bot to send the document, so I can download the original file.

Case 4 [NOT WORKING]:

	image := &models.InputMediaDocument{
		Media: <IMAGE_URL>,
	}
	video := &models.InputMediaDocument{
		Media: <VIDEO_URL>,
	}
	params := &bot.SendMediaGroupParams{
		ChatID: update.Message.Chat.ID,
		Media: []models.InputMedia{
			image,
                        video,
		},
	}

In this case, I received the error Bad Request: failed to send message #1 with the error message "MEDIA_INVALID"

Release names

Hi, probably it make sense to use version as release name? Because for now it's a little bit confusing to see on Changelog as release name and you need to go to the release and check tag to identify actual release version.

CleanShot 2023-03-19 at 17 47 33@2x

Changes from Bot API 7.0

Telegram seemed to not have forgotten about Christmas and gave us all the gift of changes.

I wanted to ask whether anybody has already decided on taking it upon themselves to start bringing those changes into this library or is that currently up in the air?

If that's still up in the air, I am willing to offer my help in adding support for 7.0 since there are some features that I need which were broken with the major version change. As such, I would love to hear what else was broken with the update so I could start putting together a list which would prioritize that which used to work and now doesn't, and just working my way down the whole changelog.

SetMessageReaction not working

Hello, I'm testing your library, great work.

One thing that I'm having trouble with is SetMessageReaction, I get errors when using it.

I'm able to make it work by defining a custom MarshalJSON like so:

func (rt *ReactionType) MarshalJSON() ([]byte, error) {
	if rt.Type == ReactionTypeTypeEmoji {
		return json.Marshal(rt.ReactionTypeEmoji)
	}
	if rt.Type == ReactionTypeTypeCustomEmoji {
		return json.Marshal(rt.ReactionTypeCustomEmoji)
	}
	return nil, fmt.Errorf("unsupported ReactionType type")
}

This creates the reaction but returns one error:

error decode response result for method setMessageReaction, json: Unmarshal(non-pointer bool)

I'm not sure why.

(feat): Add method to serve static HTML/JS files for integrate Telegram Mini Apps

First of all, thanks for supporting Telegram API at Go! 🔥
This is really helpful package to create TG bots.

IMHO, it would be great to add some methods to serve static HTML/JS files in one Go binary. For able to integrate Telegram Mini Apps, or simple embedded webviews.

For example, like this:

// ...

func main() {
	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
	defer cancel()

	opts := []bot.Option{
		bot.WithDefaultHandler(handler),
	}

	b, err := bot.New("YOUR_BOT_TOKEN_FROM_BOTFATHER", opts...)
	if err != nil {
		panic(err)
	}
	
	// New method FileServer() with defining ./static folder.
	fs := b.FileServer(http.Dir("./static"))

	// New method ServeStatic() with defining URL path.
	b.ServeStatic("/static/", http.StripPrefix("/static/", fs))

	b.Start(ctx)
}

// ...

Now, we can create buttons with links to our web pages.

Also, we can easily embed this static files to binary, like this:

// ...

//go:embed static
var staticFolder embed.FS

func main() {
	// ...

	sub, err := fs.Sub(staticFolder, "static")
	if err != nil {
		panic(err)
	}
	
	// New method FileServer() with defining ./static folder.
	fs := b.FileServer(http.FS(sub))

	// New method ServeStatic() with defining URL path.
	b.ServeStatic("/", http.StripPrefix("/", fs))

	b.Start(ctx)
}

// ...

possible webhook data race in current tests

Hi, i was checking out the tests and currently there is a data race in the webhook functionality of the bot, if it's needed i can check out the tests and propose a fix for the data race.

Steps to reproduce:

  • launch test command with race flag: go test ./... --race -v

Output log for failing test:

=== RUN   TestBot_StartWebhook
==================
WARNING: DATA RACE
Read at 0x00c00021e1af by goroutine 24:
  github.com/go-telegram/bot.TestBot_StartWebhook()
      ./bot_test.go:203 +0x688
  testing.tRunner()
      ./src/testing/testing.go:1689 +0x21e
  testing.(*T).Run.gowrap1()
      ./src/testing/testing.go:1742 +0x44

Previous write at 0x00c00021e1af by goroutine 33:
  github.com/go-telegram/bot.TestBot_StartWebhook.func1()
      ./bot_test.go:181 +0x8e
  github.com/go-telegram/bot.(*Bot).ProcessUpdate.func1()
      ./process_update.go:25 +0xaf
  runtime.deferreturn()
      ./src/runtime/panic.go:602 +0x5d
  github.com/go-telegram/bot.(*Bot).waitUpdates()
      ./wait_updates.go:17 +0xd2
  github.com/go-telegram/bot.(*Bot).StartWebhook.gowrap1()
      ./bot.go:106 +0x5d

Goroutine 24 (running) created at:
  testing.(*T).Run()
      ./src/testing/testing.go:1742 +0x825
  testing.runTests.func1()
      ./src/testing/testing.go:2161 +0x85
  testing.tRunner()
      ./src/testing/testing.go:1689 +0x21e
  testing.runTests()
      ./src/testing/testing.go:2159 +0x8be
  testing.(*M).Run()
      ./src/testing/testing.go:2027 +0xf17
  main.main()
      _testmain.go:79 +0x2bd

Goroutine 33 (finished) created at:
  github.com/go-telegram/bot.(*Bot).StartWebhook()
      ./bot.go:106 +0x150
  github.com/go-telegram/bot.TestBot_StartWebhook.gowrap2()
      ./bot_test.go:187 +0x4f
==================
    testing.go:1398: race detected during execution of test
--- FAIL: TestBot_StartWebhook (0.22s)
=== RUN   TestBot_Start
==================
WARNING: DATA RACE
Read at 0x00c00030817f by goroutine 36:
  github.com/go-telegram/bot.TestBot_Start()
      ./bot_test.go:238 +0x708
  testing.tRunner()
      ./src/testing/testing.go:1689 +0x21e
  testing.(*T).Run.gowrap1()
      ./src/testing/testing.go:1742 +0x44

Previous write at 0x00c00030817f by goroutine 45:
  github.com/go-telegram/bot.TestBot_Start.func1()
      ./bot_test.go:225 +0x10e
  github.com/go-telegram/bot.(*Bot).ProcessUpdate.func1()
      ./process_update.go:25 +0xaf
  runtime.deferreturn()
      ./src/runtime/panic.go:602 +0x5d
  github.com/go-telegram/bot.(*Bot).waitUpdates()
      ./wait_updates.go:17 +0xd2
  github.com/go-telegram/bot.(*Bot).Start.gowrap1()
      ./bot.go:116 +0x5d

Goroutine 36 (running) created at:
  testing.(*T).Run()
      ./src/testing/testing.go:1742 +0x825
  testing.runTests.func1()
      ./src/testing/testing.go:2161 +0x85
  testing.tRunner()
      ./src/testing/testing.go:1689 +0x21e
  testing.runTests()
      ./src/testing/testing.go:2159 +0x8be
  testing.(*M).Run()
      ./src/testing/testing.go:2027 +0xf17
  main.main()
      _testmain.go:79 +0x2bd

Goroutine 45 (finished) created at:
  github.com/go-telegram/bot.(*Bot).Start()
      ./bot.go:116 +0x150
  github.com/go-telegram/bot.TestBot_Start.gowrap2()
      ./bot_test.go:230 +0x4f
==================
    testing.go:1398: race detected during execution of test
--- FAIL: TestBot_Start (0.20s)

Example bot crashes with segmentation fault when added to a group

I tried the example bot from the documentation that just echoes back whatever you write (echo/main.go).

Then I added the bot to a group with myself and tried to talk to it in the group. This is the error I get:

[signal SIGSEGV: segmentation violation code=0x2 addr=0x48 pc=0x102af14f0]

goroutine 34 [running]:
main.handler({0x102bffdf8, 0x1400002a200}, 0x1400014c000, 0x14000310000)
	/Users/marito/Work/cacabot/main.go:34 +0x40
github.com/go-telegram/bot.(*Bot).ProcessUpdate.func1()
	/Users/marito/go/pkg/mod/github.com/go-telegram/[email protected]/process_update.go:25 +0x6c
github.com/go-telegram/bot.(*Bot).ProcessUpdate(0x1400004e770?, {0x102bffdf8?, 0x1400002a200?}, 0x0?)
	/Users/marito/go/pkg/mod/github.com/go-telegram/[email protected]/process_update.go:36 +0xbc
github.com/go-telegram/bot.(*Bot).waitUpdates(0x1400014c000, {0x102bffdf8, 0x1400002a200}, 0x0?)
	/Users/marito/go/pkg/mod/github.com/go-telegram/[email protected]/wait_updates.go:17 +0x6c
created by github.com/go-telegram/bot.(*Bot).Start in goroutine 1
	/Users/marito/go/pkg/mod/github.com/go-telegram/[email protected]/bot.go:116 +0xa4

Note that I get the same error whenever I run the bot since then. Probably because it's still trying to download the message that causes it to crash.

[proposal] Adding linters to the pipelines

Hi everyone,

Just wanted to say that this library has proven to be really useful. Considering its popularity, I think adding linters to the Go pipelines would be beneficial. It would help maintain code quality and catch potential issues early.

If you agree, I could create a pull request with the linters and necessary fixes. We can also discuss the specific rules to include.

Default handler processes updates out of order

At the end of the issue there is a very basic echo bot with debug enabled

When the bot is off, type in the chat messages:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Then launch the bot. The log (simplified):

[TGBOT] [DEBUG] request url: https://api.telegram.org/botFOO:BAR/getMe, payload: null
[TGBOT] [DEBUG] response from 'https://api.telegram.org/botFOO:BAR/getMe' with payload '{"ok":true,"result":{"id":botbotbot,"is_bot":true,...,"can_join_groups":true,"can_read_all_group_messages":false,"supports_inline_queries":false}}'
[TGBOT] [DEBUG] response from 'https://api.telegram.org/botFOO:BAR/getUpdates' with payload '{"ok":true,"result":[
{"update_id":180259658,"message":{"message_id":95,"from":{"...},"chat":{...},"date":1709399995,"text":"1"}},
{"update_id":180259659,"message":{"message_id":96,"from":{"...},"chat":{...},"date":1709399995,"text":"2"}},
{"update_id":180259660,"message":{"message_id":97,"from":{"...},"chat":{...},"date":1709399996,"text":"3"}},
{"update_id":180259661,"message":{"message_id":98,"from":{"...},"chat":{...},"date":1709399996,"text":"4"}},
{"update_id":180259662,"message":{"message_id":99,"from":{"...},"chat":{...},"date":1709399999,"text":"5"}},
{"update_id":180259663,"message":{"message_id":100,"from":{"...},"chat":{...},"date":1709400000,"text":"6"}},
{"update_id":180259664,"message":{"message_id":101,"from":{"...},"chat":{...},"date":1709400000,"text":"7"}},
{"update_id":180259665,"message":{"message_id":102,"from":{"...},"chat":{...},"date":1709400001,"text":"8"}}
]}'
INFO received message: text=8
INFO received message: text=4
INFO received message: text=5
INFO received message: text=7
INFO received message: text=1
INFO received message: text=6
INFO received message: text=2
INFO received message: text=3

TG server returns updates in order: messages in the array result are in the ascending order of update_id.
But the handler receives the messages in a random order.

Are there options to ensure the order of the updates in the handler?

The program:

package main

import (
	"context"
	"log/slog"
	"os"
	"os/signal"

	"github.com/go-telegram/bot"
	"github.com/go-telegram/bot/models"
)

func main() {
	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
	defer cancel()

	opts := []bot.Option{
		bot.WithDefaultHandler(handler),
		bot.WithDebug(),
	}

	token, ok := os.LookupEnv("TG_BOT")
	if !ok {
		panic("Set TG_BOT environment variable")
	}
	b, err := bot.New(token, opts...)
	if nil != err {
		panic(err)
	}

	b.Start(ctx)
}

func handler(ctx context.Context, b *bot.Bot, update *models.Update) {
	if update == nil {
		slog.Info("nil update")
		return
	}
	if update.Message == nil {
		slog.Info("nil message")
	}
	slog.Info("received message:",
		"text", update.Message.Text,
	)
	b.SendMessage(ctx, &bot.SendMessageParams{
		ChatID: update.Message.Chat.ID,
		Text:   update.Message.Text,
	})
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.