refactor(ringcentral): rename clawdbot to moltbot

- Update package name to @moltbot/ringcentral
- Update all imports from clawdbot/plugin-sdk to moltbot/plugin-sdk
- Update ClawdbotConfig to MoltbotConfig
- Update ClawdbotPluginApi to MoltbotPluginApi
- Update README and config paths
- Add moltbot/plugin-sdk alias to vitest.config.ts

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
John Lin 2026-01-28 11:03:01 +08:00
parent ab952fd8cd
commit a494d9403a
No known key found for this signature in database
11 changed files with 79 additions and 78 deletions

View File

@ -1,6 +1,6 @@
# Clawdbot RingCentral Channel
# Moltbot RingCentral Channel
RingCentral Team Messaging channel plugin for Clawdbot. Enables bidirectional messaging with AI assistants through RingCentral Team Messaging.
RingCentral Team Messaging channel plugin for Moltbot. Enables bidirectional messaging with AI assistants through RingCentral Team Messaging.
## Features
@ -18,13 +18,13 @@ RingCentral Team Messaging channel plugin for Clawdbot. Enables bidirectional me
## Installation
```bash
clawdbot plugin install @clawdbot/ringcentral
moltbot plugin install @moltbot/ringcentral
```
Or install from tarball:
```bash
clawdbot plugin install ./clawdbot-ringcentral-2026.1.25.tgz
moltbot plugin install ./moltbot-ringcentral-2026.1.25.tgz
```
## RingCentral App Setup
@ -41,7 +41,7 @@ clawdbot plugin install ./clawdbot-ringcentral-2026.1.25.tgz
## Configuration
Add to `~/.clawdbot/clawdbot.json`:
Add to `~/.moltbot/moltbot.json`:
```json
{
@ -84,10 +84,10 @@ export RINGCENTRAL_JWT="your-jwt-token"
## Usage
1. Start the Clawdbot gateway:
1. Start the Moltbot gateway:
```bash
clawdbot gateway run
moltbot gateway run
```
2. Open RingCentral app and go to your "Personal" chat (conversation with yourself)
@ -162,7 +162,7 @@ Add **WebSocket Subscriptions** permission in your app settings. Permission chan
1. Check that `selfOnly` mode matches your use case
2. Verify you're sending messages in a "Personal" chat (conversation with yourself)
3. Check gateway logs: `tail -f /tmp/clawdbot/clawdbot-*.log | grep ringcentral`
3. Check gateway logs: `tail -f /tmp/moltbot/moltbot-*.log | grep ringcentral`
### Rate limit errors

View File

@ -1,5 +1,5 @@
import type { ClawdbotPluginApi } from "clawdbot/plugin-sdk";
import { emptyPluginConfigSchema } from "clawdbot/plugin-sdk";
import type { MoltbotPluginApi } from "moltbot/plugin-sdk";
import { emptyPluginConfigSchema } from "moltbot/plugin-sdk";
import { ringcentralDock, ringcentralPlugin } from "./src/channel.js";
import { setRingCentralRuntime } from "./src/runtime.js";
@ -7,9 +7,9 @@ import { setRingCentralRuntime } from "./src/runtime.js";
const plugin = {
id: "ringcentral",
name: "RingCentral",
description: "Clawdbot RingCentral Team Messaging channel plugin",
description: "Moltbot RingCentral Team Messaging channel plugin",
configSchema: emptyPluginConfigSchema(),
register(api: ClawdbotPluginApi) {
register(api: MoltbotPluginApi) {
setRingCentralRuntime(api.runtime);
api.registerChannel({ plugin: ringcentralPlugin, dock: ringcentralDock });
// WebSocket mode: no HTTP handler needed

View File

@ -1,9 +1,9 @@
{
"name": "@clawdbot/ringcentral",
"name": "@moltbot/ringcentral",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot RingCentral Team Messaging channel plugin",
"clawdbot": {
"description": "Moltbot RingCentral Team Messaging channel plugin",
"moltbot": {
"extensions": [
"./index.ts"
],
@ -22,7 +22,7 @@
"order": 56
},
"install": {
"npmSpec": "@clawdbot/ringcentral",
"npmSpec": "@moltbot/ringcentral",
"localPath": "extensions/ringcentral",
"defaultChoice": "npm"
}
@ -34,9 +34,9 @@
"ws": "^8.18.0"
},
"devDependencies": {
"clawdbot": "workspace:*"
"moltbot": "workspace:*"
},
"peerDependencies": {
"clawdbot": ">=2026.1.25"
"moltbot": ">=2026.1.25"
}
}

View File

@ -1,5 +1,5 @@
import { describe, expect, it, beforeEach, afterEach, vi } from "vitest";
import type { ClawdbotConfig } from "clawdbot/plugin-sdk";
import type { MoltbotConfig } from "moltbot/plugin-sdk";
import {
listRingCentralAccountIds,
resolveDefaultRingCentralAccountId,
@ -9,12 +9,12 @@ import {
describe("listRingCentralAccountIds", () => {
it("returns default account when no accounts configured", () => {
const cfg = { channels: {} } as ClawdbotConfig;
const cfg = { channels: {} } as MoltbotConfig;
expect(listRingCentralAccountIds(cfg)).toEqual(["default"]);
});
it("returns default account when ringcentral channel not configured", () => {
const cfg = { channels: { telegram: { enabled: true } } } as ClawdbotConfig;
const cfg = { channels: { telegram: { enabled: true } } } as MoltbotConfig;
expect(listRingCentralAccountIds(cfg)).toEqual(["default"]);
});
@ -28,7 +28,7 @@ describe("listRingCentralAccountIds", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
expect(listRingCentralAccountIds(cfg)).toEqual(["personal", "work"]);
});
});
@ -45,7 +45,7 @@ describe("resolveDefaultRingCentralAccountId", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
expect(resolveDefaultRingCentralAccountId(cfg)).toBe("work");
});
@ -59,7 +59,7 @@ describe("resolveDefaultRingCentralAccountId", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
expect(resolveDefaultRingCentralAccountId(cfg)).toBe("default");
});
@ -73,7 +73,7 @@ describe("resolveDefaultRingCentralAccountId", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
expect(resolveDefaultRingCentralAccountId(cfg)).toBe("alpha");
});
});
@ -101,7 +101,7 @@ describe("resolveRingCentralAccount", () => {
server: "https://platform.devtest.ringcentral.com",
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const account = resolveRingCentralAccount({ cfg });
@ -126,7 +126,7 @@ describe("resolveRingCentralAccount", () => {
enabled: true,
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const account = resolveRingCentralAccount({ cfg });
@ -144,7 +144,7 @@ describe("resolveRingCentralAccount", () => {
enabled: true,
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const account = resolveRingCentralAccount({ cfg });
@ -162,7 +162,7 @@ describe("resolveRingCentralAccount", () => {
jwt: "test-jwt",
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const account = resolveRingCentralAccount({ cfg });
@ -184,7 +184,7 @@ describe("resolveRingCentralAccount", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const account = resolveRingCentralAccount({ cfg, accountId: "work" });
@ -208,7 +208,7 @@ describe("resolveRingCentralAccount", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const account = resolveRingCentralAccount({ cfg, accountId: "work" });
@ -231,7 +231,7 @@ describe("resolveRingCentralAccount", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const account = resolveRingCentralAccount({ cfg, accountId: "work" });
@ -264,7 +264,7 @@ describe("listEnabledRingCentralAccounts", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const accounts = listEnabledRingCentralAccounts(cfg);
@ -282,7 +282,7 @@ describe("listEnabledRingCentralAccounts", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const accounts = listEnabledRingCentralAccounts(cfg);

View File

@ -1,5 +1,5 @@
import type { ClawdbotConfig } from "clawdbot/plugin-sdk";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "clawdbot/plugin-sdk";
import type { MoltbotConfig } from "moltbot/plugin-sdk";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "moltbot/plugin-sdk";
import type { RingCentralAccountConfig, RingCentralConfig } from "./types.js";
@ -24,19 +24,19 @@ const ENV_SERVER = "RINGCENTRAL_SERVER";
const DEFAULT_SERVER = "https://platform.ringcentral.com";
function listConfiguredAccountIds(cfg: ClawdbotConfig): string[] {
function listConfiguredAccountIds(cfg: MoltbotConfig): string[] {
const accounts = (cfg.channels?.ringcentral as RingCentralConfig | undefined)?.accounts;
if (!accounts || typeof accounts !== "object") return [];
return Object.keys(accounts).filter(Boolean);
}
export function listRingCentralAccountIds(cfg: ClawdbotConfig): string[] {
export function listRingCentralAccountIds(cfg: MoltbotConfig): string[] {
const ids = listConfiguredAccountIds(cfg);
if (ids.length === 0) return [DEFAULT_ACCOUNT_ID];
return ids.sort((a, b) => a.localeCompare(b));
}
export function resolveDefaultRingCentralAccountId(cfg: ClawdbotConfig): string {
export function resolveDefaultRingCentralAccountId(cfg: MoltbotConfig): string {
const channel = cfg.channels?.ringcentral as RingCentralConfig | undefined;
if (channel?.defaultAccount?.trim()) return channel.defaultAccount.trim();
const ids = listRingCentralAccountIds(cfg);
@ -45,7 +45,7 @@ export function resolveDefaultRingCentralAccountId(cfg: ClawdbotConfig): string
}
function resolveAccountConfig(
cfg: ClawdbotConfig,
cfg: MoltbotConfig,
accountId: string,
): RingCentralAccountConfig | undefined {
const accounts = (cfg.channels?.ringcentral as RingCentralConfig | undefined)?.accounts;
@ -54,7 +54,7 @@ function resolveAccountConfig(
}
function mergeRingCentralAccountConfig(
cfg: ClawdbotConfig,
cfg: MoltbotConfig,
accountId: string,
): RingCentralAccountConfig {
const raw = (cfg.channels?.ringcentral ?? {}) as RingCentralConfig;
@ -131,7 +131,7 @@ function resolveCredentialsFromConfig(params: {
}
export function resolveRingCentralAccount(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
accountId?: string | null;
}): ResolvedRingCentralAccount {
const accountId = normalizeAccountId(params.accountId);
@ -155,7 +155,7 @@ export function resolveRingCentralAccount(params: {
};
}
export function listEnabledRingCentralAccounts(cfg: ClawdbotConfig): ResolvedRingCentralAccount[] {
export function listEnabledRingCentralAccounts(cfg: MoltbotConfig): ResolvedRingCentralAccount[] {
return listRingCentralAccountIds(cfg)
.map((accountId) => resolveRingCentralAccount({ cfg, accountId }))
.filter((account) => account.enabled);

View File

@ -13,8 +13,8 @@ import {
setAccountEnabledInConfigSection,
type ChannelDock,
type ChannelPlugin,
type ClawdbotConfig,
} from "clawdbot/plugin-sdk";
type MoltbotConfig,
} from "moltbot/plugin-sdk";
import {
listRingCentralAccountIds,
@ -58,7 +58,7 @@ export const ringcentralDock: ChannelDock = {
outbound: { textChunkLimit: 4000 },
config: {
resolveAllowFrom: ({ cfg, accountId }) =>
(resolveRingCentralAccount({ cfg: cfg as ClawdbotConfig, accountId }).config.dm?.allowFrom ??
(resolveRingCentralAccount({ cfg: cfg as MoltbotConfig, accountId }).config.dm?.allowFrom ??
[]
).map((entry) => String(entry)),
formatAllowFrom: ({ allowFrom }) =>
@ -69,7 +69,7 @@ export const ringcentralDock: ChannelDock = {
},
groups: {
resolveRequireMention: ({ cfg, accountId }) => {
const account = resolveRingCentralAccount({ cfg: cfg as ClawdbotConfig, accountId });
const account = resolveRingCentralAccount({ cfg: cfg as MoltbotConfig, accountId });
return account.config.requireMention ?? true;
},
},
@ -97,7 +97,7 @@ export const ringcentralPlugin: ChannelPlugin<ResolvedRingCentralAccount> = {
idLabel: "ringcentralUserId",
normalizeAllowEntry: (entry) => formatAllowFromEntry(entry),
notifyApproval: async ({ cfg, id }) => {
const account = resolveRingCentralAccount({ cfg: cfg as ClawdbotConfig });
const account = resolveRingCentralAccount({ cfg: cfg as MoltbotConfig });
if (account.credentialSource === "none") return;
const target = normalizeRingCentralTarget(id) ?? id;
// For DM approval, we need to find/create a direct chat
@ -127,13 +127,13 @@ export const ringcentralPlugin: ChannelPlugin<ResolvedRingCentralAccount> = {
reload: { configPrefixes: ["channels.ringcentral"] },
configSchema: buildChannelConfigSchema(RingCentralConfigSchema),
config: {
listAccountIds: (cfg) => listRingCentralAccountIds(cfg as ClawdbotConfig),
listAccountIds: (cfg) => listRingCentralAccountIds(cfg as MoltbotConfig),
resolveAccount: (cfg, accountId) =>
resolveRingCentralAccount({ cfg: cfg as ClawdbotConfig, accountId }),
defaultAccountId: (cfg) => resolveDefaultRingCentralAccountId(cfg as ClawdbotConfig),
resolveRingCentralAccount({ cfg: cfg as MoltbotConfig, accountId }),
defaultAccountId: (cfg) => resolveDefaultRingCentralAccountId(cfg as MoltbotConfig),
setAccountEnabled: ({ cfg, accountId, enabled }) =>
setAccountEnabledInConfigSection({
cfg: cfg as ClawdbotConfig,
cfg: cfg as MoltbotConfig,
sectionKey: "ringcentral",
accountId,
enabled,
@ -141,7 +141,7 @@ export const ringcentralPlugin: ChannelPlugin<ResolvedRingCentralAccount> = {
}),
deleteAccount: ({ cfg, accountId }) =>
deleteAccountFromConfigSection({
cfg: cfg as ClawdbotConfig,
cfg: cfg as MoltbotConfig,
sectionKey: "ringcentral",
accountId,
clearBaseFields: [
@ -163,7 +163,7 @@ export const ringcentralPlugin: ChannelPlugin<ResolvedRingCentralAccount> = {
}),
resolveAllowFrom: ({ cfg, accountId }) =>
(resolveRingCentralAccount({
cfg: cfg as ClawdbotConfig,
cfg: cfg as MoltbotConfig,
accountId,
}).config.dm?.allowFrom ?? []
).map((entry) => String(entry)),
@ -177,7 +177,7 @@ export const ringcentralPlugin: ChannelPlugin<ResolvedRingCentralAccount> = {
resolveDmPolicy: ({ cfg, accountId, account }) => {
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
const useAccountPath = Boolean(
(cfg as ClawdbotConfig).channels?.ringcentral?.accounts?.[resolvedAccountId],
(cfg as MoltbotConfig).channels?.ringcentral?.accounts?.[resolvedAccountId],
);
const allowFromPath = useAccountPath
? `channels.ringcentral.accounts.${resolvedAccountId}.dm.`
@ -209,7 +209,7 @@ export const ringcentralPlugin: ChannelPlugin<ResolvedRingCentralAccount> = {
},
groups: {
resolveRequireMention: ({ cfg, accountId }) => {
const account = resolveRingCentralAccount({ cfg: cfg as ClawdbotConfig, accountId });
const account = resolveRingCentralAccount({ cfg: cfg as MoltbotConfig, accountId });
return account.config.requireMention ?? true;
},
},
@ -231,7 +231,7 @@ export const ringcentralPlugin: ChannelPlugin<ResolvedRingCentralAccount> = {
self: async () => null,
listPeers: async ({ cfg, accountId, query, limit }) => {
const account = resolveRingCentralAccount({
cfg: cfg as ClawdbotConfig,
cfg: cfg as MoltbotConfig,
accountId,
});
const q = query?.trim().toLowerCase() || "";
@ -251,7 +251,7 @@ export const ringcentralPlugin: ChannelPlugin<ResolvedRingCentralAccount> = {
},
listGroups: async ({ cfg, accountId, query, limit }) => {
const account = resolveRingCentralAccount({
cfg: cfg as ClawdbotConfig,
cfg: cfg as MoltbotConfig,
accountId,
});
const groups = account.config.groups ?? {};
@ -290,7 +290,7 @@ export const ringcentralPlugin: ChannelPlugin<ResolvedRingCentralAccount> = {
resolveAccountId: ({ accountId }) => normalizeAccountId(accountId),
applyAccountName: ({ cfg, accountId, name }) =>
applyAccountNameToChannelSection({
cfg: cfg as ClawdbotConfig,
cfg: cfg as MoltbotConfig,
channelKey: "ringcentral",
accountId,
name,
@ -306,7 +306,7 @@ export const ringcentralPlugin: ChannelPlugin<ResolvedRingCentralAccount> = {
},
applyAccountConfig: ({ cfg, accountId, input }) => {
const namedConfig = applyAccountNameToChannelSection({
cfg: cfg as ClawdbotConfig,
cfg: cfg as MoltbotConfig,
channelKey: "ringcentral",
accountId,
name: input.name,
@ -314,7 +314,7 @@ export const ringcentralPlugin: ChannelPlugin<ResolvedRingCentralAccount> = {
const next =
accountId !== DEFAULT_ACCOUNT_ID
? migrateBaseNameToDefaultAccount({
cfg: namedConfig as ClawdbotConfig,
cfg: namedConfig as MoltbotConfig,
channelKey: "ringcentral",
})
: namedConfig;
@ -341,7 +341,7 @@ export const ringcentralPlugin: ChannelPlugin<ResolvedRingCentralAccount> = {
...configPatch,
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
}
return {
...next,
@ -360,7 +360,7 @@ export const ringcentralPlugin: ChannelPlugin<ResolvedRingCentralAccount> = {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
},
},
outbound: {
@ -407,7 +407,7 @@ export const ringcentralPlugin: ChannelPlugin<ResolvedRingCentralAccount> = {
},
sendText: async ({ cfg, to, text, accountId }) => {
const account = resolveRingCentralAccount({
cfg: cfg as ClawdbotConfig,
cfg: cfg as MoltbotConfig,
accountId,
});
const result = await sendRingCentralMessage({
@ -426,12 +426,12 @@ export const ringcentralPlugin: ChannelPlugin<ResolvedRingCentralAccount> = {
throw new Error("RingCentral mediaUrl is required.");
}
const account = resolveRingCentralAccount({
cfg: cfg as ClawdbotConfig,
cfg: cfg as MoltbotConfig,
accountId,
});
const runtime = getRingCentralRuntime();
const maxBytes = resolveChannelMediaMaxBytes({
cfg: cfg as ClawdbotConfig,
cfg: cfg as MoltbotConfig,
resolveChannelLimitMb: ({ cfg: c, accountId: aid }) =>
(c.channels?.ringcentral as { accounts?: Record<string, { mediaMaxMb?: number }>; mediaMaxMb?: number } | undefined)
?.accounts?.[aid]?.mediaMaxMb ??
@ -529,7 +529,7 @@ export const ringcentralPlugin: ChannelPlugin<ResolvedRingCentralAccount> = {
});
const unregister = await startRingCentralMonitor({
account,
config: ctx.cfg as ClawdbotConfig,
config: ctx.cfg as MoltbotConfig,
runtime: ctx.runtime,
abortSignal: ctx.abortSignal,
statusSink: (patch) => ctx.setStatus({ accountId: account.accountId, ...patch }),

View File

@ -6,7 +6,7 @@ import {
GroupPolicySchema,
MarkdownConfigSchema,
requireOpenAllowFrom,
} from "clawdbot/plugin-sdk";
} from "moltbot/plugin-sdk";
const RingCentralGroupConfigSchema = z
.object({

View File

@ -1,7 +1,7 @@
import { Subscriptions } from "@ringcentral/subscriptions";
import type { ClawdbotConfig } from "clawdbot/plugin-sdk";
import { resolveMentionGatingWithBypass } from "clawdbot/plugin-sdk";
import type { MoltbotConfig } from "moltbot/plugin-sdk";
import { resolveMentionGatingWithBypass } from "moltbot/plugin-sdk";
import type { ResolvedRingCentralAccount } from "./accounts.js";
import { getRingCentralSDK } from "./auth.js";
@ -41,7 +41,7 @@ function isOwnSentMessage(messageId: string): boolean {
export type RingCentralMonitorOptions = {
account: ResolvedRingCentralAccount;
config: ClawdbotConfig;
config: MoltbotConfig;
runtime: RingCentralRuntimeEnv;
abortSignal: AbortSignal;
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void;
@ -114,19 +114,19 @@ function extractMentionInfo(mentions: RingCentralMention[], botExtensionId?: str
function resolveBotDisplayName(params: {
accountName?: string;
agentId: string;
config: ClawdbotConfig;
config: MoltbotConfig;
}): string {
const { accountName, agentId, config } = params;
if (accountName?.trim()) return accountName.trim();
const agent = config.agents?.list?.find((a) => a.id === agentId);
if (agent?.name?.trim()) return agent.name.trim();
return "Clawdbot";
return "Moltbot";
}
async function processWebSocketEvent(params: {
event: RingCentralWebhookEvent;
account: ResolvedRingCentralAccount;
config: ClawdbotConfig;
config: MoltbotConfig;
runtime: RingCentralRuntimeEnv;
core: RingCentralCoreRuntime;
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void;
@ -162,7 +162,7 @@ async function processWebSocketEvent(params: {
async function processMessageWithPipeline(params: {
eventBody: RingCentralEventBody;
account: ResolvedRingCentralAccount;
config: ClawdbotConfig;
config: MoltbotConfig;
runtime: RingCentralRuntimeEnv;
core: RingCentralCoreRuntime;
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void;
@ -533,7 +533,7 @@ async function deliverRingCentralReply(params: {
chatId: string;
runtime: RingCentralRuntimeEnv;
core: RingCentralCoreRuntime;
config: ClawdbotConfig;
config: MoltbotConfig;
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void;
typingPostId?: string;
}): Promise<void> {

View File

@ -1,4 +1,4 @@
import type { PluginRuntime } from "clawdbot/plugin-sdk";
import type { PluginRuntime } from "moltbot/plugin-sdk";
let runtime: PluginRuntime | null = null;

View File

@ -1,4 +1,4 @@
import type { DmPolicy, GroupPolicy, MarkdownConfig } from "clawdbot/plugin-sdk";
import type { DmPolicy, GroupPolicy, MarkdownConfig } from "moltbot/plugin-sdk";
// RingCentral Team Messaging API types

View File

@ -13,6 +13,7 @@ export default defineConfig({
resolve: {
alias: {
"clawdbot/plugin-sdk": path.join(repoRoot, "src", "plugin-sdk", "index.ts"),
"moltbot/plugin-sdk": path.join(repoRoot, "src", "plugin-sdk", "index.ts"),
},
},
test: {