feat(mattermost): interactive buttons + channel name resolution#18151
Closed
tonydehnke wants to merge 9 commits intoopenclaw:mainfrom
Closed
feat(mattermost): interactive buttons + channel name resolution#18151tonydehnke wants to merge 9 commits intoopenclaw:mainfrom
tonydehnke wants to merge 9 commits intoopenclaw:mainfrom
Conversation
…AllowFrom When a message in a channel is dropped because the sender is not in groupAllowFrom (e.g. another agent's bot reply), the message is now saved as pending history. This preserves conversation context so that when an agent is later @mentioned, it sees the full conversation including other agents' replies. Previously, the early return in the groupAllowFrom check meant these messages were silently discarded with no history record, causing agents to miss parts of multi-agent channel conversations.
Enables agents to send messages using channel names (e.g. target: "infra") instead of 26-character Mattermost IDs. - Add directory.ts: multi-account channel/user discovery - Add directory property to channel plugin - Fix normalize: bare names fall through to directory lookup - Fix looksLikeId: strict 26-char regex + DM format Closes openclaw#19264
…stPeers Both functions accepted a limit param but never applied it to the returned results. Slice entries before returning when limit is set. Reported by Greptile review bot.
Keep both directory adapter and new actions adapter from upstream.
9ced58b to
08ad331
Compare
…ution Interactive message buttons: - New `interactions.ts`: HMAC-SHA256 token verification (sorted keys), localhost-only validation, HTTP callback handler, button builder with required `type: "button"` field - `client.ts`: add `props` to `createMattermostPost`, new `fetchMattermostPost` and `updateMattermostPost` functions - `send.ts`: `props` passthrough in `MattermostSendOpts` - `monitor.ts`: register `/mattermost/interactions/:accountId` HTTP route, init HMAC secret from bot token - `channel.ts`: `send` action (independent of reactions config), `supportsButtons`, `handleSendAction` with button attachment building Button click behavior: - All buttons removed on click (Mattermost strips integration data from GET responses, making remaining buttons inert) - Replaced with "✓ [name] selected by @user" - Uses both callback update response and REST API PUT for reliable UI refresh across all connected clients Channel name resolution: - `normalize.ts`: `#channel-name` returns `undefined` (triggering directory lookup) instead of `channel:name` (which skipped directory and caused 403) - `looksLikeMattermostTargetId`: `#name` no longer treated as known target, allowing directory adapter resolution
239ffa9 to
1be9e50
Compare
The core message tool schema defines buttons as Array<Array<Button>> (rows of buttons for Telegram's inline keyboard layout). The Mattermost extension was treating this as a flat 1D array, so each "button" was actually a row array — btn.text/btn.id resolved to undefined, producing empty names that Mattermost rejected with "action must have a name". Use flatMap to flatten rows into a single list (Mattermost has no row layout) and filter out any buttons with empty id or name.
Use Object.defineProperty for socket mock and proper cast for destroy to satisfy strict type checking with tsgo.
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Feb 21, 2026
…ests Port missing pieces from PR openclaw#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"
8 tasks
Contributor
Author
|
Closing in favor of #19957 which now includes all features from this PR:
Reactions were dropped since they landed upstream via #18608. |
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Feb 21, 2026
The core sends buttons as Array<Array<Button>> (2D for Telegram row layout). The consolidation from openclaw#18151 into openclaw#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.
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Feb 23, 2026
…ests Port missing pieces from PR openclaw#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"
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Feb 23, 2026
The core sends buttons as Array<Array<Button>> (2D for Telegram row layout). The consolidation from openclaw#18151 into openclaw#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.
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Feb 23, 2026
…ests Port missing pieces from PR openclaw#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"
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Feb 23, 2026
The core sends buttons as Array<Array<Button>> (2D for Telegram row layout). The consolidation from openclaw#18151 into openclaw#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.
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Feb 24, 2026
…ests Port missing pieces from PR openclaw#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"
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Feb 24, 2026
The core sends buttons as Array<Array<Button>> (2D for Telegram row layout). The consolidation from openclaw#18151 into openclaw#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.
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Feb 26, 2026
…ests Port missing pieces from PR openclaw#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"
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Feb 26, 2026
The core sends buttons as Array<Array<Button>> (2D for Telegram row layout). The consolidation from openclaw#18151 into openclaw#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.
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Feb 27, 2026
…ests Port missing pieces from PR openclaw#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"
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Feb 27, 2026
The core sends buttons as Array<Array<Button>> (2D for Telegram row layout). The consolidation from openclaw#18151 into openclaw#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.
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Mar 2, 2026
…ests Port missing pieces from PR openclaw#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"
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Mar 2, 2026
The core sends buttons as Array<Array<Button>> (2D for Telegram row layout). The consolidation from openclaw#18151 into openclaw#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.
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Mar 2, 2026
…ests Port missing pieces from PR openclaw#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"
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Mar 2, 2026
The core sends buttons as Array<Array<Button>> (2D for Telegram row layout). The consolidation from openclaw#18151 into openclaw#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.
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Mar 2, 2026
…ests Port missing pieces from PR openclaw#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"
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Mar 2, 2026
The core sends buttons as Array<Array<Button>> (2D for Telegram row layout). The consolidation from openclaw#18151 into openclaw#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.
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Mar 5, 2026
…ests Port missing pieces from PR openclaw#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"
tonydehnke
added a commit
to tonydehnke/openclaw
that referenced
this pull request
Mar 5, 2026
The core sends buttons as Array<Array<Button>> (2D for Telegram row layout). The consolidation from openclaw#18151 into openclaw#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.
mukhtharcm
pushed a commit
to tonydehnke/openclaw
that referenced
this pull request
Mar 5, 2026
…ests Port missing pieces from PR openclaw#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"
mukhtharcm
pushed a commit
to tonydehnke/openclaw
that referenced
this pull request
Mar 5, 2026
The core sends buttons as Array<Array<Button>> (2D for Telegram row layout). The consolidation from openclaw#18151 into openclaw#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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds interactive message buttons and channel name resolution for the Mattermost extension.
Depends on #19265 (directory adapter for channel/user name resolution).
Interactive buttons
interactions.ts: HMAC-SHA256 token verification (sorted keys), localhost-only validation, HTTP callback handler, button builder withtype: "button"client.ts:propsparam oncreateMattermostPost, newfetchMattermostPostandupdateMattermostPostfunctionssend.ts:propspassthrough inMattermostSendOptsmonitor.ts: register interaction HTTP route viaregisterPluginHttpRoutechannel.ts:sendaction withsupportsButtons, button attachment building, media passthroughChannel name resolution
normalize.ts:#channel-nametargets returnundefined(triggering directory lookup) instead ofchannel:name(which skipped directory and used raw name as ID, causing 403)looksLikeMattermostTargetId: removed#from known-target check so#namegoes through directoryButton click behavior
updateresponse (immediate UI refresh) and REST APIPUT /posts/{id}(WebSocket clients)integrationdata from API GET responses, making remaining buttons inertChanges
interactions.ts(new)type: "button", fetch-then-update handlerclient.tspropstocreateMattermostPost, addfetchMattermostPost, addupdateMattermostPostsend.tspropstoMattermostSendOptsand pass throughchannel.tssendaction independent of reactions config,supportsButtons,handleSendActionwith button attachment buildingmonitor.ts/mattermost/interactions/:accountIdHTTP route, init HMAC secret from bot tokennormalize.ts#name→undefined(directory lookup),looksLikeIdreturnsfalsefor#channel.test.tssendactionKey implementation details
JSON.stringify(context, Object.keys(context).sort())— Mattermost reorders context keys when storing payloadstype: "button"required: Without it, buttons render but callbacks silently never fireintegration(URL + context + token) from GET responses, so remaining buttons would lose their callback configupdateresponse for clicking user + REST API PUT for all clientsTest plan
npx oxfmt --check extensions/mattermost/— formatting cleanpnpm tsgo— no new type errorsopenclaw message send --buttonswith multiple buttons#off-topictarget resolves via directory (was 403, now works)off-topictarget resolves via directorychannel:<id>direct target still works