Files
goclaw/internal/channels/telegram/context.go
T

181 lines
4.7 KiB
Go

package telegram
import (
"fmt"
"strings"
"time"
"github.com/mymmrac/telego"
)
// MessageContext holds enriched context extracted from a Telegram message.
// Ref: TS src/telegram/bot-message-context.ts → buildTelegramMessageContext()
type MessageContext struct {
ForwardInfo *ForwardInfo
ReplyInfo *ReplyInfo
LocationInfo *LocationInfo
}
// ForwardInfo contains metadata about a forwarded message.
// Ref: TS normalizeForwardedContext()
type ForwardInfo struct {
From string // sender name or channel title
FromType string // "user", "channel", "supergroup", "hidden"
Date time.Time // original message date
}
// ReplyInfo contains metadata about the message being replied to.
// Ref: TS describeReplyTarget()
type ReplyInfo struct {
Sender string // sender name
Body string // quoted message text
IsBotReply bool // true if replying to bot's own message
}
// LocationInfo contains geographic coordinates.
// Ref: TS extractTelegramLocation()
type LocationInfo struct {
Latitude float64
Longitude float64
}
// buildMessageContext extracts forward, reply, and location context from a Telegram message.
func buildMessageContext(msg *telego.Message, botUsername string) *MessageContext {
ctx := &MessageContext{}
ctx.ForwardInfo = extractForwardInfo(msg)
ctx.ReplyInfo = extractReplyInfo(msg, botUsername)
ctx.LocationInfo = extractLocationInfo(msg)
return ctx
}
// enrichContentWithContext appends forward/reply/location context to the message content.
func enrichContentWithContext(content string, msgCtx *MessageContext) string {
if msgCtx == nil {
return content
}
var result strings.Builder
// Prepend forward context
if msgCtx.ForwardInfo != nil {
dateStr := msgCtx.ForwardInfo.Date.Format("2006-01-02 15:04")
result.WriteString(fmt.Sprintf("[Forwarded from %s at %s]\n", msgCtx.ForwardInfo.From, dateStr))
}
result.WriteString(content)
// Append reply context
if msgCtx.ReplyInfo != nil && msgCtx.ReplyInfo.Body != "" {
result.WriteString(fmt.Sprintf("\n\n[Replying to %s]\n%s\n[/Replying]",
msgCtx.ReplyInfo.Sender, msgCtx.ReplyInfo.Body))
}
// Append location
if msgCtx.LocationInfo != nil {
result.WriteString(fmt.Sprintf("\n\nCoordinates: %.6f, %.6f",
msgCtx.LocationInfo.Latitude, msgCtx.LocationInfo.Longitude))
}
return result.String()
}
// extractForwardInfo extracts forwarded message metadata.
// Ref: TS normalizeForwardedContext()
func extractForwardInfo(msg *telego.Message) *ForwardInfo {
if msg.ForwardOrigin == nil {
return nil
}
info := &ForwardInfo{}
switch origin := msg.ForwardOrigin.(type) {
case *telego.MessageOriginUser:
user := origin.SenderUser
info.From = buildUserName(&user)
info.FromType = "user"
info.Date = time.Unix(origin.Date, 0)
case *telego.MessageOriginChat:
info.From = origin.SenderChat.Title
info.FromType = string(origin.SenderChat.Type)
info.Date = time.Unix(origin.Date, 0)
case *telego.MessageOriginChannel:
info.From = origin.Chat.Title
info.FromType = "channel"
info.Date = time.Unix(origin.Date, 0)
case *telego.MessageOriginHiddenUser:
info.From = origin.SenderUserName
info.FromType = "hidden"
info.Date = time.Unix(origin.Date, 0)
default:
return nil
}
return info
}
// extractReplyInfo extracts replied-to message metadata.
// Ref: TS describeReplyTarget()
func extractReplyInfo(msg *telego.Message, botUsername string) *ReplyInfo {
reply := msg.ReplyToMessage
if reply == nil {
return nil
}
info := &ReplyInfo{}
// Determine sender name
if reply.From != nil {
info.Sender = buildUserName(reply.From)
info.IsBotReply = (reply.From.Username == botUsername)
} else {
info.Sender = "unknown"
}
// Extract reply body
if reply.Text != "" {
info.Body = reply.Text
} else if reply.Caption != "" {
info.Body = reply.Caption
}
// Truncate long reply bodies
if len(info.Body) > 500 {
info.Body = info.Body[:500] + "..."
}
// Hint for bot replies: the full response is already in session history,
// so the LLM doesn't need the full text here — just enough to identify it.
if info.IsBotReply && info.Body != "" {
info.Body += "\n(This is your previous response — full content is in session history above)"
}
return info
}
// extractLocationInfo extracts location data from a message.
// Ref: TS extractTelegramLocation()
func extractLocationInfo(msg *telego.Message) *LocationInfo {
if msg.Location == nil {
return nil
}
return &LocationInfo{
Latitude: msg.Location.Latitude,
Longitude: msg.Location.Longitude,
}
}
// buildUserName formats a Telegram user's display name.
func buildUserName(user *telego.User) string {
if user == nil {
return "unknown"
}
name := user.FirstName
if user.LastName != "" {
name += " " + user.LastName
}
return name
}