mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
fix(mattermost): read replyTo param in plugin handleAction send (#41176)
Merged via squash.
Prepared head SHA: 33cac4c33f
Co-authored-by: hnykda <2741256+hnykda@users.noreply.github.com>
Co-authored-by: mukhtharcm <56378562+mukhtharcm@users.noreply.github.com>
Reviewed-by: @mukhtharcm
This commit is contained in:
@@ -54,6 +54,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
- Agents/memory flush: forward `memoryFlushWritePath` through `runEmbeddedPiAgent` so memory-triggered flush turns keep the append-only write guard without aborting before tool setup. Follows up on #38574. (#41761) Thanks @frankekn.
|
- Agents/memory flush: forward `memoryFlushWritePath` through `runEmbeddedPiAgent` so memory-triggered flush turns keep the append-only write guard without aborting before tool setup. Follows up on #38574. (#41761) Thanks @frankekn.
|
||||||
- CI/CodeQL Swift toolchain: select Xcode 26.1 before installing Swift build tools so the CodeQL Swift job uses Swift tools 6.2 on `macos-latest`. (#41787) thanks @BunsDev.
|
- CI/CodeQL Swift toolchain: select Xcode 26.1 before installing Swift build tools so the CodeQL Swift job uses Swift tools 6.2 on `macos-latest`. (#41787) thanks @BunsDev.
|
||||||
- Sandbox/subagents: pass the real configured workspace through `sessions_spawn` inheritance when a parent agent runs in a copied-workspace sandbox, so child `/agent` mounts point at the configured workspace instead of the parent sandbox copy. (#40757) Thanks @dsantoreis.
|
- Sandbox/subagents: pass the real configured workspace through `sessions_spawn` inheritance when a parent agent runs in a copied-workspace sandbox, so child `/agent` mounts point at the configured workspace instead of the parent sandbox copy. (#40757) Thanks @dsantoreis.
|
||||||
|
- Mattermost/plugin send actions: normalize direct `replyTo` fallback handling so threaded plugin sends trim blank IDs and reuse the correct reply target again. (#41176) Thanks @hnykda.
|
||||||
|
|
||||||
## 2026.3.8
|
## 2026.3.8
|
||||||
|
|
||||||
|
|||||||
@@ -214,6 +214,57 @@ describe("mattermostPlugin", () => {
|
|||||||
]);
|
]);
|
||||||
expect(result?.details).toEqual({});
|
expect(result?.details).toEqual({});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("maps replyTo to replyToId for send actions", async () => {
|
||||||
|
const cfg = createMattermostTestConfig();
|
||||||
|
|
||||||
|
await mattermostPlugin.actions?.handleAction?.({
|
||||||
|
channel: "mattermost",
|
||||||
|
action: "send",
|
||||||
|
params: {
|
||||||
|
to: "channel:CHAN1",
|
||||||
|
message: "hello",
|
||||||
|
replyTo: "post-root",
|
||||||
|
},
|
||||||
|
cfg,
|
||||||
|
accountId: "default",
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
expect(sendMessageMattermostMock).toHaveBeenCalledWith(
|
||||||
|
"channel:CHAN1",
|
||||||
|
"hello",
|
||||||
|
expect.objectContaining({
|
||||||
|
accountId: "default",
|
||||||
|
replyToId: "post-root",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("falls back to trimmed replyTo when replyToId is blank", async () => {
|
||||||
|
const cfg = createMattermostTestConfig();
|
||||||
|
|
||||||
|
await mattermostPlugin.actions?.handleAction?.({
|
||||||
|
channel: "mattermost",
|
||||||
|
action: "send",
|
||||||
|
params: {
|
||||||
|
to: "channel:CHAN1",
|
||||||
|
message: "hello",
|
||||||
|
replyToId: " ",
|
||||||
|
replyTo: " post-root ",
|
||||||
|
},
|
||||||
|
cfg,
|
||||||
|
accountId: "default",
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
expect(sendMessageMattermostMock).toHaveBeenCalledWith(
|
||||||
|
"channel:CHAN1",
|
||||||
|
"hello",
|
||||||
|
expect.objectContaining({
|
||||||
|
accountId: "default",
|
||||||
|
replyToId: "post-root",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("outbound", () => {
|
describe("outbound", () => {
|
||||||
|
|||||||
@@ -157,7 +157,9 @@ const mattermostMessageActions: ChannelMessageActionAdapter = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const message = typeof params.message === "string" ? params.message : "";
|
const message = typeof params.message === "string" ? params.message : "";
|
||||||
const replyToId = typeof params.replyToId === "string" ? params.replyToId : undefined;
|
// Match the shared runner semantics: trim empty reply IDs away before
|
||||||
|
// falling back from replyToId to replyTo on direct plugin calls.
|
||||||
|
const replyToId = readMattermostReplyToId(params);
|
||||||
const resolvedAccountId = accountId || undefined;
|
const resolvedAccountId = accountId || undefined;
|
||||||
|
|
||||||
const mediaUrl =
|
const mediaUrl =
|
||||||
@@ -201,6 +203,18 @@ const meta = {
|
|||||||
quickstartAllowFrom: true,
|
quickstartAllowFrom: true,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
function readMattermostReplyToId(params: Record<string, unknown>): string | undefined {
|
||||||
|
const readNormalizedValue = (value: unknown) => {
|
||||||
|
if (typeof value !== "string") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const trimmed = value.trim();
|
||||||
|
return trimmed || undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
return readNormalizedValue(params.replyToId) ?? readNormalizedValue(params.replyTo);
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeAllowEntry(entry: string): string {
|
function normalizeAllowEntry(entry: string): string {
|
||||||
return entry
|
return entry
|
||||||
.trim()
|
.trim()
|
||||||
|
|||||||
Reference in New Issue
Block a user