yao/integrations/feishu/convert.go
Max 33efd3e890 Add Feishu and DingTalk integration support in robot lifecycle</message>
<message>
- Introduce Feishu and DingTalk adapters in the robot lifecycle for enhanced integration capabilities.
- Update the integration dispatcher to include new adapters for handling events from Feishu and DingTalk.
- Modify the configuration structure to support settings for both Feishu and DingTalk integrations.
- Enhance the integration parsing logic to recognize and process configurations for Feishu and DingTalk.
2026-03-02 07:36:34 +08:00

154 lines
4.5 KiB
Go

package feishu
import (
"encoding/json"
)
// ConvertedMessage is the unified output after parsing a Feishu event message.
type ConvertedMessage struct {
MessageID string `json:"message_id"`
ChatID string `json:"chat_id"`
ChatType string `json:"chat_type"` // p2p, group
SenderID string `json:"sender_id"`
SenderName string `json:"sender_name,omitempty"`
Text string `json:"text,omitempty"`
MediaItems []MediaItem `json:"media,omitempty"`
MentionBot bool `json:"mention_bot,omitempty"`
EventID string `json:"event_id,omitempty"`
LanguageCode string `json:"language_code,omitempty"`
}
// MediaItem describes a single media attachment from the message.
type MediaItem struct {
Type MediaType `json:"type"`
Key string `json:"key"`
MimeType string `json:"mime_type,omitempty"`
FileName string `json:"file_name,omitempty"`
FileSize int64 `json:"file_size,omitempty"`
Wrapper string `json:"wrapper,omitempty"`
}
// MediaType indicates the attachment type.
type MediaType string
const (
MediaImage MediaType = "image"
MediaFile MediaType = "file"
MediaAudio MediaType = "audio"
MediaVideo MediaType = "video"
MediaMedia MediaType = "media"
)
// HasMedia returns true if the message contains any media.
func (cm *ConvertedMessage) HasMedia() bool { return len(cm.MediaItems) > 0 }
// HasText returns true if the message contains text.
func (cm *ConvertedMessage) HasText() bool { return cm.Text != "" }
// feishuTextContent is the JSON structure of a text-type message body.
type feishuTextContent struct {
Text string `json:"text"`
}
// feishuImageContent is the JSON structure of an image-type message body.
type feishuImageContent struct {
ImageKey string `json:"image_key"`
}
// feishuFileContent is the JSON structure of a file-type message body.
type feishuFileContent struct {
FileKey string `json:"file_key"`
FileName string `json:"file_name"`
}
// feishuAudioContent is the JSON structure of an audio-type message body.
type feishuAudioContent struct {
FileKey string `json:"file_key"`
Duration int `json:"duration"`
}
// feishuMediaContent is the JSON structure of a media-type message body.
type feishuMediaContent struct {
FileKey string `json:"file_key"`
FileName string `json:"file_name"`
ImageKey string `json:"image_key"`
}
// ParseMessageContent parses a Feishu message body (JSON string) based on its type.
func ParseMessageContent(msgType, content string) (text string, media []MediaItem) {
switch msgType {
case "text":
var tc feishuTextContent
if err := json.Unmarshal([]byte(content), &tc); err == nil {
text = tc.Text
}
case "image":
var ic feishuImageContent
if err := json.Unmarshal([]byte(content), &ic); err == nil && ic.ImageKey != "" {
media = append(media, MediaItem{Type: MediaImage, Key: ic.ImageKey, MimeType: "image/jpeg"})
}
case "file":
var fc feishuFileContent
if err := json.Unmarshal([]byte(content), &fc); err == nil && fc.FileKey != "" {
media = append(media, MediaItem{Type: MediaFile, Key: fc.FileKey, FileName: fc.FileName})
}
case "audio":
var ac feishuAudioContent
if err := json.Unmarshal([]byte(content), &ac); err == nil && ac.FileKey != "" {
media = append(media, MediaItem{Type: MediaAudio, Key: ac.FileKey, MimeType: "audio/ogg"})
}
case "media":
var mc feishuMediaContent
if err := json.Unmarshal([]byte(content), &mc); err == nil && mc.FileKey != "" {
media = append(media, MediaItem{Type: MediaVideo, Key: mc.FileKey, FileName: mc.FileName})
}
case "post":
var post map[string]interface{}
if err := json.Unmarshal([]byte(content), &post); err == nil {
text = extractPostText(post)
}
}
return
}
// extractPostText extracts plain text from a rich-text (post) message.
func extractPostText(post map[string]interface{}) string {
for _, langContent := range post {
lc, ok := langContent.(map[string]interface{})
if !ok {
continue
}
contentArr, ok := lc["content"].([]interface{})
if !ok {
continue
}
var result string
for _, para := range contentArr {
paraArr, ok := para.([]interface{})
if !ok {
continue
}
for _, elem := range paraArr {
elemMap, ok := elem.(map[string]interface{})
if !ok {
continue
}
tag, _ := elemMap["tag"].(string)
if tag == "text" {
if t, ok := elemMap["text"].(string); ok {
result += t
}
} else if tag == "a" {
if t, ok := elemMap["text"].(string); ok {
result += t
}
}
}
result += "\n"
}
if result != "" {
return result
}
}
return ""
}