Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions business_updates.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ func (b *Bot) dispatchBusinessMessage(ctx context.Context, e tg.Entities, connec
}

u := &Update{}

u.Entities = e

if edited {
u.EditedBusinessMessage = m
} else {
Expand Down
12 changes: 7 additions & 5 deletions coverage_dispatch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,17 @@ func TestDispatchMessageRouting(t *testing.T) {

regular := &tg.Message{ID: 1, Message: "x", PeerID: &tg.PeerUser{UserID: 10}}
regular.SetFromID(&tg.PeerUser{UserID: 10})
b.dispatchMessage(ctx, regular, false)
b.dispatchMessage(ctx, regular, true)

e := tg.Entities{}
b.dispatchMessage(ctx, e, regular, false)
b.dispatchMessage(ctx, e, regular, true)

channelMsg := &tg.Message{ID: 2, Message: "x", PeerID: &tg.PeerChannel{ChannelID: 50}}
b.dispatchMessage(ctx, channelMsg, false)
b.dispatchMessage(ctx, channelMsg, true) // edited channel post (no registrar; exercises the switch)
b.dispatchMessage(ctx, e, channelMsg, false)
b.dispatchMessage(ctx, e, channelMsg, true) // edited channel post (no registrar; exercises the switch)

// A service message converts to nil and routes nothing.
b.dispatchMessage(ctx, &tg.MessageService{ID: 3, PeerID: &tg.PeerUser{UserID: 10}}, false)
b.dispatchMessage(ctx, e, &tg.MessageService{ID: 3, PeerID: &tg.PeerUser{UserID: 10}}, false)

want := []string{"message", "edited", "channel"}
if len(fired) != len(want) {
Expand Down
2 changes: 1 addition & 1 deletion dispatch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func TestDispatchMessageDropsOutgoing(t *testing.T) {

b.OnMessage(func(c *Context) error { fired = true; return nil })
// An outgoing (bot's own) message must be dropped, not dispatched.
b.dispatchMessage(context.Background(), &tg.Message{Out: true, Message: "self"}, false)
b.dispatchMessage(context.Background(), tg.Entities{}, &tg.Message{Out: true, Message: "self"}, false)

if fired {
t.Fatal("outgoing message must not be dispatched")
Expand Down
32 changes: 17 additions & 15 deletions on.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,53 +11,53 @@ import (
// called once from New. Update-conversion failures are logged and swallowed so
// a single bad update never tears down the update stream.
func (b *Bot) installHandlers() {
b.disp.OnNewMessage(func(ctx context.Context, _ tg.Entities, u *tg.UpdateNewMessage) error {
b.dispatchMessage(ctx, u.Message, false)
b.disp.OnNewMessage(func(ctx context.Context, e tg.Entities, u *tg.UpdateNewMessage) error {
b.dispatchMessage(ctx, e, u.Message, false)

return nil
})
b.disp.OnEditMessage(func(ctx context.Context, _ tg.Entities, u *tg.UpdateEditMessage) error {
b.dispatchMessage(ctx, u.Message, true)
b.disp.OnEditMessage(func(ctx context.Context, e tg.Entities, u *tg.UpdateEditMessage) error {
b.dispatchMessage(ctx, e, u.Message, true)

return nil
})
b.disp.OnNewChannelMessage(func(ctx context.Context, _ tg.Entities, u *tg.UpdateNewChannelMessage) error {
b.dispatchMessage(ctx, u.Message, false)
b.disp.OnNewChannelMessage(func(ctx context.Context, e tg.Entities, u *tg.UpdateNewChannelMessage) error {
b.dispatchMessage(ctx, e, u.Message, false)

return nil
})
b.disp.OnEditChannelMessage(func(ctx context.Context, _ tg.Entities, u *tg.UpdateEditChannelMessage) error {
b.dispatchMessage(ctx, u.Message, true)
b.disp.OnEditChannelMessage(func(ctx context.Context, e tg.Entities, u *tg.UpdateEditChannelMessage) error {
b.dispatchMessage(ctx, e, u.Message, true)

return nil
})
b.disp.OnBotCallbackQuery(func(ctx context.Context, e tg.Entities, u *tg.UpdateBotCallbackQuery) error {
b.route(ctx, &Update{CallbackQuery: callbackQueryFromTg(e, u)})
b.route(ctx, &Update{CallbackQuery: callbackQueryFromTg(e, u), Entities: e})

return nil
})
b.disp.OnBotInlineQuery(func(ctx context.Context, e tg.Entities, u *tg.UpdateBotInlineQuery) error {
b.route(ctx, &Update{InlineQuery: inlineQueryFromTg(e, u)})
b.route(ctx, &Update{InlineQuery: inlineQueryFromTg(e, u), Entities: e})

return nil
})
b.disp.OnBotInlineSend(func(ctx context.Context, e tg.Entities, u *tg.UpdateBotInlineSend) error {
b.route(ctx, &Update{ChosenInlineResult: chosenInlineResultFromTg(e, u)})
b.route(ctx, &Update{ChosenInlineResult: chosenInlineResultFromTg(e, u), Entities: e})

return nil
})
b.disp.OnInlineBotCallbackQuery(func(ctx context.Context, e tg.Entities, u *tg.UpdateInlineBotCallbackQuery) error {
b.route(ctx, &Update{CallbackQuery: inlineCallbackQueryFromTg(e, u)})
b.route(ctx, &Update{CallbackQuery: inlineCallbackQueryFromTg(e, u), Entities: e})

return nil
})
b.disp.OnBotShippingQuery(func(ctx context.Context, e tg.Entities, u *tg.UpdateBotShippingQuery) error {
b.route(ctx, &Update{ShippingQuery: shippingQueryFromTg(e, u)})
b.route(ctx, &Update{ShippingQuery: shippingQueryFromTg(e, u), Entities: e})

return nil
})
b.disp.OnBotPrecheckoutQuery(func(ctx context.Context, e tg.Entities, u *tg.UpdateBotPrecheckoutQuery) error {
b.route(ctx, &Update{PreCheckoutQuery: preCheckoutQueryFromTg(e, u)})
b.route(ctx, &Update{PreCheckoutQuery: preCheckoutQueryFromTg(e, u), Entities: e})

return nil
})
Expand All @@ -67,7 +67,7 @@ func (b *Bot) installHandlers() {
// dispatchMessage converts a message and routes it as the appropriate update
// field. Channel-broadcast messages become channel posts; everything else is a
// regular message. edited selects the edited_* fields.
func (b *Bot) dispatchMessage(ctx context.Context, msg tg.MessageClass, edited bool) {
func (b *Bot) dispatchMessage(ctx context.Context, e tg.Entities, msg tg.MessageClass, edited bool) {
// Drop the bot's own outgoing messages. MTProto echoes them back on the
// update stream, but the HTTP Bot API never delivers them — without this a
// bot that replies to messages would answer its own replies in a loop.
Expand All @@ -88,6 +88,8 @@ func (b *Bot) dispatchMessage(ctx context.Context, msg tg.MessageClass, edited b

u := &Update{}

u.Entities = e

switch {
case m.Chat.Type == ChatTypeChannel && edited:
u.EditedChannelPost = m
Expand Down
2 changes: 1 addition & 1 deletion on_self_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func TestDispatchSkipsOwnOutgoingMessage(t *testing.T) {
Message: "echo",
PeerID: &tg.PeerUser{UserID: 42},
}
b.dispatchMessage(context.Background(), own, false)
b.dispatchMessage(context.Background(), tg.Entities{}, own, false)

if fired {
t.Fatal("handler fired for the bot's own outgoing message")
Expand Down
4 changes: 4 additions & 0 deletions update.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package botapi

import "github.com/gotd/td/tg"

// Update represents one incoming update. At most one of the optional fields is
// present in any given update.
type Update struct {
Expand Down Expand Up @@ -30,6 +32,8 @@ type Update struct {
// account.
DeletedBusinessMessages *BusinessMessagesDeleted `json:"deleted_business_messages,omitempty"`

Entities tg.Entities `json:"-"`

// botUsername is this bot's @username (without the @), set by the router so
// command predicates can tell a command targeted at this bot ("/cmd@me")
// from one targeted at another ("/cmd@other"). Not part of the Bot API
Expand Down