@fased/fased 0.1.36 → 0.1.37
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/config/capability-catalog.json +241 -0
- package/config/dependency-ownership.json +130 -0
- package/config/official-channel-catalog.json +39 -0
- package/dist/agents/tools/web-fetch-utils.js +2 -1
- package/dist/agents/tools/web-fetch-visibility.js +3 -1
- package/dist/brand.js +1 -1
- package/dist/browser/pw-session.js +2 -1
- package/dist/browser/pw-tools-core.state.js +3 -2
- package/dist/browser/runtime-dependency.js +27 -0
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/capabilities/catalog.js +228 -0
- package/dist/cli/components-cli.js +105 -0
- package/dist/cli/hooks-cli.js +1 -1
- package/dist/cli/plugins-cli.js +3 -3
- package/dist/cli/program/register.subclis.js +9 -0
- package/dist/cli/update-cli/update-command.js +1 -1
- package/dist/commands/doctor-capabilities.js +26 -0
- package/dist/commands/doctor.js +2 -0
- package/dist/control-ui/assets/{app-DXYMrRJ9.js → app-JBrTSsNu.js} +1480 -1465
- package/dist/control-ui/assets/{index-iGQkJE0l.js → index-H-2DWAnH.js} +3 -3
- package/dist/control-ui/index.html +1 -1
- package/dist/gateway/method-scopes.js +1 -0
- package/dist/gateway/server-methods/plugins-marketplace.js +1 -1
- package/dist/gateway/server-methods/services.js +8 -0
- package/dist/gateway/server-methods-list.js +1 -0
- package/dist/infra/optional-runtime-dependency.js +46 -0
- package/dist/media/image-ops.js +6 -1
- package/dist/media/input-files.js +11 -2
- package/dist/media/mime.js +6 -1
- package/dist/memory/sqlite-vec.js +7 -1
- package/dist/plugin-sdk/{accounts-VqAX3HJ0.js → accounts-BwkH5bTD.js} +1 -1
- package/dist/plugin-sdk/{accounts-COY-15zJ.js → accounts-C5BjfxoO.js} +1 -1
- package/dist/plugin-sdk/{accounts-Zgt0NP54.js → accounts-RT6y7n3H.js} +1 -1
- package/dist/plugin-sdk/{actions-lB3PCwsz.js → actions-BrU_JMqM.js} +2 -2
- package/dist/plugin-sdk/{actions-BI5QViLk.js → actions-CUdVHzeJ.js} +2 -2
- package/dist/plugin-sdk/{active-listener-DKZiSaRA.js → active-listener-CE8t5g6l.js} +1 -1
- package/dist/plugin-sdk/{active-listener-D6_pdTMC.js → active-listener-Dx1SEGDn.js} +1 -1
- package/dist/plugin-sdk/{audio-preflight-Dx8J7XhV.js → audio-preflight-C8SbY4fV.js} +22 -22
- package/dist/plugin-sdk/{audio-preflight-CeRstduc.js → audio-preflight-CuABlLDL.js} +22 -22
- package/dist/plugin-sdk/{audit-BImNOQvk.js → audit-DxB2Kml5.js} +11 -11
- package/dist/plugin-sdk/{audit-CKWkq0Un.js → audit-sSeRDwJb.js} +11 -11
- package/dist/plugin-sdk/channel-plugin-common.js +3 -2
- package/dist/plugin-sdk/{chunk-skAJZWCT.js → chunk-KCxbJLsV.js} +1 -1
- package/dist/plugin-sdk/{chunk-Cjw4712g.js → chunk-M2d7r-Zt.js} +1 -1
- package/dist/plugin-sdk/{chunk-items-Df5811D0.js → chunk-items-BGbYyNvO.js} +1 -1
- package/dist/plugin-sdk/command-status.js +22 -22
- package/dist/plugin-sdk/{commands-registry-5J619bQ2.js → commands-registry-CdT-nl7v.js} +2 -2
- package/dist/plugin-sdk/{commands-registry-CoLAT03J.js → commands-registry-DoWdYAJd.js} +2 -2
- package/dist/plugin-sdk/{common-CSTxewqY.js → common-Cpw-vuuM.js} +1 -1
- package/dist/plugin-sdk/{common-BiF0sTnY.js → common-DJWrErdE.js} +1 -1
- package/dist/plugin-sdk/{config-BxVjLTIv.js → config-C0qggNKQ.js} +7 -7
- package/dist/plugin-sdk/{config-TFokWwvO.js → config-CgwTaper.js} +45 -2
- package/dist/plugin-sdk/{config-DXlISPlx.js → config-DtqAN2L3.js} +45 -2
- package/dist/plugin-sdk/{config-DgD5T-Ui.js → config-g1MG9Bet.js} +7 -7
- package/dist/plugin-sdk/discord.js +2 -1
- package/dist/plugin-sdk/{fetch-guard-DPMynms8.js → fetch-guard-BLdujDnt.js} +1 -1
- package/dist/plugin-sdk/{fetch-guard-COLmW2TM.js → fetch-guard-DTKgMN9f.js} +1 -1
- package/dist/plugin-sdk/{handle-action-C-UndB8E.js → handle-action-B61D85yo.js} +12 -12
- package/dist/plugin-sdk/{handle-action-hhufKxmU.js → handle-action-CjcxGzQD.js} +12 -12
- package/dist/plugin-sdk/index.js +22 -22
- package/dist/plugin-sdk/{login-DL1oBBfL.js → login-CB_nCjX3.js} +3 -3
- package/dist/plugin-sdk/{login-YveoqEvH.js → login-CfRfRNc7.js} +2 -2
- package/dist/plugin-sdk/{login-Dnxas1dI.js → login-CzUuDd1v.js} +3 -3
- package/dist/plugin-sdk/{login-qr-CVQsGWKq.js → login-qr-D7MEpFza.js} +3 -3
- package/dist/plugin-sdk/{login-qr-B343JNXJ.js → login-qr-DgxfBFVN.js} +3 -3
- package/dist/plugin-sdk/{manager-BytwRIfK.js → manager-9see0IX5.js} +8 -3
- package/dist/plugin-sdk/{manager-BZjd_PCt.js → manager-D2kZ0Cps.js} +8 -3
- package/dist/plugin-sdk/{markdown-tables-krKiU4cT.js → markdown-tables-CCSD52rA.js} +1 -1
- package/dist/plugin-sdk/{markdown-tables-9rJDQ1X-.js → markdown-tables-DWUNvLrc.js} +1 -1
- package/dist/plugin-sdk/{media-D7ZkWU-A.js → media-C7YT4DeU.js} +13 -4
- package/dist/plugin-sdk/{media-C6C2c9a4.js → media-Cxw-xLGU.js} +13 -4
- package/dist/plugin-sdk/{monitor-CVQNDfhs.js → monitor-9X7wapUN.js} +27 -27
- package/dist/plugin-sdk/{monitor-BCxkQBoh.js → monitor-B3IfJzKX.js} +26 -26
- package/dist/plugin-sdk/{monitor-BzDW50H6.js → monitor-CTjAST_W.js} +27 -27
- package/dist/plugin-sdk/{monitor-BsLMfn3U.js → monitor-tYFfy2u-.js} +26 -26
- package/dist/plugin-sdk/{openresponses-http-BB67Z4QM.js → openresponses-http-DEB2fEya.js} +22 -22
- package/dist/plugin-sdk/{openresponses-http-NE76GGhj.js → openresponses-http-YgMg1jnp.js} +22 -22
- package/dist/plugin-sdk/{ssrf-u0Ul7okY.js → optional-runtime-dependency-szS49voX.js} +46 -2
- package/dist/plugin-sdk/{outbound-PGNWU6T-.js → outbound-BCxOxdi-.js} +6 -6
- package/dist/plugin-sdk/{outbound-BHWJD9q0.js → outbound-JIwQbpRY.js} +6 -6
- package/dist/plugin-sdk/{outbound-attachment-BVzfhqnk.js → outbound-attachment-C-jSFaEB.js} +1 -1
- package/dist/plugin-sdk/{outbound-attachment-ClMvNW17.js → outbound-attachment-D6tj5Y5H.js} +1 -1
- package/dist/plugin-sdk/{pi-embedded-runner-DQ6UPx6-.js → pi-embedded-runner-CUVk8CIj.js} +72 -60
- package/dist/plugin-sdk/{pi-model-discovery-D51L5E9I.js → pi-model-discovery-DadlvsZW.js} +1 -1
- package/dist/plugin-sdk/{pi-model-discovery-runtime-DxoFm4Vd.js → pi-model-discovery-runtime-C5ssE6EW.js} +1 -1
- package/dist/plugin-sdk/{pi-model-discovery-runtime-DRuh4PNV.js → pi-model-discovery-runtime-DAaOPt64.js} +2 -2
- package/dist/plugin-sdk/{pi-model-discovery-runtime-CQOauapl.js → pi-model-discovery-runtime-LB-5lX5L.js} +1 -1
- package/dist/plugin-sdk/{pi-model-discovery-runtime-BTWgkx4x.js → pi-model-discovery-runtime-gd77b0UF.js} +2 -2
- package/dist/plugin-sdk/{pi-model-discovery-CkH5l9X5.js → pi-model-discovery-yifTIDIn.js} +1 -1
- package/dist/plugin-sdk/{pw-ai-DZs6uYRd.js → pw-ai-SNCI9UXT.js} +6 -5
- package/dist/plugin-sdk/{pw-ai-Cn-EUPZK.js → pw-ai-sH1Pi-Fh.js} +6 -5
- package/dist/plugin-sdk/{registry-B4HvWzob.js → registry-DNkJsNsz.js} +4 -3
- package/dist/plugin-sdk/{replies-jDMUBsNO.js → replies-fvzN5wnU.js} +2 -2
- package/dist/plugin-sdk/{replies-B_LW9c9f.js → replies-zebgZCHX.js} +2 -2
- package/dist/plugin-sdk/{resolve-route-_YZHbS-z.js → resolve-route-CNhTJG_2.js} +1 -1
- package/dist/plugin-sdk/{resolve-route-DlHzggnG.js → resolve-route-DO0WR4nB.js} +1 -1
- package/dist/plugin-sdk/{resolve-utils-zBhVX3Vx.js → resolve-utils-EFP1DYqG.js} +1 -1
- package/dist/plugin-sdk/{retry-policy-DGLs5ebP.js → retry-policy-BLYEgIwP.js} +1 -1
- package/dist/plugin-sdk/{retry-policy-DaodShGa.js → retry-policy-sM01QZT2.js} +1 -1
- package/dist/plugin-sdk/{runtime-Dd0sswuW.js → runtime-BPyuAmka.js} +4 -3
- package/dist/plugin-sdk/{paths-DW-U2r7Y.js → runtime-dependency-B-1u41Ph.js} +27 -3
- package/dist/plugin-sdk/{paths-CBCpJ5bu.js → runtime-dependency-Csswyv5r.js} +27 -3
- package/dist/plugin-sdk/sat-runtime.js +1 -0
- package/dist/plugin-sdk/{send-8remZhsH.js → send-B09HRfHN.js} +6 -6
- package/dist/plugin-sdk/{send-DaaAscWj.js → send-BBYcgXA4.js} +5 -5
- package/dist/plugin-sdk/{send-DFTB0cnx.js → send-BQXQA81e.js} +6 -6
- package/dist/plugin-sdk/{send-CtMVvMJs.js → send-BzFdvD_i.js} +6 -6
- package/dist/plugin-sdk/{send-Di9irjS-.js → send-C3u-F_8s.js} +6 -6
- package/dist/plugin-sdk/{send-Dl88ZKq2.js → send-D3836_UN.js} +6 -6
- package/dist/plugin-sdk/{send-ifgZCHVB.js → send-D7tr-EsH.js} +5 -5
- package/dist/plugin-sdk/{send-DvUpYh0d.js → send-DRGzRdI3.js} +6 -6
- package/dist/plugin-sdk/{send-D2jlPeKH.js → send-DfkPKz_0.js} +4 -4
- package/dist/plugin-sdk/{send-QSt6nbny.js → send-DsxFXrCO.js} +4 -4
- package/dist/plugin-sdk/{session-CknrceVU.js → session-DG_bYisw.js} +2 -2
- package/dist/plugin-sdk/{session-CyRTcJ8N.js → session-EIStzK0E.js} +2 -2
- package/dist/plugin-sdk/{skill-commands-DHxkTA0s.js → skill-commands-CkgGmWBS.js} +2 -2
- package/dist/plugin-sdk/{skill-commands-CLXAG5sU.js → skill-commands-hyZA0LE3.js} +2 -2
- package/dist/plugin-sdk/{slack-BpJXkwxF.js → slack-C0WZVJPZ.js} +34 -34
- package/dist/plugin-sdk/{slack-Br9c6Yzs.js → slack-DtlPw_PN.js} +34 -34
- package/dist/plugin-sdk/{slack-actions-8tLGt1Ht.js → slack-actions-BQawWGTJ.js} +11 -11
- package/dist/plugin-sdk/{slack-actions-CDSSRo8Y.js → slack-actions-DDMszx56.js} +11 -11
- package/dist/plugin-sdk/slack.js +2 -1
- package/dist/plugin-sdk/src/brand.d.ts +2 -2
- package/dist/plugin-sdk/src/browser/runtime-dependency.d.ts +3 -0
- package/dist/plugin-sdk/src/infra/optional-runtime-dependency.d.ts +6 -0
- package/dist/plugin-sdk/{ssrf-fG4UHKBw.js → ssrf-Cce6LubA.js} +41 -7
- package/dist/plugin-sdk/{status-DoxnzyMC.js → status-CIiBeb9B.js} +72 -60
- package/dist/plugin-sdk/{sticker-cache-zmJ39ByC.js → sticker-cache-9houHChV.js} +3 -3
- package/dist/plugin-sdk/{sticker-cache-D7WcMYsF.js → sticker-cache-Dwi-XsgB.js} +3 -3
- package/dist/plugin-sdk/{tables-CkxblF0u.js → tables-BD7H5ug-.js} +1 -1
- package/dist/plugin-sdk/{tables-RBtf8G4P.js → tables-CJE8kB35.js} +1 -1
- package/dist/plugin-sdk/{telegram-actions-DAeYXSLk.js → telegram-actions-Bu1edXF2.js} +25 -25
- package/dist/plugin-sdk/{telegram-actions-BDeS3ocd.js → telegram-actions-CRXVO8lG.js} +25 -25
- package/dist/plugin-sdk/telegram.js +2 -1
- package/dist/plugin-sdk/{thread-bindings.lifecycle-DsB94WbE.js → thread-bindings.lifecycle-Djem8FWF.js} +2 -2
- package/dist/plugin-sdk/{thread-bindings.lifecycle-DMhV3TsR.js → thread-bindings.lifecycle-gmHv3hc7.js} +2 -2
- package/dist/plugin-sdk/{thread-bindings.state-DYWpy7hJ.js → thread-bindings.state-CNT2vxm3.js} +2 -2
- package/dist/plugin-sdk/{thread-bindings.state-DG5cBaRn.js → thread-bindings.state-D8kN2X6H.js} +2 -2
- package/dist/plugin-sdk/{web-IJ4NoGPJ.js → web-DCrVQr6y.js} +26 -26
- package/dist/plugin-sdk/{web-DJwacWQo.js → web-Ds6OWw3C.js} +26 -26
- package/dist/plugin-sdk/{whatsapp-actions-DAY8hWpD.js → whatsapp-actions-68Mfs2dJ.js} +12 -12
- package/dist/plugin-sdk/{whatsapp-actions-6opfo-oB.js → whatsapp-actions-DRqtzvTO.js} +12 -12
- package/dist/plugin-sdk/whatsapp.js +6 -5
- package/dist/plugin-sdk/{wsl-B2euLEc2.js → wsl-DcM2PgSS.js} +3 -3
- package/dist/plugin-sdk/{wsl-B8eyviuv.js → wsl-PXrdWitE.js} +3 -3
- package/dist/plugins/update.js +1 -1
- package/dist/tts/tts-core.js +6 -1
- package/dist/wizard/onboarding.finalize.js +9 -0
- package/extensions/acpx/package.json +1 -1
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/fased-federation/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/matrix/CHANGELOG.md +6 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/mattermost/package.json +1 -1
- package/extensions/memory-core/package.json +2 -2
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +6 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +6 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/sat-mining/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/synology-chat/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +6 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +6 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +6 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +6 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +22 -15
- package/extensions/feishu/fased.plugin.json +0 -10
- package/extensions/feishu/index.ts +0 -63
- package/extensions/feishu/package.json +0 -33
- package/extensions/feishu/skills/feishu-doc/SKILL.md +0 -211
- package/extensions/feishu/skills/feishu-doc/references/block-types.md +0 -103
- package/extensions/feishu/skills/feishu-drive/SKILL.md +0 -97
- package/extensions/feishu/skills/feishu-perm/SKILL.md +0 -119
- package/extensions/feishu/skills/feishu-wiki/SKILL.md +0 -111
- package/extensions/feishu/src/accounts.ts +0 -159
- package/extensions/feishu/src/bitable.ts +0 -713
- package/extensions/feishu/src/bot.ts +0 -979
- package/extensions/feishu/src/channel.ts +0 -359
- package/extensions/feishu/src/client.ts +0 -118
- package/extensions/feishu/src/config-schema.ts +0 -226
- package/extensions/feishu/src/dedup.ts +0 -54
- package/extensions/feishu/src/directory.ts +0 -177
- package/extensions/feishu/src/doc-schema.ts +0 -122
- package/extensions/feishu/src/docx.ts +0 -998
- package/extensions/feishu/src/drive-schema.ts +0 -46
- package/extensions/feishu/src/drive.ts +0 -232
- package/extensions/feishu/src/dynamic-agent.ts +0 -131
- package/extensions/feishu/src/external-keys.ts +0 -19
- package/extensions/feishu/src/media.ts +0 -447
- package/extensions/feishu/src/mention.ts +0 -133
- package/extensions/feishu/src/monitor.ts +0 -434
- package/extensions/feishu/src/onboarding.ts +0 -430
- package/extensions/feishu/src/outbound.ts +0 -56
- package/extensions/feishu/src/perm-schema.ts +0 -52
- package/extensions/feishu/src/perm.ts +0 -180
- package/extensions/feishu/src/policy.ts +0 -116
- package/extensions/feishu/src/probe.ts +0 -44
- package/extensions/feishu/src/reactions.ts +0 -160
- package/extensions/feishu/src/reply-dispatcher.ts +0 -239
- package/extensions/feishu/src/runtime.ts +0 -14
- package/extensions/feishu/src/send-result.ts +0 -29
- package/extensions/feishu/src/send-target.ts +0 -25
- package/extensions/feishu/src/send.ts +0 -313
- package/extensions/feishu/src/streaming-card.ts +0 -218
- package/extensions/feishu/src/targets.ts +0 -78
- package/extensions/feishu/src/tool-account.ts +0 -58
- package/extensions/feishu/src/tools-config.ts +0 -21
- package/extensions/feishu/src/types.ts +0 -81
- package/extensions/feishu/src/typing.ts +0 -80
- package/extensions/feishu/src/wiki-schema.ts +0 -55
- package/extensions/feishu/src/wiki.ts +0 -237
- package/extensions/googlechat/fased.plugin.json +0 -9
- package/extensions/googlechat/index.ts +0 -19
- package/extensions/googlechat/package.json +0 -37
- package/extensions/googlechat/src/accounts.ts +0 -147
- package/extensions/googlechat/src/actions.ts +0 -176
- package/extensions/googlechat/src/api.ts +0 -282
- package/extensions/googlechat/src/auth.ts +0 -137
- package/extensions/googlechat/src/channel.ts +0 -591
- package/extensions/googlechat/src/monitor.ts +0 -996
- package/extensions/googlechat/src/onboarding.ts +0 -344
- package/extensions/googlechat/src/runtime.ts +0 -14
- package/extensions/googlechat/src/targets.ts +0 -65
- package/extensions/googlechat/src/types.config.ts +0 -3
- package/extensions/googlechat/src/types.ts +0 -73
|
@@ -1,996 +0,0 @@
|
|
|
1
|
-
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
-
import type { FasedAgentConfig } from "fased/plugin-sdk";
|
|
3
|
-
import {
|
|
4
|
-
GROUP_POLICY_BLOCKED_LABEL,
|
|
5
|
-
createScopedPairingAccess,
|
|
6
|
-
createReplyPrefixOptions,
|
|
7
|
-
readJsonBodyWithLimit,
|
|
8
|
-
registerWebhookTarget,
|
|
9
|
-
rejectNonPostWebhookRequest,
|
|
10
|
-
isDangerousNameMatchingEnabled,
|
|
11
|
-
resolveAllowlistProviderRuntimeGroupPolicy,
|
|
12
|
-
resolveDefaultGroupPolicy,
|
|
13
|
-
resolveSingleWebhookTargetAsync,
|
|
14
|
-
resolveWebhookPath,
|
|
15
|
-
resolveWebhookTargets,
|
|
16
|
-
warnMissingProviderGroupPolicyFallbackOnce,
|
|
17
|
-
requestBodyErrorToText,
|
|
18
|
-
resolveMentionGatingWithBypass,
|
|
19
|
-
resolveDmGroupAccessWithLists,
|
|
20
|
-
} from "fased/plugin-sdk";
|
|
21
|
-
import { type ResolvedGoogleChatAccount } from "./accounts.js";
|
|
22
|
-
import {
|
|
23
|
-
downloadGoogleChatMedia,
|
|
24
|
-
deleteGoogleChatMessage,
|
|
25
|
-
sendGoogleChatMessage,
|
|
26
|
-
updateGoogleChatMessage,
|
|
27
|
-
} from "./api.js";
|
|
28
|
-
import { verifyGoogleChatRequest, type GoogleChatAudienceType } from "./auth.js";
|
|
29
|
-
import { getGoogleChatRuntime } from "./runtime.js";
|
|
30
|
-
import type {
|
|
31
|
-
GoogleChatAnnotation,
|
|
32
|
-
GoogleChatAttachment,
|
|
33
|
-
GoogleChatEvent,
|
|
34
|
-
GoogleChatSpace,
|
|
35
|
-
GoogleChatMessage,
|
|
36
|
-
GoogleChatUser,
|
|
37
|
-
} from "./types.js";
|
|
38
|
-
|
|
39
|
-
export type GoogleChatRuntimeEnv = {
|
|
40
|
-
log?: (message: string) => void;
|
|
41
|
-
error?: (message: string) => void;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export type GoogleChatMonitorOptions = {
|
|
45
|
-
account: ResolvedGoogleChatAccount;
|
|
46
|
-
config: FasedAgentConfig;
|
|
47
|
-
runtime: GoogleChatRuntimeEnv;
|
|
48
|
-
abortSignal: AbortSignal;
|
|
49
|
-
webhookPath?: string;
|
|
50
|
-
webhookUrl?: string;
|
|
51
|
-
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
type GoogleChatCoreRuntime = ReturnType<typeof getGoogleChatRuntime>;
|
|
55
|
-
|
|
56
|
-
type WebhookTarget = {
|
|
57
|
-
account: ResolvedGoogleChatAccount;
|
|
58
|
-
config: FasedAgentConfig;
|
|
59
|
-
runtime: GoogleChatRuntimeEnv;
|
|
60
|
-
core: GoogleChatCoreRuntime;
|
|
61
|
-
path: string;
|
|
62
|
-
audienceType?: GoogleChatAudienceType;
|
|
63
|
-
audience?: string;
|
|
64
|
-
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void;
|
|
65
|
-
mediaMaxMb: number;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const webhookTargets = new Map<string, WebhookTarget[]>();
|
|
69
|
-
|
|
70
|
-
function logVerbose(core: GoogleChatCoreRuntime, runtime: GoogleChatRuntimeEnv, message: string) {
|
|
71
|
-
if (core.logging.shouldLogVerbose()) {
|
|
72
|
-
runtime.log?.(`[googlechat] ${message}`);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const warnedDeprecatedUsersEmailAllowFrom = new Set<string>();
|
|
77
|
-
function warnDeprecatedUsersEmailEntries(
|
|
78
|
-
core: GoogleChatCoreRuntime,
|
|
79
|
-
runtime: GoogleChatRuntimeEnv,
|
|
80
|
-
entries: string[],
|
|
81
|
-
) {
|
|
82
|
-
const deprecated = entries.map((v) => String(v).trim()).filter((v) => /^users\/.+@.+/i.test(v));
|
|
83
|
-
if (deprecated.length === 0) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
const key = deprecated
|
|
87
|
-
.map((v) => v.toLowerCase())
|
|
88
|
-
.sort()
|
|
89
|
-
.join(",");
|
|
90
|
-
if (warnedDeprecatedUsersEmailAllowFrom.has(key)) {
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
warnedDeprecatedUsersEmailAllowFrom.add(key);
|
|
94
|
-
logVerbose(
|
|
95
|
-
core,
|
|
96
|
-
runtime,
|
|
97
|
-
`Deprecated allowFrom entry detected: "users/<email>" is no longer treated as an email allowlist. Use raw email (alice@example.com) or immutable user id (users/<id>). entries=${deprecated.join(", ")}`,
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function registerGoogleChatWebhookTarget(target: WebhookTarget): () => void {
|
|
102
|
-
return registerWebhookTarget(webhookTargets, target).unregister;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function normalizeAudienceType(value?: string | null): GoogleChatAudienceType | undefined {
|
|
106
|
-
const normalized = value?.trim().toLowerCase();
|
|
107
|
-
if (normalized === "app-url" || normalized === "app_url" || normalized === "app") {
|
|
108
|
-
return "app-url";
|
|
109
|
-
}
|
|
110
|
-
if (
|
|
111
|
-
normalized === "project-number" ||
|
|
112
|
-
normalized === "project_number" ||
|
|
113
|
-
normalized === "project"
|
|
114
|
-
) {
|
|
115
|
-
return "project-number";
|
|
116
|
-
}
|
|
117
|
-
return undefined;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export async function handleGoogleChatWebhookRequest(
|
|
121
|
-
req: IncomingMessage,
|
|
122
|
-
res: ServerResponse,
|
|
123
|
-
): Promise<boolean> {
|
|
124
|
-
const resolved = resolveWebhookTargets(req, webhookTargets);
|
|
125
|
-
if (!resolved) {
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
const { targets } = resolved;
|
|
129
|
-
|
|
130
|
-
if (rejectNonPostWebhookRequest(req, res)) {
|
|
131
|
-
return true;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const authHeader = String(req.headers.authorization ?? "");
|
|
135
|
-
const bearer = authHeader.toLowerCase().startsWith("bearer ")
|
|
136
|
-
? authHeader.slice("bearer ".length)
|
|
137
|
-
: "";
|
|
138
|
-
|
|
139
|
-
const body = await readJsonBodyWithLimit(req, {
|
|
140
|
-
maxBytes: 1024 * 1024,
|
|
141
|
-
timeoutMs: 30_000,
|
|
142
|
-
emptyObjectOnEmpty: false,
|
|
143
|
-
});
|
|
144
|
-
if (!body.ok) {
|
|
145
|
-
res.statusCode =
|
|
146
|
-
body.code === "PAYLOAD_TOO_LARGE" ? 413 : body.code === "REQUEST_BODY_TIMEOUT" ? 408 : 400;
|
|
147
|
-
res.end(
|
|
148
|
-
body.code === "REQUEST_BODY_TIMEOUT"
|
|
149
|
-
? requestBodyErrorToText("REQUEST_BODY_TIMEOUT")
|
|
150
|
-
: body.error,
|
|
151
|
-
);
|
|
152
|
-
return true;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
let raw = body.value;
|
|
156
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
157
|
-
res.statusCode = 400;
|
|
158
|
-
res.end("invalid payload");
|
|
159
|
-
return true;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Transform Google Workspace Add-on format to standard Chat API format
|
|
163
|
-
const rawObj = raw as {
|
|
164
|
-
commonEventObject?: { hostApp?: string };
|
|
165
|
-
chat?: {
|
|
166
|
-
messagePayload?: { space?: GoogleChatSpace; message?: GoogleChatMessage };
|
|
167
|
-
user?: GoogleChatUser;
|
|
168
|
-
eventTime?: string;
|
|
169
|
-
};
|
|
170
|
-
authorizationEventObject?: { systemIdToken?: string };
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
if (rawObj.commonEventObject?.hostApp === "CHAT" && rawObj.chat?.messagePayload) {
|
|
174
|
-
const chat = rawObj.chat;
|
|
175
|
-
const messagePayload = chat.messagePayload;
|
|
176
|
-
raw = {
|
|
177
|
-
type: "MESSAGE",
|
|
178
|
-
space: messagePayload?.space,
|
|
179
|
-
message: messagePayload?.message,
|
|
180
|
-
user: chat.user,
|
|
181
|
-
eventTime: chat.eventTime,
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
// For Add-ons, the bearer token may be in authorizationEventObject.systemIdToken
|
|
185
|
-
const systemIdToken = rawObj.authorizationEventObject?.systemIdToken;
|
|
186
|
-
if (!bearer && systemIdToken) {
|
|
187
|
-
Object.assign(req.headers, { authorization: `Bearer ${systemIdToken}` });
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const event = raw as GoogleChatEvent;
|
|
192
|
-
const eventType = event.type ?? (raw as { eventType?: string }).eventType;
|
|
193
|
-
if (typeof eventType !== "string") {
|
|
194
|
-
res.statusCode = 400;
|
|
195
|
-
res.end("invalid payload");
|
|
196
|
-
return true;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (!event.space || typeof event.space !== "object" || Array.isArray(event.space)) {
|
|
200
|
-
res.statusCode = 400;
|
|
201
|
-
res.end("invalid payload");
|
|
202
|
-
return true;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (eventType === "MESSAGE") {
|
|
206
|
-
if (!event.message || typeof event.message !== "object" || Array.isArray(event.message)) {
|
|
207
|
-
res.statusCode = 400;
|
|
208
|
-
res.end("invalid payload");
|
|
209
|
-
return true;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Re-extract bearer in case it was updated from Add-on format
|
|
214
|
-
const authHeaderNow = String(req.headers.authorization ?? "");
|
|
215
|
-
const effectiveBearer = authHeaderNow.toLowerCase().startsWith("bearer ")
|
|
216
|
-
? authHeaderNow.slice("bearer ".length)
|
|
217
|
-
: bearer;
|
|
218
|
-
|
|
219
|
-
const matchedTarget = await resolveSingleWebhookTargetAsync(targets, async (target) => {
|
|
220
|
-
const audienceType = target.audienceType;
|
|
221
|
-
const audience = target.audience;
|
|
222
|
-
const verification = await verifyGoogleChatRequest({
|
|
223
|
-
bearer: effectiveBearer,
|
|
224
|
-
audienceType,
|
|
225
|
-
audience,
|
|
226
|
-
});
|
|
227
|
-
return verification.ok;
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
if (matchedTarget.kind === "none") {
|
|
231
|
-
res.statusCode = 401;
|
|
232
|
-
res.end("unauthorized");
|
|
233
|
-
return true;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (matchedTarget.kind === "ambiguous") {
|
|
237
|
-
res.statusCode = 401;
|
|
238
|
-
res.end("ambiguous webhook target");
|
|
239
|
-
return true;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const selected = matchedTarget.target;
|
|
243
|
-
selected.statusSink?.({ lastInboundAt: Date.now() });
|
|
244
|
-
processGoogleChatEvent(event, selected).catch((err) => {
|
|
245
|
-
selected?.runtime.error?.(
|
|
246
|
-
`[${selected.account.accountId}] Google Chat webhook failed: ${String(err)}`,
|
|
247
|
-
);
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
res.statusCode = 200;
|
|
251
|
-
res.setHeader("Content-Type", "application/json");
|
|
252
|
-
res.end("{}");
|
|
253
|
-
return true;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
async function processGoogleChatEvent(event: GoogleChatEvent, target: WebhookTarget) {
|
|
257
|
-
const eventType = event.type ?? (event as { eventType?: string }).eventType;
|
|
258
|
-
if (eventType !== "MESSAGE") {
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
if (!event.message || !event.space) {
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
await processMessageWithPipeline({
|
|
266
|
-
event,
|
|
267
|
-
account: target.account,
|
|
268
|
-
config: target.config,
|
|
269
|
-
runtime: target.runtime,
|
|
270
|
-
core: target.core,
|
|
271
|
-
statusSink: target.statusSink,
|
|
272
|
-
mediaMaxMb: target.mediaMaxMb,
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function normalizeUserId(raw?: string | null): string {
|
|
277
|
-
const trimmed = raw?.trim() ?? "";
|
|
278
|
-
if (!trimmed) {
|
|
279
|
-
return "";
|
|
280
|
-
}
|
|
281
|
-
return trimmed.replace(/^users\//i, "").toLowerCase();
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
function isEmailLike(value: string): boolean {
|
|
285
|
-
// Keep this intentionally loose; allowlists are user-provided config.
|
|
286
|
-
return value.includes("@");
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
export function isSenderAllowed(
|
|
290
|
-
senderId: string,
|
|
291
|
-
senderEmail: string | undefined,
|
|
292
|
-
allowFrom: string[],
|
|
293
|
-
allowNameMatching = false,
|
|
294
|
-
) {
|
|
295
|
-
if (allowFrom.includes("*")) {
|
|
296
|
-
return true;
|
|
297
|
-
}
|
|
298
|
-
const normalizedSenderId = normalizeUserId(senderId);
|
|
299
|
-
const normalizedEmail = senderEmail?.trim().toLowerCase() ?? "";
|
|
300
|
-
return allowFrom.some((entry) => {
|
|
301
|
-
const normalized = String(entry).trim().toLowerCase();
|
|
302
|
-
if (!normalized) {
|
|
303
|
-
return false;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// Accept `googlechat:<id>` but treat `users/...` as an *ID* only (deprecated `users/<email>`).
|
|
307
|
-
const withoutPrefix = normalized.replace(/^(googlechat|google-chat|gchat):/i, "");
|
|
308
|
-
if (withoutPrefix.startsWith("users/")) {
|
|
309
|
-
return normalizeUserId(withoutPrefix) === normalizedSenderId;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// Raw email allowlist entries are a break-glass override.
|
|
313
|
-
if (allowNameMatching && normalizedEmail && isEmailLike(withoutPrefix)) {
|
|
314
|
-
return withoutPrefix === normalizedEmail;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
return withoutPrefix.replace(/^users\//i, "") === normalizedSenderId;
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
function resolveGroupConfig(params: {
|
|
322
|
-
groupId: string;
|
|
323
|
-
groupName?: string | null;
|
|
324
|
-
groups?: Record<
|
|
325
|
-
string,
|
|
326
|
-
{
|
|
327
|
-
requireMention?: boolean;
|
|
328
|
-
allow?: boolean;
|
|
329
|
-
enabled?: boolean;
|
|
330
|
-
users?: Array<string | number>;
|
|
331
|
-
systemPrompt?: string;
|
|
332
|
-
}
|
|
333
|
-
>;
|
|
334
|
-
}) {
|
|
335
|
-
const { groupId, groupName, groups } = params;
|
|
336
|
-
const entries = groups ?? {};
|
|
337
|
-
const keys = Object.keys(entries);
|
|
338
|
-
if (keys.length === 0) {
|
|
339
|
-
return { entry: undefined, allowlistConfigured: false };
|
|
340
|
-
}
|
|
341
|
-
const normalizedName = groupName?.trim().toLowerCase();
|
|
342
|
-
const candidates = [groupId, groupName ?? "", normalizedName ?? ""].filter(Boolean);
|
|
343
|
-
let entry = candidates.map((candidate) => entries[candidate]).find(Boolean);
|
|
344
|
-
if (!entry && normalizedName) {
|
|
345
|
-
entry = entries[normalizedName];
|
|
346
|
-
}
|
|
347
|
-
const fallback = entries["*"];
|
|
348
|
-
return { entry: entry ?? fallback, allowlistConfigured: true, fallback };
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
function extractMentionInfo(annotations: GoogleChatAnnotation[], botUser?: string | null) {
|
|
352
|
-
const mentionAnnotations = annotations.filter((entry) => entry.type === "USER_MENTION");
|
|
353
|
-
const hasAnyMention = mentionAnnotations.length > 0;
|
|
354
|
-
const botTargets = new Set(["users/app", botUser?.trim()].filter(Boolean) as string[]);
|
|
355
|
-
const wasMentioned = mentionAnnotations.some((entry) => {
|
|
356
|
-
const userName = entry.userMention?.user?.name;
|
|
357
|
-
if (!userName) {
|
|
358
|
-
return false;
|
|
359
|
-
}
|
|
360
|
-
if (botTargets.has(userName)) {
|
|
361
|
-
return true;
|
|
362
|
-
}
|
|
363
|
-
return normalizeUserId(userName) === "app";
|
|
364
|
-
});
|
|
365
|
-
return { hasAnyMention, wasMentioned };
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Resolve bot display name with fallback chain:
|
|
370
|
-
* 1. Account config name
|
|
371
|
-
* 2. Agent name from config
|
|
372
|
-
* 3. "FasedAgent" as generic fallback
|
|
373
|
-
*/
|
|
374
|
-
function resolveBotDisplayName(params: {
|
|
375
|
-
accountName?: string;
|
|
376
|
-
agentId: string;
|
|
377
|
-
config: FasedAgentConfig;
|
|
378
|
-
}): string {
|
|
379
|
-
const { accountName, agentId, config } = params;
|
|
380
|
-
if (accountName?.trim()) {
|
|
381
|
-
return accountName.trim();
|
|
382
|
-
}
|
|
383
|
-
const agent = config.agents?.list?.find((a) => a.id === agentId);
|
|
384
|
-
if (agent?.name?.trim()) {
|
|
385
|
-
return agent.name.trim();
|
|
386
|
-
}
|
|
387
|
-
return "FasedAgent";
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
async function processMessageWithPipeline(params: {
|
|
391
|
-
event: GoogleChatEvent;
|
|
392
|
-
account: ResolvedGoogleChatAccount;
|
|
393
|
-
config: FasedAgentConfig;
|
|
394
|
-
runtime: GoogleChatRuntimeEnv;
|
|
395
|
-
core: GoogleChatCoreRuntime;
|
|
396
|
-
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void;
|
|
397
|
-
mediaMaxMb: number;
|
|
398
|
-
}): Promise<void> {
|
|
399
|
-
const { event, account, config, runtime, core, statusSink, mediaMaxMb } = params;
|
|
400
|
-
const pairing = createScopedPairingAccess({
|
|
401
|
-
core,
|
|
402
|
-
channel: "googlechat",
|
|
403
|
-
accountId: account.accountId,
|
|
404
|
-
});
|
|
405
|
-
const space = event.space;
|
|
406
|
-
const message = event.message;
|
|
407
|
-
if (!space || !message) {
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
const spaceId = space.name ?? "";
|
|
412
|
-
if (!spaceId) {
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
const spaceType = (space.type ?? "").toUpperCase();
|
|
416
|
-
const isGroup = spaceType !== "DM";
|
|
417
|
-
const sender = message.sender ?? event.user;
|
|
418
|
-
const senderId = sender?.name ?? "";
|
|
419
|
-
const senderName = sender?.displayName ?? "";
|
|
420
|
-
const senderEmail = sender?.email ?? undefined;
|
|
421
|
-
const allowNameMatching = isDangerousNameMatchingEnabled(account.config);
|
|
422
|
-
|
|
423
|
-
const allowBots = account.config.allowBots === true;
|
|
424
|
-
if (!allowBots) {
|
|
425
|
-
if (sender?.type?.toUpperCase() === "BOT") {
|
|
426
|
-
logVerbose(core, runtime, `skip bot-authored message (${senderId || "unknown"})`);
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
429
|
-
if (senderId === "users/app") {
|
|
430
|
-
logVerbose(core, runtime, "skip app-authored message");
|
|
431
|
-
return;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
const messageText = (message.argumentText ?? message.text ?? "").trim();
|
|
436
|
-
const attachments = message.attachment ?? [];
|
|
437
|
-
const hasMedia = attachments.length > 0;
|
|
438
|
-
const rawBody = messageText || (hasMedia ? "<media:attachment>" : "");
|
|
439
|
-
if (!rawBody) {
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
const defaultGroupPolicy = resolveDefaultGroupPolicy(config);
|
|
444
|
-
const { groupPolicy, providerMissingFallbackApplied } =
|
|
445
|
-
resolveAllowlistProviderRuntimeGroupPolicy({
|
|
446
|
-
providerConfigPresent: config.channels?.googlechat !== undefined,
|
|
447
|
-
groupPolicy: account.config.groupPolicy,
|
|
448
|
-
defaultGroupPolicy,
|
|
449
|
-
});
|
|
450
|
-
warnMissingProviderGroupPolicyFallbackOnce({
|
|
451
|
-
providerMissingFallbackApplied,
|
|
452
|
-
providerKey: "googlechat",
|
|
453
|
-
accountId: account.accountId,
|
|
454
|
-
blockedLabel: GROUP_POLICY_BLOCKED_LABEL.space,
|
|
455
|
-
log: (message) => logVerbose(core, runtime, message),
|
|
456
|
-
});
|
|
457
|
-
const groupConfigResolved = resolveGroupConfig({
|
|
458
|
-
groupId: spaceId,
|
|
459
|
-
groupName: space.displayName ?? null,
|
|
460
|
-
groups: account.config.groups ?? undefined,
|
|
461
|
-
});
|
|
462
|
-
const groupEntry = groupConfigResolved.entry;
|
|
463
|
-
const groupUsers = groupEntry?.users ?? account.config.groupAllowFrom ?? [];
|
|
464
|
-
let effectiveWasMentioned: boolean | undefined;
|
|
465
|
-
|
|
466
|
-
if (isGroup) {
|
|
467
|
-
if (groupPolicy === "disabled") {
|
|
468
|
-
logVerbose(core, runtime, `drop group message (groupPolicy=disabled, space=${spaceId})`);
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
|
-
const groupAllowlistConfigured = groupConfigResolved.allowlistConfigured;
|
|
472
|
-
const groupAllowed = Boolean(groupEntry) || Boolean((account.config.groups ?? {})["*"]);
|
|
473
|
-
if (groupPolicy === "allowlist") {
|
|
474
|
-
if (!groupAllowlistConfigured) {
|
|
475
|
-
logVerbose(
|
|
476
|
-
core,
|
|
477
|
-
runtime,
|
|
478
|
-
`drop group message (groupPolicy=allowlist, no allowlist, space=${spaceId})`,
|
|
479
|
-
);
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
if (!groupAllowed) {
|
|
483
|
-
logVerbose(core, runtime, `drop group message (not allowlisted, space=${spaceId})`);
|
|
484
|
-
return;
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
if (groupEntry?.enabled === false || groupEntry?.allow === false) {
|
|
488
|
-
logVerbose(core, runtime, `drop group message (space disabled, space=${spaceId})`);
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
if (groupUsers.length > 0) {
|
|
493
|
-
warnDeprecatedUsersEmailEntries(
|
|
494
|
-
core,
|
|
495
|
-
runtime,
|
|
496
|
-
groupUsers.map((v) => String(v)),
|
|
497
|
-
);
|
|
498
|
-
const ok = isSenderAllowed(
|
|
499
|
-
senderId,
|
|
500
|
-
senderEmail,
|
|
501
|
-
groupUsers.map((v) => String(v)),
|
|
502
|
-
allowNameMatching,
|
|
503
|
-
);
|
|
504
|
-
if (!ok) {
|
|
505
|
-
logVerbose(core, runtime, `drop group message (sender not allowed, ${senderId})`);
|
|
506
|
-
return;
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
const dmPolicy = account.config.dm?.policy ?? "pairing";
|
|
512
|
-
const configAllowFrom = (account.config.dm?.allowFrom ?? []).map((v) => String(v));
|
|
513
|
-
const normalizedGroupUsers = groupUsers.map((v) => String(v));
|
|
514
|
-
const senderGroupPolicy =
|
|
515
|
-
groupPolicy === "disabled"
|
|
516
|
-
? "disabled"
|
|
517
|
-
: normalizedGroupUsers.length > 0
|
|
518
|
-
? "allowlist"
|
|
519
|
-
: "open";
|
|
520
|
-
const shouldComputeAuth = core.channel.commands.shouldComputeCommandAuthorized(rawBody, config);
|
|
521
|
-
const storeAllowFrom =
|
|
522
|
-
!isGroup && dmPolicy !== "allowlist" && (dmPolicy !== "open" || shouldComputeAuth)
|
|
523
|
-
? await pairing.readAllowFromStore().catch(() => [])
|
|
524
|
-
: [];
|
|
525
|
-
const access = resolveDmGroupAccessWithLists({
|
|
526
|
-
isGroup,
|
|
527
|
-
dmPolicy,
|
|
528
|
-
groupPolicy: senderGroupPolicy,
|
|
529
|
-
allowFrom: configAllowFrom,
|
|
530
|
-
groupAllowFrom: normalizedGroupUsers,
|
|
531
|
-
storeAllowFrom,
|
|
532
|
-
groupAllowFromFallbackToAllowFrom: false,
|
|
533
|
-
isSenderAllowed: (allowFrom) =>
|
|
534
|
-
isSenderAllowed(senderId, senderEmail, allowFrom, allowNameMatching),
|
|
535
|
-
});
|
|
536
|
-
const effectiveAllowFrom = access.effectiveAllowFrom;
|
|
537
|
-
const effectiveGroupAllowFrom = access.effectiveGroupAllowFrom;
|
|
538
|
-
warnDeprecatedUsersEmailEntries(core, runtime, effectiveAllowFrom);
|
|
539
|
-
const commandAllowFrom = isGroup ? effectiveGroupAllowFrom : effectiveAllowFrom;
|
|
540
|
-
const useAccessGroups = config.commands?.useAccessGroups !== false;
|
|
541
|
-
const senderAllowedForCommands = isSenderAllowed(
|
|
542
|
-
senderId,
|
|
543
|
-
senderEmail,
|
|
544
|
-
commandAllowFrom,
|
|
545
|
-
allowNameMatching,
|
|
546
|
-
);
|
|
547
|
-
const commandAuthorized = shouldComputeAuth
|
|
548
|
-
? core.channel.commands.resolveCommandAuthorizedFromAuthorizers({
|
|
549
|
-
useAccessGroups,
|
|
550
|
-
authorizers: [
|
|
551
|
-
{ configured: commandAllowFrom.length > 0, allowed: senderAllowedForCommands },
|
|
552
|
-
],
|
|
553
|
-
})
|
|
554
|
-
: undefined;
|
|
555
|
-
|
|
556
|
-
if (isGroup) {
|
|
557
|
-
const requireMention = groupEntry?.requireMention ?? account.config.requireMention ?? true;
|
|
558
|
-
const annotations = message.annotations ?? [];
|
|
559
|
-
const mentionInfo = extractMentionInfo(annotations, account.config.botUser);
|
|
560
|
-
const allowTextCommands = core.channel.commands.shouldHandleTextCommands({
|
|
561
|
-
cfg: config,
|
|
562
|
-
surface: "googlechat",
|
|
563
|
-
});
|
|
564
|
-
const mentionGate = resolveMentionGatingWithBypass({
|
|
565
|
-
isGroup: true,
|
|
566
|
-
requireMention,
|
|
567
|
-
canDetectMention: true,
|
|
568
|
-
wasMentioned: mentionInfo.wasMentioned,
|
|
569
|
-
implicitMention: false,
|
|
570
|
-
hasAnyMention: mentionInfo.hasAnyMention,
|
|
571
|
-
allowTextCommands,
|
|
572
|
-
hasControlCommand: core.channel.text.hasControlCommand(rawBody, config),
|
|
573
|
-
commandAuthorized: commandAuthorized === true,
|
|
574
|
-
});
|
|
575
|
-
effectiveWasMentioned = mentionGate.effectiveWasMentioned;
|
|
576
|
-
if (mentionGate.shouldSkip) {
|
|
577
|
-
logVerbose(core, runtime, `drop group message (mention required, space=${spaceId})`);
|
|
578
|
-
return;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
if (isGroup && access.decision !== "allow") {
|
|
583
|
-
logVerbose(
|
|
584
|
-
core,
|
|
585
|
-
runtime,
|
|
586
|
-
`drop group message (sender policy blocked, reason=${access.reason}, space=${spaceId})`,
|
|
587
|
-
);
|
|
588
|
-
return;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
if (!isGroup) {
|
|
592
|
-
if (account.config.dm?.enabled === false) {
|
|
593
|
-
logVerbose(core, runtime, `Blocked Google Chat DM from ${senderId} (dmPolicy=disabled)`);
|
|
594
|
-
return;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
if (access.decision !== "allow") {
|
|
598
|
-
if (access.decision === "pairing") {
|
|
599
|
-
const { code, created } = await pairing.upsertPairingRequest({
|
|
600
|
-
id: senderId,
|
|
601
|
-
meta: { name: senderName || undefined, email: senderEmail },
|
|
602
|
-
});
|
|
603
|
-
if (created) {
|
|
604
|
-
logVerbose(core, runtime, `googlechat pairing request sender=${senderId}`);
|
|
605
|
-
try {
|
|
606
|
-
await sendGoogleChatMessage({
|
|
607
|
-
account,
|
|
608
|
-
space: spaceId,
|
|
609
|
-
text: core.channel.pairing.buildPairingReply({
|
|
610
|
-
channel: "googlechat",
|
|
611
|
-
idLine: `Your Google Chat user id: ${senderId}`,
|
|
612
|
-
code,
|
|
613
|
-
}),
|
|
614
|
-
});
|
|
615
|
-
statusSink?.({ lastOutboundAt: Date.now() });
|
|
616
|
-
} catch (err) {
|
|
617
|
-
logVerbose(core, runtime, `pairing reply failed for ${senderId}: ${String(err)}`);
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
} else {
|
|
621
|
-
logVerbose(
|
|
622
|
-
core,
|
|
623
|
-
runtime,
|
|
624
|
-
`Blocked unauthorized Google Chat sender ${senderId} (dmPolicy=${dmPolicy})`,
|
|
625
|
-
);
|
|
626
|
-
}
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
if (
|
|
632
|
-
isGroup &&
|
|
633
|
-
core.channel.commands.isControlCommandMessage(rawBody, config) &&
|
|
634
|
-
commandAuthorized !== true
|
|
635
|
-
) {
|
|
636
|
-
logVerbose(core, runtime, `googlechat: drop control command from ${senderId}`);
|
|
637
|
-
return;
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
const route = core.channel.routing.resolveAgentRoute({
|
|
641
|
-
cfg: config,
|
|
642
|
-
channel: "googlechat",
|
|
643
|
-
accountId: account.accountId,
|
|
644
|
-
peer: {
|
|
645
|
-
kind: isGroup ? "group" : "direct",
|
|
646
|
-
id: spaceId,
|
|
647
|
-
},
|
|
648
|
-
});
|
|
649
|
-
|
|
650
|
-
let mediaPath: string | undefined;
|
|
651
|
-
let mediaType: string | undefined;
|
|
652
|
-
if (attachments.length > 0) {
|
|
653
|
-
const first = attachments[0];
|
|
654
|
-
const attachmentData = await downloadAttachment(first, account, mediaMaxMb, core);
|
|
655
|
-
if (attachmentData) {
|
|
656
|
-
mediaPath = attachmentData.path;
|
|
657
|
-
mediaType = attachmentData.contentType;
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
const fromLabel = isGroup
|
|
662
|
-
? space.displayName || `space:${spaceId}`
|
|
663
|
-
: senderName || `user:${senderId}`;
|
|
664
|
-
const storePath = core.channel.session.resolveStorePath(config.session?.store, {
|
|
665
|
-
agentId: route.agentId,
|
|
666
|
-
});
|
|
667
|
-
const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(config);
|
|
668
|
-
const previousTimestamp = core.channel.session.readSessionUpdatedAt({
|
|
669
|
-
storePath,
|
|
670
|
-
sessionKey: route.sessionKey,
|
|
671
|
-
});
|
|
672
|
-
const body = core.channel.reply.formatAgentEnvelope({
|
|
673
|
-
channel: "Google Chat",
|
|
674
|
-
from: fromLabel,
|
|
675
|
-
timestamp: event.eventTime ? Date.parse(event.eventTime) : undefined,
|
|
676
|
-
previousTimestamp,
|
|
677
|
-
envelope: envelopeOptions,
|
|
678
|
-
body: rawBody,
|
|
679
|
-
});
|
|
680
|
-
|
|
681
|
-
const groupSystemPrompt = groupConfigResolved.entry?.systemPrompt?.trim() || undefined;
|
|
682
|
-
|
|
683
|
-
const ctxPayload = core.channel.reply.finalizeInboundContext({
|
|
684
|
-
Body: body,
|
|
685
|
-
BodyForAgent: rawBody,
|
|
686
|
-
RawBody: rawBody,
|
|
687
|
-
CommandBody: rawBody,
|
|
688
|
-
From: `googlechat:${senderId}`,
|
|
689
|
-
To: `googlechat:${spaceId}`,
|
|
690
|
-
SessionKey: route.sessionKey,
|
|
691
|
-
AccountId: route.accountId,
|
|
692
|
-
ChatType: isGroup ? "channel" : "direct",
|
|
693
|
-
ConversationLabel: fromLabel,
|
|
694
|
-
SenderName: senderName || undefined,
|
|
695
|
-
SenderId: senderId,
|
|
696
|
-
SenderUsername: senderEmail,
|
|
697
|
-
WasMentioned: isGroup ? effectiveWasMentioned : undefined,
|
|
698
|
-
CommandAuthorized: commandAuthorized,
|
|
699
|
-
Provider: "googlechat",
|
|
700
|
-
Surface: "googlechat",
|
|
701
|
-
MessageSid: message.name,
|
|
702
|
-
MessageSidFull: message.name,
|
|
703
|
-
ReplyToId: message.thread?.name,
|
|
704
|
-
ReplyToIdFull: message.thread?.name,
|
|
705
|
-
MediaPath: mediaPath,
|
|
706
|
-
MediaType: mediaType,
|
|
707
|
-
MediaUrl: mediaPath,
|
|
708
|
-
GroupSpace: isGroup ? (space.displayName ?? undefined) : undefined,
|
|
709
|
-
GroupSystemPrompt: isGroup ? groupSystemPrompt : undefined,
|
|
710
|
-
OriginatingChannel: "googlechat",
|
|
711
|
-
OriginatingTo: `googlechat:${spaceId}`,
|
|
712
|
-
});
|
|
713
|
-
|
|
714
|
-
void core.channel.session
|
|
715
|
-
.recordSessionMetaFromInbound({
|
|
716
|
-
storePath,
|
|
717
|
-
sessionKey: ctxPayload.SessionKey ?? route.sessionKey,
|
|
718
|
-
ctx: ctxPayload,
|
|
719
|
-
})
|
|
720
|
-
.catch((err) => {
|
|
721
|
-
runtime.error?.(`googlechat: failed updating session meta: ${String(err)}`);
|
|
722
|
-
});
|
|
723
|
-
|
|
724
|
-
// Typing indicator setup
|
|
725
|
-
// Note: Reaction mode requires user OAuth, not available with service account auth.
|
|
726
|
-
// If reaction is configured, we fall back to message mode with a warning.
|
|
727
|
-
let typingIndicator = account.config.typingIndicator ?? "message";
|
|
728
|
-
if (typingIndicator === "reaction") {
|
|
729
|
-
runtime.error?.(
|
|
730
|
-
`[${account.accountId}] typingIndicator="reaction" requires user OAuth (not supported with service account). Falling back to "message" mode.`,
|
|
731
|
-
);
|
|
732
|
-
typingIndicator = "message";
|
|
733
|
-
}
|
|
734
|
-
let typingMessageName: string | undefined;
|
|
735
|
-
|
|
736
|
-
// Start typing indicator (message mode only, reaction mode not supported with app auth)
|
|
737
|
-
if (typingIndicator === "message") {
|
|
738
|
-
try {
|
|
739
|
-
const botName = resolveBotDisplayName({
|
|
740
|
-
accountName: account.config.name,
|
|
741
|
-
agentId: route.agentId,
|
|
742
|
-
config,
|
|
743
|
-
});
|
|
744
|
-
const result = await sendGoogleChatMessage({
|
|
745
|
-
account,
|
|
746
|
-
space: spaceId,
|
|
747
|
-
text: `_${botName} is typing..._`,
|
|
748
|
-
thread: message.thread?.name,
|
|
749
|
-
});
|
|
750
|
-
typingMessageName = result?.messageName;
|
|
751
|
-
} catch (err) {
|
|
752
|
-
runtime.error?.(`Failed sending typing message: ${String(err)}`);
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
|
|
757
|
-
cfg: config,
|
|
758
|
-
agentId: route.agentId,
|
|
759
|
-
channel: "googlechat",
|
|
760
|
-
accountId: route.accountId,
|
|
761
|
-
});
|
|
762
|
-
|
|
763
|
-
await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
764
|
-
ctx: ctxPayload,
|
|
765
|
-
cfg: config,
|
|
766
|
-
dispatcherOptions: {
|
|
767
|
-
...prefixOptions,
|
|
768
|
-
deliver: async (payload) => {
|
|
769
|
-
await deliverGoogleChatReply({
|
|
770
|
-
payload,
|
|
771
|
-
account,
|
|
772
|
-
spaceId,
|
|
773
|
-
runtime,
|
|
774
|
-
core,
|
|
775
|
-
config,
|
|
776
|
-
statusSink,
|
|
777
|
-
typingMessageName,
|
|
778
|
-
});
|
|
779
|
-
// Only use typing message for first delivery
|
|
780
|
-
typingMessageName = undefined;
|
|
781
|
-
},
|
|
782
|
-
onError: (err, info) => {
|
|
783
|
-
runtime.error?.(
|
|
784
|
-
`[${account.accountId}] Google Chat ${info.kind} reply failed: ${String(err)}`,
|
|
785
|
-
);
|
|
786
|
-
},
|
|
787
|
-
},
|
|
788
|
-
replyOptions: {
|
|
789
|
-
onModelSelected,
|
|
790
|
-
},
|
|
791
|
-
});
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
async function downloadAttachment(
|
|
795
|
-
attachment: GoogleChatAttachment,
|
|
796
|
-
account: ResolvedGoogleChatAccount,
|
|
797
|
-
mediaMaxMb: number,
|
|
798
|
-
core: GoogleChatCoreRuntime,
|
|
799
|
-
): Promise<{ path: string; contentType?: string } | null> {
|
|
800
|
-
const resourceName = attachment.attachmentDataRef?.resourceName;
|
|
801
|
-
if (!resourceName) {
|
|
802
|
-
return null;
|
|
803
|
-
}
|
|
804
|
-
const maxBytes = Math.max(1, mediaMaxMb) * 1024 * 1024;
|
|
805
|
-
const downloaded = await downloadGoogleChatMedia({ account, resourceName, maxBytes });
|
|
806
|
-
const saved = await core.channel.media.saveMediaBuffer(
|
|
807
|
-
downloaded.buffer,
|
|
808
|
-
downloaded.contentType ?? attachment.contentType,
|
|
809
|
-
"inbound",
|
|
810
|
-
maxBytes,
|
|
811
|
-
attachment.contentName,
|
|
812
|
-
);
|
|
813
|
-
return { path: saved.path, contentType: saved.contentType };
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
async function deliverGoogleChatReply(params: {
|
|
817
|
-
payload: { text?: string; mediaUrls?: string[]; mediaUrl?: string; replyToId?: string };
|
|
818
|
-
account: ResolvedGoogleChatAccount;
|
|
819
|
-
spaceId: string;
|
|
820
|
-
runtime: GoogleChatRuntimeEnv;
|
|
821
|
-
core: GoogleChatCoreRuntime;
|
|
822
|
-
config: FasedAgentConfig;
|
|
823
|
-
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void;
|
|
824
|
-
typingMessageName?: string;
|
|
825
|
-
}): Promise<void> {
|
|
826
|
-
const { payload, account, spaceId, runtime, core, config, statusSink, typingMessageName } =
|
|
827
|
-
params;
|
|
828
|
-
const mediaList = payload.mediaUrls?.length
|
|
829
|
-
? payload.mediaUrls
|
|
830
|
-
: payload.mediaUrl
|
|
831
|
-
? [payload.mediaUrl]
|
|
832
|
-
: [];
|
|
833
|
-
|
|
834
|
-
if (mediaList.length > 0) {
|
|
835
|
-
let suppressCaption = false;
|
|
836
|
-
if (typingMessageName) {
|
|
837
|
-
try {
|
|
838
|
-
await deleteGoogleChatMessage({
|
|
839
|
-
account,
|
|
840
|
-
messageName: typingMessageName,
|
|
841
|
-
});
|
|
842
|
-
} catch (err) {
|
|
843
|
-
runtime.error?.(`Google Chat typing cleanup failed: ${String(err)}`);
|
|
844
|
-
const fallbackText = payload.text?.trim()
|
|
845
|
-
? payload.text
|
|
846
|
-
: mediaList.length > 1
|
|
847
|
-
? "Sent attachments."
|
|
848
|
-
: "Sent attachment.";
|
|
849
|
-
try {
|
|
850
|
-
await updateGoogleChatMessage({
|
|
851
|
-
account,
|
|
852
|
-
messageName: typingMessageName,
|
|
853
|
-
text: fallbackText,
|
|
854
|
-
});
|
|
855
|
-
suppressCaption = Boolean(payload.text?.trim());
|
|
856
|
-
} catch (updateErr) {
|
|
857
|
-
runtime.error?.(`Google Chat typing update failed: ${String(updateErr)}`);
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
let first = true;
|
|
862
|
-
for (const mediaUrl of mediaList) {
|
|
863
|
-
const caption = first && !suppressCaption ? payload.text : undefined;
|
|
864
|
-
first = false;
|
|
865
|
-
try {
|
|
866
|
-
const loaded = await core.channel.media.fetchRemoteMedia({
|
|
867
|
-
url: mediaUrl,
|
|
868
|
-
maxBytes: (account.config.mediaMaxMb ?? 20) * 1024 * 1024,
|
|
869
|
-
});
|
|
870
|
-
const upload = await uploadAttachmentForReply({
|
|
871
|
-
account,
|
|
872
|
-
spaceId,
|
|
873
|
-
buffer: loaded.buffer,
|
|
874
|
-
contentType: loaded.contentType,
|
|
875
|
-
filename: loaded.fileName ?? "attachment",
|
|
876
|
-
});
|
|
877
|
-
if (!upload.attachmentUploadToken) {
|
|
878
|
-
throw new Error("missing attachment upload token");
|
|
879
|
-
}
|
|
880
|
-
await sendGoogleChatMessage({
|
|
881
|
-
account,
|
|
882
|
-
space: spaceId,
|
|
883
|
-
text: caption,
|
|
884
|
-
thread: payload.replyToId,
|
|
885
|
-
attachments: [
|
|
886
|
-
{ attachmentUploadToken: upload.attachmentUploadToken, contentName: loaded.fileName },
|
|
887
|
-
],
|
|
888
|
-
});
|
|
889
|
-
statusSink?.({ lastOutboundAt: Date.now() });
|
|
890
|
-
} catch (err) {
|
|
891
|
-
runtime.error?.(`Google Chat attachment send failed: ${String(err)}`);
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
return;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
if (payload.text) {
|
|
898
|
-
const chunkLimit = account.config.textChunkLimit ?? 4000;
|
|
899
|
-
const chunkMode = core.channel.text.resolveChunkMode(config, "googlechat", account.accountId);
|
|
900
|
-
const chunks = core.channel.text.chunkMarkdownTextWithMode(payload.text, chunkLimit, chunkMode);
|
|
901
|
-
for (let i = 0; i < chunks.length; i++) {
|
|
902
|
-
const chunk = chunks[i];
|
|
903
|
-
try {
|
|
904
|
-
// Edit typing message with first chunk if available
|
|
905
|
-
if (i === 0 && typingMessageName) {
|
|
906
|
-
await updateGoogleChatMessage({
|
|
907
|
-
account,
|
|
908
|
-
messageName: typingMessageName,
|
|
909
|
-
text: chunk,
|
|
910
|
-
});
|
|
911
|
-
} else {
|
|
912
|
-
await sendGoogleChatMessage({
|
|
913
|
-
account,
|
|
914
|
-
space: spaceId,
|
|
915
|
-
text: chunk,
|
|
916
|
-
thread: payload.replyToId,
|
|
917
|
-
});
|
|
918
|
-
}
|
|
919
|
-
statusSink?.({ lastOutboundAt: Date.now() });
|
|
920
|
-
} catch (err) {
|
|
921
|
-
runtime.error?.(`Google Chat message send failed: ${String(err)}`);
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
async function uploadAttachmentForReply(params: {
|
|
928
|
-
account: ResolvedGoogleChatAccount;
|
|
929
|
-
spaceId: string;
|
|
930
|
-
buffer: Buffer;
|
|
931
|
-
contentType?: string;
|
|
932
|
-
filename: string;
|
|
933
|
-
}) {
|
|
934
|
-
const { account, spaceId, buffer, contentType, filename } = params;
|
|
935
|
-
const { uploadGoogleChatAttachment } = await import("./api.js");
|
|
936
|
-
return await uploadGoogleChatAttachment({
|
|
937
|
-
account,
|
|
938
|
-
space: spaceId,
|
|
939
|
-
filename,
|
|
940
|
-
buffer,
|
|
941
|
-
contentType,
|
|
942
|
-
});
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
export function monitorGoogleChatProvider(options: GoogleChatMonitorOptions): () => void {
|
|
946
|
-
const core = getGoogleChatRuntime();
|
|
947
|
-
const webhookPath = resolveWebhookPath({
|
|
948
|
-
webhookPath: options.webhookPath,
|
|
949
|
-
webhookUrl: options.webhookUrl,
|
|
950
|
-
defaultPath: "/googlechat",
|
|
951
|
-
});
|
|
952
|
-
if (!webhookPath) {
|
|
953
|
-
options.runtime.error?.(`[${options.account.accountId}] invalid webhook path`);
|
|
954
|
-
return () => {};
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
const audienceType = normalizeAudienceType(options.account.config.audienceType);
|
|
958
|
-
const audience = options.account.config.audience?.trim();
|
|
959
|
-
const mediaMaxMb = options.account.config.mediaMaxMb ?? 20;
|
|
960
|
-
|
|
961
|
-
const unregister = registerGoogleChatWebhookTarget({
|
|
962
|
-
account: options.account,
|
|
963
|
-
config: options.config,
|
|
964
|
-
runtime: options.runtime,
|
|
965
|
-
core,
|
|
966
|
-
path: webhookPath,
|
|
967
|
-
audienceType,
|
|
968
|
-
audience,
|
|
969
|
-
statusSink: options.statusSink,
|
|
970
|
-
mediaMaxMb,
|
|
971
|
-
});
|
|
972
|
-
|
|
973
|
-
return unregister;
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
export async function startGoogleChatMonitor(
|
|
977
|
-
params: GoogleChatMonitorOptions,
|
|
978
|
-
): Promise<() => void> {
|
|
979
|
-
return monitorGoogleChatProvider(params);
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
export function resolveGoogleChatWebhookPath(params: {
|
|
983
|
-
account: ResolvedGoogleChatAccount;
|
|
984
|
-
}): string {
|
|
985
|
-
return (
|
|
986
|
-
resolveWebhookPath({
|
|
987
|
-
webhookPath: params.account.config.webhookPath,
|
|
988
|
-
webhookUrl: params.account.config.webhookUrl,
|
|
989
|
-
defaultPath: "/googlechat",
|
|
990
|
-
}) ?? "/googlechat"
|
|
991
|
-
);
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
export function computeGoogleChatMediaMaxMb(params: { account: ResolvedGoogleChatAccount }) {
|
|
995
|
-
return params.account.config.mediaMaxMb ?? 20;
|
|
996
|
-
}
|