2026-04-23 22:29:34 +08:00
|
|
|
|
package dto
|
2026-03-09 21:28:58 +08:00
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"encoding/json"
|
|
|
|
|
|
"fmt"
|
2026-03-17 00:47:17 +08:00
|
|
|
|
"slices"
|
2026-03-09 21:28:58 +08:00
|
|
|
|
|
2026-04-22 16:01:59 +08:00
|
|
|
|
"with_you/internal/model"
|
2026-03-09 21:28:58 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// NewTextSegment 创建文本Segment
|
|
|
|
|
|
func NewTextSegment(content string) model.MessageSegment {
|
|
|
|
|
|
return model.MessageSegment{
|
|
|
|
|
|
Type: string(SegmentTypeText),
|
2026-03-30 04:49:35 +08:00
|
|
|
|
Data: map[string]any{"text": content},
|
2026-03-09 21:28:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-23 22:29:34 +08:00
|
|
|
|
// ExtractVoteSegmentData 从消息链中提取投票Segment数据
|
|
|
|
|
|
func ExtractVoteSegmentData(segments model.MessageSegments) *VoteSegmentData {
|
|
|
|
|
|
for _, segment := range segments {
|
|
|
|
|
|
if segment.Type == string(SegmentTypeVote) {
|
|
|
|
|
|
dataBytes, err := json.Marshal(segment.Data)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
var voteData VoteSegmentData
|
|
|
|
|
|
if err := json.Unmarshal(dataBytes, &voteData); err != nil {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
return &voteData
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// HasVoteSegment 检查消息链中是否包含投票Segment
|
|
|
|
|
|
func HasVoteSegment(segments model.MessageSegments) bool {
|
|
|
|
|
|
return HasSegmentType(segments, SegmentTypeVote)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 21:28:58 +08:00
|
|
|
|
// ExtractTextContentFromModel 从 model.MessageSegments 中提取纯文本内容
|
|
|
|
|
|
func ExtractTextContentFromModel(segments model.MessageSegments) string {
|
|
|
|
|
|
var result string
|
|
|
|
|
|
for _, segment := range segments {
|
|
|
|
|
|
switch segment.Type {
|
|
|
|
|
|
case "text":
|
|
|
|
|
|
if text, ok := segment.Data["text"].(string); ok {
|
|
|
|
|
|
result += text
|
|
|
|
|
|
}
|
|
|
|
|
|
case "at":
|
|
|
|
|
|
userID, _ := segment.Data["user_id"].(string)
|
|
|
|
|
|
if userID == "all" {
|
|
|
|
|
|
result += "@所有人 "
|
|
|
|
|
|
} else if userID != "" {
|
|
|
|
|
|
// 昵称由前端实时解析,后端文本提取仅用于推送通知兜底
|
|
|
|
|
|
result += "@某人 "
|
|
|
|
|
|
}
|
|
|
|
|
|
case "image":
|
|
|
|
|
|
result += "[图片]"
|
|
|
|
|
|
case "voice":
|
|
|
|
|
|
result += "[语音]"
|
|
|
|
|
|
case "video":
|
|
|
|
|
|
result += "[视频]"
|
|
|
|
|
|
case "file":
|
|
|
|
|
|
if name, ok := segment.Data["name"].(string); ok && name != "" {
|
|
|
|
|
|
result += fmt.Sprintf("[文件:%s]", name)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
result += "[文件]"
|
|
|
|
|
|
}
|
|
|
|
|
|
case "face":
|
|
|
|
|
|
if name, ok := segment.Data["name"].(string); ok && name != "" {
|
|
|
|
|
|
result += fmt.Sprintf("[%s]", name)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
result += "[表情]"
|
|
|
|
|
|
}
|
|
|
|
|
|
case "link":
|
|
|
|
|
|
if title, ok := segment.Data["title"].(string); ok && title != "" {
|
|
|
|
|
|
result += fmt.Sprintf("[链接:%s]", title)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
result += "[链接]"
|
|
|
|
|
|
}
|
2026-04-23 22:29:34 +08:00
|
|
|
|
case "vote":
|
|
|
|
|
|
result += "[投票]"
|
2026-04-26 00:37:20 +08:00
|
|
|
|
case "post_ref":
|
|
|
|
|
|
if title, ok := segment.Data["title"].(string); ok && title != "" {
|
|
|
|
|
|
result += fmt.Sprintf("[帖子:%s]", title)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
result += "[帖子引用]"
|
|
|
|
|
|
}
|
2026-03-09 21:28:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return result
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ExtractTextContent 从消息链中提取纯文本内容
|
|
|
|
|
|
// 用于搜索、通知展示等场景
|
|
|
|
|
|
func ExtractTextContent(segments model.MessageSegments) string {
|
|
|
|
|
|
return ExtractTextContentFromModel(segments)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ExtractMentionedUsers 从消息链中提取被@的用户ID列表
|
|
|
|
|
|
// 不包括 "all"(@所有人)
|
|
|
|
|
|
func ExtractMentionedUsers(segments model.MessageSegments) []string {
|
|
|
|
|
|
var userIDs []string
|
|
|
|
|
|
seen := make(map[string]bool)
|
|
|
|
|
|
|
|
|
|
|
|
for _, segment := range segments {
|
|
|
|
|
|
if segment.Type == string(SegmentTypeAt) {
|
|
|
|
|
|
userID, _ := segment.Data["user_id"].(string)
|
|
|
|
|
|
if userID != "all" && userID != "" && !seen[userID] {
|
|
|
|
|
|
userIDs = append(userIDs, userID)
|
|
|
|
|
|
seen[userID] = true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return userIDs
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-26 00:37:20 +08:00
|
|
|
|
// ExtractReferencedPosts 从消息链中提取所有被引用的帖子ID
|
|
|
|
|
|
func ExtractReferencedPosts(segments model.MessageSegments) []string {
|
|
|
|
|
|
var postIDs []string
|
|
|
|
|
|
seen := make(map[string]bool)
|
|
|
|
|
|
|
|
|
|
|
|
for _, segment := range segments {
|
|
|
|
|
|
if segment.Type == string(SegmentTypePostRef) {
|
|
|
|
|
|
postID, _ := segment.Data["post_id"].(string)
|
|
|
|
|
|
if postID != "" && !seen[postID] {
|
|
|
|
|
|
postIDs = append(postIDs, postID)
|
|
|
|
|
|
seen[postID] = true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return postIDs
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 21:28:58 +08:00
|
|
|
|
// HasSegmentType 检查消息链中是否包含指定类型的segment
|
|
|
|
|
|
func HasSegmentType(segments model.MessageSegments, segmentType SegmentType) bool {
|
2026-03-17 00:47:17 +08:00
|
|
|
|
return slices.ContainsFunc(segments, func(segment model.MessageSegment) bool {
|
|
|
|
|
|
return segment.Type == string(segmentType)
|
|
|
|
|
|
})
|
2026-03-09 21:28:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|