Skip to content

Commit cdbed3c

Browse files
steipetenovan
andcommitted
fix(googlechat): land #30965 thread reply option support (@novan)
Landed from contributor PR #30965 by @novan. Co-authored-by: novan <[email protected]>
1 parent 355b4c6 commit cdbed3c

3 files changed

Lines changed: 54 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ Docs: https://docs.openclaw.ai
112112

113113
### Fixes
114114

115+
- Google Chat/Thread replies: set `messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD` on threaded sends so replies attach to existing threads instead of silently failing thread placement. Landed from contributor PR #30965 by @novan. Thanks @novan.
115116
- Mattermost/Private channel policy routing: map Mattermost private channel type `P` to group chat type so `groupPolicy`/`groupAllowFrom` gates apply correctly instead of being treated as open public channels. Landed from contributor PR #30891 by @BlueBirdBack. Thanks @BlueBirdBack.
116117
- Models/Custom provider keys: trim custom provider map keys during normalization so image-capable models remain discoverable when provider keys are configured with leading/trailing whitespace. Landed from contributor PR #31202 by @stakeswky. Thanks @stakeswky.
117118
- Discord/Agent component interactions: accept Components v2 `cid` payloads alongside legacy `componentId`, and safely decode percent-encoded IDs without throwing on malformed `%` sequences. Landed from contributor PR #29013 by @Jacky1n7. Thanks @Jacky1n7.

extensions/googlechat/src/api.test.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { afterEach, describe, expect, it, vi } from "vitest";
22
import type { ResolvedGoogleChatAccount } from "./accounts.js";
3-
import { downloadGoogleChatMedia } from "./api.js";
3+
import { downloadGoogleChatMedia, sendGoogleChatMessage } from "./api.js";
44

55
vi.mock("./auth.js", () => ({
66
getGoogleChatAccessToken: vi.fn().mockResolvedValue("token"),
@@ -59,3 +59,50 @@ describe("downloadGoogleChatMedia", () => {
5959
).rejects.toThrow(/max bytes/i);
6060
});
6161
});
62+
63+
describe("sendGoogleChatMessage", () => {
64+
afterEach(() => {
65+
vi.unstubAllGlobals();
66+
});
67+
68+
it("adds messageReplyOption when sending to an existing thread", async () => {
69+
const fetchMock = vi
70+
.fn()
71+
.mockResolvedValue(
72+
new Response(JSON.stringify({ name: "spaces/AAA/messages/123" }), { status: 200 }),
73+
);
74+
vi.stubGlobal("fetch", fetchMock);
75+
76+
await sendGoogleChatMessage({
77+
account,
78+
space: "spaces/AAA",
79+
text: "hello",
80+
thread: "spaces/AAA/threads/xyz",
81+
});
82+
83+
const [url, init] = fetchMock.mock.calls[0] ?? [];
84+
expect(String(url)).toContain("messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD");
85+
expect(JSON.parse(String(init?.body))).toMatchObject({
86+
text: "hello",
87+
thread: { name: "spaces/AAA/threads/xyz" },
88+
});
89+
});
90+
91+
it("does not set messageReplyOption for non-thread sends", async () => {
92+
const fetchMock = vi
93+
.fn()
94+
.mockResolvedValue(
95+
new Response(JSON.stringify({ name: "spaces/AAA/messages/124" }), { status: 200 }),
96+
);
97+
vi.stubGlobal("fetch", fetchMock);
98+
99+
await sendGoogleChatMessage({
100+
account,
101+
space: "spaces/AAA",
102+
text: "hello",
103+
});
104+
105+
const [url] = fetchMock.mock.calls[0] ?? [];
106+
expect(String(url)).not.toContain("messageReplyOption=");
107+
});
108+
});

extensions/googlechat/src/api.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,11 @@ export async function sendGoogleChatMessage(params: {
128128
...(item.contentName ? { contentName: item.contentName } : {}),
129129
}));
130130
}
131-
const url = `${CHAT_API_BASE}/${space}/messages`;
131+
const urlObj = new URL(`${CHAT_API_BASE}/${space}/messages`);
132+
if (thread) {
133+
urlObj.searchParams.set("messageReplyOption", "REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD");
134+
}
135+
const url = urlObj.toString();
132136
const result = await fetchJson<{ name?: string }>(account, url, {
133137
method: "POST",
134138
body: JSON.stringify(body),

0 commit comments

Comments
 (0)