mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-04 01:34:36 +00:00
feat(feishu): sync with clawdbot-feishu #137 (multi-account support)
- Sync latest changes from clawdbot-feishu including multi-account support - Add eslint-disable comments for SDK-related any types - Remove unused imports - Fix no-floating-promises in monitor.ts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
import type { ChannelPlugin, ClawdbotConfig } from "openclaw/plugin-sdk";
|
||||
import { DEFAULT_ACCOUNT_ID, PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk";
|
||||
import type { ResolvedFeishuAccount, FeishuConfig } from "./types.js";
|
||||
import { resolveFeishuAccount, resolveFeishuCredentials } from "./accounts.js";
|
||||
import {
|
||||
resolveFeishuAccount,
|
||||
listFeishuAccountIds,
|
||||
resolveDefaultFeishuAccountId,
|
||||
} from "./accounts.js";
|
||||
import {
|
||||
listFeishuDirectoryPeers,
|
||||
listFeishuDirectoryGroups,
|
||||
@@ -34,11 +38,12 @@ export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount> = {
|
||||
pairing: {
|
||||
idLabel: "feishuUserId",
|
||||
normalizeAllowEntry: (entry) => entry.replace(/^(feishu|user|open_id):/i, ""),
|
||||
notifyApproval: async ({ cfg, id }) => {
|
||||
notifyApproval: async ({ cfg, id, accountId }) => {
|
||||
await sendMessageFeishu({
|
||||
cfg,
|
||||
to: id,
|
||||
text: PAIRING_APPROVED_MESSAGE,
|
||||
accountId,
|
||||
});
|
||||
},
|
||||
},
|
||||
@@ -94,43 +99,111 @@ export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount> = {
|
||||
chunkMode: { type: "string", enum: ["length", "newline"] },
|
||||
mediaMaxMb: { type: "number", minimum: 0 },
|
||||
renderMode: { type: "string", enum: ["auto", "raw", "card"] },
|
||||
accounts: {
|
||||
type: "object",
|
||||
additionalProperties: {
|
||||
type: "object",
|
||||
properties: {
|
||||
enabled: { type: "boolean" },
|
||||
name: { type: "string" },
|
||||
appId: { type: "string" },
|
||||
appSecret: { type: "string" },
|
||||
encryptKey: { type: "string" },
|
||||
verificationToken: { type: "string" },
|
||||
domain: { type: "string", enum: ["feishu", "lark"] },
|
||||
connectionMode: { type: "string", enum: ["websocket", "webhook"] },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
config: {
|
||||
listAccountIds: () => [DEFAULT_ACCOUNT_ID],
|
||||
resolveAccount: (cfg) => resolveFeishuAccount({ cfg }),
|
||||
defaultAccountId: () => DEFAULT_ACCOUNT_ID,
|
||||
setAccountEnabled: ({ cfg, enabled }) => ({
|
||||
...cfg,
|
||||
channels: {
|
||||
...cfg.channels,
|
||||
feishu: {
|
||||
...cfg.channels?.feishu,
|
||||
enabled,
|
||||
},
|
||||
},
|
||||
}),
|
||||
deleteAccount: ({ cfg }) => {
|
||||
const next = { ...cfg } as ClawdbotConfig;
|
||||
const nextChannels = { ...cfg.channels };
|
||||
delete (nextChannels as Record<string, unknown>).feishu;
|
||||
if (Object.keys(nextChannels).length > 0) {
|
||||
next.channels = nextChannels;
|
||||
} else {
|
||||
delete next.channels;
|
||||
listAccountIds: (cfg) => listFeishuAccountIds(cfg),
|
||||
resolveAccount: (cfg, accountId) => resolveFeishuAccount({ cfg, accountId }),
|
||||
defaultAccountId: (cfg) => resolveDefaultFeishuAccountId(cfg),
|
||||
setAccountEnabled: ({ cfg, accountId, enabled }) => {
|
||||
const _account = resolveFeishuAccount({ cfg, accountId });
|
||||
const isDefault = accountId === DEFAULT_ACCOUNT_ID;
|
||||
|
||||
if (isDefault) {
|
||||
// For default account, set top-level enabled
|
||||
return {
|
||||
...cfg,
|
||||
channels: {
|
||||
...cfg.channels,
|
||||
feishu: {
|
||||
...cfg.channels?.feishu,
|
||||
enabled,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
return next;
|
||||
|
||||
// For named accounts, set enabled in accounts[accountId]
|
||||
const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined;
|
||||
return {
|
||||
...cfg,
|
||||
channels: {
|
||||
...cfg.channels,
|
||||
feishu: {
|
||||
...feishuCfg,
|
||||
accounts: {
|
||||
...feishuCfg?.accounts,
|
||||
[accountId]: {
|
||||
...feishuCfg?.accounts?.[accountId],
|
||||
enabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
isConfigured: (_account, cfg) =>
|
||||
Boolean(resolveFeishuCredentials(cfg.channels?.feishu as FeishuConfig | undefined)),
|
||||
deleteAccount: ({ cfg, accountId }) => {
|
||||
const isDefault = accountId === DEFAULT_ACCOUNT_ID;
|
||||
|
||||
if (isDefault) {
|
||||
// Delete entire feishu config
|
||||
const next = { ...cfg } as ClawdbotConfig;
|
||||
const nextChannels = { ...cfg.channels };
|
||||
delete (nextChannels as Record<string, unknown>).feishu;
|
||||
if (Object.keys(nextChannels).length > 0) {
|
||||
next.channels = nextChannels;
|
||||
} else {
|
||||
delete next.channels;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
// Delete specific account from accounts
|
||||
const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined;
|
||||
const accounts = { ...feishuCfg?.accounts };
|
||||
delete accounts[accountId];
|
||||
|
||||
return {
|
||||
...cfg,
|
||||
channels: {
|
||||
...cfg.channels,
|
||||
feishu: {
|
||||
...feishuCfg,
|
||||
accounts: Object.keys(accounts).length > 0 ? accounts : undefined,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
isConfigured: (account) => account.configured,
|
||||
describeAccount: (account) => ({
|
||||
accountId: account.accountId,
|
||||
enabled: account.enabled,
|
||||
configured: account.configured,
|
||||
name: account.name,
|
||||
appId: account.appId,
|
||||
domain: account.domain,
|
||||
}),
|
||||
resolveAllowFrom: ({ cfg }) =>
|
||||
(cfg.channels?.feishu as FeishuConfig | undefined)?.allowFrom ?? [],
|
||||
resolveAllowFrom: ({ cfg, accountId }) => {
|
||||
const account = resolveFeishuAccount({ cfg, accountId });
|
||||
return account.config?.allowFrom ?? [];
|
||||
},
|
||||
formatAllowFrom: ({ allowFrom }) =>
|
||||
allowFrom
|
||||
.map((entry) => String(entry).trim())
|
||||
@@ -138,8 +211,9 @@ export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount> = {
|
||||
.map((entry) => entry.toLowerCase()),
|
||||
},
|
||||
security: {
|
||||
collectWarnings: ({ cfg }) => {
|
||||
const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined;
|
||||
collectWarnings: ({ cfg, accountId }) => {
|
||||
const account = resolveFeishuAccount({ cfg, accountId });
|
||||
const feishuCfg = account.config;
|
||||
const defaultGroupPolicy = (
|
||||
cfg.channels as Record<string, { groupPolicy?: string }> | undefined
|
||||
)?.defaults?.groupPolicy;
|
||||
@@ -148,22 +222,46 @@ export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount> = {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
`- Feishu groups: groupPolicy="open" allows any member to trigger (mention-gated). Set channels.feishu.groupPolicy="allowlist" + channels.feishu.groupAllowFrom to restrict senders.`,
|
||||
`- Feishu[${account.accountId}] groups: groupPolicy="open" allows any member to trigger (mention-gated). Set channels.feishu.groupPolicy="allowlist" + channels.feishu.groupAllowFrom to restrict senders.`,
|
||||
];
|
||||
},
|
||||
},
|
||||
setup: {
|
||||
resolveAccountId: () => DEFAULT_ACCOUNT_ID,
|
||||
applyAccountConfig: ({ cfg }) => ({
|
||||
...cfg,
|
||||
channels: {
|
||||
...cfg.channels,
|
||||
feishu: {
|
||||
...cfg.channels?.feishu,
|
||||
enabled: true,
|
||||
applyAccountConfig: ({ cfg, accountId }) => {
|
||||
const isDefault = !accountId || accountId === DEFAULT_ACCOUNT_ID;
|
||||
|
||||
if (isDefault) {
|
||||
return {
|
||||
...cfg,
|
||||
channels: {
|
||||
...cfg.channels,
|
||||
feishu: {
|
||||
...cfg.channels?.feishu,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined;
|
||||
return {
|
||||
...cfg,
|
||||
channels: {
|
||||
...cfg.channels,
|
||||
feishu: {
|
||||
...feishuCfg,
|
||||
accounts: {
|
||||
...feishuCfg?.accounts,
|
||||
[accountId]: {
|
||||
...feishuCfg?.accounts?.[accountId],
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
};
|
||||
},
|
||||
},
|
||||
onboarding: feishuOnboardingAdapter,
|
||||
messaging: {
|
||||
@@ -175,12 +273,14 @@ export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount> = {
|
||||
},
|
||||
directory: {
|
||||
self: async () => null,
|
||||
listPeers: async ({ cfg, query, limit }) => listFeishuDirectoryPeers({ cfg, query, limit }),
|
||||
listGroups: async ({ cfg, query, limit }) => listFeishuDirectoryGroups({ cfg, query, limit }),
|
||||
listPeersLive: async ({ cfg, query, limit }) =>
|
||||
listFeishuDirectoryPeersLive({ cfg, query, limit }),
|
||||
listGroupsLive: async ({ cfg, query, limit }) =>
|
||||
listFeishuDirectoryGroupsLive({ cfg, query, limit }),
|
||||
listPeers: async ({ cfg, query, limit, accountId }) =>
|
||||
listFeishuDirectoryPeers({ cfg, query, limit, accountId }),
|
||||
listGroups: async ({ cfg, query, limit, accountId }) =>
|
||||
listFeishuDirectoryGroups({ cfg, query, limit, accountId }),
|
||||
listPeersLive: async ({ cfg, query, limit, accountId }) =>
|
||||
listFeishuDirectoryPeersLive({ cfg, query, limit, accountId }),
|
||||
listGroupsLive: async ({ cfg, query, limit, accountId }) =>
|
||||
listFeishuDirectoryGroupsLive({ cfg, query, limit, accountId }),
|
||||
},
|
||||
outbound: feishuOutbound,
|
||||
status: {
|
||||
@@ -202,12 +302,17 @@ export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount> = {
|
||||
probe: snapshot.probe,
|
||||
lastProbeAt: snapshot.lastProbeAt ?? null,
|
||||
}),
|
||||
probeAccount: async ({ cfg }) =>
|
||||
await probeFeishu(cfg.channels?.feishu as FeishuConfig | undefined),
|
||||
probeAccount: async ({ cfg, accountId }) => {
|
||||
const account = resolveFeishuAccount({ cfg, accountId });
|
||||
return await probeFeishu(account);
|
||||
},
|
||||
buildAccountSnapshot: ({ account, runtime, probe }) => ({
|
||||
accountId: account.accountId,
|
||||
enabled: account.enabled,
|
||||
configured: account.configured,
|
||||
name: account.name,
|
||||
appId: account.appId,
|
||||
domain: account.domain,
|
||||
running: runtime?.running ?? false,
|
||||
lastStartAt: runtime?.lastStartAt ?? null,
|
||||
lastStopAt: runtime?.lastStopAt ?? null,
|
||||
@@ -219,10 +324,12 @@ export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount> = {
|
||||
gateway: {
|
||||
startAccount: async (ctx) => {
|
||||
const { monitorFeishuProvider } = await import("./monitor.js");
|
||||
const feishuCfg = ctx.cfg.channels?.feishu as FeishuConfig | undefined;
|
||||
const port = feishuCfg?.webhookPort ?? null;
|
||||
const account = resolveFeishuAccount({ cfg: ctx.cfg, accountId: ctx.accountId });
|
||||
const port = account.config?.webhookPort ?? null;
|
||||
ctx.setStatus({ accountId: ctx.accountId, port });
|
||||
ctx.log?.info(`starting feishu provider (mode: ${feishuCfg?.connectionMode ?? "websocket"})`);
|
||||
ctx.log?.info(
|
||||
`starting feishu[${ctx.accountId}] (mode: ${account.config?.connectionMode ?? "websocket"})`,
|
||||
);
|
||||
return monitorFeishuProvider({
|
||||
config: ctx.cfg,
|
||||
runtime: ctx.runtime,
|
||||
|
||||
Reference in New Issue
Block a user