mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-06-30 19:03:51 +00:00
bdb60de7ae
- Update go.mod and Dockerfile to Go 1.26 - Apply `go fix ./...` stdlib modernizations across 170+ files - Add `go fix` to post-implementation checklist in CLAUDE.md - Fix go fix misapplied rewrite in loop_history.go
199 lines
4.4 KiB
Go
199 lines
4.4 KiB
Go
package feishu
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
func (c *Channel) parseMessageEvent(event *MessageEvent) *messageContext {
|
|
msg := &event.Event.Message
|
|
sender := &event.Event.Sender
|
|
|
|
chatID := msg.ChatID
|
|
messageID := msg.MessageID
|
|
chatType := msg.ChatType
|
|
contentType := msg.MessageType
|
|
rootID := msg.RootID
|
|
parentID := msg.ParentID
|
|
|
|
senderID := ""
|
|
if sender != nil {
|
|
senderID = sender.SenderID.OpenID
|
|
}
|
|
|
|
// Parse content
|
|
content := parseMessageContent(msg.Content, contentType)
|
|
|
|
// Parse mentions
|
|
var mentions []mentionInfo
|
|
mentionedBot := false
|
|
for _, m := range msg.Mentions {
|
|
mi := mentionInfo{
|
|
Key: m.Key,
|
|
OpenID: m.ID.OpenID,
|
|
Name: m.Name,
|
|
}
|
|
mentions = append(mentions, mi)
|
|
|
|
// Check if bot is mentioned.
|
|
// If botOpenID is known, match exactly; otherwise treat any mention as bot mention
|
|
// (fallback when probeBotInfo fails — better to process than silently drop).
|
|
if c.botOpenID == "" || mi.OpenID == c.botOpenID {
|
|
mentionedBot = true
|
|
}
|
|
}
|
|
|
|
// Replace mention placeholders with readable names.
|
|
// Bot mention is stripped entirely; other user mentions become @Name.
|
|
content = resolveMentions(content, mentions, c.botOpenID)
|
|
|
|
return &messageContext{
|
|
ChatID: chatID,
|
|
MessageID: messageID,
|
|
SenderID: senderID,
|
|
ChatType: chatType,
|
|
Content: content,
|
|
ContentType: contentType,
|
|
MentionedBot: mentionedBot,
|
|
RootID: rootID,
|
|
ParentID: parentID,
|
|
Mentions: mentions,
|
|
}
|
|
}
|
|
|
|
// --- Content parsing ---
|
|
|
|
func parseMessageContent(rawContent, messageType string) string {
|
|
if rawContent == "" {
|
|
return ""
|
|
}
|
|
|
|
switch messageType {
|
|
case "text":
|
|
var textMsg struct {
|
|
Text string `json:"text"`
|
|
}
|
|
if err := json.Unmarshal([]byte(rawContent), &textMsg); err == nil {
|
|
return textMsg.Text
|
|
}
|
|
return rawContent
|
|
|
|
case "post":
|
|
return parsePostContent(rawContent)
|
|
|
|
case "image":
|
|
return "[image]"
|
|
|
|
case "file":
|
|
var fileMsg struct {
|
|
FileName string `json:"file_name"`
|
|
}
|
|
if err := json.Unmarshal([]byte(rawContent), &fileMsg); err == nil {
|
|
return fmt.Sprintf("[file: %s]", fileMsg.FileName)
|
|
}
|
|
return "[file]"
|
|
|
|
default:
|
|
return fmt.Sprintf("[%s message]", messageType)
|
|
}
|
|
}
|
|
|
|
func parsePostContent(rawContent string) string {
|
|
var post map[string]any
|
|
if err := json.Unmarshal([]byte(rawContent), &post); err != nil {
|
|
return rawContent
|
|
}
|
|
|
|
var langContent any
|
|
for _, lang := range []string{"zh_cn", "en_us"} {
|
|
if lc, ok := post[lang]; ok {
|
|
langContent = lc
|
|
break
|
|
}
|
|
}
|
|
if langContent == nil {
|
|
for _, v := range post {
|
|
langContent = v
|
|
break
|
|
}
|
|
}
|
|
if langContent == nil {
|
|
return rawContent
|
|
}
|
|
|
|
langMap, ok := langContent.(map[string]any)
|
|
if !ok {
|
|
return rawContent
|
|
}
|
|
|
|
contentArr, ok := langMap["content"].([]any)
|
|
if !ok {
|
|
return rawContent
|
|
}
|
|
|
|
var textParts []string
|
|
for _, para := range contentArr {
|
|
paraArr, ok := para.([]any)
|
|
if !ok {
|
|
continue
|
|
}
|
|
var lineParts []string
|
|
for _, elem := range paraArr {
|
|
elemMap, ok := elem.(map[string]any)
|
|
if !ok {
|
|
continue
|
|
}
|
|
tag, _ := elemMap["tag"].(string)
|
|
switch tag {
|
|
case "text":
|
|
if t, ok := elemMap["text"].(string); ok {
|
|
lineParts = append(lineParts, t)
|
|
}
|
|
case "md":
|
|
if t, ok := elemMap["text"].(string); ok {
|
|
lineParts = append(lineParts, t)
|
|
}
|
|
case "at":
|
|
if name, ok := elemMap["user_name"].(string); ok {
|
|
lineParts = append(lineParts, "@"+name)
|
|
}
|
|
case "a":
|
|
if href, ok := elemMap["href"].(string); ok {
|
|
text, _ := elemMap["text"].(string)
|
|
if text != "" {
|
|
lineParts = append(lineParts, fmt.Sprintf("[%s](%s)", text, href))
|
|
} else {
|
|
lineParts = append(lineParts, href)
|
|
}
|
|
}
|
|
case "img":
|
|
lineParts = append(lineParts, "[image]")
|
|
}
|
|
}
|
|
if len(lineParts) > 0 {
|
|
textParts = append(textParts, strings.Join(lineParts, ""))
|
|
}
|
|
}
|
|
|
|
return strings.Join(textParts, "\n")
|
|
}
|
|
|
|
// resolveMentions replaces mention placeholders (@_user_1, @_user_2, etc.) in content.
|
|
// Bot mention is stripped entirely; other user mentions become @Name.
|
|
func resolveMentions(text string, mentions []mentionInfo, botOpenID string) string {
|
|
for _, m := range mentions {
|
|
if m.Key == "" {
|
|
continue
|
|
}
|
|
if botOpenID != "" && m.OpenID == botOpenID {
|
|
// Strip bot mention
|
|
text = strings.ReplaceAll(text, m.Key, "")
|
|
} else if m.Name != "" {
|
|
// Replace with @Name
|
|
text = strings.ReplaceAll(text, m.Key, "@"+m.Name)
|
|
}
|
|
}
|
|
return strings.TrimSpace(text)
|
|
}
|