Cover normalizeMattermostMessagingTarget and looksLikeMattermostTargetId
with 17 tests: prefix handling, #channel directory fallback, bare name
fallback, 26-char ID matching, DM channel format, and false-positive
rejection for short strings.
Allow fetching attachments from the Mattermost server host, which may
be localhost or a private IP. Without this, SSRF guards block inbound
media downloads silently.
Credit: @webclerk (#22594) for identifying this gap.
- normalize.ts: Restore #channel-name → undefined (directory lookup) fix
that was lost during PR consolidation. Also restore strict ID matching
in looksLikeMattermostTargetId (26-char alnum only, not loose 3+ regex).
- interactions.ts: Add dispatchButtonClick callback so the agent responds
immediately to button clicks. Show the clicked button's display name
(not raw action ID) in the "selected by" confirmation message.
- monitor.ts: Wire up dispatchButtonClick with full reply pipeline
(routing, typing indicator, chunking, dispatchReplyFromConfig).
- interactions.test.ts: Update test to expect sanitized action IDs.
The core sends buttons as Array<Array<Button>> (2D for Telegram row
layout). The consolidation from #18151 into #19957 lost the flatMap
that flattens to 1D and the .filter() that drops malformed buttons.
Without flatMap, each "button" is actually a row array — btn.text is
undefined, producing empty-name buttons that render as white boxes
with a blue left border in Mattermost.
Mattermost uses action IDs in URL paths for server-side routing
(/api/v4/posts/{id}/actions/{actionId}). IDs containing hyphens or
underscores break this routing silently — buttons render but clicks
do nothing.
Strip hyphens and underscores from action IDs before sending.
Ref: https://github.com/mattermost/mattermost/issues/25747
Port missing pieces from PR #18151:
- Directory adapter for channel/user name resolution (listGroups, listPeers)
- Config schema validation for interactions.callbackBaseUrl
- TypeScript types for interactions config
- Channel-level tests for send/buttons action support
- Fix listActions to include "send" alongside "react"
Mattermost reorders context keys when storing and returning interactive
message payloads. Without stable key ordering, JSON.stringify produces
different output for the same context, causing HMAC verification to fail
on button clicks.
Sort keys before serialization in generateInteractionToken so tokens
remain valid regardless of key order. Add tests covering key reordering.
Add interactive message buttons and emoji reactions to the Mattermost
extension, enabling agents to send messages with clickable action buttons
and react to posts with emoji.
Interactive buttons:
- HMAC-SHA256 token verification for secure button callbacks
- HTTP callback handler registered via registerPluginHttpRoute
- Button click completion: replaces buttons with confirmation text
- Localhost-only validation for callback requests
- Stable HMAC secret derived from bot token (works across CLI/gateway)
Reactions:
- Add/remove emoji reactions via Mattermost REST API
- Bot user ID caching with TTL for reaction requests
- Reaction event handling in WebSocket monitor with DM/group policy enforcement
Channel actions adapter:
- supportsButtons, handleAction, listActions for the ChannelPlugin interface
- Send action with optional button attachments
- React action with add/remove support and emoji colon stripping
Also includes:
- updateMattermostPost for modifying existing posts (button completion)
- props passthrough in createMattermostPost for attachments
- parseMattermostTarget with channel-name and isMattermostId support
- Comprehensive test coverage (58 new tests across 4 test files)
* fix(feishu): use msg_type media for mp4 video (fixes#33674)
* Feishu: harden streaming merge semantics and final reply dedupe
Use explicit streaming update semantics in the Feishu reply dispatcher:
treat onPartialReply payloads as snapshot updates and block fallback payloads
as delta chunks, then merge final text with the shared overlap-aware
mergeStreamingText helper before closing the stream.
Prevent duplicate final text delivery within the same dispatch cycle, and add
regression tests covering overlap snapshot merge, duplicate final suppression,
and block-as-delta behavior to guard against repeated/truncated output.
* fix(feishu): prefer message.reply for streaming cards in topic threads
* fix: reduce Feishu streaming card print_step to avoid duplicate rendering
Fixesopenclaw/openclaw#33751
* Feishu: preserve media sends on duplicate finals and add media synthesis changelog
* Feishu: only dedupe exact duplicate final replies
* Feishu: use scoped plugin-sdk import in streaming-card tests
---------
Co-authored-by: 倪汉杰0668001185 <ni.hanjie@xydigit.com>
Co-authored-by: zhengquanliu <zhengquanliu@bytedance.com>
Co-authored-by: nick <nickzj@qq.com>
Co-authored-by: linhey <linhey@mini.local>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
* fix(feishu): comprehensive reply mechanism fix — outbound replyToId forwarding + topic-aware reply targeting
- Forward replyToId from ChannelOutboundContext through sendText/sendMedia
to sendMessageFeishu/sendMarkdownCardFeishu/sendMediaFeishu, enabling
reply-to-message via the message tool.
- Fix group reply targeting: use ctx.messageId (triggering message) in
normal groups to prevent silent topic thread creation (#32980). Preserve
ctx.rootId targeting for topic-mode groups (group_topic/group_topic_sender)
and groups with explicit replyInThread config.
- Add regression tests for both fixes.
Fixes#32980Fixes#32958
Related #19784
* fix: normalize Feishu delivery.to before comparing with messaging tool targets
- Add normalizeDeliveryTarget helper to strip user:/chat: prefixes for Feishu
- Apply normalization in matchesMessagingToolDeliveryTarget before comparison
- This ensures cron duplicate suppression works when session uses prefixed targets
(user:ou_xxx) but messaging tool extract uses normalized bare IDs (ou_xxx)
Fixes review comment on PR #32755
(cherry picked from commit fc20106f16)
* fix(feishu): catch thrown SDK errors for withdrawn reply targets
The Feishu Lark SDK can throw exceptions (SDK errors with .code or
AxiosErrors with .response.data.code) for withdrawn/deleted reply
targets, in addition to returning error codes in the response object.
Wrap reply calls in sendMessageFeishu and sendCardFeishu with
try-catch to handle thrown withdrawn/not-found errors (230011,
231003) and fall back to client.im.message.create, matching the
existing response-level fallback behavior.
Also extract sendFallbackDirect helper to deduplicate the
direct-send fallback block across both functions.
Closes#33496
(cherry picked from commit ad0901aec1)
* feishu: forward outbound reply target context
(cherry picked from commit c129a691fc)
* feishu extension: tighten reply target fallback semantics
(cherry picked from commit f85ec610f2)
* fix(feishu): align synthesized fallback typing and changelog attribution
* test(feishu): cover group_topic_sender reply targeting
---------
Co-authored-by: Xu Zimo <xuzimojimmy@163.com>
Co-authored-by: Munem Hashmi <munem.hashmi@gmail.com>
Co-authored-by: bmendonca3 <bmendonca3@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>