- Updated the GetStepsForResume method to return only steps with valid resume statuses (failed or interrupted), improving recovery context handling. - Enhanced error handling in message processing across various integrations, including friendly error messages for users in different locales. - Modified file handling in message integrations (Dingtalk, Discord, Feishu, Telegram, Weixin) to differentiate between image and file types, ensuring appropriate content delivery.
139 lines
2.9 KiB
Go
139 lines
2.9 KiB
Go
package dingtalk
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
agentcontext "github.com/yaoapp/yao/agent/context"
|
|
events "github.com/yaoapp/yao/agent/robot/events"
|
|
"github.com/yaoapp/yao/event"
|
|
dtapi "github.com/yaoapp/yao/integrations/dingtalk"
|
|
)
|
|
|
|
// handleMessages processes a batch of DingTalk messages.
|
|
func (a *Adapter) handleMessages(ctx context.Context, entry *botEntry, cms []*dtapi.ConvertedMessage) {
|
|
if len(cms) == 0 {
|
|
return
|
|
}
|
|
|
|
var allParts []interface{}
|
|
var lastCM *dtapi.ConvertedMessage
|
|
|
|
for _, cm := range cms {
|
|
if cm == nil {
|
|
continue
|
|
}
|
|
|
|
dedupKey := fmt.Sprintf("dt:%s:%s", entry.robotID, cm.MessageID)
|
|
if !a.dedup.markSeen(dedupKey) {
|
|
continue
|
|
}
|
|
|
|
parts := buildContentParts(cm)
|
|
if len(parts) == 0 {
|
|
continue
|
|
}
|
|
|
|
allParts = append(allParts, parts...)
|
|
lastCM = cm
|
|
}
|
|
|
|
if len(allParts) == 0 || lastCM == nil {
|
|
return
|
|
}
|
|
|
|
content := mergeContentParts(allParts)
|
|
|
|
msgPayload := events.MessagePayload{
|
|
RobotID: entry.robotID,
|
|
Messages: []agentcontext.Message{
|
|
{Role: agentcontext.RoleUser, Content: content},
|
|
},
|
|
Metadata: &events.MessageMetadata{
|
|
Channel: "dingtalk",
|
|
MessageID: lastCM.MessageID,
|
|
AppID: entry.clientID,
|
|
ChatID: lastCM.ConversationID,
|
|
SenderID: lastCM.SenderID,
|
|
SenderName: lastCM.SenderNick,
|
|
Locale: "zh-cn",
|
|
Extra: map[string]any{
|
|
"session_webhook": lastCM.SessionWebhook,
|
|
"conversation_type": lastCM.ConversationType,
|
|
"dt_message_id": lastCM.MessageID,
|
|
"sender_id": lastCM.SenderID,
|
|
"app_id": entry.clientID,
|
|
},
|
|
},
|
|
}
|
|
|
|
if _, err := event.Push(ctx, events.Message, msgPayload); err != nil {
|
|
log.Error("dingtalk adapter: event.Push robot.message failed robot=%s: %v", entry.robotID, err)
|
|
}
|
|
}
|
|
|
|
func buildContentParts(cm *dtapi.ConvertedMessage) []interface{} {
|
|
var parts []interface{}
|
|
|
|
if cm.HasText() {
|
|
parts = append(parts, map[string]interface{}{
|
|
"type": "text",
|
|
"text": cm.Text,
|
|
})
|
|
}
|
|
|
|
for _, mi := range cm.MediaItems {
|
|
if mi.Wrapper == "" && mi.URL == "" {
|
|
continue
|
|
}
|
|
url := mi.Wrapper
|
|
if url == "" {
|
|
url = mi.URL
|
|
}
|
|
if strings.HasPrefix(mi.MimeType, "image/") {
|
|
parts = append(parts, map[string]interface{}{
|
|
"type": "image_url",
|
|
"image_url": map[string]interface{}{
|
|
"url": url,
|
|
"detail": "auto",
|
|
},
|
|
})
|
|
} else {
|
|
parts = append(parts, map[string]interface{}{
|
|
"type": "file",
|
|
"file": map[string]interface{}{
|
|
"url": url,
|
|
"filename": mi.FileName,
|
|
},
|
|
})
|
|
}
|
|
}
|
|
|
|
return parts
|
|
}
|
|
|
|
func mergeContentParts(parts []interface{}) interface{} {
|
|
allText := true
|
|
for _, p := range parts {
|
|
m, ok := p.(map[string]interface{})
|
|
if !ok || m["type"] != "text" {
|
|
allText = false
|
|
break
|
|
}
|
|
}
|
|
|
|
if allText {
|
|
var buf strings.Builder
|
|
for i, p := range parts {
|
|
if i > 0 {
|
|
buf.WriteString("\n")
|
|
}
|
|
m := p.(map[string]interface{})
|
|
buf.WriteString(m["text"].(string))
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
return parts
|
|
}
|