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:
parent
7373b77baf
commit
bc109c4ae3
@ -11,12 +11,11 @@ Status: production-ready plugin for Zoom Team Chat direct messages via Team Chat
|
|||||||
## Quick setup
|
## Quick setup
|
||||||
|
|
||||||
1) Create a 'General App' in [Zoom App Marketplace](https://marketplace.zoom.us/develop/create)
|
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)
|
2) Production tab: Add OAuth Redirect URL (`https://gateway-host/api/zoomapp/auth`)
|
||||||
3) Install the plugin: `pnpm install` (the zoom extension is included in the workspace)
|
3) Features tab → Surfaces → Team Chat: Toggle on Team Chat Subscription, add Bot endpoint URL (`https://gateway-host/webhooks/zoom`) and Welcome Message
|
||||||
4) Configure URLs (see URL Configuration below):
|
4) Note your credentials: Client ID, Client Secret, Bot JID, Secret Token
|
||||||
- Webhook URL: `https://gateway-host/webhooks/zoom`
|
5) Install the plugin: `pnpm install` (the zoom extension is included in the workspace)
|
||||||
- OAuth Redirect URL: `https://gateway-host/api/zoomapp/auth`
|
6) Set credentials in config:
|
||||||
5) Set credentials in config:
|
|
||||||
```json5
|
```json5
|
||||||
{
|
{
|
||||||
channels: {
|
channels: {
|
||||||
@ -43,16 +42,23 @@ Status: production-ready plugin for Zoom Team Chat direct messages via Team Chat
|
|||||||
|
|
||||||
## Setup (detailed)
|
## Setup (detailed)
|
||||||
|
|
||||||
### 1. Create Zoom Team Chat App
|
### 1. Create Zoom App
|
||||||
|
|
||||||
1) Go to [Zoom App Marketplace](https://marketplace.zoom.us/develop/create)
|
1) Go to [Zoom App Marketplace](https://marketplace.zoom.us/develop/create)
|
||||||
2) Click "Create" and select "Team Chat App"
|
2) Click "Create" and select "**General App**"
|
||||||
3) Enable the **Bot** feature
|
3) Fill in basic app information (name, description, etc.)
|
||||||
4) Note your credentials from the app settings:
|
4) Go to the **Production** tab:
|
||||||
- **Client ID**
|
- Add **OAuth Redirect URL**: `https://gateway-host/api/zoomapp/auth`
|
||||||
- **Client Secret**
|
5) Go to the **Features** tab:
|
||||||
- **Bot JID** (e.g., `bot@xmppdev.zoom.us`)
|
- Under **Surfaces**, select "**Team Chat**"
|
||||||
- **Secret Token** (for webhook verification)
|
- 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
|
### 2. Configure URLs
|
||||||
|
|
||||||
@ -83,20 +89,25 @@ Subscribe to these event types:
|
|||||||
|
|
||||||
### 4. Configure Moltbot
|
### 4. Configure Moltbot
|
||||||
|
|
||||||
Add to `moltbot.yaml`:
|
Add to `~/.clawdbot/moltbot.json`:
|
||||||
|
|
||||||
```yaml
|
```json5
|
||||||
channels:
|
{
|
||||||
zoom:
|
channels: {
|
||||||
enabled: true
|
zoom: {
|
||||||
clientId: YOUR_CLIENT_ID
|
enabled: true,
|
||||||
clientSecret: YOUR_CLIENT_SECRET
|
clientId: "YOUR_CLIENT_ID",
|
||||||
botJid: YOUR_BOT_JID@xmppdev.zoom.us
|
clientSecret: "YOUR_CLIENT_SECRET",
|
||||||
secretToken: YOUR_SECRET_TOKEN
|
botJid: "YOUR_BOT_JID@xmppdev.zoom.us",
|
||||||
apiHost: https://zoomdev.us # Use https://api.zoom.us for production
|
secretToken: "YOUR_SECRET_TOKEN",
|
||||||
oauthHost: https://zoomdev.us # Use https://zoom.us for production
|
apiHost: "https://zoomdev.us", // Use https://api.zoom.us for production
|
||||||
dm:
|
oauthHost: "https://zoomdev.us", // Use https://zoom.us for production
|
||||||
policy: open # open | closed | allowlist
|
dm: {
|
||||||
|
policy: "open" // open | allowlist | pairing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. Start Gateway
|
### 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
|
### DM Policies
|
||||||
|
|
||||||
**Open** (default):
|
**Open** (default):
|
||||||
```yaml
|
```json5
|
||||||
dm:
|
{
|
||||||
policy: open
|
channels: {
|
||||||
|
zoom: {
|
||||||
|
dm: {
|
||||||
|
policy: "open"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
Anyone can message the bot.
|
Anyone can message the bot.
|
||||||
|
|
||||||
**Closed**:
|
**Closed**:
|
||||||
```yaml
|
```json5
|
||||||
dm:
|
{
|
||||||
policy: closed
|
channels: {
|
||||||
|
zoom: {
|
||||||
|
dm: {
|
||||||
|
policy: "closed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
Bot rejects all DMs.
|
Bot rejects all DMs.
|
||||||
|
|
||||||
**Allowlist**:
|
**Allowlist**:
|
||||||
```yaml
|
```json5
|
||||||
dm:
|
{
|
||||||
policy: allowlist
|
channels: {
|
||||||
allowFrom:
|
zoom: {
|
||||||
- user1@xmppdev.zoom.us
|
dm: {
|
||||||
- user2@xmppdev.zoom.us
|
policy: "allowlist",
|
||||||
|
allowFrom: [
|
||||||
|
"user1@xmppdev.zoom.us",
|
||||||
|
"user2@xmppdev.zoom.us"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
Only specified users can message the bot.
|
Only specified users can message the bot.
|
||||||
|
|
||||||
### Development vs Production
|
### Development vs Production
|
||||||
|
|
||||||
**Development (zoomdev.us):**
|
**Development (zoomdev.us):**
|
||||||
```yaml
|
```json5
|
||||||
channels:
|
{
|
||||||
zoom:
|
channels: {
|
||||||
apiHost: https://zoomdev.us
|
zoom: {
|
||||||
oauthHost: https://zoomdev.us
|
apiHost: "https://zoomdev.us",
|
||||||
botJid: bot@xmppdev.zoom.us
|
oauthHost: "https://zoomdev.us",
|
||||||
|
botJid: "bot@xmppdev.zoom.us"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Production (zoom.us):**
|
**Production (zoom.us):**
|
||||||
```yaml
|
```json5
|
||||||
channels:
|
{
|
||||||
zoom:
|
channels: {
|
||||||
apiHost: https://api.zoom.us
|
zoom: {
|
||||||
oauthHost: https://zoom.us
|
apiHost: "https://api.zoom.us",
|
||||||
botJid: bot@xmpp.zoom.us
|
oauthHost: "https://zoom.us",
|
||||||
|
botJid: "bot@xmpp.zoom.us"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
@ -205,8 +246,8 @@ If you see "port 3000 already in use":
|
|||||||
|
|
||||||
### Bot Not Responding
|
### Bot Not Responding
|
||||||
|
|
||||||
1. Verify credentials in `moltbot.yaml` are correct
|
1. Verify credentials in `~/.clawdbot/moltbot.json` are correct
|
||||||
2. Check Anthropic API key is valid
|
2. Check LLM provider API key is configured (see `moltbot login`)
|
||||||
3. Ensure `botJid` matches your app's Bot JID exactly
|
3. Ensure `botJid` matches your app's Bot JID exactly
|
||||||
4. Confirm app is installed to your Zoom account
|
4. Confirm app is installed to your Zoom account
|
||||||
5. Check gateway logs for authentication errors
|
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!**
|
**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.
|
Webhook events are verified using the `secretToken` from your Zoom app configuration.
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@moltbot/zoom",
|
"name": "@moltbot/zoom",
|
||||||
"version": "2026.1.27",
|
"version": "2026.1.26",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Moltbot Zoom Team Chat channel plugin",
|
"description": "Moltbot Zoom Team Chat channel plugin",
|
||||||
"moltbot": {
|
"moltbot": {
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import {
|
import {
|
||||||
buildChannelConfigSchema,
|
buildChannelConfigSchema,
|
||||||
DEFAULT_ACCOUNT_ID,
|
DEFAULT_ACCOUNT_ID,
|
||||||
|
getChatChannelMeta,
|
||||||
|
looksLikeZoomTargetId,
|
||||||
|
normalizeZoomMessagingTarget,
|
||||||
type ChannelPlugin,
|
type ChannelPlugin,
|
||||||
type ResolvedZoomAccount,
|
type ResolvedZoomAccount,
|
||||||
resolveZoomAccount,
|
resolveZoomAccount,
|
||||||
@ -9,16 +12,7 @@ import {
|
|||||||
zoomOnboardingAdapter,
|
zoomOnboardingAdapter,
|
||||||
} from "clawdbot/plugin-sdk";
|
} from "clawdbot/plugin-sdk";
|
||||||
|
|
||||||
const meta = {
|
const meta = getChatChannelMeta("zoom");
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const zoomPlugin: ChannelPlugin<ResolvedZoomAccount> = {
|
export const zoomPlugin: ChannelPlugin<ResolvedZoomAccount> = {
|
||||||
id: "zoom",
|
id: "zoom",
|
||||||
@ -88,9 +82,9 @@ export const zoomPlugin: ChannelPlugin<ResolvedZoomAccount> = {
|
|||||||
collectWarnings: () => [],
|
collectWarnings: () => [],
|
||||||
},
|
},
|
||||||
messaging: {
|
messaging: {
|
||||||
normalizeTarget: (target) => String(target ?? "").trim(),
|
normalizeTarget: (target) => normalizeZoomMessagingTarget(String(target ?? "")),
|
||||||
targetResolver: {
|
targetResolver: {
|
||||||
looksLikeId: (input) => input.includes("@xmpp") || input.includes("@xmppdev"),
|
looksLikeId: (input) => looksLikeZoomTargetId(input),
|
||||||
hint: "<userJid>",
|
hint: "<userJid>",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { normalizeAccountId } from "../routing/session-key.js";
|
|||||||
import { normalizeE164 } from "../utils.js";
|
import { normalizeE164 } from "../utils.js";
|
||||||
import { resolveWhatsAppAccount } from "../web/accounts.js";
|
import { resolveWhatsAppAccount } from "../web/accounts.js";
|
||||||
import { normalizeWhatsAppTarget } from "../whatsapp/normalize.js";
|
import { normalizeWhatsAppTarget } from "../whatsapp/normalize.js";
|
||||||
|
import { resolveZoomAccount } from "../zoom/config.js";
|
||||||
import { requireActivePluginRegistry } from "../plugins/runtime.js";
|
import { requireActivePluginRegistry } from "../plugins/runtime.js";
|
||||||
import {
|
import {
|
||||||
resolveDiscordGroupRequireMention,
|
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 {
|
function buildDockFromPlugin(plugin: ChannelPlugin): ChannelDock {
|
||||||
|
|||||||
23
src/channels/plugins/normalize/zoom.ts
Normal file
23
src/channels/plugins/normalize/zoom.ts
Normal 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;
|
||||||
|
}
|
||||||
@ -33,13 +33,14 @@ async function noteZoomHelp(prompter: WizardPrompter): Promise<void> {
|
|||||||
await prompter.note(
|
await prompter.note(
|
||||||
[
|
[
|
||||||
"1) Go to Zoom App Marketplace (marketplace.zoom.us/develop/create)",
|
"1) Go to Zoom App Marketplace (marketplace.zoom.us/develop/create)",
|
||||||
"2) Create a Team Chat App and enable Bot feature",
|
"2) Create a General App",
|
||||||
"3) Copy Client ID, Client Secret, Bot JID, and Secret Token",
|
"3) Production tab: add OAuth Redirect URL",
|
||||||
"4) You'll configure webhook and OAuth redirect URLs after setup",
|
"4) Features tab > Surfaces > Team Chat: enable subscription + bot endpoint",
|
||||||
|
"5) Copy Client ID, Client Secret, Bot JID, Secret Token",
|
||||||
`Docs: ${formatDocsLink("/channels/zoom")}`,
|
`Docs: ${formatDocsLink("/channels/zoom")}`,
|
||||||
"Website: https://molt.bot",
|
"Website: https://molt.bot",
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
"Zoom Team Chat setup",
|
"Zoom setup",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,7 @@ export const CHAT_CHANNEL_ORDER = [
|
|||||||
"slack",
|
"slack",
|
||||||
"signal",
|
"signal",
|
||||||
"imessage",
|
"imessage",
|
||||||
|
"zoom",
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type ChatChannelId = (typeof CHAT_CHANNEL_ORDER)[number];
|
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.",
|
blurb: "this is still a work in progress.",
|
||||||
systemImage: "message.fill",
|
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> = {
|
export const CHAT_CHANNEL_ALIASES: Record<string, ChatChannelId> = {
|
||||||
|
|||||||
@ -312,6 +312,10 @@ export { resolveZoomAccount, type ResolvedZoomAccount, type ZoomConfig } from ".
|
|||||||
export { ZoomConfigSchema } from "../zoom/config.js";
|
export { ZoomConfigSchema } from "../zoom/config.js";
|
||||||
export { monitorZoomProvider, type MonitorZoomOpts } from "../zoom/monitor.js";
|
export { monitorZoomProvider, type MonitorZoomOpts } from "../zoom/monitor.js";
|
||||||
export { zoomOnboardingAdapter } from "../channels/plugins/onboarding/zoom.js";
|
export { zoomOnboardingAdapter } from "../channels/plugins/onboarding/zoom.js";
|
||||||
|
export {
|
||||||
|
looksLikeZoomTargetId,
|
||||||
|
normalizeZoomMessagingTarget,
|
||||||
|
} from "../channels/plugins/normalize/zoom.js";
|
||||||
|
|
||||||
// Channel: Signal
|
// Channel: Signal
|
||||||
export {
|
export {
|
||||||
|
|||||||
@ -46,7 +46,7 @@ export function resolveZoomAccount(params: {
|
|||||||
clientSecret: config.clientSecret,
|
clientSecret: config.clientSecret,
|
||||||
botJid: config.botJid,
|
botJid: config.botJid,
|
||||||
secretToken: config.secretToken,
|
secretToken: config.secretToken,
|
||||||
apiHost: config.apiHost || "https://zoom.us",
|
apiHost: config.apiHost || "https://api.zoom.us",
|
||||||
oauthHost: config.oauthHost || "https://zoom.us",
|
oauthHost: config.oauthHost || "https://zoom.us",
|
||||||
config,
|
config,
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user