From b76cd6695d11bd3cb5776e7c038abe9dbaa5f4a5 Mon Sep 17 00:00:00 2001 From: iHildy Date: Fri, 23 Jan 2026 16:45:37 -0600 Subject: [PATCH] feat: add beta googlechat channel --- README.md | 14 +- .../ChannelsSettings+ChannelState.swift | 65 +- .../Sources/Clawdbot/ChannelsStore.swift | 22 + .../Sources/Clawdbot/GatewayConnection.swift | 1 + .../GatewayAgentChannelTests.swift | 2 + docs/channels/googlechat.md | 172 ++++ docs/channels/index.md | 1 + docs/cli/channels.md | 2 +- docs/cli/index.md | 4 +- docs/cli/message.md | 11 +- docs/cli/status.md | 2 +- docs/docs.json | 9 + docs/gateway/configuration.md | 56 +- docs/gateway/heartbeat.md | 2 +- docs/help/faq.md | 2 +- docs/start/wizard.md | 13 +- docs/tools/agent-send.md | 2 +- docs/tools/index.md | 2 +- docs/tools/reactions.md | 1 + docs/tools/slash-commands.md | 2 +- extensions/googlechat/clawdbot.plugin.json | 11 + extensions/googlechat/index.ts | 20 + extensions/googlechat/package.json | 34 + extensions/googlechat/src/accounts.ts | 133 ++++ extensions/googlechat/src/actions.ts | 162 ++++ extensions/googlechat/src/api.ts | 207 +++++ extensions/googlechat/src/auth.ts | 110 +++ extensions/googlechat/src/channel.ts | 578 ++++++++++++++ extensions/googlechat/src/monitor.ts | 751 ++++++++++++++++++ extensions/googlechat/src/onboarding.ts | 276 +++++++ extensions/googlechat/src/runtime.ts | 14 + extensions/googlechat/src/targets.ts | 49 ++ extensions/googlechat/src/types.config.ts | 3 + extensions/googlechat/src/types.ts | 73 ++ pnpm-lock.yaml | 11 + src/channels/dock.ts | 61 ++ src/channels/plugins/group-mentions.ts | 9 + src/channels/plugins/types.core.ts | 8 + src/channels/registry.test.ts | 2 + src/channels/registry.ts | 13 + src/cli/channels-cli.ts | 8 +- src/commands/channels/add-mutators.ts | 6 + src/commands/channels/add.ts | 9 + src/commands/channels/resolve.ts | 6 +- src/config/types.channels.ts | 2 + src/config/types.googlechat.ts | 97 +++ src/config/types.hooks.ts | 1 + src/config/types.queue.ts | 1 + src/config/types.ts | 1 + src/config/zod-schema.providers-core.ts | 69 ++ src/config/zod-schema.providers.ts | 2 + src/gateway/chat-sanitize.ts | 1 + src/plugin-sdk/index.ts | 7 + src/utils/message-channel.ts | 1 + ui/src/ui/types.ts | 27 + ui/src/ui/views/channels.googlechat.ts | 74 ++ ui/src/ui/views/channels.ts | 21 +- ui/src/ui/views/channels.types.ts | 24 +- 58 files changed, 3216 insertions(+), 51 deletions(-) create mode 100644 docs/channels/googlechat.md create mode 100644 extensions/googlechat/clawdbot.plugin.json create mode 100644 extensions/googlechat/index.ts create mode 100644 extensions/googlechat/package.json create mode 100644 extensions/googlechat/src/accounts.ts create mode 100644 extensions/googlechat/src/actions.ts create mode 100644 extensions/googlechat/src/api.ts create mode 100644 extensions/googlechat/src/auth.ts create mode 100644 extensions/googlechat/src/channel.ts create mode 100644 extensions/googlechat/src/monitor.ts create mode 100644 extensions/googlechat/src/onboarding.ts create mode 100644 extensions/googlechat/src/runtime.ts create mode 100644 extensions/googlechat/src/targets.ts create mode 100644 extensions/googlechat/src/types.config.ts create mode 100644 extensions/googlechat/src/types.ts create mode 100644 src/config/types.googlechat.ts create mode 100644 ui/src/ui/views/channels.googlechat.ts diff --git a/README.md b/README.md index 8f4411f12..a20c60f45 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@

