feat(feishu): add emoji reaction feedback and markdown card support
- Add emoji reaction (THINKING) when receiving messages for quick user feedback - Use Feishu Interactive Card (JSON 2.0) for full Markdown support - Add addReaction method for emoji reaction API calls 📝 Description Add quick feedback mechanism for Feishu channel using emoji reactions, and improve Markdown rendering by using Feishu Interactive Card format. 🗣️ Type of Change - ✨ New feature (non-breaking change which adds functionality) 🤖 AI Code Generation - 🤖 Fully AI-generated (100% AI, 0% Human) 🔗 Related Issue N/A 📚 Technical Context - Uses Feishu Message Reaction API for emoji feedback - Uses Feishu Interactive Card JSON 2.0 schema for Markdown rendering - THINKING emoji indicates system is processing the request 🧪 Test Environment - __Hardware:__ PC - __OS:__ macOS - __Channels:__ Feishu (飞书) ☑️ Checklist - My code/docs follow the style of this project. - I have performed a self-review of my own changes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d5370c9605
commit
1315e9024a
1 changed files with 64 additions and 5 deletions
|
|
@ -102,16 +102,29 @@ func (c *FeishuChannel) Send(ctx context.Context, msg bus.OutboundMessage) error
|
|||
return fmt.Errorf("chat ID is empty")
|
||||
}
|
||||
|
||||
payload, err := json.Marshal(map[string]string{"text": msg.Content})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal feishu content: %w", err)
|
||||
// Construct a Feishu Interactive Card (JSON 2.0) for full Markdown support
|
||||
card := map[string]interface{}{
|
||||
"schema": "2.0",
|
||||
"config": map[string]interface{}{
|
||||
"wide_screen_mode": true,
|
||||
},
|
||||
"body": map[string]interface{}{
|
||||
"elements": []map[string]interface{}{
|
||||
{
|
||||
"tag": "markdown",
|
||||
"content": msg.Content,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
payload, _ := json.Marshal(card)
|
||||
msgType := larkim.MsgTypeInteractive
|
||||
|
||||
req := larkim.NewCreateMessageReqBuilder().
|
||||
ReceiveIdType(larkim.ReceiveIdTypeChatId).
|
||||
Body(larkim.NewCreateMessageReqBodyBuilder().
|
||||
ReceiveId(msg.ChatID).
|
||||
MsgType(larkim.MsgTypeText).
|
||||
MsgType(msgType).
|
||||
Content(string(payload)).
|
||||
Uuid(fmt.Sprintf("picoclaw-%d", time.Now().UnixNano())).
|
||||
Build()).
|
||||
|
|
@ -127,7 +140,9 @@ func (c *FeishuChannel) Send(ctx context.Context, msg bus.OutboundMessage) error
|
|||
}
|
||||
|
||||
logger.DebugCF("feishu", "Feishu message sent", map[string]any{
|
||||
"chat_id": msg.ChatID,
|
||||
"chat_id": msg.ChatID,
|
||||
"msg_type": msgType,
|
||||
"payload": string(payload),
|
||||
})
|
||||
|
||||
return nil
|
||||
|
|
@ -201,6 +216,16 @@ func (c *FeishuChannel) handleMessageReceive(ctx context.Context, event *larkim.
|
|||
return nil
|
||||
}
|
||||
|
||||
// Add emoji reaction for quick feedback (using "THINKING" to indicate processing)
|
||||
if messageID != "" {
|
||||
if _, err := c.addReaction(ctx, messageID, "THINKING"); err != nil {
|
||||
logger.DebugCF("feishu", "Failed to add reaction", map[string]any{
|
||||
"message_id": messageID,
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
c.HandleMessage(ctx, peer, messageID, senderID, chatID, content, nil, metadata, senderInfo)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -239,3 +264,37 @@ func extractFeishuMessageContent(message *larkim.EventMessage) string {
|
|||
|
||||
return *message.Content
|
||||
}
|
||||
|
||||
// addReaction adds an emoji reaction to a message for quick user feedback.
|
||||
// Returns an undo function that removes the reaction, or nil on error.
|
||||
func (c *FeishuChannel) addReaction(ctx context.Context, messageID, emojiType string) (func(), error) {
|
||||
req := larkim.NewCreateMessageReactionReqBuilder().
|
||||
MessageId(messageID).
|
||||
Body(larkim.NewCreateMessageReactionReqBodyBuilder().
|
||||
ReactionType(&larkim.Emoji{EmojiType: &emojiType}).
|
||||
Build()).
|
||||
Build()
|
||||
|
||||
resp, err := c.client.Im.V1.MessageReaction.Create(ctx, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("feishu add reaction: %w", err)
|
||||
}
|
||||
if !resp.Success() {
|
||||
return nil, fmt.Errorf("feishu add reaction failed (code=%d msg=%s)", resp.Code, resp.Msg)
|
||||
}
|
||||
|
||||
reactionID := stringValue(resp.Data.ReactionId)
|
||||
|
||||
undo := func() {
|
||||
if reactionID == "" {
|
||||
return
|
||||
}
|
||||
delReq := larkim.NewDeleteMessageReactionReqBuilder().
|
||||
MessageId(messageID).
|
||||
ReactionId(reactionID).
|
||||
Build()
|
||||
_, _ = c.client.Im.V1.MessageReaction.Delete(ctx, delReq)
|
||||
}
|
||||
|
||||
return undo, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue