diff --git a/business_updates.go b/business_updates.go index b9fd493..5100a80 100644 --- a/business_updates.go +++ b/business_updates.go @@ -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 { diff --git a/coverage_dispatch_test.go b/coverage_dispatch_test.go index fb1d531..776d2bd 100644 --- a/coverage_dispatch_test.go +++ b/coverage_dispatch_test.go @@ -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) { diff --git a/dispatch_test.go b/dispatch_test.go index a739850..0003857 100644 --- a/dispatch_test.go +++ b/dispatch_test.go @@ -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") diff --git a/on.go b/on.go index f67afd4..fb8d4b2 100644 --- a/on.go +++ b/on.go @@ -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 }) @@ -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. @@ -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 diff --git a/on_self_test.go b/on_self_test.go index 0546e54..4df7e22 100644 --- a/on_self_test.go +++ b/on_self_test.go @@ -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") diff --git a/update.go b/update.go index 173c40a..9242b32 100644 --- a/update.go +++ b/update.go @@ -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 { @@ -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