Skip to content

Commit bccfe63

Browse files
ernadoclaude
andcommitted
feat(updates): resolve forward origin on incoming messages
convertMessage now fills Message.ForwardOrigin from the MTProto forward header: hidden senders become MessageOriginHiddenUser (no resolution); known users resolve to MessageOriginUser; channel posts and other chats resolve to MessageOriginChannel / MessageOriginChat with the author signature. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 6c70ff1 commit bccfe63

2 files changed

Lines changed: 82 additions & 0 deletions

File tree

convert.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,49 @@ func (b *Bot) fillFrom(ctx context.Context, from tg.PeerClass, m *Message) error
118118
return nil
119119
}
120120

121+
// forwardOrigin maps an MTProto forward header into a Bot API MessageOrigin.
122+
// A hidden sender yields a hidden-user origin (no resolution); otherwise the
123+
// user or chat/channel is resolved via the peer manager.
124+
func (b *Bot) forwardOrigin(ctx context.Context, h *tg.MessageFwdHeader) (MessageOrigin, error) {
125+
if name, ok := h.GetFromName(); ok {
126+
return &MessageOriginHiddenUser{Type: OriginHiddenUser, Date: h.Date, SenderUserName: name}, nil
127+
}
128+
129+
fromID, ok := h.GetFromID()
130+
if !ok {
131+
return nil, nil
132+
}
133+
134+
if pu, ok := fromID.(*tg.PeerUser); ok {
135+
u, err := b.peers.GetUser(ctx, &tg.InputUser{UserID: pu.UserID})
136+
if err != nil {
137+
return nil, asAPIError(err)
138+
}
139+
return &MessageOriginUser{Type: OriginUser, Date: h.Date, SenderUser: userFromTgUser(u.Raw())}, nil
140+
}
141+
142+
chat, err := b.chatByPeer(ctx, fromID)
143+
if err != nil {
144+
return nil, err
145+
}
146+
author, _ := h.GetPostAuthor()
147+
if post, ok := h.GetChannelPost(); ok && chat.Type == ChatTypeChannel {
148+
return &MessageOriginChannel{
149+
Type: OriginChannel,
150+
Date: h.Date,
151+
Chat: chat,
152+
MessageID: post,
153+
AuthorSignature: author,
154+
}, nil
155+
}
156+
return &MessageOriginChat{
157+
Type: OriginChat,
158+
Date: h.Date,
159+
SenderChat: chat,
160+
AuthorSignature: author,
161+
}, nil
162+
}
163+
121164
// convertMessage translates a tg.Message into a Bot API Message, resolving the
122165
// chat and sender via the peer manager. It fills the core fields (text,
123166
// entities, reply target, inline markup); richer media mapping arrives with the
@@ -156,6 +199,14 @@ func (b *Bot) convertMessage(ctx context.Context, m *tg.Message) (*Message, erro
156199
r.ReplyToMessage = &Message{MessageID: rh.ReplyToMsgID}
157200
}
158201

202+
if h, ok := m.GetFwdFrom(); ok {
203+
origin, err := b.forwardOrigin(ctx, &h)
204+
if err != nil {
205+
return nil, err
206+
}
207+
r.ForwardOrigin = origin
208+
}
209+
159210
if m.Message != "" {
160211
r.Text = m.Message
161212
}

convert_fwd_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package botapi
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/gotd/td/tg"
8+
)
9+
10+
func TestForwardOriginHiddenUser(t *testing.T) {
11+
b := newTestBot(t)
12+
h := &tg.MessageFwdHeader{Date: 123}
13+
h.SetFromName("Anon")
14+
15+
origin, err := b.forwardOrigin(context.Background(), h)
16+
if err != nil {
17+
t.Fatal(err)
18+
}
19+
hidden, ok := origin.(*MessageOriginHiddenUser)
20+
if !ok || hidden.SenderUserName != "Anon" || hidden.Date != 123 {
21+
t.Fatalf("expected hidden-user origin, got %#v", origin)
22+
}
23+
}
24+
25+
func TestForwardOriginNoSender(t *testing.T) {
26+
b := newTestBot(t)
27+
origin, err := b.forwardOrigin(context.Background(), &tg.MessageFwdHeader{Date: 1})
28+
if err != nil || origin != nil {
29+
t.Fatalf("no sender should yield (nil, nil), got (%#v, %v)", origin, err)
30+
}
31+
}

0 commit comments

Comments
 (0)