@openclaw/googlechat 2026.5.2-beta.2 → 2026.5.3-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-C67jv0ow.js +627 -0
- package/dist/api.js +3 -0
- package/dist/channel-DE2hroMn.js +682 -0
- package/dist/channel-config-api.js +5 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-CoSLc-4R.js +920 -0
- package/dist/contract-api.js +3 -0
- package/dist/doctor-contract-CG1sLToP.js +151 -0
- package/dist/doctor-contract-api.js +2 -0
- package/dist/index.js +22 -0
- package/dist/runtime-api-wkIdfwqY.js +30 -0
- package/dist/runtime-api.js +2 -0
- package/dist/secret-contract-DtQ_IO7J.js +99 -0
- package/dist/secret-contract-api.js +2 -0
- package/dist/setup-entry.js +15 -0
- package/dist/setup-plugin-api.js +75 -0
- package/dist/setup-surface-CofP-Gg9.js +309 -0
- package/dist/test-api.js +3 -0
- package/package.json +14 -6
- package/api.ts +0 -3
- package/channel-config-api.ts +0 -1
- package/channel-plugin-api.ts +0 -1
- package/contract-api.ts +0 -5
- package/doctor-contract-api.ts +0 -1
- package/index.ts +0 -20
- package/runtime-api.ts +0 -60
- package/secret-contract-api.ts +0 -5
- package/setup-entry.ts +0 -13
- package/setup-plugin-api.ts +0 -3
- package/src/accounts.ts +0 -169
- package/src/actions.test.ts +0 -265
- package/src/actions.ts +0 -227
- package/src/api.ts +0 -322
- package/src/approval-auth.test.ts +0 -24
- package/src/approval-auth.ts +0 -32
- package/src/auth.ts +0 -209
- package/src/channel-config.test.ts +0 -39
- package/src/channel.adapters.ts +0 -293
- package/src/channel.deps.runtime.ts +0 -28
- package/src/channel.runtime.ts +0 -17
- package/src/channel.setup.ts +0 -98
- package/src/channel.test.ts +0 -695
- package/src/channel.ts +0 -256
- package/src/config-schema.test.ts +0 -16
- package/src/config-schema.ts +0 -3
- package/src/doctor-contract.test.ts +0 -75
- package/src/doctor-contract.ts +0 -182
- package/src/doctor.ts +0 -57
- package/src/gateway.ts +0 -63
- package/src/google-auth.runtime.test.ts +0 -462
- package/src/google-auth.runtime.ts +0 -539
- package/src/group-policy.ts +0 -17
- package/src/monitor-access.test.ts +0 -443
- package/src/monitor-access.ts +0 -405
- package/src/monitor-reply-delivery.ts +0 -156
- package/src/monitor-routing.ts +0 -55
- package/src/monitor-types.ts +0 -33
- package/src/monitor-webhook.test.ts +0 -446
- package/src/monitor-webhook.ts +0 -285
- package/src/monitor.reply-delivery.test.ts +0 -139
- package/src/monitor.ts +0 -428
- package/src/monitor.webhook-routing.test.ts +0 -252
- package/src/runtime.ts +0 -9
- package/src/secret-contract.test.ts +0 -60
- package/src/secret-contract.ts +0 -161
- package/src/sender-allow.ts +0 -46
- package/src/setup-core.ts +0 -40
- package/src/setup-surface.ts +0 -236
- package/src/setup.test.ts +0 -560
- package/src/targets.test.ts +0 -421
- package/src/targets.ts +0 -66
- package/src/types.config.ts +0 -3
- package/src/types.ts +0 -73
- package/test-api.ts +0 -2
- package/tsconfig.json +0 -16
package/src/actions.test.ts
DELETED
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
-
|
|
4
|
-
const listEnabledGoogleChatAccounts = vi.hoisted(() => vi.fn());
|
|
5
|
-
const resolveGoogleChatAccount = vi.hoisted(() => vi.fn());
|
|
6
|
-
const createGoogleChatReaction = vi.hoisted(() => vi.fn());
|
|
7
|
-
const deleteGoogleChatReaction = vi.hoisted(() => vi.fn());
|
|
8
|
-
const listGoogleChatReactions = vi.hoisted(() => vi.fn());
|
|
9
|
-
const sendGoogleChatMessage = vi.hoisted(() => vi.fn());
|
|
10
|
-
const uploadGoogleChatAttachment = vi.hoisted(() => vi.fn());
|
|
11
|
-
const resolveGoogleChatOutboundSpace = vi.hoisted(() => vi.fn());
|
|
12
|
-
const getGoogleChatRuntime = vi.hoisted(() => vi.fn());
|
|
13
|
-
|
|
14
|
-
vi.mock("./accounts.js", () => ({
|
|
15
|
-
listEnabledGoogleChatAccounts,
|
|
16
|
-
resolveGoogleChatAccount,
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
|
-
vi.mock("./api.js", () => ({
|
|
20
|
-
createGoogleChatReaction,
|
|
21
|
-
deleteGoogleChatReaction,
|
|
22
|
-
listGoogleChatReactions,
|
|
23
|
-
sendGoogleChatMessage,
|
|
24
|
-
uploadGoogleChatAttachment,
|
|
25
|
-
}));
|
|
26
|
-
|
|
27
|
-
vi.mock("./runtime.js", () => ({
|
|
28
|
-
getGoogleChatRuntime,
|
|
29
|
-
}));
|
|
30
|
-
|
|
31
|
-
vi.mock("./targets.js", () => ({
|
|
32
|
-
resolveGoogleChatOutboundSpace,
|
|
33
|
-
}));
|
|
34
|
-
|
|
35
|
-
let googlechatMessageActions: typeof import("./actions.js").googlechatMessageActions;
|
|
36
|
-
|
|
37
|
-
describe("googlechat message actions", () => {
|
|
38
|
-
beforeAll(async () => {
|
|
39
|
-
({ googlechatMessageActions } = await import("./actions.js"));
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
beforeEach(() => {
|
|
43
|
-
vi.clearAllMocks();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("describes send and reaction actions only when enabled accounts exist", async () => {
|
|
47
|
-
listEnabledGoogleChatAccounts.mockReturnValueOnce([]);
|
|
48
|
-
expect(googlechatMessageActions.describeMessageTool?.({ cfg: {} as never })).toBeNull();
|
|
49
|
-
|
|
50
|
-
listEnabledGoogleChatAccounts.mockReturnValueOnce([
|
|
51
|
-
{
|
|
52
|
-
enabled: true,
|
|
53
|
-
credentialSource: "service-account",
|
|
54
|
-
config: { actions: { reactions: true } },
|
|
55
|
-
},
|
|
56
|
-
]);
|
|
57
|
-
|
|
58
|
-
expect(googlechatMessageActions.describeMessageTool?.({ cfg: {} as never })).toEqual({
|
|
59
|
-
actions: ["send", "upload-file", "react", "reactions"],
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("honors account-scoped reaction gates during discovery", () => {
|
|
64
|
-
resolveGoogleChatAccount.mockImplementation(({ accountId }: { accountId?: string | null }) => ({
|
|
65
|
-
enabled: true,
|
|
66
|
-
credentialSource: "service-account",
|
|
67
|
-
config: {
|
|
68
|
-
actions: { reactions: accountId === "work" },
|
|
69
|
-
},
|
|
70
|
-
}));
|
|
71
|
-
|
|
72
|
-
expect(
|
|
73
|
-
googlechatMessageActions.describeMessageTool?.({ cfg: {} as never, accountId: "default" }),
|
|
74
|
-
).toEqual({
|
|
75
|
-
actions: ["send", "upload-file"],
|
|
76
|
-
});
|
|
77
|
-
expect(
|
|
78
|
-
googlechatMessageActions.describeMessageTool?.({ cfg: {} as never, accountId: "work" }),
|
|
79
|
-
).toEqual({
|
|
80
|
-
actions: ["send", "upload-file", "react", "reactions"],
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("sends messages with uploaded media through the resolved space", async () => {
|
|
85
|
-
resolveGoogleChatAccount.mockReturnValue({
|
|
86
|
-
credentialSource: "service-account",
|
|
87
|
-
config: { mediaMaxMb: 5 },
|
|
88
|
-
});
|
|
89
|
-
resolveGoogleChatOutboundSpace.mockResolvedValue("spaces/AAA");
|
|
90
|
-
getGoogleChatRuntime.mockReturnValue({
|
|
91
|
-
channel: {
|
|
92
|
-
media: {
|
|
93
|
-
fetchRemoteMedia: vi.fn(async () => ({
|
|
94
|
-
buffer: Buffer.from("remote-bytes"),
|
|
95
|
-
fileName: "remote.png",
|
|
96
|
-
contentType: "image/png",
|
|
97
|
-
})),
|
|
98
|
-
},
|
|
99
|
-
},
|
|
100
|
-
});
|
|
101
|
-
uploadGoogleChatAttachment.mockResolvedValue({
|
|
102
|
-
attachmentUploadToken: "token-1",
|
|
103
|
-
});
|
|
104
|
-
sendGoogleChatMessage.mockResolvedValue({
|
|
105
|
-
messageName: "spaces/AAA/messages/msg-1",
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
if (!googlechatMessageActions.handleAction) {
|
|
109
|
-
throw new Error("Expected googlechatMessageActions.handleAction to be defined");
|
|
110
|
-
}
|
|
111
|
-
const result = await googlechatMessageActions.handleAction({
|
|
112
|
-
action: "send",
|
|
113
|
-
params: {
|
|
114
|
-
to: "spaces/AAA",
|
|
115
|
-
message: "caption",
|
|
116
|
-
media: "https://example.com/file.png",
|
|
117
|
-
threadId: "thread-1",
|
|
118
|
-
},
|
|
119
|
-
cfg: {},
|
|
120
|
-
accountId: "default",
|
|
121
|
-
} as never);
|
|
122
|
-
|
|
123
|
-
expect(resolveGoogleChatOutboundSpace).toHaveBeenCalledWith(
|
|
124
|
-
expect.objectContaining({
|
|
125
|
-
target: "spaces/AAA",
|
|
126
|
-
}),
|
|
127
|
-
);
|
|
128
|
-
expect(uploadGoogleChatAttachment).toHaveBeenCalledWith(
|
|
129
|
-
expect.objectContaining({
|
|
130
|
-
space: "spaces/AAA",
|
|
131
|
-
filename: "remote.png",
|
|
132
|
-
}),
|
|
133
|
-
);
|
|
134
|
-
expect(sendGoogleChatMessage).toHaveBeenCalledWith(
|
|
135
|
-
expect.objectContaining({
|
|
136
|
-
space: "spaces/AAA",
|
|
137
|
-
text: "caption",
|
|
138
|
-
thread: "thread-1",
|
|
139
|
-
}),
|
|
140
|
-
);
|
|
141
|
-
expect(result).toMatchObject({
|
|
142
|
-
details: {
|
|
143
|
-
ok: true,
|
|
144
|
-
to: "spaces/AAA",
|
|
145
|
-
},
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it("routes upload-file through the same attachment upload path with filename override", async () => {
|
|
150
|
-
resolveGoogleChatAccount.mockReturnValue({
|
|
151
|
-
credentialSource: "service-account",
|
|
152
|
-
config: { mediaMaxMb: 5 },
|
|
153
|
-
});
|
|
154
|
-
resolveGoogleChatOutboundSpace.mockResolvedValue("spaces/BBB");
|
|
155
|
-
const localRoot = "/tmp/googlechat-action-test";
|
|
156
|
-
const localPath = path.join(localRoot, "local.md");
|
|
157
|
-
const readFile = vi.fn(async () => Buffer.from("local-bytes"));
|
|
158
|
-
getGoogleChatRuntime.mockReturnValue({
|
|
159
|
-
channel: {
|
|
160
|
-
media: {
|
|
161
|
-
fetchRemoteMedia: vi.fn(),
|
|
162
|
-
},
|
|
163
|
-
},
|
|
164
|
-
});
|
|
165
|
-
uploadGoogleChatAttachment.mockResolvedValue({
|
|
166
|
-
attachmentUploadToken: "token-2",
|
|
167
|
-
});
|
|
168
|
-
sendGoogleChatMessage.mockResolvedValue({
|
|
169
|
-
messageName: "spaces/BBB/messages/msg-2",
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
if (!googlechatMessageActions.handleAction) {
|
|
173
|
-
throw new Error("Expected googlechatMessageActions.handleAction to be defined");
|
|
174
|
-
}
|
|
175
|
-
const result = await googlechatMessageActions.handleAction({
|
|
176
|
-
action: "upload-file",
|
|
177
|
-
params: {
|
|
178
|
-
to: "spaces/BBB",
|
|
179
|
-
path: localPath,
|
|
180
|
-
message: "notes",
|
|
181
|
-
filename: "renamed.txt",
|
|
182
|
-
},
|
|
183
|
-
cfg: {},
|
|
184
|
-
accountId: "default",
|
|
185
|
-
mediaLocalRoots: [localRoot],
|
|
186
|
-
mediaReadFile: readFile,
|
|
187
|
-
} as never);
|
|
188
|
-
|
|
189
|
-
expect(readFile).toHaveBeenCalledWith(localPath);
|
|
190
|
-
expect(uploadGoogleChatAttachment).toHaveBeenCalledWith(
|
|
191
|
-
expect.objectContaining({
|
|
192
|
-
space: "spaces/BBB",
|
|
193
|
-
filename: "renamed.txt",
|
|
194
|
-
buffer: Buffer.from("local-bytes"),
|
|
195
|
-
}),
|
|
196
|
-
);
|
|
197
|
-
expect(sendGoogleChatMessage).toHaveBeenCalledWith(
|
|
198
|
-
expect.objectContaining({
|
|
199
|
-
space: "spaces/BBB",
|
|
200
|
-
text: "notes",
|
|
201
|
-
attachments: [{ attachmentUploadToken: "token-2", contentName: "renamed.txt" }],
|
|
202
|
-
}),
|
|
203
|
-
);
|
|
204
|
-
expect(result).toMatchObject({
|
|
205
|
-
details: {
|
|
206
|
-
ok: true,
|
|
207
|
-
to: "spaces/BBB",
|
|
208
|
-
},
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
it("removes only matching app reactions on react remove", async () => {
|
|
213
|
-
resolveGoogleChatAccount.mockReturnValue({
|
|
214
|
-
credentialSource: "service-account",
|
|
215
|
-
config: { botUser: "users/app-bot" },
|
|
216
|
-
});
|
|
217
|
-
listGoogleChatReactions.mockResolvedValue([
|
|
218
|
-
{
|
|
219
|
-
name: "reactions/1",
|
|
220
|
-
emoji: { unicode: "👍" },
|
|
221
|
-
user: { name: "users/app" },
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
name: "reactions/2",
|
|
225
|
-
emoji: { unicode: "👍" },
|
|
226
|
-
user: { name: "users/app-bot" },
|
|
227
|
-
},
|
|
228
|
-
{
|
|
229
|
-
name: "reactions/3",
|
|
230
|
-
emoji: { unicode: "👍" },
|
|
231
|
-
user: { name: "users/other" },
|
|
232
|
-
},
|
|
233
|
-
]);
|
|
234
|
-
|
|
235
|
-
if (!googlechatMessageActions.handleAction) {
|
|
236
|
-
throw new Error("Expected googlechatMessageActions.handleAction to be defined");
|
|
237
|
-
}
|
|
238
|
-
const result = await googlechatMessageActions.handleAction({
|
|
239
|
-
action: "react",
|
|
240
|
-
params: {
|
|
241
|
-
messageId: "spaces/AAA/messages/msg-1",
|
|
242
|
-
emoji: "👍",
|
|
243
|
-
remove: true,
|
|
244
|
-
},
|
|
245
|
-
cfg: {},
|
|
246
|
-
accountId: "default",
|
|
247
|
-
} as never);
|
|
248
|
-
|
|
249
|
-
expect(deleteGoogleChatReaction).toHaveBeenCalledTimes(2);
|
|
250
|
-
expect(deleteGoogleChatReaction).toHaveBeenNthCalledWith(1, {
|
|
251
|
-
account: expect.anything(),
|
|
252
|
-
reactionName: "reactions/1",
|
|
253
|
-
});
|
|
254
|
-
expect(deleteGoogleChatReaction).toHaveBeenNthCalledWith(2, {
|
|
255
|
-
account: expect.anything(),
|
|
256
|
-
reactionName: "reactions/2",
|
|
257
|
-
});
|
|
258
|
-
expect(result).toMatchObject({
|
|
259
|
-
details: {
|
|
260
|
-
ok: true,
|
|
261
|
-
removed: 2,
|
|
262
|
-
},
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
});
|
package/src/actions.ts
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createActionGate,
|
|
3
|
-
jsonResult,
|
|
4
|
-
readNumberParam,
|
|
5
|
-
readReactionParams,
|
|
6
|
-
readStringParam,
|
|
7
|
-
} from "openclaw/plugin-sdk/channel-actions";
|
|
8
|
-
import type {
|
|
9
|
-
ChannelMessageActionAdapter,
|
|
10
|
-
ChannelMessageActionName,
|
|
11
|
-
} from "openclaw/plugin-sdk/channel-contract";
|
|
12
|
-
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-types";
|
|
13
|
-
import { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";
|
|
14
|
-
import { extractToolSend } from "openclaw/plugin-sdk/tool-send";
|
|
15
|
-
import { listEnabledGoogleChatAccounts, resolveGoogleChatAccount } from "./accounts.js";
|
|
16
|
-
import {
|
|
17
|
-
createGoogleChatReaction,
|
|
18
|
-
deleteGoogleChatReaction,
|
|
19
|
-
listGoogleChatReactions,
|
|
20
|
-
sendGoogleChatMessage,
|
|
21
|
-
uploadGoogleChatAttachment,
|
|
22
|
-
} from "./api.js";
|
|
23
|
-
import { getGoogleChatRuntime } from "./runtime.js";
|
|
24
|
-
import { resolveGoogleChatOutboundSpace } from "./targets.js";
|
|
25
|
-
|
|
26
|
-
const providerId = "googlechat";
|
|
27
|
-
|
|
28
|
-
function listEnabledAccounts(cfg: OpenClawConfig) {
|
|
29
|
-
return listEnabledGoogleChatAccounts(cfg).filter(
|
|
30
|
-
(account) => account.enabled && account.credentialSource !== "none",
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function isReactionsEnabled(accounts: Array<{ config: { actions?: unknown } }>) {
|
|
35
|
-
for (const account of accounts) {
|
|
36
|
-
const gate = createActionGate(account.config.actions as Record<string, boolean | undefined>);
|
|
37
|
-
if (gate("reactions")) {
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function resolveAppUserNames(account: { config: { botUser?: string | null } }) {
|
|
45
|
-
return new Set(["users/app", account.config.botUser?.trim()].filter(Boolean) as string[]);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async function loadGoogleChatActionMedia(params: {
|
|
49
|
-
mediaUrl: string;
|
|
50
|
-
maxBytes: number;
|
|
51
|
-
mediaAccess?: {
|
|
52
|
-
localRoots?: readonly string[];
|
|
53
|
-
readFile?: (filePath: string) => Promise<Buffer>;
|
|
54
|
-
};
|
|
55
|
-
mediaLocalRoots?: readonly string[];
|
|
56
|
-
mediaReadFile?: (filePath: string) => Promise<Buffer>;
|
|
57
|
-
}) {
|
|
58
|
-
const runtime = getGoogleChatRuntime();
|
|
59
|
-
return /^https?:\/\//i.test(params.mediaUrl)
|
|
60
|
-
? await runtime.channel.media.fetchRemoteMedia({
|
|
61
|
-
url: params.mediaUrl,
|
|
62
|
-
maxBytes: params.maxBytes,
|
|
63
|
-
})
|
|
64
|
-
: await loadOutboundMediaFromUrl(params.mediaUrl, {
|
|
65
|
-
maxBytes: params.maxBytes,
|
|
66
|
-
mediaAccess: params.mediaAccess,
|
|
67
|
-
mediaLocalRoots: params.mediaLocalRoots,
|
|
68
|
-
mediaReadFile: params.mediaReadFile,
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export const googlechatMessageActions: ChannelMessageActionAdapter = {
|
|
73
|
-
describeMessageTool: ({ cfg, accountId }) => {
|
|
74
|
-
const accounts = accountId
|
|
75
|
-
? [resolveGoogleChatAccount({ cfg, accountId })].filter(
|
|
76
|
-
(account) => account.enabled && account.credentialSource !== "none",
|
|
77
|
-
)
|
|
78
|
-
: listEnabledAccounts(cfg);
|
|
79
|
-
if (accounts.length === 0) {
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
const actions = new Set<ChannelMessageActionName>([]);
|
|
83
|
-
actions.add("send");
|
|
84
|
-
actions.add("upload-file");
|
|
85
|
-
if (isReactionsEnabled(accounts)) {
|
|
86
|
-
actions.add("react");
|
|
87
|
-
actions.add("reactions");
|
|
88
|
-
}
|
|
89
|
-
return { actions: Array.from(actions) };
|
|
90
|
-
},
|
|
91
|
-
extractToolSend: ({ args }) => {
|
|
92
|
-
return extractToolSend(args, "sendMessage");
|
|
93
|
-
},
|
|
94
|
-
handleAction: async ({
|
|
95
|
-
action,
|
|
96
|
-
params,
|
|
97
|
-
cfg,
|
|
98
|
-
accountId,
|
|
99
|
-
mediaAccess,
|
|
100
|
-
mediaLocalRoots,
|
|
101
|
-
mediaReadFile,
|
|
102
|
-
}) => {
|
|
103
|
-
const account = resolveGoogleChatAccount({
|
|
104
|
-
cfg: cfg,
|
|
105
|
-
accountId,
|
|
106
|
-
});
|
|
107
|
-
if (account.credentialSource === "none") {
|
|
108
|
-
throw new Error("Google Chat credentials are missing.");
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (action === "send" || action === "upload-file") {
|
|
112
|
-
const to = readStringParam(params, "to", { required: true });
|
|
113
|
-
const content =
|
|
114
|
-
readStringParam(params, "message", {
|
|
115
|
-
required: action === "send",
|
|
116
|
-
allowEmpty: true,
|
|
117
|
-
}) ??
|
|
118
|
-
readStringParam(params, "initialComment", {
|
|
119
|
-
allowEmpty: true,
|
|
120
|
-
}) ??
|
|
121
|
-
"";
|
|
122
|
-
const mediaUrl =
|
|
123
|
-
readStringParam(params, "media", { trim: false }) ??
|
|
124
|
-
readStringParam(params, "filePath", { trim: false }) ??
|
|
125
|
-
readStringParam(params, "path", { trim: false });
|
|
126
|
-
const threadId = readStringParam(params, "threadId") ?? readStringParam(params, "replyTo");
|
|
127
|
-
const space = await resolveGoogleChatOutboundSpace({ account, target: to });
|
|
128
|
-
|
|
129
|
-
if (mediaUrl) {
|
|
130
|
-
const maxBytes = (account.config.mediaMaxMb ?? 20) * 1024 * 1024;
|
|
131
|
-
const loaded = await loadGoogleChatActionMedia({
|
|
132
|
-
mediaUrl,
|
|
133
|
-
maxBytes,
|
|
134
|
-
mediaAccess,
|
|
135
|
-
mediaLocalRoots,
|
|
136
|
-
mediaReadFile,
|
|
137
|
-
});
|
|
138
|
-
const uploadFileName =
|
|
139
|
-
readStringParam(params, "filename") ??
|
|
140
|
-
readStringParam(params, "title") ??
|
|
141
|
-
loaded.fileName ??
|
|
142
|
-
"attachment";
|
|
143
|
-
const upload = await uploadGoogleChatAttachment({
|
|
144
|
-
account,
|
|
145
|
-
space,
|
|
146
|
-
filename: uploadFileName,
|
|
147
|
-
buffer: loaded.buffer,
|
|
148
|
-
contentType: loaded.contentType,
|
|
149
|
-
});
|
|
150
|
-
await sendGoogleChatMessage({
|
|
151
|
-
account,
|
|
152
|
-
space,
|
|
153
|
-
text: content,
|
|
154
|
-
thread: threadId ?? undefined,
|
|
155
|
-
attachments: upload.attachmentUploadToken
|
|
156
|
-
? [
|
|
157
|
-
{
|
|
158
|
-
attachmentUploadToken: upload.attachmentUploadToken,
|
|
159
|
-
contentName: uploadFileName,
|
|
160
|
-
},
|
|
161
|
-
]
|
|
162
|
-
: undefined,
|
|
163
|
-
});
|
|
164
|
-
return jsonResult({ ok: true, to: space });
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (action === "upload-file") {
|
|
168
|
-
throw new Error("upload-file requires media, filePath, or path");
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
await sendGoogleChatMessage({
|
|
172
|
-
account,
|
|
173
|
-
space,
|
|
174
|
-
text: content,
|
|
175
|
-
thread: threadId ?? undefined,
|
|
176
|
-
});
|
|
177
|
-
return jsonResult({ ok: true, to: space });
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (action === "react") {
|
|
181
|
-
const messageName = readStringParam(params, "messageId", { required: true });
|
|
182
|
-
const { emoji, remove, isEmpty } = readReactionParams(params, {
|
|
183
|
-
removeErrorMessage: "Emoji is required to remove a Google Chat reaction.",
|
|
184
|
-
});
|
|
185
|
-
if (remove || isEmpty) {
|
|
186
|
-
const reactions = await listGoogleChatReactions({ account, messageName });
|
|
187
|
-
const appUsers = resolveAppUserNames(account);
|
|
188
|
-
const toRemove = reactions.filter((reaction) => {
|
|
189
|
-
const userName = reaction.user?.name?.trim();
|
|
190
|
-
if (appUsers.size > 0 && !appUsers.has(userName ?? "")) {
|
|
191
|
-
return false;
|
|
192
|
-
}
|
|
193
|
-
if (emoji) {
|
|
194
|
-
return reaction.emoji?.unicode === emoji;
|
|
195
|
-
}
|
|
196
|
-
return true;
|
|
197
|
-
});
|
|
198
|
-
for (const reaction of toRemove) {
|
|
199
|
-
if (!reaction.name) {
|
|
200
|
-
continue;
|
|
201
|
-
}
|
|
202
|
-
await deleteGoogleChatReaction({ account, reactionName: reaction.name });
|
|
203
|
-
}
|
|
204
|
-
return jsonResult({ ok: true, removed: toRemove.length });
|
|
205
|
-
}
|
|
206
|
-
const reaction = await createGoogleChatReaction({
|
|
207
|
-
account,
|
|
208
|
-
messageName,
|
|
209
|
-
emoji,
|
|
210
|
-
});
|
|
211
|
-
return jsonResult({ ok: true, reaction });
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (action === "reactions") {
|
|
215
|
-
const messageName = readStringParam(params, "messageId", { required: true });
|
|
216
|
-
const limit = readNumberParam(params, "limit", { integer: true });
|
|
217
|
-
const reactions = await listGoogleChatReactions({
|
|
218
|
-
account,
|
|
219
|
-
messageName,
|
|
220
|
-
limit: limit ?? undefined,
|
|
221
|
-
});
|
|
222
|
-
return jsonResult({ ok: true, reactions });
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
throw new Error(`Action ${action} is not supported for provider ${providerId}.`);
|
|
226
|
-
},
|
|
227
|
-
};
|