feat(feishu): enhance channel with markdown cards, media, mentions, and editing#1000
feat(feishu): enhance channel with markdown cards, media, mentions, and editing#1000yinwm merged 5 commits intosipeed:mainfrom
Conversation
…nd editing Upgrade the Feishu channel from basic text-only to full feature parity with Telegram/Discord: interactive card messages with markdown rendering, message editing (MessageEditor), placeholder messages (PlaceholderCapable), emoji reactions (ReactionCapable), and inbound/outbound media support (MediaSender). Also add @mention detection with lazy bot open_id discovery, group trigger filtering with mention awareness, and multi-type inbound message parsing (text, post, image, file, audio, video).
There was a problem hiding this comment.
Pull request overview
Enhances the Feishu channel implementation to support richer messaging capabilities (interactive markdown cards, editing/placeholders/reactions, and inbound/outbound media handling) and improves group-chat behavior with @mention awareness.
Changes:
- Add placeholder configuration support for Feishu.
- Implement interactive-card based sending, message editing, placeholder messages, reactions, and media send/upload.
- Expand inbound parsing to support multiple message types, mention detection, and media downloading into the shared MediaStore flow.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| pkg/config/config.go | Adds Placeholder config to FeishuConfig to align with other channels’ placeholder support. |
| pkg/channels/feishu/common.go | Adds shared helpers for card building, media key extraction, and stripping mention placeholders. |
| pkg/channels/feishu/feishu_64.go | Main feature work: interactive card send/edit, placeholder/reaction/media support, mention-aware group filtering, and inbound media download/storage. |
| pkg/channels/feishu/feishu_32.go | Adds stub methods to satisfy new optional channel capability interfaces on unsupported architectures. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Remove stale "falls back to plain text" comment on Send - Add empty ChatID validation in SendMedia to match Send - Use messageID+fileKey as local filename to avoid write collisions - Check allowlist before downloading inbound media to avoid wasted I/O - Return errUnsupported consistently from all 32-bit stub methods
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
1000! Such a milestone! |
xiaket
left a comment
There was a problem hiding this comment.
Overall LGTM, would benefit from some extra tests.
- Consolidate extractImageKey/extractFileKey/extractFileName into shared extractJSONStringField helper to reduce code duplication - Move mentionPlaceholderRegex to package-level position after imports - Rename feishuCfg field to config for clarity within FeishuChannel - Replace @_user_1 heuristic with GET /open-apis/bot/v3/info API call at startup for reliable bot @mention detection - Fix double close on file handle in downloadResource by removing defer and using explicit close in both success and error paths - Add unit tests for common.go and feishu_64.go helpers (53 test cases)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| package feishu | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "testing" | ||
|
|
||
| larkim "github.com/larksuite/oapi-sdk-go/v3/service/im/v1" | ||
| ) |
There was a problem hiding this comment.
This test file imports the Feishu SDK (larkim) but has no build tag, while the channel explicitly stubs out 32-bit builds because the SDK isn’t supported there. Add the same 64-bit-only build constraint used by feishu_64.go/feishu_64_test.go so go test ./... works on 32-bit architectures.
| return *resp.Data.MessageId, nil | ||
| } | ||
| return "", nil |
There was a problem hiding this comment.
If the placeholder message is successfully created but the API response doesn’t include MessageId, this returns "", nil. BaseChannel will then not record a placeholder ID, leaving an uneditable orphan placeholder message in the chat. Treat missing MessageId as an error (or attempt a fallback strategy) so placeholder sending is all-or-nothing.
| return *resp.Data.MessageId, nil | |
| } | |
| return "", nil | |
| if id := *resp.Data.MessageId; id != "" { | |
| return id, nil | |
| } | |
| } | |
| return "", fmt.Errorf("feishu placeholder: missing message ID in successful response") |
| Body(larkim.NewCreateMessageReqBodyBuilder(). | ||
| ReceiveId(chatID). | ||
| MsgType(larkim.MsgTypeFile). | ||
| Content(string(content)). | ||
| Build()). |
There was a problem hiding this comment.
sendFile maps fileType to an upload file type (opus/mp4/stream) but always sends the message with MsgTypeFile. This means outbound MediaPart{Type:"audio"|"video"} will show up as a generic file message, losing type-specific behavior and diverging from how inbound audio/video are classified. Use MsgTypeAudio / MsgTypeMedia (video) when fileType indicates those types, or otherwise align the msg type with bus.MediaPart.Type.
| package feishu | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "regexp" | ||
| "strings" | ||
|
|
||
| larkim "github.com/larksuite/oapi-sdk-go/v3/service/im/v1" | ||
| ) |
There was a problem hiding this comment.
pkg/channels/feishu has a 32-bit stub (feishu_32.go) because the Feishu SDK isn’t supported on 32-bit, but this file is missing the same 64-bit-only build tag and imports the Feishu SDK (larkim). As-is, 32-bit builds will still try to compile this file and fail. Add the same //go:build amd64 || arm64 || riscv64 || mips64 || ppc64 constraint here (or split SDK-dependent helpers into a *_64.go file and provide a *_32.go stub).
yinwm
left a comment
There was a problem hiding this comment.
Review Summary
This is a high-quality PR that successfully upgrades the Feishu channel to feature parity with Telegram/Discord.
✅ What's Good
- Complete interface implementation - , , , all properly implemented
- Good test coverage - 53 test cases added for helpers and content extraction
- Clean code organization - Helper functions like
extractJSONStringFieldreduce duplication - Correct resource management - MediaStore only records mappings, files must be preserved for downstream agent processing (not a leak!)
- Iterative improvements - Multiple commits show responsiveness to review feedback
💡 Minor Suggestions (non-blocking)
- Consider extracting media-related code to
feishu_media.goasfeishu_64.goapproaches 627 lines - The
sendMediaPartsilent error handling (returns nil on media resolve/open failure) is a design choice - might be intentional to skip corrupted media
🎯 Verdict
Approved ✅
Great work @alexhoshina!
yinwm
left a comment
There was a problem hiding this comment.
Review Summary
This is a high-quality PR that successfully upgrades the Feishu channel to feature parity with Telegram/Discord.
✅ What's Good
- Complete interface implementation -
MessageEditor,PlaceholderCapable,ReactionCapable,MediaSender - Good test coverage - 53 test cases added
- Clean code organization - Helper functions reduce duplication
- Correct resource management - MediaStore records mappings, files preserved for downstream processing
- Iterative improvements - Multiple commits show responsiveness to feedback
💡 Minor Suggestions (non-blocking)
- Consider extracting media code to separate file as
feishu_64.goapproaches 627 lines sendMediaPartsilent error handling is a design choice worth noting
🎯 Verdict
Approved ✅ Great work!
- Consolidate extractImageKey/extractFileKey/extractFileName into shared extractJSONStringField helper to reduce code duplication - Move mentionPlaceholderRegex to package-level position after imports - Rename feishuCfg field to config for clarity within FeishuChannel - Replace @_user_1 heuristic with GET /open-apis/bot/v3/info API call at startup for reliable bot @mention detection - Fix double close on file handle in downloadResource by removing defer and using explicit close in both success and error paths - Add unit tests for common.go and feishu_64.go helpers (53 test cases)
feat(feishu): enhance channel with markdown cards, media, mentions, and editing
- Consolidate extractImageKey/extractFileKey/extractFileName into shared extractJSONStringField helper to reduce code duplication - Move mentionPlaceholderRegex to package-level position after imports - Rename feishuCfg field to config for clarity within FeishuChannel - Replace @_user_1 heuristic with GET /open-apis/bot/v3/info API call at startup for reliable bot @mention detection - Fix double close on file handle in downloadResource by removing defer and using explicit close in both success and error paths - Add unit tests for common.go and feishu_64.go helpers (53 test cases)
feat(feishu): enhance channel with markdown cards, media, mentions, and editing
📝 Description
Upgrade the Feishu channel from basic text-only to full feature parity with Telegram/Discord: interactive card messages with markdown rendering, message editing (MessageEditor), placeholder messages (PlaceholderCapable), emoji reactions (ReactionCapable), and inbound/outbound media support (MediaSender).
Also add @mention detection with lazy bot open_id discovery, group trigger filtering with mention awareness, and multi-type inbound message parsing (text, post, image, file, audio, video).
Close: #978 #894