<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.
142 lines
4.7 KiB
Go
142 lines
4.7 KiB
Go
package dingtalk
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
)
|
|
|
|
// ConvertedMessage is the unified output after parsing a DingTalk message.
|
|
type ConvertedMessage struct {
|
|
MessageID string `json:"message_id"`
|
|
ConversationID string `json:"conversation_id"`
|
|
ConversationType string `json:"conversation_type"` // "1" = private, "2" = group
|
|
SenderID string `json:"sender_id"`
|
|
SenderNick string `json:"sender_nick,omitempty"`
|
|
SenderStaffID string `json:"sender_staff_id,omitempty"`
|
|
Text string `json:"text,omitempty"`
|
|
MediaItems []MediaItem `json:"media,omitempty"`
|
|
ChatbotUserID string `json:"chatbot_user_id,omitempty"`
|
|
IsInAtList bool `json:"is_in_at_list,omitempty"`
|
|
SessionWebhook string `json:"session_webhook,omitempty"`
|
|
}
|
|
|
|
// MediaItem describes a single attachment in a DingTalk message.
|
|
type MediaItem struct {
|
|
Type MediaType `json:"type"`
|
|
URL string `json:"url,omitempty"`
|
|
MimeType string `json:"mime_type,omitempty"`
|
|
FileName string `json:"file_name,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"
|
|
MediaRichText MediaType = "richText"
|
|
)
|
|
|
|
// HasMedia returns true if the message contains 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 != "" }
|
|
|
|
// StreamCallbackData is the data structure from DingTalk stream callback.
|
|
type StreamCallbackData struct {
|
|
ConversationID string `json:"conversationId"`
|
|
ConversationType string `json:"conversationType"`
|
|
AtUsers []AtUser `json:"atUsers"`
|
|
ChatbotCorpID string `json:"chatbotCorpId"`
|
|
ChatbotUserID string `json:"chatbotUserId"`
|
|
MsgID string `json:"msgId"`
|
|
SenderID string `json:"senderId"`
|
|
SenderNick string `json:"senderNick"`
|
|
SenderCorpID string `json:"senderCorpId"`
|
|
SenderStaffID string `json:"senderStaffId"`
|
|
SessionWebhook string `json:"sessionWebhook"`
|
|
SessionWebhookExpiredTime int64 `json:"sessionWebhookExpiredTime"`
|
|
IsAdmin bool `json:"isAdmin"`
|
|
IsInAtList bool `json:"isInAtList"`
|
|
Text *TextContent `json:"text,omitempty"`
|
|
Msgtype string `json:"msgtype"`
|
|
RichText json.RawMessage `json:"richText,omitempty"`
|
|
}
|
|
|
|
// AtUser represents a mentioned user in a DingTalk message.
|
|
type AtUser struct {
|
|
DingtalkID string `json:"dingtalkId"`
|
|
StaffID string `json:"staffId,omitempty"`
|
|
}
|
|
|
|
// TextContent holds plain text content.
|
|
type TextContent struct {
|
|
Content string `json:"content"`
|
|
}
|
|
|
|
// ConvertStreamData transforms a DingTalk stream callback into a ConvertedMessage.
|
|
func ConvertStreamData(data *StreamCallbackData) *ConvertedMessage {
|
|
if data == nil {
|
|
return nil
|
|
}
|
|
|
|
cm := &ConvertedMessage{
|
|
MessageID: data.MsgID,
|
|
ConversationID: data.ConversationID,
|
|
ConversationType: data.ConversationType,
|
|
SenderID: data.SenderID,
|
|
SenderNick: data.SenderNick,
|
|
SenderStaffID: data.SenderStaffID,
|
|
ChatbotUserID: data.ChatbotUserID,
|
|
IsInAtList: data.IsInAtList,
|
|
SessionWebhook: data.SessionWebhook,
|
|
}
|
|
|
|
switch data.Msgtype {
|
|
case "text":
|
|
if data.Text != nil {
|
|
text := strings.TrimSpace(data.Text.Content)
|
|
cm.Text = text
|
|
}
|
|
case "richText":
|
|
if len(data.RichText) > 0 {
|
|
text, media := parseRichText(data.RichText)
|
|
cm.Text = text
|
|
cm.MediaItems = media
|
|
}
|
|
case "picture":
|
|
cm.MediaItems = append(cm.MediaItems, MediaItem{Type: MediaImage})
|
|
}
|
|
|
|
return cm
|
|
}
|
|
|
|
func parseRichText(raw json.RawMessage) (string, []MediaItem) {
|
|
var richText struct {
|
|
RichText []struct {
|
|
Text string `json:"text,omitempty"`
|
|
PicURL string `json:"pictureDownloadUrl,omitempty"`
|
|
Type string `json:"type,omitempty"`
|
|
DownURL string `json:"downloadCode,omitempty"`
|
|
} `json:"richText"`
|
|
}
|
|
if err := json.Unmarshal(raw, &richText); err != nil {
|
|
return "", nil
|
|
}
|
|
|
|
var text string
|
|
var media []MediaItem
|
|
for _, item := range richText.RichText {
|
|
if item.Text != "" {
|
|
text += item.Text
|
|
}
|
|
if item.PicURL != "" {
|
|
media = append(media, MediaItem{Type: MediaImage, URL: item.PicURL, MimeType: "image/jpeg"})
|
|
}
|
|
}
|
|
return text, media
|
|
}
|