fix(telegram): preserve reply context without breaking commands (#2197)
This commit is contained in:
parent
7b3f47128f
commit
5f07db934b
3 changed files with 234 additions and 0 deletions
|
|
@ -659,6 +659,9 @@ func (c *TelegramChannel) handleMessage(ctx context.Context, message *telego.Mes
|
|||
}
|
||||
content = cleaned
|
||||
}
|
||||
if quoted := formatQuotedReply(message); quoted != "" && !isCommandMessage(content) {
|
||||
content = fmt.Sprintf("%s\n\n%s", quoted, content)
|
||||
}
|
||||
|
||||
// For forum topics, embed the thread ID as "chatID/threadID" so replies
|
||||
// route to the correct topic and each topic gets its own session.
|
||||
|
|
@ -693,6 +696,9 @@ func (c *TelegramChannel) handleMessage(ctx context.Context, message *telego.Mes
|
|||
"first_name": user.FirstName,
|
||||
"is_group": fmt.Sprintf("%t", message.Chat.Type != "private"),
|
||||
}
|
||||
if message.ReplyToMessage != nil {
|
||||
metadata["reply_to_message_id"] = fmt.Sprintf("%d", message.ReplyToMessage.MessageID)
|
||||
}
|
||||
|
||||
// Set parent_peer metadata for per-topic agent binding.
|
||||
if message.Chat.IsForum && threadID != 0 {
|
||||
|
|
@ -713,6 +719,36 @@ func (c *TelegramChannel) handleMessage(ctx context.Context, message *telego.Mes
|
|||
return nil
|
||||
}
|
||||
|
||||
func formatQuotedReply(message *telego.Message) string {
|
||||
if message == nil || message.ReplyToMessage == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
reply := message.ReplyToMessage
|
||||
refContent := strings.TrimSpace(reply.Text)
|
||||
if refContent == "" {
|
||||
refContent = strings.TrimSpace(reply.Caption)
|
||||
}
|
||||
if refContent == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
refAuthor := "unknown"
|
||||
if reply.From != nil {
|
||||
if username := strings.TrimSpace(reply.From.Username); username != "" {
|
||||
refAuthor = username
|
||||
} else if firstName := strings.TrimSpace(reply.From.FirstName); firstName != "" {
|
||||
refAuthor = firstName
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("[quoted message from %s]: %s", refAuthor, refContent)
|
||||
}
|
||||
|
||||
func isCommandMessage(content string) bool {
|
||||
return commands.HasCommandPrefix(content)
|
||||
}
|
||||
|
||||
func (c *TelegramChannel) downloadPhoto(ctx context.Context, fileID string) string {
|
||||
file, err := c.bot.GetFile(ctx, &telego.GetFileParams{FileID: fileID})
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -147,3 +147,53 @@ func TestIsBotMentioned_MentionEntityUnaffected(t *testing.T) {
|
|||
t.Fatal("expected mention entity to be treated as bot mention")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMessage_GroupReplyCommandKeepsCommandPrefixAndMetadata(t *testing.T) {
|
||||
ch, messageBus := newGroupMentionOnlyChannel(t, "testbot")
|
||||
|
||||
msg := &telego.Message{
|
||||
Text: "/new@testbot",
|
||||
Entities: []telego.MessageEntity{{
|
||||
Type: telego.EntityTypeBotCommand,
|
||||
Offset: 0,
|
||||
Length: len([]rune("/new@testbot")),
|
||||
}},
|
||||
MessageID: 43,
|
||||
Chat: telego.Chat{
|
||||
ID: 123,
|
||||
Type: "group",
|
||||
},
|
||||
From: &telego.User{
|
||||
ID: 7,
|
||||
FirstName: "Alice",
|
||||
},
|
||||
ReplyToMessage: &telego.Message{
|
||||
MessageID: 101,
|
||||
Text: "old context",
|
||||
From: &telego.User{
|
||||
Username: "bob",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := ch.handleMessage(context.Background(), msg); err != nil {
|
||||
t.Fatalf("handleMessage error: %v", err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
defer cancel()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Fatal("timeout waiting for message to be forwarded")
|
||||
case inbound, ok := <-messageBus.InboundChan():
|
||||
if !ok {
|
||||
t.Fatal("expected inbound message to be forwarded")
|
||||
}
|
||||
if inbound.Content != "/new" {
|
||||
t.Fatalf("content=%q want=%q", inbound.Content, "/new")
|
||||
}
|
||||
if got := inbound.Metadata["reply_to_message_id"]; got != "101" {
|
||||
t.Fatalf("reply_to_message_id=%q want=%q", got, "101")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -673,3 +673,151 @@ func TestHandleMessage_EmptyContent_Ignored(t *testing.T) {
|
|||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMessage_ReplyCarriesMetadataAndQuotedContext(t *testing.T) {
|
||||
messageBus := bus.NewMessageBus()
|
||||
ch := &TelegramChannel{
|
||||
BaseChannel: channels.NewBaseChannel("telegram", nil, messageBus, nil),
|
||||
chatIDs: make(map[string]int64),
|
||||
ctx: context.Background(),
|
||||
}
|
||||
|
||||
msg := &telego.Message{
|
||||
Text: "what do you think?",
|
||||
MessageID: 21,
|
||||
Chat: telego.Chat{
|
||||
ID: 12345,
|
||||
Type: "private",
|
||||
},
|
||||
From: &telego.User{
|
||||
ID: 10,
|
||||
FirstName: "Bob",
|
||||
},
|
||||
ReplyToMessage: &telego.Message{
|
||||
MessageID: 99,
|
||||
Text: "this is the old message",
|
||||
From: &telego.User{
|
||||
Username: "alice",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := ch.handleMessage(context.Background(), msg)
|
||||
require.NoError(t, err)
|
||||
|
||||
inbound, ok := <-messageBus.InboundChan()
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "99", inbound.Metadata["reply_to_message_id"])
|
||||
assert.Equal(
|
||||
t,
|
||||
"[quoted message from alice]: this is the old message\n\nwhat do you think?",
|
||||
inbound.Content,
|
||||
)
|
||||
}
|
||||
|
||||
func TestHandleMessage_ReplyWithoutTextOnlySetsReplyMetadata(t *testing.T) {
|
||||
messageBus := bus.NewMessageBus()
|
||||
ch := &TelegramChannel{
|
||||
BaseChannel: channels.NewBaseChannel("telegram", nil, messageBus, nil),
|
||||
chatIDs: make(map[string]int64),
|
||||
ctx: context.Background(),
|
||||
}
|
||||
|
||||
msg := &telego.Message{
|
||||
Text: "ping",
|
||||
MessageID: 22,
|
||||
Chat: telego.Chat{
|
||||
ID: 12345,
|
||||
Type: "private",
|
||||
},
|
||||
From: &telego.User{
|
||||
ID: 10,
|
||||
FirstName: "Bob",
|
||||
},
|
||||
ReplyToMessage: &telego.Message{
|
||||
MessageID: 100,
|
||||
},
|
||||
}
|
||||
|
||||
err := ch.handleMessage(context.Background(), msg)
|
||||
require.NoError(t, err)
|
||||
|
||||
inbound, ok := <-messageBus.InboundChan()
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "100", inbound.Metadata["reply_to_message_id"])
|
||||
assert.Equal(t, "ping", inbound.Content)
|
||||
}
|
||||
|
||||
func TestHandleMessage_ReplyCommandKeepsCommandPrefix(t *testing.T) {
|
||||
messageBus := bus.NewMessageBus()
|
||||
ch := &TelegramChannel{
|
||||
BaseChannel: channels.NewBaseChannel("telegram", nil, messageBus, nil),
|
||||
chatIDs: make(map[string]int64),
|
||||
ctx: context.Background(),
|
||||
}
|
||||
|
||||
msg := &telego.Message{
|
||||
Text: "/new",
|
||||
MessageID: 23,
|
||||
Chat: telego.Chat{
|
||||
ID: 12345,
|
||||
Type: "private",
|
||||
},
|
||||
From: &telego.User{
|
||||
ID: 10,
|
||||
FirstName: "Bob",
|
||||
},
|
||||
ReplyToMessage: &telego.Message{
|
||||
MessageID: 101,
|
||||
Text: "old context text",
|
||||
From: &telego.User{
|
||||
Username: "alice",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := ch.handleMessage(context.Background(), msg)
|
||||
require.NoError(t, err)
|
||||
|
||||
inbound, ok := <-messageBus.InboundChan()
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "101", inbound.Metadata["reply_to_message_id"])
|
||||
assert.Equal(t, "/new", inbound.Content)
|
||||
}
|
||||
|
||||
func TestHandleMessage_ReplyBangCommandKeepsCommandPrefix(t *testing.T) {
|
||||
messageBus := bus.NewMessageBus()
|
||||
ch := &TelegramChannel{
|
||||
BaseChannel: channels.NewBaseChannel("telegram", nil, messageBus, nil),
|
||||
chatIDs: make(map[string]int64),
|
||||
ctx: context.Background(),
|
||||
}
|
||||
|
||||
msg := &telego.Message{
|
||||
Text: "!new",
|
||||
MessageID: 24,
|
||||
Chat: telego.Chat{
|
||||
ID: 12345,
|
||||
Type: "private",
|
||||
},
|
||||
From: &telego.User{
|
||||
ID: 10,
|
||||
FirstName: "Bob",
|
||||
},
|
||||
ReplyToMessage: &telego.Message{
|
||||
MessageID: 102,
|
||||
Text: "old context text",
|
||||
From: &telego.User{
|
||||
Username: "alice",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := ch.handleMessage(context.Background(), msg)
|
||||
require.NoError(t, err)
|
||||
|
||||
inbound, ok := <-messageBus.InboundChan()
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "102", inbound.Metadata["reply_to_message_id"])
|
||||
assert.Equal(t, "!new", inbound.Content)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue