feat: register Zoom as core channel and fix critical issues

- Register Zoom in CHAT_CHANNEL_ORDER and CHAT_CHANNEL_META (core channel)
- Add Zoom to channel dock with config/capabilities
- Fix wrong default apiHost: https://api.zoom.us (was https://zoom.us)
- Create normalize helpers (normalizeZoomMessagingTarget, looksLikeZoomTargetId)
- Export normalize helpers from plugin-sdk
- Update channel.ts to use normalize helpers from SDK
- Fix version mismatch (2026.1.26 across all packages)

Docs improvements:
- Convert all config examples from YAML to JSON5 (matches Moltbot format)
- Fix config path references (moltbot.yaml → ~/.clawdbot/moltbot.json)
- Make LLM provider reference generic (not hardcoded to Anthropic)
- Correct Zoom app setup: General App (not Team Chat App)
- Add detailed steps: Production tab OAuth, Features tab Team Chat setup
- Update onboarding wizard instructions to match actual Zoom UI flow
This commit is contained in:
Pranav Katariya 2026-01-27 22:01:03 -08:00
parent 7373b77baf
commit bc109c4ae3
9 changed files with 162 additions and 72 deletions

View File

@ -11,12 +11,11 @@ Status: production-ready plugin for Zoom Team Chat direct messages via Team Chat
## Quick setup
1) Create a 'General App' in [Zoom App Marketplace](https://marketplace.zoom.us/develop/create)
2) Enable the Team Chat surface and note your credentials (Client ID, Client Secret, Bot JID, Webhook Secret Token)
3) Install the plugin: `pnpm install` (the zoom extension is included in the workspace)
4) Configure URLs (see URL Configuration below):
- Webhook URL: `https://gateway-host/webhooks/zoom`
- OAuth Redirect URL: `https://gateway-host/api/zoomapp/auth`
5) Set credentials in config:
2) Production tab: Add OAuth Redirect URL (`https://gateway-host/api/zoomapp/auth`)
3) Features tab → Surfaces → Team Chat: Toggle on Team Chat Subscription, add Bot endpoint URL (`https://gateway-host/webhooks/zoom`) and Welcome Message
4) Note your credentials: Client ID, Client Secret, Bot JID, Secret Token
5) Install the plugin: `pnpm install` (the zoom extension is included in the workspace)
6) Set credentials in config:
```json5
{
channels: {
@ -43,16 +42,23 @@ Status: production-ready plugin for Zoom Team Chat direct messages via Team Chat
## Setup (detailed)
### 1. Create Zoom Team Chat App
### 1. Create Zoom App
1) Go to [Zoom App Marketplace](https://marketplace.zoom.us/develop/create)
2) Click "Create" and select "Team Chat App"
3) Enable the **Bot** feature
4) Note your credentials from the app settings:
- **Client ID**
- **Client Secret**
- **Bot JID** (e.g., `bot@xmppdev.zoom.us`)
- **Secret Token** (for webhook verification)
2) Click "Create" and select "**General App**"
3) Fill in basic app information (name, description, etc.)
4) Go to the **Production** tab:
- Add **OAuth Redirect URL**: `https://gateway-host/api/zoomapp/auth`
5) Go to the **Features** tab:
- Under **Surfaces**, select "**Team Chat**"
- Toggle on "**Team Chat Subscription**"
- Add **Bot endpoint URL**: `https://gateway-host/webhooks/zoom`
- Set **Welcome Message** for the bot (e.g., "Hello! I'm your AI assistant.")
6) Note your credentials from the app settings:
- **Client ID** (from App Credentials)
- **Client Secret** (from App Credentials)
- **Bot JID** (from Team Chat section, e.g., `bot@xmppdev.zoom.us`)
- **Secret Token** (from Team Chat section, for webhook verification)
### 2. Configure URLs
@ -83,20 +89,25 @@ Subscribe to these event types:
### 4. Configure Moltbot
Add to `moltbot.yaml`:
Add to `~/.clawdbot/moltbot.json`:
```yaml
channels:
zoom:
enabled: true
clientId: YOUR_CLIENT_ID
clientSecret: YOUR_CLIENT_SECRET
botJid: YOUR_BOT_JID@xmppdev.zoom.us
secretToken: YOUR_SECRET_TOKEN
apiHost: https://zoomdev.us # Use https://api.zoom.us for production
oauthHost: https://zoomdev.us # Use https://zoom.us for production
dm:
policy: open # open | closed | allowlist
```json5
{
channels: {
zoom: {
enabled: true,
clientId: "YOUR_CLIENT_ID",
clientSecret: "YOUR_CLIENT_SECRET",
botJid: "YOUR_BOT_JID@xmppdev.zoom.us",
secretToken: "YOUR_SECRET_TOKEN",
apiHost: "https://zoomdev.us", // Use https://api.zoom.us for production
oauthHost: "https://zoomdev.us", // Use https://zoom.us for production
dm: {
policy: "open" // open | allowlist | pairing
}
}
}
}
```
### 5. Start Gateway
@ -116,47 +127,77 @@ Send a direct message to your bot in Zoom Team Chat. The bot should respond with
### DM Policies
**Open** (default):
```yaml
dm:
policy: open
```json5
{
channels: {
zoom: {
dm: {
policy: "open"
}
}
}
}
```
Anyone can message the bot.
**Closed**:
```yaml
dm:
policy: closed
```json5
{
channels: {
zoom: {
dm: {
policy: "closed"
}
}
}
}
```
Bot rejects all DMs.
**Allowlist**:
```yaml
dm:
policy: allowlist
allowFrom:
- user1@xmppdev.zoom.us
- user2@xmppdev.zoom.us
```json5
{
channels: {
zoom: {
dm: {
policy: "allowlist",
allowFrom: [
"user1@xmppdev.zoom.us",
"user2@xmppdev.zoom.us"
]
}
}
}
}
```
Only specified users can message the bot.
### Development vs Production
**Development (zoomdev.us):**
```yaml
channels:
zoom:
apiHost: https://zoomdev.us
oauthHost: https://zoomdev.us
botJid: bot@xmppdev.zoom.us
```json5
{
channels: {
zoom: {
apiHost: "https://zoomdev.us",
oauthHost: "https://zoomdev.us",
botJid: "bot@xmppdev.zoom.us"
}
}
}
```
**Production (zoom.us):**
```yaml
channels:
zoom:
apiHost: https://api.zoom.us
oauthHost: https://zoom.us
botJid: bot@xmpp.zoom.us
```json5
{
channels: {
zoom: {
apiHost: "https://api.zoom.us",
oauthHost: "https://zoom.us",
botJid: "bot@xmpp.zoom.us"
}
}
}
```
## Features
@ -205,8 +246,8 @@ If you see "port 3000 already in use":
### Bot Not Responding
1. Verify credentials in `moltbot.yaml` are correct
2. Check Anthropic API key is valid
1. Verify credentials in `~/.clawdbot/moltbot.json` are correct
2. Check LLM provider API key is configured (see `moltbot login`)
3. Ensure `botJid` matches your app's Bot JID exactly
4. Confirm app is installed to your Zoom account
5. Check gateway logs for authentication errors
@ -233,7 +274,7 @@ For implementation details, see the [plugin source code](https://github.com/molt
**Never commit credentials!**
Store sensitive values in `moltbot.yaml` (gitignored) and use `moltbot-example.yaml` for documentation templates.
Store sensitive values in `~/.clawdbot/moltbot.json` (user's home directory, not in repo).
Webhook events are verified using the `secretToken` from your Zoom app configuration.

View File

@ -1,6 +1,6 @@
{
"name": "@moltbot/zoom",
"version": "2026.1.27",
"version": "2026.1.26",
"type": "module",
"description": "Moltbot Zoom Team Chat channel plugin",
"moltbot": {

View File

@ -1,6 +1,9 @@
import {
buildChannelConfigSchema,
DEFAULT_ACCOUNT_ID,
getChatChannelMeta,
looksLikeZoomTargetId,
normalizeZoomMessagingTarget,
type ChannelPlugin,
type ResolvedZoomAccount,
resolveZoomAccount,
@ -9,16 +12,7 @@ import {
zoomOnboardingAdapter,
} from "clawdbot/plugin-sdk";
const meta = {
id: "zoom",
label: "Zoom",
selectionLabel: "Zoom Team Chat",
docsPath: "/channels/zoom",
docsLabel: "zoom",
blurb: "Zoom Team Chat via Team Chat Bot API.",
order: 45,
quickstartAllowFrom: true,
};
const meta = getChatChannelMeta("zoom");
export const zoomPlugin: ChannelPlugin<ResolvedZoomAccount> = {
id: "zoom",
@ -88,9 +82,9 @@ export const zoomPlugin: ChannelPlugin<ResolvedZoomAccount> = {
collectWarnings: () => [],
},
messaging: {
normalizeTarget: (target) => String(target ?? "").trim(),
normalizeTarget: (target) => normalizeZoomMessagingTarget(String(target ?? "")),
targetResolver: {
looksLikeId: (input) => input.includes("@xmpp") || input.includes("@xmppdev"),
looksLikeId: (input) => looksLikeZoomTargetId(input),
hint: "<userJid>",
},
},

View File

@ -9,6 +9,7 @@ import { normalizeAccountId } from "../routing/session-key.js";
import { normalizeE164 } from "../utils.js";
import { resolveWhatsAppAccount } from "../web/accounts.js";
import { normalizeWhatsAppTarget } from "../whatsapp/normalize.js";
import { resolveZoomAccount } from "../zoom/config.js";
import { requireActivePluginRegistry } from "../plugins/runtime.js";
import {
resolveDiscordGroupRequireMention,
@ -369,6 +370,21 @@ const DOCKS: Record<ChatChannelId, ChannelDock> = {
},
},
},
zoom: {
id: "zoom",
capabilities: {
chatTypes: ["direct"],
},
outbound: { textChunkLimit: 4000 },
config: {
resolveAllowFrom: ({ cfg, accountId }) =>
(resolveZoomAccount({ cfg, accountId }).config.dm?.allowFrom ?? []).map((entry) =>
String(entry),
),
formatAllowFrom: ({ allowFrom }) =>
allowFrom.map((entry) => String(entry).trim()).filter(Boolean),
},
},
};
function buildDockFromPlugin(plugin: ChannelPlugin): ChannelDock {

View File

@ -0,0 +1,23 @@
export function normalizeZoomMessagingTarget(raw: string): string | undefined {
const trimmed = raw.trim();
if (!trimmed) return undefined;
// Strip optional zoom: prefix
const withoutPrefix = trimmed.replace(/^zoom:/i, "").trim();
if (!withoutPrefix) return undefined;
return withoutPrefix;
}
export function looksLikeZoomTargetId(raw: string): boolean {
const trimmed = raw.trim();
if (!trimmed) return false;
// Zoom JIDs contain @xmpp or @xmppdev
if (trimmed.includes("@xmpp") || trimmed.includes("@xmppdev")) return true;
// Or starts with zoom: prefix
if (/^zoom:/i.test(trimmed)) return true;
return false;
}

View File

@ -33,13 +33,14 @@ async function noteZoomHelp(prompter: WizardPrompter): Promise<void> {
await prompter.note(
[
"1) Go to Zoom App Marketplace (marketplace.zoom.us/develop/create)",
"2) Create a Team Chat App and enable Bot feature",
"3) Copy Client ID, Client Secret, Bot JID, and Secret Token",
"4) You'll configure webhook and OAuth redirect URLs after setup",
"2) Create a General App",
"3) Production tab: add OAuth Redirect URL",
"4) Features tab > Surfaces > Team Chat: enable subscription + bot endpoint",
"5) Copy Client ID, Client Secret, Bot JID, Secret Token",
`Docs: ${formatDocsLink("/channels/zoom")}`,
"Website: https://molt.bot",
].join("\n"),
"Zoom Team Chat setup",
"Zoom setup",
);
}

View File

@ -12,6 +12,7 @@ export const CHAT_CHANNEL_ORDER = [
"slack",
"signal",
"imessage",
"zoom",
] as const;
export type ChatChannelId = (typeof CHAT_CHANNEL_ORDER)[number];
@ -98,6 +99,16 @@ const CHAT_CHANNEL_META: Record<ChatChannelId, ChannelMeta> = {
blurb: "this is still a work in progress.",
systemImage: "message.fill",
},
zoom: {
id: "zoom",
label: "Zoom",
selectionLabel: "Zoom Team Chat",
detailLabel: "Zoom Team Chat",
docsPath: "/channels/zoom",
docsLabel: "zoom",
blurb: "Zoom Team Chat via Team Chat Bot API.",
systemImage: "video",
},
};
export const CHAT_CHANNEL_ALIASES: Record<string, ChatChannelId> = {

View File

@ -312,6 +312,10 @@ export { resolveZoomAccount, type ResolvedZoomAccount, type ZoomConfig } from ".
export { ZoomConfigSchema } from "../zoom/config.js";
export { monitorZoomProvider, type MonitorZoomOpts } from "../zoom/monitor.js";
export { zoomOnboardingAdapter } from "../channels/plugins/onboarding/zoom.js";
export {
looksLikeZoomTargetId,
normalizeZoomMessagingTarget,
} from "../channels/plugins/normalize/zoom.js";
// Channel: Signal
export {

View File

@ -46,7 +46,7 @@ export function resolveZoomAccount(params: {
clientSecret: config.clientSecret,
botJid: config.botJid,
secretToken: config.secretToken,
apiHost: config.apiHost || "https://zoom.us",
apiHost: config.apiHost || "https://api.zoom.us",
oauthHost: config.oauthHost || "https://zoom.us",
config,
};