mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-27 07:16:19 +09:00
feat(telegram): add in-reply-to and forwarded-from header and clarify user name (#177)
This commit is contained in:
@@ -462,6 +462,20 @@ func (a *TelegramAdapter) toInboundTelegramMessage(
|
|||||||
if text == "" && len(attachments) == 0 {
|
if text == "" && len(attachments) == 0 {
|
||||||
return channel.InboundMessage{}, false
|
return channel.InboundMessage{}, false
|
||||||
}
|
}
|
||||||
|
// Prepend quoted message context so the AI can see what is being replied to,
|
||||||
|
// and include quoted attachments so the LLM can see the actual media.
|
||||||
|
if raw.ReplyToMessage != nil {
|
||||||
|
if quotedText := a.buildTelegramQuotedText(raw.ReplyToMessage); quotedText != "" {
|
||||||
|
text = quotedText + "\n" + text
|
||||||
|
}
|
||||||
|
if quotedAttachments := a.collectTelegramAttachments(bot, raw.ReplyToMessage); len(quotedAttachments) > 0 {
|
||||||
|
attachments = append(attachments, quotedAttachments...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Prepend forward origin so the AI knows where the message was forwarded from.
|
||||||
|
if forwardCtx := buildTelegramForwardContext(raw); forwardCtx != "" {
|
||||||
|
text = forwardCtx + "\n" + text
|
||||||
|
}
|
||||||
subjectID, displayName, attrs := resolveTelegramSender(raw)
|
subjectID, displayName, attrs := resolveTelegramSender(raw)
|
||||||
chatID := ""
|
chatID := ""
|
||||||
chatType := ""
|
chatType := ""
|
||||||
@@ -638,10 +652,7 @@ func resolveTelegramSender(msg *tgbotapi.Message) (string, string, map[string]st
|
|||||||
if username != "" {
|
if username != "" {
|
||||||
attrs["username"] = username
|
attrs["username"] = username
|
||||||
}
|
}
|
||||||
displayName := strings.TrimSpace(msg.From.UserName)
|
displayName := resolveTelegramDisplayName(msg.From)
|
||||||
if displayName == "" {
|
|
||||||
displayName = strings.TrimSpace(msg.From.FirstName + " " + msg.From.LastName)
|
|
||||||
}
|
|
||||||
externalID := userID
|
externalID := userID
|
||||||
if externalID == "" {
|
if externalID == "" {
|
||||||
externalID = username
|
externalID = username
|
||||||
@@ -1027,6 +1038,98 @@ func buildTelegramReplyRef(msg *tgbotapi.Message, chatID string) *channel.ReplyR
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const telegramQuotedTextMaxLength = 200
|
||||||
|
|
||||||
|
// buildTelegramQuotedText extracts a textual summary of the replied-to message
|
||||||
|
// so the AI can see what message the user is replying to.
|
||||||
|
// Returns an empty string when no useful context can be extracted.
|
||||||
|
func (a *TelegramAdapter) buildTelegramQuotedText(replyTo *tgbotapi.Message) string {
|
||||||
|
if replyTo == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
senderName := resolveTelegramDisplayName(replyTo.From)
|
||||||
|
text := strings.TrimSpace(replyTo.Text)
|
||||||
|
if text == "" {
|
||||||
|
text = strings.TrimSpace(replyTo.Caption)
|
||||||
|
}
|
||||||
|
if text == "" {
|
||||||
|
// Reuse collectTelegramAttachments with nil bot to detect content
|
||||||
|
// types without making API calls to resolve file URLs.
|
||||||
|
attachments := a.collectTelegramAttachments(nil, replyTo)
|
||||||
|
if len(attachments) > 0 {
|
||||||
|
types := make([]string, 0, len(attachments))
|
||||||
|
for _, att := range attachments {
|
||||||
|
types = append(types, string(att.Type))
|
||||||
|
}
|
||||||
|
text = "[" + strings.Join(types, ", ") + "]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if text == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if len([]rune(text)) > telegramQuotedTextMaxLength {
|
||||||
|
text = string([]rune(text)[:telegramQuotedTextMaxLength]) + "..."
|
||||||
|
}
|
||||||
|
if senderName != "" {
|
||||||
|
return fmt.Sprintf("[Reply to %s: %s]", senderName, text)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("[Reply to: %s]", text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveTelegramDisplayName returns a display name for a Telegram user.
|
||||||
|
// Format: "FirstName LastName (@username)" when both are available,
|
||||||
|
// "FirstName LastName" when only name is set, "@username" when only username is set.
|
||||||
|
func resolveTelegramDisplayName(user *tgbotapi.User) string {
|
||||||
|
if user == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
name := strings.TrimSpace(user.FirstName + " " + user.LastName)
|
||||||
|
username := strings.TrimSpace(user.UserName)
|
||||||
|
if name != "" && username != "" {
|
||||||
|
return name + " (@" + username + ")"
|
||||||
|
}
|
||||||
|
if name != "" {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
if username != "" {
|
||||||
|
return "@" + username
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildTelegramForwardContext returns a text prefix describing the forward origin.
|
||||||
|
// Handles three cases: forwarded from a user, from a channel, or from a hidden sender.
|
||||||
|
// Returns an empty string when the message is not forwarded.
|
||||||
|
func buildTelegramForwardContext(msg *tgbotapi.Message) string {
|
||||||
|
if msg == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if msg.ForwardFrom != nil {
|
||||||
|
name := resolveTelegramDisplayName(msg.ForwardFrom)
|
||||||
|
if name != "" {
|
||||||
|
return fmt.Sprintf("[Forwarded from %s]", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msg.ForwardFromChat != nil {
|
||||||
|
title := strings.TrimSpace(msg.ForwardFromChat.Title)
|
||||||
|
username := strings.TrimSpace(msg.ForwardFromChat.UserName)
|
||||||
|
if title != "" && username != "" {
|
||||||
|
return fmt.Sprintf("[Forwarded from %s (@%s)]", title, username)
|
||||||
|
}
|
||||||
|
if title != "" {
|
||||||
|
return fmt.Sprintf("[Forwarded from %s]", title)
|
||||||
|
}
|
||||||
|
if username != "" {
|
||||||
|
return fmt.Sprintf("[Forwarded from @%s]", username)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
senderName := strings.TrimSpace(msg.ForwardSenderName)
|
||||||
|
if senderName != "" {
|
||||||
|
return fmt.Sprintf("[Forwarded from %s]", senderName)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func buildTelegramAudio(target string, file tgbotapi.RequestFileData) (tgbotapi.AudioConfig, error) {
|
func buildTelegramAudio(target string, file tgbotapi.RequestFileData) (tgbotapi.AudioConfig, error) {
|
||||||
if strings.HasPrefix(target, "@") {
|
if strings.HasPrefix(target, "@") {
|
||||||
audio := tgbotapi.NewAudio(0, file)
|
audio := tgbotapi.NewAudio(0, file)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ func TestResolveTelegramSender(t *testing.T) {
|
|||||||
From: &tgbotapi.User{ID: 123, UserName: "alice"},
|
From: &tgbotapi.User{ID: 123, UserName: "alice"},
|
||||||
}
|
}
|
||||||
externalID, displayName, attrs = resolveTelegramSender(msg)
|
externalID, displayName, attrs = resolveTelegramSender(msg)
|
||||||
if externalID != "123" || displayName != "alice" {
|
if externalID != "123" || displayName != "@alice" {
|
||||||
t.Fatalf("unexpected sender: %s %s", externalID, displayName)
|
t.Fatalf("unexpected sender: %s %s", externalID, displayName)
|
||||||
}
|
}
|
||||||
if attrs["user_id"] != "123" || attrs["username"] != "alice" {
|
if attrs["user_id"] != "123" || attrs["username"] != "alice" {
|
||||||
|
|||||||
Reference in New Issue
Block a user