**Clawdbot** is a *personal AI assistant* you run on your own devices. -It answers you on the channels you already use (WhatsApp, Telegram, Slack, Discord, Signal, iMessage, Microsoft Teams, WebChat), plus extension channels like BlueBubbles, Matrix, Zalo, and Zalo Personal. It can speak and listen on macOS/iOS/Android, and can render a live Canvas you control. The Gateway is just the control plane — the product is the assistant. +It answers you on the channels you already use (WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, Microsoft Teams, WebChat), plus extension channels like BlueBubbles, Matrix, Zalo, and Zalo Personal. It can speak and listen on macOS/iOS/Android, and can render a live Canvas you control. The Gateway is just the control plane — the product is the assistant. If you want a personal, single-user assistant that feels local, fast, and always-on, this is it. @@ -65,7 +65,7 @@ clawdbot gateway --port 18789 --verbose # Send a message clawdbot message send --to +1234567890 --message "Hello from Clawdbot" -# Talk to the assistant (optionally deliver back to any connected channel: WhatsApp/Telegram/Slack/Discord/Signal/iMessage/BlueBubbles/Microsoft Teams/Matrix/Zalo/Zalo Personal/WebChat) +# Talk to the assistant (optionally deliver back to any connected channel: WhatsApp/Telegram/Slack/Discord/Google Chat/Signal/iMessage/BlueBubbles/Microsoft Teams/Matrix/Zalo/Zalo Personal/WebChat) clawdbot agent --message "Ship checklist" --thinking high ``` @@ -106,7 +106,7 @@ Clawdbot connects to real messaging surfaces. Treat inbound DMs as **untrusted i Full security guide: [Security](https://docs.clawd.bot/gateway/security) -Default behavior on Telegram/WhatsApp/Signal/iMessage/Microsoft Teams/Discord/Slack: +Default behavior on Telegram/WhatsApp/Signal/iMessage/Microsoft Teams/Discord/Google Chat/Slack: - **DM pairing** (`dmPolicy="pairing"` / `channels.discord.dm.policy="pairing"` / `channels.slack.dm.policy="pairing"`): unknown senders receive a short pairing code and the bot does not process their message. - Approve with: `clawdbot pairing approve ` (then the sender is added to a local allowlist store). - Public inbound DMs require an explicit opt-in: set `dmPolicy="open"` and include `"*"` in the channel allowlist (`allowFrom` / `channels.discord.dm.allowFrom` / `channels.slack.dm.allowFrom`). @@ -116,7 +116,7 @@ Run `clawdbot doctor` to surface risky/misconfigured DM policies. ## Highlights - **[Local-first Gateway](https://docs.clawd.bot/gateway)** — single control plane for sessions, channels, tools, and events. -- **[Multi-channel inbox](https://docs.clawd.bot/channels)** — WhatsApp, Telegram, Slack, Discord, Signal, iMessage, BlueBubbles, Microsoft Teams, Matrix, Zalo, Zalo Personal, WebChat, macOS, iOS/Android. +- **[Multi-channel inbox](https://docs.clawd.bot/channels)** — WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, BlueBubbles, Microsoft Teams, Matrix, Zalo, Zalo Personal, WebChat, macOS, iOS/Android. - **[Multi-agent routing](https://docs.clawd.bot/gateway/configuration)** — route inbound channels/accounts/peers to isolated agents (workspaces + per-agent sessions). - **[Voice Wake](https://docs.clawd.bot/nodes/voicewake) + [Talk Mode](https://docs.clawd.bot/nodes/talk)** — always-on speech for macOS/iOS/Android with ElevenLabs. - **[Live Canvas](https://docs.clawd.bot/platforms/mac/canvas)** — agent-driven visual workspace with [A2UI](https://docs.clawd.bot/platforms/mac/canvas#canvas-a2ui). @@ -138,7 +138,7 @@ Run `clawdbot doctor` to surface risky/misconfigured DM policies. - [Media pipeline](https://docs.clawd.bot/nodes/images): images/audio/video, transcription hooks, size caps, temp file lifecycle. Audio details: [Audio](https://docs.clawd.bot/nodes/audio). ### Channels -- [Channels](https://docs.clawd.bot/channels): [WhatsApp](https://docs.clawd.bot/channels/whatsapp) (Baileys), [Telegram](https://docs.clawd.bot/channels/telegram) (grammY), [Slack](https://docs.clawd.bot/channels/slack) (Bolt), [Discord](https://docs.clawd.bot/channels/discord) (discord.js), [Signal](https://docs.clawd.bot/channels/signal) (signal-cli), [iMessage](https://docs.clawd.bot/channels/imessage) (imsg), [BlueBubbles](https://docs.clawd.bot/channels/bluebubbles) (extension), [Microsoft Teams](https://docs.clawd.bot/channels/msteams) (extension), [Matrix](https://docs.clawd.bot/channels/matrix) (extension), [Zalo](https://docs.clawd.bot/channels/zalo) (extension), [Zalo Personal](https://docs.clawd.bot/channels/zalouser) (extension), [WebChat](https://docs.clawd.bot/web/webchat). +- [Channels](https://docs.clawd.bot/channels): [WhatsApp](https://docs.clawd.bot/channels/whatsapp) (Baileys), [Telegram](https://docs.clawd.bot/channels/telegram) (grammY), [Slack](https://docs.clawd.bot/channels/slack) (Bolt), [Discord](https://docs.clawd.bot/channels/discord) (discord.js), [Google Chat](https://docs.clawd.bot/channels/googlechat) (Chat API), [Signal](https://docs.clawd.bot/channels/signal) (signal-cli), [iMessage](https://docs.clawd.bot/channels/imessage) (imsg), [BlueBubbles](https://docs.clawd.bot/channels/bluebubbles) (extension), [Microsoft Teams](https://docs.clawd.bot/channels/msteams) (extension), [Matrix](https://docs.clawd.bot/channels/matrix) (extension), [Zalo](https://docs.clawd.bot/channels/zalo) (extension), [Zalo Personal](https://docs.clawd.bot/channels/zalouser) (extension), [WebChat](https://docs.clawd.bot/web/webchat). - [Group routing](https://docs.clawd.bot/concepts/group-messages): mention gating, reply tags, per-channel chunking and routing. Channel rules: [Channels](https://docs.clawd.bot/channels). ### Apps + nodes @@ -169,7 +169,7 @@ Run `clawdbot doctor` to surface risky/misconfigured DM policies. ## How it works (short) ``` -WhatsApp / Telegram / Slack / Discord / Signal / iMessage / BlueBubbles / Microsoft Teams / Matrix / Zalo / Zalo Personal / WebChat +WhatsApp / Telegram / Slack / Discord / Google Chat / Signal / iMessage / BlueBubbles / Microsoft Teams / Matrix / Zalo / Zalo Personal / WebChat │ ▼ ┌───────────────────────────────┐ @@ -252,7 +252,7 @@ ClawdHub is a minimal skill registry. With ClawdHub enabled, the agent can searc ## Chat commands -Send these in WhatsApp/Telegram/Slack/Microsoft Teams/WebChat (group commands are owner-only): +Send these in WhatsApp/Telegram/Slack/Google Chat/Microsoft Teams/WebChat (group commands are owner-only): - `/status` — compact session status (model + tokens, cost when available) - `/new` or `/reset` — reset the session diff --git a/apps/macos/Sources/Clawdbot/ChannelsSettings+ChannelState.swift b/apps/macos/Sources/Clawdbot/ChannelsSettings+ChannelState.swift index 79dd97cf9..efce42781 100644 --- a/apps/macos/Sources/Clawdbot/ChannelsSettings+ChannelState.swift +++ b/apps/macos/Sources/Clawdbot/ChannelsSettings+ChannelState.swift @@ -40,6 +40,16 @@ extension ChannelsSettings { return .orange } + var googlechatTint: Color { + guard let status = self.channelStatus("googlechat", as: ChannelsStatusSnapshot.GoogleChatStatus.self) + else { return .secondary } + if !status.configured { return .secondary } + if status.lastError != nil { return .orange } + if status.probe?.ok == false { return .orange } + if status.running { return .green } + return .orange + } + var signalTint: Color { guard let status = self.channelStatus("signal", as: ChannelsStatusSnapshot.SignalStatus.self) else { return .secondary } @@ -85,6 +95,14 @@ extension ChannelsSettings { return "Configured" } + var googlechatSummary: String { + guard let status = self.channelStatus("googlechat", as: ChannelsStatusSnapshot.GoogleChatStatus.self) + else { return "Checking…" } + if !status.configured { return "Not configured" } + if status.running { return "Running" } + return "Configured" + } + var signalSummary: String { guard let status = self.channelStatus("signal", as: ChannelsStatusSnapshot.SignalStatus.self) else { return "Checking…" } @@ -193,6 +211,37 @@ extension ChannelsSettings { return lines.isEmpty ? nil : lines.joined(separator: " · ") } + var googlechatDetails: String? { + guard let status = self.channelStatus("googlechat", as: ChannelsStatusSnapshot.GoogleChatStatus.self) + else { return nil } + var lines: [String] = [] + if let source = status.credentialSource { + lines.append("Credential: \(source)") + } + if let audienceType = status.audienceType { + let audience = status.audience ?? "" + let label = audience.isEmpty ? audienceType : "\(audienceType) \(audience)" + lines.append("Audience: \(label)") + } + if let probe = status.probe { + if probe.ok { + if let elapsed = probe.elapsedMs { + lines.append("Probe \(Int(elapsed))ms") + } + } else { + let code = probe.status.map { String($0) } ?? "unknown" + lines.append("Probe failed (\(code))") + } + } + if let last = self.date(fromMs: status.lastProbeAt) { + lines.append("Last probe \(relativeAge(from: last))") + } + if let err = status.lastError, !err.isEmpty { + lines.append("Error: \(err)") + } + return lines.isEmpty ? nil : lines.joined(separator: " · ") + } + var signalDetails: String? { guard let status = self.channelStatus("signal", as: ChannelsStatusSnapshot.SignalStatus.self) else { return nil } @@ -244,7 +293,7 @@ extension ChannelsSettings { } var orderedChannels: [ChannelItem] { - let fallback = ["whatsapp", "telegram", "discord", "slack", "signal", "imessage"] + let fallback = ["whatsapp", "telegram", "discord", "googlechat", "slack", "signal", "imessage"] let order = self.store.snapshot?.channelOrder ?? fallback let channels = order.enumerated().map { index, id in ChannelItem( @@ -307,6 +356,8 @@ extension ChannelsSettings { return self.telegramTint case "discord": return self.discordTint + case "googlechat": + return self.googlechatTint case "signal": return self.signalTint case "imessage": @@ -326,6 +377,8 @@ extension ChannelsSettings { return self.telegramSummary case "discord": return self.discordSummary + case "googlechat": + return self.googlechatSummary case "signal": return self.signalSummary case "imessage": @@ -345,6 +398,8 @@ extension ChannelsSettings { return self.telegramDetails case "discord": return self.discordDetails + case "googlechat": + return self.googlechatDetails case "signal": return self.signalDetails case "imessage": @@ -377,6 +432,10 @@ extension ChannelsSettings { return self .date(fromMs: self.channelStatus("discord", as: ChannelsStatusSnapshot.DiscordStatus.self)? .lastProbeAt) + case "googlechat": + return self + .date(fromMs: self.channelStatus("googlechat", as: ChannelsStatusSnapshot.GoogleChatStatus.self)? + .lastProbeAt) case "signal": return self .date(fromMs: self.channelStatus("signal", as: ChannelsStatusSnapshot.SignalStatus.self)?.lastProbeAt) @@ -411,6 +470,10 @@ extension ChannelsSettings { guard let status = self.channelStatus("discord", as: ChannelsStatusSnapshot.DiscordStatus.self) else { return false } return status.lastError?.isEmpty == false || status.probe?.ok == false + case "googlechat": + guard let status = self.channelStatus("googlechat", as: ChannelsStatusSnapshot.GoogleChatStatus.self) + else { return false } + return status.lastError?.isEmpty == false || status.probe?.ok == false case "signal": guard let status = self.channelStatus("signal", as: ChannelsStatusSnapshot.SignalStatus.self) else { return false } diff --git a/apps/macos/Sources/Clawdbot/ChannelsStore.swift b/apps/macos/Sources/Clawdbot/ChannelsStore.swift index e62e737a4..1cbeca381 100644 --- a/apps/macos/Sources/Clawdbot/ChannelsStore.swift +++ b/apps/macos/Sources/Clawdbot/ChannelsStore.swift @@ -85,6 +85,28 @@ struct ChannelsStatusSnapshot: Codable { let lastProbeAt: Double? } + struct GoogleChatProbe: Codable { + let ok: Bool + let status: Int? + let error: String? + let elapsedMs: Double? + } + + struct GoogleChatStatus: Codable { + let configured: Bool + let credentialSource: String? + let audienceType: String? + let audience: String? + let webhookPath: String? + let webhookUrl: String? + let running: Bool + let lastStartAt: Double? + let lastStopAt: Double? + let lastError: String? + let probe: GoogleChatProbe? + let lastProbeAt: Double? + } + struct SignalProbe: Codable { let ok: Bool let status: Int? diff --git a/apps/macos/Sources/Clawdbot/GatewayConnection.swift b/apps/macos/Sources/Clawdbot/GatewayConnection.swift index 9feb98ba9..7facc6d61 100644 --- a/apps/macos/Sources/Clawdbot/GatewayConnection.swift +++ b/apps/macos/Sources/Clawdbot/GatewayConnection.swift @@ -11,6 +11,7 @@ enum GatewayAgentChannel: String, Codable, CaseIterable, Sendable { case whatsapp case telegram case discord + case googlechat case slack case signal case imessage diff --git a/apps/macos/Tests/ClawdbotIPCTests/GatewayAgentChannelTests.swift b/apps/macos/Tests/ClawdbotIPCTests/GatewayAgentChannelTests.swift index bf72af7e5..5db5476cb 100644 --- a/apps/macos/Tests/ClawdbotIPCTests/GatewayAgentChannelTests.swift +++ b/apps/macos/Tests/ClawdbotIPCTests/GatewayAgentChannelTests.swift @@ -11,6 +11,7 @@ import Testing #expect(GatewayAgentChannel.last.shouldDeliver(true) == true) #expect(GatewayAgentChannel.whatsapp.shouldDeliver(true) == true) #expect(GatewayAgentChannel.telegram.shouldDeliver(true) == true) + #expect(GatewayAgentChannel.googlechat.shouldDeliver(true) == true) #expect(GatewayAgentChannel.bluebubbles.shouldDeliver(true) == true) #expect(GatewayAgentChannel.last.shouldDeliver(false) == false) } @@ -19,6 +20,7 @@ import Testing #expect(GatewayAgentChannel(raw: nil) == .last) #expect(GatewayAgentChannel(raw: " ") == .last) #expect(GatewayAgentChannel(raw: "WEBCHAT") == .webchat) + #expect(GatewayAgentChannel(raw: "googlechat") == .googlechat) #expect(GatewayAgentChannel(raw: "BLUEBUBBLES") == .bluebubbles) #expect(GatewayAgentChannel(raw: "unknown") == .last) } diff --git a/docs/channels/googlechat.md b/docs/channels/googlechat.md new file mode 100644 index 000000000..fa8529b19 --- /dev/null +++ b/docs/channels/googlechat.md @@ -0,0 +1,172 @@ +--- +summary: "Google Chat app support status, capabilities, and configuration" +read_when: + - Working on Google Chat channel features +--- +# Google Chat (Chat API) + +Status: ready for DMs + spaces via Google Chat API webhooks (HTTP only). + +## Quick setup (beginner) +1) Create a Google Cloud project and enable the **Google Chat API**. + - Go to: [Google Chat API Credentials](https://console.cloud.google.com/apis/api/chat.googleapis.com/credentials) + - Enable the API if it is not already enabled. +2) Create a **Service Account**: + - Press **Create Credentials** > **Service Account**. + - Name it whatever you want (e.g., `clawdbot-chat`). + - Leave permissions blank (press **Continue**). + - Leave principals with access blank (press **Done**). +3) Create and download the **JSON Key**: + - In the list of service accounts, click on the one you just created. + - Go to the **Keys** tab. + - Click **Add Key** > **Create new key**. + - Select **JSON** and press **Create**. +4) Store the downloaded JSON file on your gateway host (e.g., `~/.clawdbot/googlechat-service-account.json`). +5) Create a Google Chat app in the [Google Cloud Console Chat Configuration](https://console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat): + - Fill in the **Application info**: + - **App name**: (e.g. `Clawdbot`) + - **Avatar URL**: (e.g. `https://clawd.bot/logo.png`) + - **Description**: (e.g. `Personal AI Assistant`) + - Enable **Interactive features**. + - Under **Functionality**, check **Join spaces and group conversations**. + - Under **Connection settings**, select **HTTP endpoint URL**. + - Under **Triggers**, select **Use a common HTTP endpoint URL for all triggers** and set it to your gateway's public URL followed by `/googlechat`. + - *Tip: Run `clawdbot status` to find your gateway's public URL.* + - Under **Visibility**, check **Make this Chat app available to specific people and groups in **. + - Enter your email address (e.g. `user@example.com`) in the text box. + - Click **Save** at the bottom. +6) **Enable the app status**: + - After saving, **refresh the page**. + - Look for the **App status** section (usually near the top or bottom after saving). + - Change the status to **Live - available to users**. + - Click **Save** again. +7) Configure Clawdbot with the service account path + webhook audience: + - Env: `GOOGLE_CHAT_SERVICE_ACCOUNT_FILE=/path/to/service-account.json` + - Or config: `channels.googlechat.serviceAccountFile: "/path/to/service-account.json"`. +8) Set the webhook audience type + value (matches your Chat app config). +9) Start the gateway. Google Chat will POST to your webhook path. + +## Add to Google Chat +Once the gateway is running and your email is added to the visibility list: +1) Go to [Google Chat](https://chat.google.com/). +2) Click the **+** (plus) icon next to **Direct Messages**. +3) In the search bar (where you usually add people), type the **App name** you configured in the Google Cloud Console. + - **Note**: The bot will *not* appear in the "Marketplace" browse list because it is a private app. You must search for it by name. +4) Select your bot from the results. +5) Click **Add** or **Chat** to start a 1:1 conversation. +6) Send "Hello" to trigger the assistant! + +## Public URL (Webhook-only) +Google Chat webhooks require a public HTTPS endpoint. For security, **only expose the `/googlechat` path** to the internet. Keep the Clawdbot dashboard and other sensitive endpoints on your private network. + +### Option A: Tailscale Funnel (Recommended) +If you use Tailscale, you can expose **only** the webhook path using Tailscale Funnel. This keeps your dashboard private while allowing Google Chat to reach your gateway. + +1. **Check what address your gateway is bound to:** + ```bash + ss -tlnp | grep 18789 + ``` + Note the IP address (e.g., `127.0.0.1`, `0.0.0.0`, or your Tailscale IP like `100.x.x.x`). + +2. **Configure the path mapping** (use the IP from step 1): + ```bash + # If bound to localhost (127.0.0.1 or 0.0.0.0): + tailscale funnel --bg --set-path /googlechat http://127.0.0.1:18789/googlechat + + # If bound to Tailscale IP only (e.g., 100.106.161.80): + tailscale funnel --bg --set-path /googlechat http://100.106.161.80:18789/googlechat + ``` + +3. **Authorize the node for Funnel access:** + If prompted, visit the authorization URL shown in the output to enable Funnel for this node in your tailnet policy. + +4. **Verify the configuration:** + ```bash + tailscale funnel status + ``` + +Your public webhook URL will be: +`https://..ts.net/googlechat` + +The rest of your gateway (like the dashboard at `/`) remains inaccessible from the public web unless you explicitly add it. + +> Note: This configuration persists across reboots. To remove it later, run `tailscale funnel reset`. + +### Option B: Reverse Proxy (Caddy) +If you use a reverse proxy like Caddy, only proxy the specific path: +```caddy +your-domain.com { + reverse_proxy /googlechat* localhost:18789 +} +``` +With this config, any request to `your-domain.com/` will be ignored or returned as 404, while `your-domain.com/googlechat` is safely routed to Clawdbot. + +### Option C: Cloudflare Tunnel +Configure your tunnel's ingress rules to only route the webhook path: +- **Path**: `/googlechat` -> `http://localhost:18789/googlechat` +- **Default Rule**: HTTP 404 (Not Found) + +## How it works + +1. Google Chat sends webhook POSTs to the gateway. Each request includes an `Authorization: Bearer ` header. +2. Clawdbot verifies the token against the configured `audienceType` + `audience`: + - `audienceType: "app-url"` → audience is your HTTPS webhook URL. + - `audienceType: "project-number"` → audience is the Cloud project number. +3. Messages are routed by space: + - DMs use session key `agent::googlechat:dm:`. + - Spaces use session key `agent::googlechat:group:`. +4. DM access is pairing by default. Unknown senders receive a pairing code; approve with: + - `clawdbot pairing approve googlechat ` +5. Group spaces require @-mention by default. Use `botUser` if mention detection needs the app’s user name. + +## Targets +Use these identifiers for delivery and allowlists: +- Direct messages: `users/` (Clawdbot resolves to a DM space automatically). +- Spaces: `spaces/`. + +## Config highlights +```json5 +{ + channels: { + "googlechat": { + enabled: true, + serviceAccountFile: "/path/to/service-account.json", + audienceType: "app-url", + audience: "https://gateway.example.com/googlechat", + webhookPath: "/googlechat", + botUser: "users/1234567890", // optional; helps mention detection + dm: { + policy: "pairing", + allowFrom: ["users/1234567890", "name@example.com"] + }, + groupPolicy: "allowlist", + groups: { + "spaces/AAAA": { + allow: true, + requireMention: true, + users: ["users/1234567890"], + systemPrompt: "Short answers only." + } + }, + actions: { reactions: true }, + mediaMaxMb: 20 + } + } +} +``` + +Notes: +- Service account credentials can also be passed inline with `serviceAccount` (JSON string). +- Default webhook path is `/googlechat` if `webhookPath` isn’t set. +- Reactions are available via the `reactions` tool and `channels action` when `actions.reactions` is enabled. +- Attachments are downloaded through the Chat API and stored in the media pipeline (size capped by `mediaMaxMb`). + +## Troubleshooting +- Check `clawdbot channels status --probe` for auth errors or missing audience config. +- If no messages arrive, confirm the Chat app’s webhook URL + event subscriptions. +- If mention gating blocks replies, set `botUser` to the app’s user resource name and verify `requireMention`. + +Related docs: +- [Gateway configuration](/gateway/configuration) +- [Security](/gateway/security) +- [Reactions](/tools/reactions) diff --git a/docs/channels/index.md b/docs/channels/index.md index f8fd860c3..ee8a281d1 100644 --- a/docs/channels/index.md +++ b/docs/channels/index.md @@ -15,6 +15,7 @@ Text is supported everywhere; media and reactions vary by channel. - [Telegram](/channels/telegram) — Bot API via grammY; supports groups. - [Discord](/channels/discord) — Discord Bot API + Gateway; supports servers, channels, and DMs. - [Slack](/channels/slack) — Bolt SDK; workspace apps. +- [Google Chat](/channels/googlechat) — Google Chat API app via HTTP webhook. - [Mattermost](/channels/mattermost) — Bot API + WebSocket; channels, groups, DMs (plugin, installed separately). - [Signal](/channels/signal) — signal-cli; privacy-focused. - [BlueBubbles](/channels/bluebubbles) — **Recommended for iMessage**; uses the BlueBubbles macOS server REST API with full feature support (edit, unsend, effects, reactions, group management — edit currently broken on macOS 26 Tahoe). diff --git a/docs/cli/channels.md b/docs/cli/channels.md index 2fe9e90df..dcb95b0f7 100644 --- a/docs/cli/channels.md +++ b/docs/cli/channels.md @@ -1,7 +1,7 @@ --- summary: "CLI reference for `clawdbot channels` (accounts, status, login/logout, logs)" read_when: - - You want to add/remove channel accounts (WhatsApp/Telegram/Discord/Slack/Mattermost (plugin)/Signal/iMessage) + - You want to add/remove channel accounts (WhatsApp/Telegram/Discord/Google Chat/Slack/Mattermost (plugin)/Signal/iMessage) - You want to check channel status or tail channel logs --- diff --git a/docs/cli/index.md b/docs/cli/index.md index ce1c619d5..d10942dc8 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -355,7 +355,7 @@ Options: ## Channel helpers ### `channels` -Manage chat channel accounts (WhatsApp/Telegram/Discord/Slack/Mattermost (plugin)/Signal/iMessage/MS Teams). +Manage chat channel accounts (WhatsApp/Telegram/Discord/Google Chat/Slack/Mattermost (plugin)/Signal/iMessage/MS Teams). Subcommands: - `channels list`: show configured channels and auth profiles (Claude Code + Codex CLI OAuth sync included). @@ -368,7 +368,7 @@ Subcommands: - `channels logout`: log out of a channel session (if supported). Common options: -- `--channel `: `whatsapp|telegram|discord|slack|mattermost|signal|imessage|msteams` +- `--channel `: `whatsapp|telegram|discord|googlechat|slack|mattermost|signal|imessage|msteams` - `--account `: channel account id (default `default`) - `--name