@openclaw/googlechat 2026.5.12 → 2026.5.14-beta.2
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/{actions-nBGKFgiH.js → actions-Cp9JuEzB.js} +5 -5
- package/dist/{api-DubPSfme.js → api-BJ9DOsBR.js} +21 -20
- package/dist/api.js +2 -2
- package/dist/{channel-DoEjIr9p.js → channel-DzmqN0DY.js} +51 -12
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.runtime-DMNTL2NR.js → channel.runtime-DFdC1y3n.js} +69 -6
- package/dist/contract-api.js +2 -2
- package/dist/doctor-contract-api.js +1 -1
- package/dist/{runtime-api-CAQFEJsQ.js → runtime-api-DuEfWHnr.js} +2 -2
- package/dist/runtime-api.js +2 -2
- package/dist/secret-contract-api.js +1 -1
- package/dist/setup-plugin-api.js +1 -2
- package/dist/{setup-surface-HU4P5FzD.js → setup-surface-BlDqtKML.js} +101 -5
- package/dist/test-api.js +2 -2
- package/openclaw.plugin.json +96 -0
- package/package.json +4 -4
- package/dist/accounts-Dt3H6z0l.js +0 -96
- package/dist/targets-C_U5SbnT.js +0 -43
- /package/dist/{doctor-contract-BsERSjSe.js → doctor-contract-CG1sLToP.js} +0 -0
- /package/dist/{secret-contract-DLAG2_NI.js → secret-contract-DtQ_IO7J.js} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { P as getGoogleChatRuntime } from "./runtime-api-
|
|
3
|
-
import { c as sendGoogleChatMessage, o as listGoogleChatReactions, r as deleteGoogleChatReaction, t as createGoogleChatReaction, u as uploadGoogleChatAttachment } from "./api-
|
|
4
|
-
import {
|
|
1
|
+
import { o as resolveGoogleChatAccount, r as listEnabledGoogleChatAccounts } from "./setup-surface-BlDqtKML.js";
|
|
2
|
+
import { P as getGoogleChatRuntime } from "./runtime-api-DuEfWHnr.js";
|
|
3
|
+
import { c as sendGoogleChatMessage, o as listGoogleChatReactions, r as deleteGoogleChatReaction, t as createGoogleChatReaction, u as uploadGoogleChatAttachment } from "./api-BJ9DOsBR.js";
|
|
4
|
+
import { n as resolveGoogleChatOutboundSpace } from "./channel-DzmqN0DY.js";
|
|
5
5
|
import { extractToolSend } from "openclaw/plugin-sdk/tool-send";
|
|
6
6
|
import { createActionGate, jsonResult, readNumberParam, readReactionParams, readStringParam } from "openclaw/plugin-sdk/channel-actions";
|
|
7
7
|
import { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";
|
|
@@ -19,7 +19,7 @@ function resolveAppUserNames(account) {
|
|
|
19
19
|
}
|
|
20
20
|
async function loadGoogleChatActionMedia(params) {
|
|
21
21
|
const runtime = getGoogleChatRuntime();
|
|
22
|
-
return /^https?:\/\//i.test(params.mediaUrl) ? await runtime.channel.media.
|
|
22
|
+
return /^https?:\/\//i.test(params.mediaUrl) ? await runtime.channel.media.readRemoteMediaBuffer({
|
|
23
23
|
url: params.mediaUrl,
|
|
24
24
|
maxBytes: params.maxBytes
|
|
25
25
|
}) : await loadOutboundMediaFromUrl(params.mediaUrl, {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { p as fetchWithSsrFGuard$1 } from "./runtime-api-DuEfWHnr.js";
|
|
2
2
|
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
3
|
import crypto from "node:crypto";
|
|
4
4
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
5
|
+
import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
|
|
5
6
|
import { buildHostnameAllowlistPolicyFromSuffixAllowlist, fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
6
7
|
import fs from "node:fs/promises";
|
|
7
8
|
import { resolveUserPath } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
@@ -313,6 +314,13 @@ const CHAT_SCOPE = "https://www.googleapis.com/auth/chat.bot";
|
|
|
313
314
|
const CHAT_ISSUER = "chat@system.gserviceaccount.com";
|
|
314
315
|
const ADDON_ISSUER_PATTERN = /^service-\d+@gcp-sa-gsuiteaddons\.iam\.gserviceaccount\.com$/;
|
|
315
316
|
const CHAT_CERTS_URL = "https://www.googleapis.com/service_accounts/v1/metadata/x509/chat@system.gserviceaccount.com";
|
|
317
|
+
async function readGoogleChatCertsResponse(response) {
|
|
318
|
+
try {
|
|
319
|
+
return await response.json();
|
|
320
|
+
} catch (cause) {
|
|
321
|
+
throw new Error("Google Chat cert fetch failed: malformed JSON response", { cause });
|
|
322
|
+
}
|
|
323
|
+
}
|
|
316
324
|
const MAX_AUTH_CACHE_SIZE = 32;
|
|
317
325
|
const authCache = /* @__PURE__ */ new Map();
|
|
318
326
|
let cachedCerts = null;
|
|
@@ -377,7 +385,7 @@ async function fetchChatCerts() {
|
|
|
377
385
|
});
|
|
378
386
|
try {
|
|
379
387
|
if (!response.ok) throw new Error(`Failed to fetch Chat certs (${response.status})`);
|
|
380
|
-
const certs = await response
|
|
388
|
+
const certs = await readGoogleChatCertsResponse(response);
|
|
381
389
|
cachedCerts = {
|
|
382
390
|
fetchedAt: now,
|
|
383
391
|
certs
|
|
@@ -451,6 +459,13 @@ async function verifyGoogleChatRequest(params) {
|
|
|
451
459
|
//#region extensions/googlechat/src/api.ts
|
|
452
460
|
const CHAT_API_BASE = "https://chat.googleapis.com/v1";
|
|
453
461
|
const CHAT_UPLOAD_BASE = "https://chat.googleapis.com/upload/v1";
|
|
462
|
+
async function readGoogleChatJsonResponse(response, label) {
|
|
463
|
+
try {
|
|
464
|
+
return await response.json();
|
|
465
|
+
} catch (cause) {
|
|
466
|
+
throw new Error(`${label}: malformed JSON response`, { cause });
|
|
467
|
+
}
|
|
468
|
+
}
|
|
454
469
|
const headersToObject = (headers) => headers instanceof Headers ? Object.fromEntries(headers.entries()) : Array.isArray(headers) ? Object.fromEntries(headers) : headers || {};
|
|
455
470
|
async function withGoogleChatResponse(params) {
|
|
456
471
|
const { account, url, init, auditContext, errorPrefix = "Google Chat API", handleResponse } = params;
|
|
@@ -488,7 +503,7 @@ async function fetchJson(account, url, init) {
|
|
|
488
503
|
}
|
|
489
504
|
},
|
|
490
505
|
auditContext: "googlechat.api.json",
|
|
491
|
-
handleResponse: async (response) => await response
|
|
506
|
+
handleResponse: async (response) => await readGoogleChatJsonResponse(response, "Google Chat API request failed")
|
|
492
507
|
});
|
|
493
508
|
}
|
|
494
509
|
async function fetchOk(account, url, init) {
|
|
@@ -513,26 +528,12 @@ async function fetchBuffer(account, url, init, options) {
|
|
|
513
528
|
const length = Number(lengthHeader);
|
|
514
529
|
if (Number.isFinite(length) && length > maxBytes) throw new Error(`Google Chat media exceeds max bytes (${maxBytes})`);
|
|
515
530
|
}
|
|
516
|
-
if (!maxBytes
|
|
531
|
+
if (!maxBytes) return {
|
|
517
532
|
buffer: Buffer.from(await res.arrayBuffer()),
|
|
518
533
|
contentType: res.headers.get("content-type") ?? void 0
|
|
519
534
|
};
|
|
520
|
-
const reader = res.body.getReader();
|
|
521
|
-
const chunks = [];
|
|
522
|
-
let total = 0;
|
|
523
|
-
while (true) {
|
|
524
|
-
const { done, value } = await reader.read();
|
|
525
|
-
if (done) break;
|
|
526
|
-
if (!value) continue;
|
|
527
|
-
total += value.length;
|
|
528
|
-
if (total > maxBytes) {
|
|
529
|
-
await reader.cancel();
|
|
530
|
-
throw new Error(`Google Chat media exceeds max bytes (${maxBytes})`);
|
|
531
|
-
}
|
|
532
|
-
chunks.push(Buffer.from(value));
|
|
533
|
-
}
|
|
534
535
|
return {
|
|
535
|
-
buffer:
|
|
536
|
+
buffer: await readResponseWithLimit(res, maxBytes, { onOverflow: () => /* @__PURE__ */ new Error(`Google Chat media exceeds max bytes (${maxBytes})`) }),
|
|
536
537
|
contentType: res.headers.get("content-type") ?? void 0
|
|
537
538
|
};
|
|
538
539
|
}
|
|
@@ -585,7 +586,7 @@ async function uploadGoogleChatAttachment(params) {
|
|
|
585
586
|
},
|
|
586
587
|
auditContext: "googlechat.upload",
|
|
587
588
|
errorPrefix: "Google Chat upload",
|
|
588
|
-
handleResponse: async (response) => await response
|
|
589
|
+
handleResponse: async (response) => await readGoogleChatJsonResponse(response, "Google Chat upload failed")
|
|
589
590
|
})).attachmentDataRef?.attachmentUploadToken };
|
|
590
591
|
}
|
|
591
592
|
async function downloadGoogleChatMedia(params) {
|
package/dist/api.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
import {
|
|
1
|
+
import { n as googlechatSetupAdapter, t as googlechatSetupWizard } from "./setup-surface-BlDqtKML.js";
|
|
2
|
+
import { t as googlechatPlugin } from "./channel-DzmqN0DY.js";
|
|
3
3
|
export { googlechatPlugin, googlechatSetupAdapter, googlechatSetupWizard };
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { T as resolveChannelMediaMaxBytes, _ as
|
|
3
|
-
import {
|
|
4
|
-
import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-
|
|
5
|
-
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-
|
|
6
|
-
import { n as googlechatSetupAdapter, t as googlechatSetupWizard } from "./setup-surface-HU4P5FzD.js";
|
|
1
|
+
import { a as resolveDefaultGoogleChatAccountId, i as listGoogleChatAccountIds, n as googlechatSetupAdapter, o as resolveGoogleChatAccount, s as resolveGoogleChatConfigAccessorAccount, t as googlechatSetupWizard } from "./setup-surface-BlDqtKML.js";
|
|
2
|
+
import { T as resolveChannelMediaMaxBytes, _ as missingTargetError, a as buildChannelConfigSchema, g as loadOutboundMediaFromUrl, i as PAIRING_APPROVED_MESSAGE, o as chunkTextForOutbound, r as GoogleChatConfigSchema, t as DEFAULT_ACCOUNT_ID, x as readRemoteMediaBuffer } from "./runtime-api-DuEfWHnr.js";
|
|
3
|
+
import { a as findGoogleChatDirectMessage } from "./api-BJ9DOsBR.js";
|
|
4
|
+
import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-CG1sLToP.js";
|
|
5
|
+
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-DtQ_IO7J.js";
|
|
7
6
|
import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
|
8
7
|
import { formatNormalizedAllowFromEntries } from "openclaw/plugin-sdk/allow-from";
|
|
9
8
|
import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
|
|
@@ -19,6 +18,46 @@ import { createMessageReceiptFromOutboundResults, defineChannelMessageAdapter }
|
|
|
19
18
|
import { composeAccountWarningCollectors, createAllowlistProviderOpenWarningCollector, createDangerousNameMatchingMutableAllowlistWarningCollector, resolveChannelGroupRequireMention } from "openclaw/plugin-sdk/channel-policy";
|
|
20
19
|
import { createChannelDirectoryAdapter, listResolvedDirectoryGroupEntriesFromMapKeys, listResolvedDirectoryUserEntriesFromAllowFrom } from "openclaw/plugin-sdk/directory-runtime";
|
|
21
20
|
import { sanitizeForPlainText } from "openclaw/plugin-sdk/outbound-runtime";
|
|
21
|
+
//#region extensions/googlechat/src/targets.ts
|
|
22
|
+
function normalizeGoogleChatTarget(raw) {
|
|
23
|
+
const trimmed = raw?.trim();
|
|
24
|
+
if (!trimmed) return;
|
|
25
|
+
const normalized = trimmed.replace(/^(googlechat|google-chat|gchat):/i, "").replace(/^user:(users\/)?/i, "users/").replace(/^space:(spaces\/)?/i, "spaces/");
|
|
26
|
+
if (isGoogleChatUserTarget(normalized)) {
|
|
27
|
+
const suffix = normalized.slice(6);
|
|
28
|
+
return suffix.includes("@") ? `users/${normalizeLowercaseStringOrEmpty(suffix)}` : normalized;
|
|
29
|
+
}
|
|
30
|
+
if (isGoogleChatSpaceTarget(normalized)) return normalized;
|
|
31
|
+
if (normalized.includes("@")) return `users/${normalizeLowercaseStringOrEmpty(normalized)}`;
|
|
32
|
+
return normalized;
|
|
33
|
+
}
|
|
34
|
+
function isGoogleChatUserTarget(value) {
|
|
35
|
+
return normalizeLowercaseStringOrEmpty(value).startsWith("users/");
|
|
36
|
+
}
|
|
37
|
+
function isGoogleChatSpaceTarget(value) {
|
|
38
|
+
return normalizeLowercaseStringOrEmpty(value).startsWith("spaces/");
|
|
39
|
+
}
|
|
40
|
+
function stripMessageSuffix(target) {
|
|
41
|
+
const index = target.indexOf("/messages/");
|
|
42
|
+
if (index === -1) return target;
|
|
43
|
+
return target.slice(0, index);
|
|
44
|
+
}
|
|
45
|
+
async function resolveGoogleChatOutboundSpace(params) {
|
|
46
|
+
const normalized = normalizeGoogleChatTarget(params.target);
|
|
47
|
+
if (!normalized) throw new Error("Missing Google Chat target.");
|
|
48
|
+
const base = stripMessageSuffix(normalized);
|
|
49
|
+
if (isGoogleChatSpaceTarget(base)) return base;
|
|
50
|
+
if (isGoogleChatUserTarget(base)) {
|
|
51
|
+
const dm = await findGoogleChatDirectMessage({
|
|
52
|
+
account: params.account,
|
|
53
|
+
userName: base
|
|
54
|
+
});
|
|
55
|
+
if (!dm?.name) throw new Error(`No Google Chat DM found for ${base}`);
|
|
56
|
+
return dm.name;
|
|
57
|
+
}
|
|
58
|
+
return base;
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
22
61
|
//#region extensions/googlechat/src/approval-auth.ts
|
|
23
62
|
function normalizeGoogleChatApproverId(value) {
|
|
24
63
|
const normalized = normalizeGoogleChatTarget(String(value));
|
|
@@ -54,7 +93,7 @@ function resolveGoogleChatGroupRequireMention(params) {
|
|
|
54
93
|
}
|
|
55
94
|
//#endregion
|
|
56
95
|
//#region extensions/googlechat/src/channel.adapters.ts
|
|
57
|
-
const loadGoogleChatChannelRuntime$2 = createLazyRuntimeNamedExport(() => import("./channel.runtime-
|
|
96
|
+
const loadGoogleChatChannelRuntime$2 = createLazyRuntimeNamedExport(() => import("./channel.runtime-DFdC1y3n.js"), "googleChatChannelRuntime");
|
|
58
97
|
function createGoogleChatSendReceipt(params) {
|
|
59
98
|
const messageId = params.messageId?.trim();
|
|
60
99
|
return createMessageReceiptFromOutboundResults({
|
|
@@ -204,7 +243,7 @@ const googlechatOutboundAdapter = {
|
|
|
204
243
|
resolveChannelLimitMb: ({ cfg, accountId }) => (cfg.channels?.googlechat)?.accounts?.[accountId]?.mediaMaxMb ?? (cfg.channels?.googlechat)?.mediaMaxMb,
|
|
205
244
|
accountId
|
|
206
245
|
}) ?? (account.config.mediaMaxMb ?? 20) * 1024 * 1024;
|
|
207
|
-
const loaded = /^https?:\/\//i.test(mediaUrl) ? await
|
|
246
|
+
const loaded = /^https?:\/\//i.test(mediaUrl) ? await readRemoteMediaBuffer({
|
|
208
247
|
url: mediaUrl,
|
|
209
248
|
maxBytes: effectiveMaxBytes
|
|
210
249
|
}) : await loadOutboundMediaFromUrl(mediaUrl, {
|
|
@@ -295,7 +334,7 @@ const collectGoogleChatMutableAllowlistWarnings = createDangerousNameMatchingMut
|
|
|
295
334
|
});
|
|
296
335
|
//#endregion
|
|
297
336
|
//#region extensions/googlechat/src/gateway.ts
|
|
298
|
-
const loadGoogleChatChannelRuntime$1 = createLazyRuntimeNamedExport(() => import("./channel.runtime-
|
|
337
|
+
const loadGoogleChatChannelRuntime$1 = createLazyRuntimeNamedExport(() => import("./channel.runtime-DFdC1y3n.js"), "googleChatChannelRuntime");
|
|
299
338
|
async function startGoogleChatGatewayAccount(ctx) {
|
|
300
339
|
const account = ctx.account;
|
|
301
340
|
const statusSink = createAccountStatusSink({
|
|
@@ -335,7 +374,7 @@ async function startGoogleChatGatewayAccount(ctx) {
|
|
|
335
374
|
}
|
|
336
375
|
//#endregion
|
|
337
376
|
//#region extensions/googlechat/src/channel.ts
|
|
338
|
-
const loadGoogleChatChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-
|
|
377
|
+
const loadGoogleChatChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-DFdC1y3n.js"), "googleChatChannelRuntime");
|
|
339
378
|
const meta = {
|
|
340
379
|
id: "googlechat",
|
|
341
380
|
label: "Google Chat",
|
|
@@ -391,7 +430,7 @@ const googlechatActions = {
|
|
|
391
430
|
},
|
|
392
431
|
extractToolSend: ({ args }) => extractToolSend(args, "sendMessage"),
|
|
393
432
|
handleAction: async (ctx) => {
|
|
394
|
-
const { googlechatMessageActions } = await import("./actions-
|
|
433
|
+
const { googlechatMessageActions } = await import("./actions-Cp9JuEzB.js");
|
|
395
434
|
if (!googlechatMessageActions.handleAction) throw new Error("Google Chat actions are not available.");
|
|
396
435
|
return await googlechatMessageActions.handleAction(ctx);
|
|
397
436
|
}
|
|
@@ -542,4 +581,4 @@ const googlechatPlugin = createChatChannelPlugin({
|
|
|
542
581
|
outbound: googlechatOutboundAdapter
|
|
543
582
|
});
|
|
544
583
|
//#endregion
|
|
545
|
-
export { googlechatPlugin as t };
|
|
584
|
+
export { resolveGoogleChatOutboundSpace as n, googlechatPlugin as t };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as googlechatPlugin } from "./channel-
|
|
1
|
+
import { t as googlechatPlugin } from "./channel-DzmqN0DY.js";
|
|
2
2
|
export { googlechatPlugin };
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { E as resolveDefaultGroupPolicy, M as warnMissingProviderGroupPolicyFallbackOnce, O as resolveInboundRouteEnvelopeBuilderWithRuntime, P as getGoogleChatRuntime,
|
|
2
|
-
import { c as sendGoogleChatMessage, d as verifyGoogleChatRequest, i as downloadGoogleChatMedia, l as updateGoogleChatMessage, n as deleteGoogleChatMessage, s as probeGoogleChat, u as uploadGoogleChatAttachment } from "./api-
|
|
1
|
+
import { E as resolveDefaultGroupPolicy, M as warnMissingProviderGroupPolicyFallbackOnce, O as resolveInboundRouteEnvelopeBuilderWithRuntime, P as getGoogleChatRuntime, k as resolveWebhookPath, m as isDangerousNameMatchingEnabled, n as GROUP_POLICY_BLOCKED_LABEL, u as createChannelPairingController, w as resolveAllowlistProviderRuntimeGroupPolicy } from "./runtime-api-DuEfWHnr.js";
|
|
2
|
+
import { c as sendGoogleChatMessage, d as verifyGoogleChatRequest, i as downloadGoogleChatMedia, l as updateGoogleChatMessage, n as deleteGoogleChatMessage, s as probeGoogleChat, u as uploadGoogleChatAttachment } from "./api-BJ9DOsBR.js";
|
|
3
3
|
import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
4
|
+
import { mergePairLoopGuardConfig } from "openclaw/plugin-sdk/pair-loop-guard-runtime";
|
|
5
|
+
import { WEBHOOK_RATE_LIMIT_DEFAULTS, createFixedWindowRateLimiter, normalizeWebhookPath, resolveRequestClientIp } from "openclaw/plugin-sdk/webhook-ingress";
|
|
4
6
|
import { registerWebhookTargetWithPluginRoute, resolveWebhookTargetWithAuthOrReject, withResolvedWebhookRequestPipeline } from "openclaw/plugin-sdk/webhook-targets";
|
|
5
7
|
import { createWebhookInFlightLimiter, readJsonWebhookBodyOrReject } from "openclaw/plugin-sdk/webhook-request-guards";
|
|
8
|
+
import { recordChannelBotPairLoopAndCheckSuppression } from "openclaw/plugin-sdk/inbound-reply-dispatch";
|
|
6
9
|
import { channelIngressRoutes, createChannelIngressResolver, defineStableChannelIngressIdentity } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
7
10
|
import { deliverTextOrMediaReply, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
|
|
8
11
|
//#region extensions/googlechat/src/monitor-access.ts
|
|
@@ -277,6 +280,7 @@ async function applyGoogleChatInboundAccessPolicy(params) {
|
|
|
277
280
|
ok: true,
|
|
278
281
|
commandAuthorized,
|
|
279
282
|
effectiveWasMentioned,
|
|
283
|
+
groupBotLoopProtection: groupEntry?.botLoopProtection,
|
|
280
284
|
groupSystemPrompt: normalizeOptionalString(groupEntry?.systemPrompt)
|
|
281
285
|
};
|
|
282
286
|
}
|
|
@@ -368,7 +372,7 @@ async function deliverGoogleChatReply(params) {
|
|
|
368
372
|
},
|
|
369
373
|
sendMedia: async ({ mediaUrl, caption }) => {
|
|
370
374
|
try {
|
|
371
|
-
const loaded = await core.channel.media.
|
|
375
|
+
const loaded = await core.channel.media.readRemoteMediaBuffer({
|
|
372
376
|
url: mediaUrl,
|
|
373
377
|
maxBytes: (account.config.mediaMaxMb ?? 20) * 1024 * 1024
|
|
374
378
|
});
|
|
@@ -512,12 +516,17 @@ function warnAppPrincipalMisconfiguration(params) {
|
|
|
512
516
|
}
|
|
513
517
|
function createGoogleChatWebhookRequestHandler(params) {
|
|
514
518
|
return async (req, res) => {
|
|
519
|
+
const path = normalizeWebhookPath(new URL(req.url ?? "/", "http://localhost").pathname);
|
|
520
|
+
const config = params.webhookTargets.get(path)?.[0]?.config;
|
|
521
|
+
const clientIp = resolveRequestClientIp(req, config?.gateway?.trustedProxies, config?.gateway?.allowRealIpFallback === true) ?? "unknown";
|
|
515
522
|
return await withResolvedWebhookRequestPipeline({
|
|
516
523
|
req,
|
|
517
524
|
res,
|
|
518
525
|
targetsByPath: params.webhookTargets,
|
|
519
526
|
allowMethods: ["POST"],
|
|
520
527
|
requireJsonContentType: true,
|
|
528
|
+
rateLimiter: params.webhookRateLimiter,
|
|
529
|
+
rateLimitKey: `${path}:${clientIp}`,
|
|
521
530
|
inFlightLimiter: params.webhookInFlightLimiter,
|
|
522
531
|
handle: async ({ targets }) => {
|
|
523
532
|
const headerBearer = extractBearerToken(req.headers.authorization);
|
|
@@ -587,6 +596,11 @@ function createGoogleChatWebhookRequestHandler(params) {
|
|
|
587
596
|
//#endregion
|
|
588
597
|
//#region extensions/googlechat/src/monitor-routing.ts
|
|
589
598
|
const webhookTargets = /* @__PURE__ */ new Map();
|
|
599
|
+
const webhookRateLimiter = createFixedWindowRateLimiter({
|
|
600
|
+
windowMs: WEBHOOK_RATE_LIMIT_DEFAULTS.windowMs,
|
|
601
|
+
maxRequests: WEBHOOK_RATE_LIMIT_DEFAULTS.maxRequests,
|
|
602
|
+
maxTrackedKeys: WEBHOOK_RATE_LIMIT_DEFAULTS.maxTrackedKeys
|
|
603
|
+
});
|
|
590
604
|
const webhookInFlightLimiter = createWebhookInFlightLimiter();
|
|
591
605
|
let processGoogleChatEvent$1 = async () => {};
|
|
592
606
|
function setGoogleChatWebhookEventProcessor(processEvent) {
|
|
@@ -594,6 +608,7 @@ function setGoogleChatWebhookEventProcessor(processEvent) {
|
|
|
594
608
|
}
|
|
595
609
|
const googleChatWebhookRequestHandler = createGoogleChatWebhookRequestHandler({
|
|
596
610
|
webhookTargets,
|
|
611
|
+
webhookRateLimiter,
|
|
597
612
|
webhookInFlightLimiter,
|
|
598
613
|
processEvent: async (event, target) => {
|
|
599
614
|
await processGoogleChatEvent$1(event, target);
|
|
@@ -634,6 +649,33 @@ function normalizeAudienceType(value) {
|
|
|
634
649
|
if (normalized === "app-url" || normalized === "app_url" || normalized === "app") return "app-url";
|
|
635
650
|
if (normalized === "project-number" || normalized === "project_number" || normalized === "project") return "project-number";
|
|
636
651
|
}
|
|
652
|
+
function resolveGoogleChatTimestampMs(eventTime) {
|
|
653
|
+
if (!eventTime) return;
|
|
654
|
+
const parsed = Date.parse(eventTime);
|
|
655
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
656
|
+
}
|
|
657
|
+
function resolveGoogleChatBotLoopProtection(params) {
|
|
658
|
+
if (!params.allowBots || !params.isBotSender || !params.senderId || params.senderId === params.appUserId) return;
|
|
659
|
+
return {
|
|
660
|
+
scopeId: params.accountId,
|
|
661
|
+
conversationId: params.conversationId,
|
|
662
|
+
senderId: params.senderId,
|
|
663
|
+
receiverId: params.appUserId,
|
|
664
|
+
config: params.config,
|
|
665
|
+
defaultsConfig: params.defaultsConfig,
|
|
666
|
+
defaultEnabled: true,
|
|
667
|
+
nowMs: resolveGoogleChatTimestampMs(params.eventTime)
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
function resolveGoogleChatBotLoopProtectionConfig(params) {
|
|
671
|
+
return mergePairLoopGuardConfig(params.accountConfig, params.groupConfig);
|
|
672
|
+
}
|
|
673
|
+
function shouldSuppressGoogleChatBotLoop(params) {
|
|
674
|
+
if (!params.botLoopProtection) return false;
|
|
675
|
+
if (!recordChannelBotPairLoopAndCheckSuppression(params.botLoopProtection).suppressed) return false;
|
|
676
|
+
logVerbose(params.core, params.runtime, `skip bot-to-bot loop in ${params.botLoopProtection.conversationId}`);
|
|
677
|
+
return true;
|
|
678
|
+
}
|
|
637
679
|
async function processGoogleChatEvent(event, target) {
|
|
638
680
|
if ((event.type ?? event.eventType) !== "MESSAGE") return;
|
|
639
681
|
if (!event.message || !event.space) return;
|
|
@@ -672,8 +714,11 @@ async function processMessageWithPipeline(params) {
|
|
|
672
714
|
const senderId = sender?.name ?? "";
|
|
673
715
|
const senderName = sender?.displayName ?? "";
|
|
674
716
|
const senderEmail = sender?.email ?? void 0;
|
|
675
|
-
|
|
676
|
-
|
|
717
|
+
const isBotSender = sender?.type?.toUpperCase() === "BOT";
|
|
718
|
+
const appUserId = account.config.botUser?.trim() || "users/app";
|
|
719
|
+
const allowBots = account.config.allowBots === true;
|
|
720
|
+
if (!allowBots) {
|
|
721
|
+
if (isBotSender) {
|
|
677
722
|
logVerbose(core, runtime, `skip bot-authored message (${senderId || "unknown"})`);
|
|
678
723
|
return;
|
|
679
724
|
}
|
|
@@ -702,7 +747,25 @@ async function processMessageWithPipeline(params) {
|
|
|
702
747
|
logVerbose: (message) => logVerbose(core, runtime, message)
|
|
703
748
|
});
|
|
704
749
|
if (!access.ok) return;
|
|
705
|
-
const { commandAuthorized, effectiveWasMentioned, groupSystemPrompt } = access;
|
|
750
|
+
const { commandAuthorized, effectiveWasMentioned, groupBotLoopProtection, groupSystemPrompt } = access;
|
|
751
|
+
if (shouldSuppressGoogleChatBotLoop({
|
|
752
|
+
botLoopProtection: resolveGoogleChatBotLoopProtection({
|
|
753
|
+
allowBots,
|
|
754
|
+
isBotSender,
|
|
755
|
+
senderId,
|
|
756
|
+
appUserId,
|
|
757
|
+
accountId: account.accountId,
|
|
758
|
+
conversationId: spaceId,
|
|
759
|
+
config: resolveGoogleChatBotLoopProtectionConfig({
|
|
760
|
+
accountConfig: account.config.botLoopProtection,
|
|
761
|
+
groupConfig: groupBotLoopProtection
|
|
762
|
+
}),
|
|
763
|
+
defaultsConfig: config.channels?.defaults?.botLoopProtection,
|
|
764
|
+
eventTime: event.eventTime
|
|
765
|
+
}),
|
|
766
|
+
core,
|
|
767
|
+
runtime
|
|
768
|
+
})) return;
|
|
706
769
|
const { route, buildEnvelope } = resolveInboundRouteEnvelopeBuilderWithRuntime({
|
|
707
770
|
cfg: config,
|
|
708
771
|
channel: "googlechat",
|
package/dist/contract-api.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-
|
|
2
|
-
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-
|
|
1
|
+
import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-CG1sLToP.js";
|
|
2
|
+
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-DtQ_IO7J.js";
|
|
3
3
|
export { collectRuntimeConfigAssignments, legacyConfigRules, normalizeCompatibilityConfig, secretTargetRegistryEntries };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-
|
|
1
|
+
import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-CG1sLToP.js";
|
|
2
2
|
export { legacyConfigRules, normalizeCompatibilityConfig };
|
|
@@ -12,7 +12,7 @@ import { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";
|
|
|
12
12
|
import { GoogleChatConfigSchema } from "openclaw/plugin-sdk/bundled-channel-config-schema";
|
|
13
13
|
import { GROUP_POLICY_BLOCKED_LABEL, resolveAllowlistProviderRuntimeGroupPolicy, resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
|
|
14
14
|
import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
|
|
15
|
-
import {
|
|
15
|
+
import { readRemoteMediaBuffer, resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/media-runtime";
|
|
16
16
|
import { loadOutboundMediaFromUrl as loadOutboundMediaFromUrl$1 } from "openclaw/plugin-sdk/outbound-media";
|
|
17
17
|
import { resolveInboundMentionDecision } from "openclaw/plugin-sdk/channel-inbound";
|
|
18
18
|
import { resolveInboundRouteEnvelopeBuilderWithRuntime } from "openclaw/plugin-sdk/inbound-envelope";
|
|
@@ -26,4 +26,4 @@ const { setRuntime: setGoogleChatRuntime, getRuntime: getGoogleChatRuntime } = c
|
|
|
26
26
|
errorMessage: "Google Chat runtime not initialized"
|
|
27
27
|
});
|
|
28
28
|
//#endregion
|
|
29
|
-
export { resolveWebhookTargetWithAuthOrReject$1 as A, registerWebhookTargetWithPluginRoute$1 as C, resolveInboundMentionDecision as D, resolveDefaultGroupPolicy as E, setGoogleChatRuntime as F, warnMissingProviderGroupPolicyFallbackOnce as M, withResolvedWebhookRequestPipeline$1 as N, resolveInboundRouteEnvelopeBuilderWithRuntime as O, getGoogleChatRuntime as P, readStringParam$1 as S, resolveChannelMediaMaxBytes as T,
|
|
29
|
+
export { resolveWebhookTargetWithAuthOrReject$1 as A, registerWebhookTargetWithPluginRoute$1 as C, resolveInboundMentionDecision as D, resolveDefaultGroupPolicy as E, setGoogleChatRuntime as F, warnMissingProviderGroupPolicyFallbackOnce as M, withResolvedWebhookRequestPipeline$1 as N, resolveInboundRouteEnvelopeBuilderWithRuntime as O, getGoogleChatRuntime as P, readStringParam$1 as S, resolveChannelMediaMaxBytes as T, missingTargetError as _, buildChannelConfigSchema as a, readReactionParams$1 as b, createActionGate$1 as c, createWebhookInFlightLimiter$1 as d, extractToolSend$1 as f, loadOutboundMediaFromUrl$1 as g, jsonResult$1 as h, PAIRING_APPROVED_MESSAGE as i, runPassiveAccountLifecycle$1 as j, resolveWebhookPath as k, createChannelMessageReplyPipeline as l, isDangerousNameMatchingEnabled as m, GROUP_POLICY_BLOCKED_LABEL as n, chunkTextForOutbound as o, fetchWithSsrFGuard$1 as p, GoogleChatConfigSchema as r, createAccountStatusSink$1 as s, DEFAULT_ACCOUNT_ID as t, createChannelPairingController as u, readJsonWebhookBodyOrReject$1 as v, resolveAllowlistProviderRuntimeGroupPolicy as w, readRemoteMediaBuffer as x, readNumberParam$1 as y };
|
package/dist/runtime-api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as resolveWebhookTargetWithAuthOrReject, C as registerWebhookTargetWithPluginRoute, D as resolveInboundMentionDecision, E as resolveDefaultGroupPolicy, F as setGoogleChatRuntime, M as warnMissingProviderGroupPolicyFallbackOnce, N as withResolvedWebhookRequestPipeline, O as resolveInboundRouteEnvelopeBuilderWithRuntime, S as readStringParam, T as resolveChannelMediaMaxBytes, _ as
|
|
2
|
-
export { DEFAULT_ACCOUNT_ID, GROUP_POLICY_BLOCKED_LABEL, GoogleChatConfigSchema, PAIRING_APPROVED_MESSAGE, buildChannelConfigSchema, chunkTextForOutbound, createAccountStatusSink, createActionGate, createChannelMessageReplyPipeline, createChannelPairingController, createWebhookInFlightLimiter, extractToolSend,
|
|
1
|
+
import { A as resolveWebhookTargetWithAuthOrReject, C as registerWebhookTargetWithPluginRoute, D as resolveInboundMentionDecision, E as resolveDefaultGroupPolicy, F as setGoogleChatRuntime, M as warnMissingProviderGroupPolicyFallbackOnce, N as withResolvedWebhookRequestPipeline, O as resolveInboundRouteEnvelopeBuilderWithRuntime, S as readStringParam, T as resolveChannelMediaMaxBytes, _ as missingTargetError, a as buildChannelConfigSchema, b as readReactionParams, c as createActionGate, d as createWebhookInFlightLimiter, f as extractToolSend, g as loadOutboundMediaFromUrl, h as jsonResult, i as PAIRING_APPROVED_MESSAGE, j as runPassiveAccountLifecycle, k as resolveWebhookPath, l as createChannelMessageReplyPipeline, m as isDangerousNameMatchingEnabled, n as GROUP_POLICY_BLOCKED_LABEL, o as chunkTextForOutbound, p as fetchWithSsrFGuard, r as GoogleChatConfigSchema, s as createAccountStatusSink, t as DEFAULT_ACCOUNT_ID, u as createChannelPairingController, v as readJsonWebhookBodyOrReject, w as resolveAllowlistProviderRuntimeGroupPolicy, x as readRemoteMediaBuffer, y as readNumberParam } from "./runtime-api-DuEfWHnr.js";
|
|
2
|
+
export { DEFAULT_ACCOUNT_ID, GROUP_POLICY_BLOCKED_LABEL, GoogleChatConfigSchema, PAIRING_APPROVED_MESSAGE, buildChannelConfigSchema, chunkTextForOutbound, createAccountStatusSink, createActionGate, createChannelMessageReplyPipeline, createChannelPairingController, createWebhookInFlightLimiter, extractToolSend, fetchWithSsrFGuard, isDangerousNameMatchingEnabled, jsonResult, loadOutboundMediaFromUrl, missingTargetError, readJsonWebhookBodyOrReject, readNumberParam, readReactionParams, readRemoteMediaBuffer, readStringParam, registerWebhookTargetWithPluginRoute, resolveAllowlistProviderRuntimeGroupPolicy, resolveChannelMediaMaxBytes, resolveDefaultGroupPolicy, resolveInboundMentionDecision, resolveInboundRouteEnvelopeBuilderWithRuntime, resolveWebhookPath, resolveWebhookTargetWithAuthOrReject, runPassiveAccountLifecycle, setGoogleChatRuntime, warnMissingProviderGroupPolicyFallbackOnce, withResolvedWebhookRequestPipeline };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries, t as channelSecrets } from "./secret-contract-
|
|
1
|
+
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries, t as channelSecrets } from "./secret-contract-DtQ_IO7J.js";
|
|
2
2
|
export { channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries };
|
package/dist/setup-plugin-api.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { n as googlechatSetupAdapter, t as googlechatSetupWizard } from "./setup-surface-HU4P5FzD.js";
|
|
1
|
+
import { a as resolveDefaultGoogleChatAccountId, i as listGoogleChatAccountIds, n as googlechatSetupAdapter, o as resolveGoogleChatAccount, s as resolveGoogleChatConfigAccessorAccount, t as googlechatSetupWizard } from "./setup-surface-BlDqtKML.js";
|
|
3
2
|
import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
|
4
3
|
import { formatNormalizedAllowFromEntries } from "openclaw/plugin-sdk/allow-from";
|
|
5
4
|
import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
|
|
@@ -1,7 +1,103 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { safeParseJsonWithSchema, safeParseWithSchema } from "openclaw/plugin-sdk/extension-shared";
|
|
2
2
|
import { normalizeOptionalString, normalizeStringifiedOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
|
+
import { DEFAULT_ACCOUNT_ID, createAccountListHelpers, normalizeAccountId, resolveAccountEntry, resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
|
|
4
|
+
import { mergePairLoopGuardConfig } from "openclaw/plugin-sdk/pair-loop-guard-runtime";
|
|
5
|
+
import { isSecretRef } from "openclaw/plugin-sdk/secret-input";
|
|
6
|
+
import { z } from "zod";
|
|
3
7
|
import { createPatchedAccountSetupAdapter, createSetupInputPresenceValidator } from "openclaw/plugin-sdk/setup-runtime";
|
|
4
|
-
import { DEFAULT_ACCOUNT_ID, addWildcardAllowFrom, applySetupAccountConfigPatch, createPromptParsedAllowFromForAccount, createStandardChannelSetupStatus, formatDocsLink, mergeAllowFromEntries, migrateBaseNameToDefaultAccount, splitSetupEntries } from "openclaw/plugin-sdk/setup";
|
|
8
|
+
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, addWildcardAllowFrom, applySetupAccountConfigPatch, createPromptParsedAllowFromForAccount, createStandardChannelSetupStatus, formatDocsLink, mergeAllowFromEntries, migrateBaseNameToDefaultAccount, splitSetupEntries } from "openclaw/plugin-sdk/setup";
|
|
9
|
+
//#region extensions/googlechat/src/accounts.ts
|
|
10
|
+
const ENV_SERVICE_ACCOUNT$1 = "GOOGLE_CHAT_SERVICE_ACCOUNT";
|
|
11
|
+
const ENV_SERVICE_ACCOUNT_FILE$1 = "GOOGLE_CHAT_SERVICE_ACCOUNT_FILE";
|
|
12
|
+
const JsonRecordSchema = z.record(z.string(), z.unknown());
|
|
13
|
+
const { listAccountIds: listGoogleChatAccountIds, resolveDefaultAccountId: resolveDefaultGoogleChatAccountId } = createAccountListHelpers("googlechat");
|
|
14
|
+
function mergeGoogleChatAccountConfig(cfg, accountId) {
|
|
15
|
+
const raw = cfg.channels?.["googlechat"] ?? {};
|
|
16
|
+
const base = resolveMergedAccountConfig({
|
|
17
|
+
channelConfig: raw,
|
|
18
|
+
accounts: raw.accounts,
|
|
19
|
+
accountId,
|
|
20
|
+
omitKeys: ["defaultAccount"],
|
|
21
|
+
nestedObjectKeys: ["botLoopProtection"]
|
|
22
|
+
});
|
|
23
|
+
const defaultAccountConfig = resolveAccountEntry(raw.accounts, DEFAULT_ACCOUNT_ID) ?? {};
|
|
24
|
+
if (accountId === DEFAULT_ACCOUNT_ID) return base;
|
|
25
|
+
const { enabled: _ignoredEnabled, dangerouslyAllowNameMatching: _ignoredDangerouslyAllowNameMatching, serviceAccount: _ignoredServiceAccount, serviceAccountRef: _ignoredServiceAccountRef, serviceAccountFile: _ignoredServiceAccountFile, ...defaultAccountShared } = defaultAccountConfig;
|
|
26
|
+
const botLoopProtection = mergePairLoopGuardConfig(defaultAccountShared.botLoopProtection, base.botLoopProtection);
|
|
27
|
+
return {
|
|
28
|
+
...defaultAccountShared,
|
|
29
|
+
...base,
|
|
30
|
+
...botLoopProtection ? { botLoopProtection } : {}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function resolveGoogleChatConfigAccessorAccount(params) {
|
|
34
|
+
const accountId = normalizeAccountId(params.accountId ?? params.cfg.channels?.googlechat?.defaultAccount);
|
|
35
|
+
return { config: mergeGoogleChatAccountConfig(params.cfg, accountId) };
|
|
36
|
+
}
|
|
37
|
+
function parseServiceAccount(value) {
|
|
38
|
+
if (isSecretRef(value)) return null;
|
|
39
|
+
if (typeof value === "string") {
|
|
40
|
+
const trimmed = value.trim();
|
|
41
|
+
if (!trimmed) return null;
|
|
42
|
+
return safeParseJsonWithSchema(JsonRecordSchema, trimmed);
|
|
43
|
+
}
|
|
44
|
+
return safeParseWithSchema(JsonRecordSchema, value);
|
|
45
|
+
}
|
|
46
|
+
function resolveCredentialsFromConfig(params) {
|
|
47
|
+
const { account, accountId } = params;
|
|
48
|
+
const inline = parseServiceAccount(account.serviceAccount);
|
|
49
|
+
if (inline) return {
|
|
50
|
+
credentials: inline,
|
|
51
|
+
source: "inline"
|
|
52
|
+
};
|
|
53
|
+
if (isSecretRef(account.serviceAccount)) throw new Error(`channels.googlechat.accounts.${accountId}.serviceAccount: unresolved SecretRef "${account.serviceAccount.source}:${account.serviceAccount.provider}:${account.serviceAccount.id}". Resolve this command against an active gateway runtime snapshot before reading it.`);
|
|
54
|
+
if (isSecretRef(account.serviceAccountRef)) throw new Error(`channels.googlechat.accounts.${accountId}.serviceAccount: unresolved SecretRef "${account.serviceAccountRef.source}:${account.serviceAccountRef.provider}:${account.serviceAccountRef.id}". Resolve this command against an active gateway runtime snapshot before reading it.`);
|
|
55
|
+
const file = normalizeOptionalString(account.serviceAccountFile);
|
|
56
|
+
if (file) return {
|
|
57
|
+
credentialsFile: file,
|
|
58
|
+
source: "file"
|
|
59
|
+
};
|
|
60
|
+
if (accountId === DEFAULT_ACCOUNT_ID) {
|
|
61
|
+
const envJson = process.env[ENV_SERVICE_ACCOUNT$1];
|
|
62
|
+
const envInline = parseServiceAccount(envJson);
|
|
63
|
+
if (envInline) return {
|
|
64
|
+
credentials: envInline,
|
|
65
|
+
source: "env"
|
|
66
|
+
};
|
|
67
|
+
const envFile = normalizeOptionalString(process.env[ENV_SERVICE_ACCOUNT_FILE$1]);
|
|
68
|
+
if (envFile) return {
|
|
69
|
+
credentialsFile: envFile,
|
|
70
|
+
source: "env"
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return { source: "none" };
|
|
74
|
+
}
|
|
75
|
+
function resolveGoogleChatAccount(params) {
|
|
76
|
+
const accountId = normalizeAccountId(params.accountId ?? params.cfg.channels?.["googlechat"]?.defaultAccount);
|
|
77
|
+
const baseEnabled = params.cfg.channels?.["googlechat"]?.enabled !== false;
|
|
78
|
+
const merged = mergeGoogleChatAccountConfig(params.cfg, accountId);
|
|
79
|
+
const accountEnabled = merged.enabled !== false;
|
|
80
|
+
const enabled = baseEnabled && accountEnabled;
|
|
81
|
+
const credentials = resolveCredentialsFromConfig({
|
|
82
|
+
accountId,
|
|
83
|
+
account: merged
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
accountId,
|
|
87
|
+
name: normalizeOptionalString(merged.name),
|
|
88
|
+
enabled,
|
|
89
|
+
config: merged,
|
|
90
|
+
credentialSource: credentials.source,
|
|
91
|
+
credentials: credentials.credentials,
|
|
92
|
+
credentialsFile: credentials.credentialsFile
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function listEnabledGoogleChatAccounts(cfg) {
|
|
96
|
+
return listGoogleChatAccountIds(cfg).map((accountId) => resolveGoogleChatAccount({
|
|
97
|
+
cfg,
|
|
98
|
+
accountId
|
|
99
|
+
})).filter((account) => account.enabled);
|
|
100
|
+
}
|
|
5
101
|
const googlechatSetupAdapter = createPatchedAccountSetupAdapter({
|
|
6
102
|
channelKey: "googlechat",
|
|
7
103
|
validateInput: createSetupInputPresenceValidator({
|
|
@@ -38,7 +134,7 @@ const googlechatDmPolicy = {
|
|
|
38
134
|
channel,
|
|
39
135
|
policyKey: "channels.googlechat.dm.policy",
|
|
40
136
|
allowFromKey: "channels.googlechat.dm.allowFrom",
|
|
41
|
-
resolveConfigKeys: (cfg, accountId) => (accountId ?? resolveDefaultGoogleChatAccountId(cfg)) !== DEFAULT_ACCOUNT_ID ? {
|
|
137
|
+
resolveConfigKeys: (cfg, accountId) => (accountId ?? resolveDefaultGoogleChatAccountId(cfg)) !== DEFAULT_ACCOUNT_ID$1 ? {
|
|
42
138
|
policyKey: `channels.googlechat.accounts.${accountId ?? resolveDefaultGoogleChatAccountId(cfg)}.dm.policy`,
|
|
43
139
|
allowFromKey: `channels.googlechat.accounts.${accountId ?? resolveDefaultGoogleChatAccountId(cfg)}.dm.allowFrom`
|
|
44
140
|
} : {
|
|
@@ -129,7 +225,7 @@ const googlechatSetupWizard = {
|
|
|
129
225
|
]
|
|
130
226
|
},
|
|
131
227
|
prepare: async ({ cfg, accountId, credentialValues, prompter }) => {
|
|
132
|
-
if (accountId === DEFAULT_ACCOUNT_ID && (Boolean(process.env[ENV_SERVICE_ACCOUNT]) || Boolean(process.env[ENV_SERVICE_ACCOUNT_FILE]))) {
|
|
228
|
+
if (accountId === DEFAULT_ACCOUNT_ID$1 && (Boolean(process.env[ENV_SERVICE_ACCOUNT]) || Boolean(process.env[ENV_SERVICE_ACCOUNT_FILE]))) {
|
|
133
229
|
if (await prompter.confirm({
|
|
134
230
|
message: "Use GOOGLE_CHAT_SERVICE_ACCOUNT env vars?",
|
|
135
231
|
initialValue: true
|
|
@@ -214,4 +310,4 @@ const googlechatSetupWizard = {
|
|
|
214
310
|
dmPolicy: googlechatDmPolicy
|
|
215
311
|
};
|
|
216
312
|
//#endregion
|
|
217
|
-
export { googlechatSetupAdapter as n, googlechatSetupWizard as t };
|
|
313
|
+
export { resolveDefaultGoogleChatAccountId as a, listGoogleChatAccountIds as i, googlechatSetupAdapter as n, resolveGoogleChatAccount as o, listEnabledGoogleChatAccounts as r, resolveGoogleChatConfigAccessorAccount as s, googlechatSetupWizard as t };
|
package/dist/test-api.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { F as setGoogleChatRuntime } from "./runtime-api-
|
|
2
|
-
import { t as googlechatPlugin } from "./channel-
|
|
1
|
+
import { F as setGoogleChatRuntime } from "./runtime-api-DuEfWHnr.js";
|
|
2
|
+
import { t as googlechatPlugin } from "./channel-DzmqN0DY.js";
|
|
3
3
|
export { googlechatPlugin, setGoogleChatRuntime };
|
package/openclaw.plugin.json
CHANGED
|
@@ -41,6 +41,30 @@
|
|
|
41
41
|
"allowBots": {
|
|
42
42
|
"type": "boolean"
|
|
43
43
|
},
|
|
44
|
+
"botLoopProtection": {
|
|
45
|
+
"type": "object",
|
|
46
|
+
"properties": {
|
|
47
|
+
"enabled": {
|
|
48
|
+
"type": "boolean"
|
|
49
|
+
},
|
|
50
|
+
"maxEventsPerWindow": {
|
|
51
|
+
"type": "integer",
|
|
52
|
+
"exclusiveMinimum": 0,
|
|
53
|
+
"maximum": 9007199254740991
|
|
54
|
+
},
|
|
55
|
+
"windowSeconds": {
|
|
56
|
+
"type": "integer",
|
|
57
|
+
"exclusiveMinimum": 0,
|
|
58
|
+
"maximum": 9007199254740991
|
|
59
|
+
},
|
|
60
|
+
"cooldownSeconds": {
|
|
61
|
+
"type": "integer",
|
|
62
|
+
"exclusiveMinimum": 0,
|
|
63
|
+
"maximum": 9007199254740991
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"additionalProperties": false
|
|
67
|
+
},
|
|
44
68
|
"dangerouslyAllowNameMatching": {
|
|
45
69
|
"type": "boolean"
|
|
46
70
|
},
|
|
@@ -83,6 +107,30 @@
|
|
|
83
107
|
"requireMention": {
|
|
84
108
|
"type": "boolean"
|
|
85
109
|
},
|
|
110
|
+
"botLoopProtection": {
|
|
111
|
+
"type": "object",
|
|
112
|
+
"properties": {
|
|
113
|
+
"enabled": {
|
|
114
|
+
"type": "boolean"
|
|
115
|
+
},
|
|
116
|
+
"maxEventsPerWindow": {
|
|
117
|
+
"type": "integer",
|
|
118
|
+
"exclusiveMinimum": 0,
|
|
119
|
+
"maximum": 9007199254740991
|
|
120
|
+
},
|
|
121
|
+
"windowSeconds": {
|
|
122
|
+
"type": "integer",
|
|
123
|
+
"exclusiveMinimum": 0,
|
|
124
|
+
"maximum": 9007199254740991
|
|
125
|
+
},
|
|
126
|
+
"cooldownSeconds": {
|
|
127
|
+
"type": "integer",
|
|
128
|
+
"exclusiveMinimum": 0,
|
|
129
|
+
"maximum": 9007199254740991
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
"additionalProperties": false
|
|
133
|
+
},
|
|
86
134
|
"users": {
|
|
87
135
|
"type": "array",
|
|
88
136
|
"items": {
|
|
@@ -464,6 +512,30 @@
|
|
|
464
512
|
"allowBots": {
|
|
465
513
|
"type": "boolean"
|
|
466
514
|
},
|
|
515
|
+
"botLoopProtection": {
|
|
516
|
+
"type": "object",
|
|
517
|
+
"properties": {
|
|
518
|
+
"enabled": {
|
|
519
|
+
"type": "boolean"
|
|
520
|
+
},
|
|
521
|
+
"maxEventsPerWindow": {
|
|
522
|
+
"type": "integer",
|
|
523
|
+
"exclusiveMinimum": 0,
|
|
524
|
+
"maximum": 9007199254740991
|
|
525
|
+
},
|
|
526
|
+
"windowSeconds": {
|
|
527
|
+
"type": "integer",
|
|
528
|
+
"exclusiveMinimum": 0,
|
|
529
|
+
"maximum": 9007199254740991
|
|
530
|
+
},
|
|
531
|
+
"cooldownSeconds": {
|
|
532
|
+
"type": "integer",
|
|
533
|
+
"exclusiveMinimum": 0,
|
|
534
|
+
"maximum": 9007199254740991
|
|
535
|
+
}
|
|
536
|
+
},
|
|
537
|
+
"additionalProperties": false
|
|
538
|
+
},
|
|
467
539
|
"dangerouslyAllowNameMatching": {
|
|
468
540
|
"type": "boolean"
|
|
469
541
|
},
|
|
@@ -506,6 +578,30 @@
|
|
|
506
578
|
"requireMention": {
|
|
507
579
|
"type": "boolean"
|
|
508
580
|
},
|
|
581
|
+
"botLoopProtection": {
|
|
582
|
+
"type": "object",
|
|
583
|
+
"properties": {
|
|
584
|
+
"enabled": {
|
|
585
|
+
"type": "boolean"
|
|
586
|
+
},
|
|
587
|
+
"maxEventsPerWindow": {
|
|
588
|
+
"type": "integer",
|
|
589
|
+
"exclusiveMinimum": 0,
|
|
590
|
+
"maximum": 9007199254740991
|
|
591
|
+
},
|
|
592
|
+
"windowSeconds": {
|
|
593
|
+
"type": "integer",
|
|
594
|
+
"exclusiveMinimum": 0,
|
|
595
|
+
"maximum": 9007199254740991
|
|
596
|
+
},
|
|
597
|
+
"cooldownSeconds": {
|
|
598
|
+
"type": "integer",
|
|
599
|
+
"exclusiveMinimum": 0,
|
|
600
|
+
"maximum": 9007199254740991
|
|
601
|
+
}
|
|
602
|
+
},
|
|
603
|
+
"additionalProperties": false
|
|
604
|
+
},
|
|
509
605
|
"users": {
|
|
510
606
|
"type": "array",
|
|
511
607
|
"items": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/googlechat",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.14-beta.2",
|
|
4
4
|
"description": "OpenClaw Google Chat channel plugin",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"openclaw": "workspace:*"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"openclaw": ">=2026.5.
|
|
20
|
+
"openclaw": ">=2026.5.14-beta.2"
|
|
21
21
|
},
|
|
22
22
|
"peerDependenciesMeta": {
|
|
23
23
|
"openclaw": {
|
|
@@ -75,10 +75,10 @@
|
|
|
75
75
|
"minHostVersion": ">=2026.4.10"
|
|
76
76
|
},
|
|
77
77
|
"compat": {
|
|
78
|
-
"pluginApi": ">=2026.5.
|
|
78
|
+
"pluginApi": ">=2026.5.14-beta.2"
|
|
79
79
|
},
|
|
80
80
|
"build": {
|
|
81
|
-
"openclawVersion": "2026.5.
|
|
81
|
+
"openclawVersion": "2026.5.14-beta.2"
|
|
82
82
|
},
|
|
83
83
|
"release": {
|
|
84
84
|
"publishToClawHub": true,
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { safeParseJsonWithSchema, safeParseWithSchema } from "openclaw/plugin-sdk/extension-shared";
|
|
2
|
-
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
|
-
import { DEFAULT_ACCOUNT_ID, createAccountListHelpers, normalizeAccountId, resolveAccountEntry, resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
|
|
4
|
-
import { isSecretRef } from "openclaw/plugin-sdk/secret-input";
|
|
5
|
-
import { z } from "zod";
|
|
6
|
-
//#region extensions/googlechat/src/accounts.ts
|
|
7
|
-
const ENV_SERVICE_ACCOUNT = "GOOGLE_CHAT_SERVICE_ACCOUNT";
|
|
8
|
-
const ENV_SERVICE_ACCOUNT_FILE = "GOOGLE_CHAT_SERVICE_ACCOUNT_FILE";
|
|
9
|
-
const JsonRecordSchema = z.record(z.string(), z.unknown());
|
|
10
|
-
const { listAccountIds: listGoogleChatAccountIds, resolveDefaultAccountId: resolveDefaultGoogleChatAccountId } = createAccountListHelpers("googlechat");
|
|
11
|
-
function mergeGoogleChatAccountConfig(cfg, accountId) {
|
|
12
|
-
const raw = cfg.channels?.["googlechat"] ?? {};
|
|
13
|
-
const base = resolveMergedAccountConfig({
|
|
14
|
-
channelConfig: raw,
|
|
15
|
-
accounts: raw.accounts,
|
|
16
|
-
accountId,
|
|
17
|
-
omitKeys: ["defaultAccount"]
|
|
18
|
-
});
|
|
19
|
-
const defaultAccountConfig = resolveAccountEntry(raw.accounts, DEFAULT_ACCOUNT_ID) ?? {};
|
|
20
|
-
if (accountId === DEFAULT_ACCOUNT_ID) return base;
|
|
21
|
-
const { enabled: _ignoredEnabled, dangerouslyAllowNameMatching: _ignoredDangerouslyAllowNameMatching, serviceAccount: _ignoredServiceAccount, serviceAccountRef: _ignoredServiceAccountRef, serviceAccountFile: _ignoredServiceAccountFile, ...defaultAccountShared } = defaultAccountConfig;
|
|
22
|
-
return {
|
|
23
|
-
...defaultAccountShared,
|
|
24
|
-
...base
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
function resolveGoogleChatConfigAccessorAccount(params) {
|
|
28
|
-
const accountId = normalizeAccountId(params.accountId ?? params.cfg.channels?.googlechat?.defaultAccount);
|
|
29
|
-
return { config: mergeGoogleChatAccountConfig(params.cfg, accountId) };
|
|
30
|
-
}
|
|
31
|
-
function parseServiceAccount(value) {
|
|
32
|
-
if (isSecretRef(value)) return null;
|
|
33
|
-
if (typeof value === "string") {
|
|
34
|
-
const trimmed = value.trim();
|
|
35
|
-
if (!trimmed) return null;
|
|
36
|
-
return safeParseJsonWithSchema(JsonRecordSchema, trimmed);
|
|
37
|
-
}
|
|
38
|
-
return safeParseWithSchema(JsonRecordSchema, value);
|
|
39
|
-
}
|
|
40
|
-
function resolveCredentialsFromConfig(params) {
|
|
41
|
-
const { account, accountId } = params;
|
|
42
|
-
const inline = parseServiceAccount(account.serviceAccount);
|
|
43
|
-
if (inline) return {
|
|
44
|
-
credentials: inline,
|
|
45
|
-
source: "inline"
|
|
46
|
-
};
|
|
47
|
-
if (isSecretRef(account.serviceAccount)) throw new Error(`channels.googlechat.accounts.${accountId}.serviceAccount: unresolved SecretRef "${account.serviceAccount.source}:${account.serviceAccount.provider}:${account.serviceAccount.id}". Resolve this command against an active gateway runtime snapshot before reading it.`);
|
|
48
|
-
if (isSecretRef(account.serviceAccountRef)) throw new Error(`channels.googlechat.accounts.${accountId}.serviceAccount: unresolved SecretRef "${account.serviceAccountRef.source}:${account.serviceAccountRef.provider}:${account.serviceAccountRef.id}". Resolve this command against an active gateway runtime snapshot before reading it.`);
|
|
49
|
-
const file = normalizeOptionalString(account.serviceAccountFile);
|
|
50
|
-
if (file) return {
|
|
51
|
-
credentialsFile: file,
|
|
52
|
-
source: "file"
|
|
53
|
-
};
|
|
54
|
-
if (accountId === DEFAULT_ACCOUNT_ID) {
|
|
55
|
-
const envJson = process.env[ENV_SERVICE_ACCOUNT];
|
|
56
|
-
const envInline = parseServiceAccount(envJson);
|
|
57
|
-
if (envInline) return {
|
|
58
|
-
credentials: envInline,
|
|
59
|
-
source: "env"
|
|
60
|
-
};
|
|
61
|
-
const envFile = normalizeOptionalString(process.env[ENV_SERVICE_ACCOUNT_FILE]);
|
|
62
|
-
if (envFile) return {
|
|
63
|
-
credentialsFile: envFile,
|
|
64
|
-
source: "env"
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
return { source: "none" };
|
|
68
|
-
}
|
|
69
|
-
function resolveGoogleChatAccount(params) {
|
|
70
|
-
const accountId = normalizeAccountId(params.accountId ?? params.cfg.channels?.["googlechat"]?.defaultAccount);
|
|
71
|
-
const baseEnabled = params.cfg.channels?.["googlechat"]?.enabled !== false;
|
|
72
|
-
const merged = mergeGoogleChatAccountConfig(params.cfg, accountId);
|
|
73
|
-
const accountEnabled = merged.enabled !== false;
|
|
74
|
-
const enabled = baseEnabled && accountEnabled;
|
|
75
|
-
const credentials = resolveCredentialsFromConfig({
|
|
76
|
-
accountId,
|
|
77
|
-
account: merged
|
|
78
|
-
});
|
|
79
|
-
return {
|
|
80
|
-
accountId,
|
|
81
|
-
name: normalizeOptionalString(merged.name),
|
|
82
|
-
enabled,
|
|
83
|
-
config: merged,
|
|
84
|
-
credentialSource: credentials.source,
|
|
85
|
-
credentials: credentials.credentials,
|
|
86
|
-
credentialsFile: credentials.credentialsFile
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
function listEnabledGoogleChatAccounts(cfg) {
|
|
90
|
-
return listGoogleChatAccountIds(cfg).map((accountId) => resolveGoogleChatAccount({
|
|
91
|
-
cfg,
|
|
92
|
-
accountId
|
|
93
|
-
})).filter((account) => account.enabled);
|
|
94
|
-
}
|
|
95
|
-
//#endregion
|
|
96
|
-
export { resolveGoogleChatConfigAccessorAccount as a, resolveGoogleChatAccount as i, listGoogleChatAccountIds as n, resolveDefaultGoogleChatAccountId as r, listEnabledGoogleChatAccounts as t };
|
package/dist/targets-C_U5SbnT.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { a as findGoogleChatDirectMessage } from "./api-DubPSfme.js";
|
|
2
|
-
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
|
-
//#region extensions/googlechat/src/targets.ts
|
|
4
|
-
function normalizeGoogleChatTarget(raw) {
|
|
5
|
-
const trimmed = raw?.trim();
|
|
6
|
-
if (!trimmed) return;
|
|
7
|
-
const normalized = trimmed.replace(/^(googlechat|google-chat|gchat):/i, "").replace(/^user:(users\/)?/i, "users/").replace(/^space:(spaces\/)?/i, "spaces/");
|
|
8
|
-
if (isGoogleChatUserTarget(normalized)) {
|
|
9
|
-
const suffix = normalized.slice(6);
|
|
10
|
-
return suffix.includes("@") ? `users/${normalizeLowercaseStringOrEmpty(suffix)}` : normalized;
|
|
11
|
-
}
|
|
12
|
-
if (isGoogleChatSpaceTarget(normalized)) return normalized;
|
|
13
|
-
if (normalized.includes("@")) return `users/${normalizeLowercaseStringOrEmpty(normalized)}`;
|
|
14
|
-
return normalized;
|
|
15
|
-
}
|
|
16
|
-
function isGoogleChatUserTarget(value) {
|
|
17
|
-
return normalizeLowercaseStringOrEmpty(value).startsWith("users/");
|
|
18
|
-
}
|
|
19
|
-
function isGoogleChatSpaceTarget(value) {
|
|
20
|
-
return normalizeLowercaseStringOrEmpty(value).startsWith("spaces/");
|
|
21
|
-
}
|
|
22
|
-
function stripMessageSuffix(target) {
|
|
23
|
-
const index = target.indexOf("/messages/");
|
|
24
|
-
if (index === -1) return target;
|
|
25
|
-
return target.slice(0, index);
|
|
26
|
-
}
|
|
27
|
-
async function resolveGoogleChatOutboundSpace(params) {
|
|
28
|
-
const normalized = normalizeGoogleChatTarget(params.target);
|
|
29
|
-
if (!normalized) throw new Error("Missing Google Chat target.");
|
|
30
|
-
const base = stripMessageSuffix(normalized);
|
|
31
|
-
if (isGoogleChatSpaceTarget(base)) return base;
|
|
32
|
-
if (isGoogleChatUserTarget(base)) {
|
|
33
|
-
const dm = await findGoogleChatDirectMessage({
|
|
34
|
-
account: params.account,
|
|
35
|
-
userName: base
|
|
36
|
-
});
|
|
37
|
-
if (!dm?.name) throw new Error(`No Google Chat DM found for ${base}`);
|
|
38
|
-
return dm.name;
|
|
39
|
-
}
|
|
40
|
-
return base;
|
|
41
|
-
}
|
|
42
|
-
//#endregion
|
|
43
|
-
export { resolveGoogleChatOutboundSpace as i, isGoogleChatUserTarget as n, normalizeGoogleChatTarget as r, isGoogleChatSpaceTarget as t };
|
|
File without changes
|
|
File without changes
|