Skip to content

Commit

Permalink
Merge branch 'go-telegram:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
TBXark authored Sep 15, 2024
2 parents 46bc731 + 581c533 commit 2993046
Show file tree
Hide file tree
Showing 17 changed files with 179 additions and 33 deletions.
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
# Changelog

## v1.8.0 (2024-09-13)

- support API v7.10
- change type for field Type in models.Chat and models.ChatFullInfo from `string` to `models.ChatType`
- add consts for ChatType
- models.ChatTypePrivate
- models.ChatTypeGroup
- models.ChatTypeSupergroup
- models.ChatTypeChannel


## v1.7.3 (2024-09-10)

- Fix findHandler behavior

## v1.7.2 (2024-08-23)

- fix: pass error with `%w` instead `%v` after rawRequest

## v1.7.1 (2024-08-22)

- add option `UseTestEnvironment` for use test environment in API requests

## v1.7.0 (2024-08-14)

- support API v7.9
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

> [Telegram Group](https://t.me/gotelegrambotui)
> Supports Bot API version: [7.9](https://core.telegram.org/bots/api#august-14-2024) from August 14, 2024
> Supports Bot API version: [7.10](https://core.telegram.org/bots/api#september-6-2024) from September 6, 2024
It's a Go zero-dependencies telegram bot framework

Expand Down Expand Up @@ -183,6 +183,7 @@ b, err := bot.New("YOUR_BOT_TOKEN_FROM_BOTFATHER", opts...)
- `WithAllowedUpdates(params AllowedUpdates)` - set [allowed_updates](https://core.telegram.org/bots/api#getupdates) 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

Expand Down
1 change: 1 addition & 0 deletions bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type Bot struct {
pollTimeout time.Duration
skipGetMe bool
webhookSecretToken string
testEnvironment bool

defaultHandlerFunc HandlerFunc

Expand Down
2 changes: 1 addition & 1 deletion get_updates.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func (b *Bot) getUpdates(ctx context.Context, wg *sync.WaitGroup) {
if errors.Is(errRequest, context.Canceled) {
return
}
b.error("error get updates, %v", errRequest)
b.error("error get updates, %w", errRequest)
timeoutAfterError = incErrTimeout(timeoutAfterError)
continue
}
Expand Down
7 changes: 7 additions & 0 deletions handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,17 @@ func (h handler) match(update *models.Update) bool {
}

var data string

switch h.handlerType {
case HandlerTypeMessageText:
if update.Message == nil {
return false
}
data = update.Message.Text
case HandlerTypeCallbackQueryData:
if update.CallbackQuery == nil {
return false
}
data = update.CallbackQuery.Data
}

Expand Down
1 change: 1 addition & 0 deletions methods_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ type SendPaidMediaParams struct {
ChatID any `json:"chat_id"`
StarCount int `json:"star_count"`
Media []models.InputPaidMedia `json:"media"`
Payload string `json:"payload,omitempty"`
Caption string `json:"caption,omitempty"`
ParseMode models.ParseMode `json:"parse_mode,omitempty"`
CaptionEntities []models.MessageEntity `json:"caption_entities,omitempty"`
Expand Down
7 changes: 5 additions & 2 deletions models/boost.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ type ChatBoostSourceGiftCode struct {

// ChatBoostSourceGiveaway https://core.telegram.org/bots/api#chatboostsourcegiveaway
type ChatBoostSourceGiveaway struct {
Source ChatBoostSourceType `json:"source"` // always “giveaway”
User User `json:"user"`
Source ChatBoostSourceType `json:"source"` // always “giveaway”
GiveawayMessageID int `json:"giveaway_message_id"`
User User `json:"user"`
PrizeStarCount int `json:"prize_star_count,omitempty"`
IsUnclaimed bool `json:"is_unclaimed,omitempty"`
}
25 changes: 17 additions & 8 deletions models/chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,30 @@ type Birthdate struct {
Year int `json:"year,omitempty"`
}

type ChatType string

const (
ChatTypePrivate ChatType = "private"
ChatTypeGroup ChatType = "group"
ChatTypeSupergroup ChatType = "supergroup"
ChatTypeChannel ChatType = "channel"
)

// Chat https://core.telegram.org/bots/api#chat
type Chat struct {
ID int64 `json:"id"`
Type string `json:"type"`
Title string `json:"title,omitempty"`
Username string `json:"username,omitempty"`
FirstName string `json:"first_name,omitempty"`
LastName string `json:"last_name,omitempty"`
IsForum bool `json:"is_forum,omitempty"`
ID int64 `json:"id"`
Type ChatType `json:"type"`
Title string `json:"title,omitempty"`
Username string `json:"username,omitempty"`
FirstName string `json:"first_name,omitempty"`
LastName string `json:"last_name,omitempty"`
IsForum bool `json:"is_forum,omitempty"`
}

// ChatFullInfo https://core.telegram.org/bots/api#chatfullinfo
type ChatFullInfo struct {
ID int64 `json:"id"`
Type string `json:"type"`
Type ChatType `json:"type"`
Title string `json:"title,omitempty"`
Username string `json:"username,omitempty"`
FirstName string `json:"first_name,omitempty"`
Expand Down
7 changes: 6 additions & 1 deletion models/giveaway.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ type Giveaway struct {
HasPublicWinners bool `json:"has_public_winners,omitempty"`
PrizeDescription string `json:"prize_description,omitempty"`
CountryCodes []string `json:"country_codes,omitempty"`
PrizeStarCount int `json:"prize_star_count,omitempty"`
PremiumSubscriptionMonthCount int `json:"premium_subscription_month_count,omitempty"`
}

// GiveawayCreated https://core.telegram.org/bots/api#giveawaycreated
type GiveawayCreated struct{}
type GiveawayCreated struct {
PrizeStarCount int `json:"prize_star_count,omitempty"`
}

// GiveawayWinners https://core.telegram.org/bots/api#giveawaywinners
type GiveawayWinners struct {
Expand All @@ -25,6 +28,7 @@ type GiveawayWinners struct {
AdditionalChatCount int `json:"additional_chat_count,omitempty"`
PremiumSubscriptionMonthCount int `json:"premium_subscription_month_count,omitempty"`
UnclaimedPrizeCount int `json:"unclaimed_prize_count,omitempty"`
PrizeStarCount int `json:"prize_star_count,omitempty"`
OnlyNewMembers bool `json:"only_new_members,omitempty"`
WasRefunded bool `json:"was_refunded,omitempty"`
PrizeDescription string `json:"prize_description,omitempty"`
Expand All @@ -35,4 +39,5 @@ type GiveawayCompleted struct {
WinnerCount int `json:"winner_count"`
UnclaimedPrizeCount int `json:"unclaimed_prize_count,omitempty"`
GiveawayMessage *Message `json:"giveaway_message,omitempty"`
IsStarGiveaway bool `json:"is_star_giveaway,omitempty"`
}
6 changes: 6 additions & 0 deletions models/paid.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,9 @@ type PaidMediaInfo struct {
StarCount int `json:"star_count"`
PaidMedia []PaidMedia `json:"paid_media"`
}

// PaidMediaPurchased https://core.telegram.org/bots/api#paidmediapurchased
type PaidMediaPurchased struct {
From User `json:"from"`
PaidMediaPayload string `json:"paid_media_payload"`
}
9 changes: 5 additions & 4 deletions models/star.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,11 @@ type TransactionPartnerFragment struct {

// TransactionPartnerUser https://core.telegram.org/bots/api#transactionpartneruser
type TransactionPartnerUser struct {
Type TransactionPartnerType `json:"type"`
User User `json:"user"`
InvoicePayload string `json:"invoice_payload,omitempty"`
PaidMedia []*PaidMedia `json:"paid_media,omitempty"`
Type TransactionPartnerType `json:"type"`
User User `json:"user"`
InvoicePayload string `json:"invoice_payload,omitempty"`
PaidMedia []*PaidMedia `json:"paid_media,omitempty"`
PaidMediaPayload string `json:"paid_media_payload,omitempty"`
}

// TransactionPartnerOther https://core.telegram.org/bots/api#transactionpartnerother
Expand Down
1 change: 1 addition & 0 deletions models/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type Update struct {
CallbackQuery *CallbackQuery `json:"callback_query,omitempty"`
ShippingQuery *ShippingQuery `json:"shipping_query,omitempty"`
PreCheckoutQuery *PreCheckoutQuery `json:"pre_checkout_query,omitempty"`
PurchasedPaidMedia *PaidMediaPurchased `json:"purchased_paid_media,omitempty"`
Poll *Poll `json:"poll,omitempty"`
PollAnswer *PollAnswer `json:"poll_answer,omitempty"`
MyChatMember *ChatMemberUpdated `json:"my_chat_member,omitempty"`
Expand Down
7 changes: 7 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,10 @@ func WithWebhookSecretToken(webhookSecretToken string) Option {
b.webhookSecretToken = webhookSecretToken
}
}

// UseTestEnvironment allows to use test environment
func UseTestEnvironment() Option {
return func(b *Bot) {
b.testEnvironment = true
}
}
17 changes: 4 additions & 13 deletions process_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,16 @@ func (b *Bot) ProcessUpdate(ctx context.Context, upd *models.Update) {
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
}
h = b.findHandler(upd)
}

func (b *Bot) findHandler(handlerType HandlerType, upd *models.Update) HandlerFunc {
func (b *Bot) findHandler(upd *models.Update) HandlerFunc {
b.handlersMx.RLock()
defer b.handlersMx.RUnlock()

for _, h := range b.handlers {
if h.handlerType == handlerType {
if h.match(upd) {
return h.handler
}
if h.match(upd) {
return h.handler
}
}

Expand Down
33 changes: 31 additions & 2 deletions process_update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,35 @@ func TestProcessUpdate_WithMiddlewares(t *testing.T) {
}
}

func TestProcessUpdate_WithMatchTypeFunc(t *testing.T) {
var called string
h1 := func(ctx context.Context, bot *Bot, update *models.Update) {
called = "h1"
}
h2 := func(ctx context.Context, bot *Bot, update *models.Update) {
called = "h2"
}
m := func(update *models.Update) bool {
return update.CallbackQuery.GameShortName == "game"
}

bot := &Bot{
defaultHandlerFunc: h1,
handlersMx: &sync.RWMutex{},
handlers: map[string]handler{},
}

bot.RegisterHandlerMatchFunc(m, h2)

ctx := context.Background()
upd := &models.Update{ID: 42, CallbackQuery: &models.CallbackQuery{ID: "test", GameShortName: "game"}}

bot.ProcessUpdate(ctx, upd)
if called != "h2" {
t.Fatalf("Expected h2 handler to be called but %s handler was called", called)
}
}

func Test_findHandler(t *testing.T) {
var called bool
h := func(ctx context.Context, bot *Bot, update *models.Update) {
Expand All @@ -102,7 +131,7 @@ func Test_findHandler(t *testing.T) {
ctx := context.Background()
upd := &models.Update{Message: &models.Message{Text: "test"}}

handler := bot.findHandler(HandlerTypeMessageText, upd)
handler := bot.findHandler(upd)
handler(ctx, bot, upd)

if !called {
Expand All @@ -125,7 +154,7 @@ func Test_findHandler_Default(t *testing.T) {
ctx := context.Background()
upd := &models.Update{Message: &models.Message{Text: "test"}}

handler := bot.findHandler(HandlerTypeCallbackQueryData, upd)
handler := bot.findHandler(upd)
handler(ctx, bot, upd)

if !called {
Expand Down
6 changes: 5 additions & 1 deletion raw_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ func (b *Bot) rawRequest(ctx context.Context, method string, params any, dest an
}
}

u := b.url + "/bot" + b.token + "/" + method
u := b.url + "/bot" + b.token + "/"
if b.testEnvironment {
u += "test/"
}
u += method

if b.isDebug && strings.ToLower(method) != "getupdates" {
requestDebugData, _ := json.Marshal(params)
Expand Down
57 changes: 57 additions & 0 deletions raw_request_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package bot

import (
"context"
"io"
"net/http"
"strings"
"testing"
)

type clientMock struct {
requestURI string
}

func (c *clientMock) Do(req *http.Request) (*http.Response, error) {
c.requestURI = req.URL.RequestURI()
resp := http.Response{
StatusCode: 200,
Body: io.NopCloser(strings.NewReader(`{"ok":true}`)),
}
return &resp, nil
}

func Test_rawRequest_url(t *testing.T) {
cm := &clientMock{}
b := &Bot{
token: "XXX",
client: cm,
}

err := b.rawRequest(context.Background(), "foo", nil, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

if cm.requestURI != "/botXXX/foo" {
t.Fatalf("unexpected requestURI: %s", cm.requestURI)
}
}

func Test_rawRequest_url_testEnv(t *testing.T) {
cm := &clientMock{}
b := &Bot{
token: "XXX",
client: cm,
testEnvironment: true,
}

err := b.rawRequest(context.Background(), "foo", nil, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

if cm.requestURI != "/botXXX/test/foo" {
t.Fatalf("unexpected requestURI: %s", cm.requestURI)
}
}

0 comments on commit 2993046

Please sign in to comment.