mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-23 16:01:17 +00:00
refactor: share computed channel status adapters
This commit is contained in:
@@ -104,8 +104,8 @@ const meta = {
|
||||
preferOver: ["imessage"],
|
||||
};
|
||||
|
||||
export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount> = createChatChannelPlugin(
|
||||
{
|
||||
export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount, BlueBubblesProbe> =
|
||||
createChatChannelPlugin<ResolvedBlueBubblesAccount, BlueBubblesProbe>({
|
||||
base: {
|
||||
id: "bluebubbles",
|
||||
meta,
|
||||
@@ -365,5 +365,4 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount> = crea
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -309,396 +309,397 @@ function resolveDiscordOutboundSessionRoute(params: {
|
||||
};
|
||||
}
|
||||
|
||||
export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount> = createChatChannelPlugin({
|
||||
base: {
|
||||
...createDiscordPluginBase({
|
||||
setup: discordSetupAdapter,
|
||||
}),
|
||||
allowlist: {
|
||||
...buildLegacyDmAccountAllowlistAdapter({
|
||||
channelId: "discord",
|
||||
resolveAccount: resolveDiscordAccount,
|
||||
normalize: ({ cfg, accountId, values }) =>
|
||||
discordConfigAdapter.formatAllowFrom!({ cfg, accountId, allowFrom: values }),
|
||||
resolveDmAllowFrom: (account) => account.config.allowFrom ?? account.config.dm?.allowFrom,
|
||||
resolveGroupPolicy: (account) => account.config.groupPolicy,
|
||||
resolveGroupOverrides: resolveDiscordAllowlistGroupOverrides,
|
||||
export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount, DiscordProbe> =
|
||||
createChatChannelPlugin<ResolvedDiscordAccount, DiscordProbe>({
|
||||
base: {
|
||||
...createDiscordPluginBase({
|
||||
setup: discordSetupAdapter,
|
||||
}),
|
||||
resolveNames: resolveDiscordAllowlistNames,
|
||||
},
|
||||
groups: {
|
||||
resolveRequireMention: resolveDiscordGroupRequireMention,
|
||||
resolveToolPolicy: resolveDiscordGroupToolPolicy,
|
||||
},
|
||||
mentions: {
|
||||
stripPatterns: () => ["<@!?\\d+>"],
|
||||
},
|
||||
agentPrompt: {
|
||||
messageToolHints: () => [
|
||||
"- Discord components: set `components` when sending messages to include buttons, selects, or v2 containers.",
|
||||
"- Forms: add `components.modal` (title, fields). OpenClaw adds a trigger button and routes submissions as new messages.",
|
||||
],
|
||||
},
|
||||
messaging: {
|
||||
normalizeTarget: normalizeDiscordMessagingTarget,
|
||||
resolveSessionTarget: ({ id }) => normalizeDiscordMessagingTarget(`channel:${id}`),
|
||||
parseExplicitTarget: ({ raw }) => parseDiscordExplicitTarget(raw),
|
||||
inferTargetChatType: ({ to }) => parseDiscordExplicitTarget(to)?.chatType,
|
||||
buildCrossContextComponents: buildDiscordCrossContextComponents,
|
||||
resolveOutboundSessionRoute: (params) => resolveDiscordOutboundSessionRoute(params),
|
||||
targetResolver: {
|
||||
looksLikeId: looksLikeDiscordTargetId,
|
||||
hint: "<channelId|user:ID|channel:ID>",
|
||||
},
|
||||
},
|
||||
execApprovals: {
|
||||
getInitiatingSurfaceState: ({ cfg, accountId }) =>
|
||||
isDiscordExecApprovalClientEnabled({ cfg, accountId })
|
||||
? { kind: "enabled" }
|
||||
: { kind: "disabled" },
|
||||
shouldSuppressLocalPrompt: ({ cfg, accountId, payload }) =>
|
||||
shouldSuppressLocalDiscordExecApprovalPrompt({
|
||||
cfg,
|
||||
accountId,
|
||||
payload,
|
||||
allowlist: {
|
||||
...buildLegacyDmAccountAllowlistAdapter({
|
||||
channelId: "discord",
|
||||
resolveAccount: resolveDiscordAccount,
|
||||
normalize: ({ cfg, accountId, values }) =>
|
||||
discordConfigAdapter.formatAllowFrom!({ cfg, accountId, allowFrom: values }),
|
||||
resolveDmAllowFrom: (account) => account.config.allowFrom ?? account.config.dm?.allowFrom,
|
||||
resolveGroupPolicy: (account) => account.config.groupPolicy,
|
||||
resolveGroupOverrides: resolveDiscordAllowlistGroupOverrides,
|
||||
}),
|
||||
resolveNames: resolveDiscordAllowlistNames,
|
||||
},
|
||||
groups: {
|
||||
resolveRequireMention: resolveDiscordGroupRequireMention,
|
||||
resolveToolPolicy: resolveDiscordGroupToolPolicy,
|
||||
},
|
||||
mentions: {
|
||||
stripPatterns: () => ["<@!?\\d+>"],
|
||||
},
|
||||
agentPrompt: {
|
||||
messageToolHints: () => [
|
||||
"- Discord components: set `components` when sending messages to include buttons, selects, or v2 containers.",
|
||||
"- Forms: add `components.modal` (title, fields). OpenClaw adds a trigger button and routes submissions as new messages.",
|
||||
],
|
||||
},
|
||||
messaging: {
|
||||
normalizeTarget: normalizeDiscordMessagingTarget,
|
||||
resolveSessionTarget: ({ id }) => normalizeDiscordMessagingTarget(`channel:${id}`),
|
||||
parseExplicitTarget: ({ raw }) => parseDiscordExplicitTarget(raw),
|
||||
inferTargetChatType: ({ to }) => parseDiscordExplicitTarget(to)?.chatType,
|
||||
buildCrossContextComponents: buildDiscordCrossContextComponents,
|
||||
resolveOutboundSessionRoute: (params) => resolveDiscordOutboundSessionRoute(params),
|
||||
targetResolver: {
|
||||
looksLikeId: looksLikeDiscordTargetId,
|
||||
hint: "<channelId|user:ID|channel:ID>",
|
||||
},
|
||||
},
|
||||
execApprovals: {
|
||||
getInitiatingSurfaceState: ({ cfg, accountId }) =>
|
||||
isDiscordExecApprovalClientEnabled({ cfg, accountId })
|
||||
? { kind: "enabled" }
|
||||
: { kind: "disabled" },
|
||||
shouldSuppressLocalPrompt: ({ cfg, accountId, payload }) =>
|
||||
shouldSuppressLocalDiscordExecApprovalPrompt({
|
||||
cfg,
|
||||
accountId,
|
||||
payload,
|
||||
}),
|
||||
hasConfiguredDmRoute: ({ cfg }) => hasDiscordExecApprovalDmRoute(cfg),
|
||||
shouldSuppressForwardingFallback: ({ cfg, target }) =>
|
||||
(normalizeMessageChannel(target.channel) ?? target.channel) === "discord" &&
|
||||
isDiscordExecApprovalClientEnabled({ cfg, accountId: target.accountId }),
|
||||
},
|
||||
directory: createChannelDirectoryAdapter({
|
||||
listPeers: async (params) => listDiscordDirectoryPeersFromConfig(params),
|
||||
listGroups: async (params) => listDiscordDirectoryGroupsFromConfig(params),
|
||||
...createRuntimeDirectoryLiveAdapter({
|
||||
getRuntime: () => getDiscordRuntime().channel.discord,
|
||||
listPeersLive: (runtime) => runtime.listDirectoryPeersLive,
|
||||
listGroupsLive: (runtime) => runtime.listDirectoryGroupsLive,
|
||||
}),
|
||||
hasConfiguredDmRoute: ({ cfg }) => hasDiscordExecApprovalDmRoute(cfg),
|
||||
shouldSuppressForwardingFallback: ({ cfg, target }) =>
|
||||
(normalizeMessageChannel(target.channel) ?? target.channel) === "discord" &&
|
||||
isDiscordExecApprovalClientEnabled({ cfg, accountId: target.accountId }),
|
||||
},
|
||||
directory: createChannelDirectoryAdapter({
|
||||
listPeers: async (params) => listDiscordDirectoryPeersFromConfig(params),
|
||||
listGroups: async (params) => listDiscordDirectoryGroupsFromConfig(params),
|
||||
...createRuntimeDirectoryLiveAdapter({
|
||||
getRuntime: () => getDiscordRuntime().channel.discord,
|
||||
listPeersLive: (runtime) => runtime.listDirectoryPeersLive,
|
||||
listGroupsLive: (runtime) => runtime.listDirectoryGroupsLive,
|
||||
}),
|
||||
}),
|
||||
resolver: {
|
||||
resolveTargets: async ({ cfg, accountId, inputs, kind }) => {
|
||||
const account = resolveDiscordAccount({ cfg, accountId });
|
||||
if (kind === "group") {
|
||||
resolver: {
|
||||
resolveTargets: async ({ cfg, accountId, inputs, kind }) => {
|
||||
const account = resolveDiscordAccount({ cfg, accountId });
|
||||
if (kind === "group") {
|
||||
return resolveTargetsWithOptionalToken({
|
||||
token: account.token,
|
||||
inputs,
|
||||
missingTokenNote: "missing Discord token",
|
||||
resolveWithToken: ({ token, inputs }) =>
|
||||
getDiscordRuntime().channel.discord.resolveChannelAllowlist({
|
||||
token,
|
||||
entries: inputs,
|
||||
}),
|
||||
mapResolved: (entry) => ({
|
||||
input: entry.input,
|
||||
resolved: entry.resolved,
|
||||
id: entry.channelId ?? entry.guildId,
|
||||
name:
|
||||
entry.channelName ??
|
||||
entry.guildName ??
|
||||
(entry.guildId && !entry.channelId ? entry.guildId : undefined),
|
||||
note: entry.note,
|
||||
}),
|
||||
});
|
||||
}
|
||||
return resolveTargetsWithOptionalToken({
|
||||
token: account.token,
|
||||
inputs,
|
||||
missingTokenNote: "missing Discord token",
|
||||
resolveWithToken: ({ token, inputs }) =>
|
||||
getDiscordRuntime().channel.discord.resolveChannelAllowlist({
|
||||
getDiscordRuntime().channel.discord.resolveUserAllowlist({
|
||||
token,
|
||||
entries: inputs,
|
||||
}),
|
||||
mapResolved: (entry) => ({
|
||||
input: entry.input,
|
||||
resolved: entry.resolved,
|
||||
id: entry.channelId ?? entry.guildId,
|
||||
name:
|
||||
entry.channelName ??
|
||||
entry.guildName ??
|
||||
(entry.guildId && !entry.channelId ? entry.guildId : undefined),
|
||||
id: entry.id,
|
||||
name: entry.name,
|
||||
note: entry.note,
|
||||
}),
|
||||
});
|
||||
}
|
||||
return resolveTargetsWithOptionalToken({
|
||||
token: account.token,
|
||||
inputs,
|
||||
missingTokenNote: "missing Discord token",
|
||||
resolveWithToken: ({ token, inputs }) =>
|
||||
getDiscordRuntime().channel.discord.resolveUserAllowlist({
|
||||
token,
|
||||
entries: inputs,
|
||||
}),
|
||||
mapResolved: (entry) => ({
|
||||
input: entry.input,
|
||||
resolved: entry.resolved,
|
||||
id: entry.id,
|
||||
name: entry.name,
|
||||
note: entry.note,
|
||||
},
|
||||
},
|
||||
actions: discordMessageActions,
|
||||
bindings: {
|
||||
compileConfiguredBinding: ({ conversationId }) =>
|
||||
normalizeDiscordAcpConversationId(conversationId),
|
||||
matchInboundConversation: ({ compiledBinding, conversationId, parentConversationId }) =>
|
||||
matchDiscordAcpConversation({
|
||||
bindingConversationId: compiledBinding.conversationId,
|
||||
conversationId,
|
||||
parentConversationId,
|
||||
}),
|
||||
});
|
||||
},
|
||||
},
|
||||
actions: discordMessageActions,
|
||||
bindings: {
|
||||
compileConfiguredBinding: ({ conversationId }) =>
|
||||
normalizeDiscordAcpConversationId(conversationId),
|
||||
matchInboundConversation: ({ compiledBinding, conversationId, parentConversationId }) =>
|
||||
matchDiscordAcpConversation({
|
||||
bindingConversationId: compiledBinding.conversationId,
|
||||
conversationId,
|
||||
parentConversationId,
|
||||
status: createComputedAccountStatusAdapter<ResolvedDiscordAccount, DiscordProbe, unknown>({
|
||||
defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID, {
|
||||
connected: false,
|
||||
reconnectAttempts: 0,
|
||||
lastConnectedAt: null,
|
||||
lastDisconnect: null,
|
||||
lastEventAt: null,
|
||||
}),
|
||||
},
|
||||
status: createComputedAccountStatusAdapter<ResolvedDiscordAccount, DiscordProbe, unknown>({
|
||||
defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID, {
|
||||
connected: false,
|
||||
reconnectAttempts: 0,
|
||||
lastConnectedAt: null,
|
||||
lastDisconnect: null,
|
||||
lastEventAt: null,
|
||||
}),
|
||||
collectStatusIssues: collectDiscordStatusIssues,
|
||||
buildChannelSummary: ({ snapshot }) =>
|
||||
buildTokenChannelStatusSummary(snapshot, { includeMode: false }),
|
||||
probeAccount: async ({ account, timeoutMs }) =>
|
||||
probeDiscord(account.token, timeoutMs, {
|
||||
includeApplication: true,
|
||||
}),
|
||||
formatCapabilitiesProbe: ({ probe }) => {
|
||||
const discordProbe = probe as DiscordProbe | undefined;
|
||||
const lines = [];
|
||||
if (discordProbe?.bot?.username) {
|
||||
const botId = discordProbe.bot.id ? ` (${discordProbe.bot.id})` : "";
|
||||
lines.push({ text: `Bot: @${discordProbe.bot.username}${botId}` });
|
||||
}
|
||||
if (discordProbe?.application?.intents) {
|
||||
lines.push({
|
||||
text: `Intents: ${formatDiscordIntents(discordProbe.application.intents)}`,
|
||||
});
|
||||
}
|
||||
return lines;
|
||||
},
|
||||
buildCapabilitiesDiagnostics: async ({ account, timeoutMs, target }) => {
|
||||
if (!target?.trim()) {
|
||||
return undefined;
|
||||
}
|
||||
const parsedTarget = parseDiscordTarget(target.trim(), { defaultKind: "channel" });
|
||||
const details: Record<string, unknown> = {
|
||||
target: {
|
||||
raw: target,
|
||||
normalized: parsedTarget?.normalized,
|
||||
kind: parsedTarget?.kind,
|
||||
channelId: parsedTarget?.kind === "channel" ? parsedTarget.id : undefined,
|
||||
},
|
||||
};
|
||||
if (!parsedTarget || parsedTarget.kind !== "channel") {
|
||||
return {
|
||||
details,
|
||||
lines: [
|
||||
{
|
||||
text: "Permissions: Target looks like a DM user; pass channel:<id> to audit channel permissions.",
|
||||
tone: "error",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
const token = account.token?.trim();
|
||||
if (!token) {
|
||||
return {
|
||||
details,
|
||||
lines: [
|
||||
{
|
||||
text: "Permissions: Discord bot token missing for permission audit.",
|
||||
tone: "error",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
try {
|
||||
const perms = await fetchChannelPermissionsDiscord(parsedTarget.id, {
|
||||
token,
|
||||
accountId: account.accountId ?? undefined,
|
||||
});
|
||||
const missingRequired = REQUIRED_DISCORD_PERMISSIONS.filter(
|
||||
(permission) => !perms.permissions.includes(permission),
|
||||
);
|
||||
details.permissions = {
|
||||
channelId: perms.channelId,
|
||||
guildId: perms.guildId,
|
||||
isDm: perms.isDm,
|
||||
channelType: perms.channelType,
|
||||
permissions: perms.permissions,
|
||||
missingRequired,
|
||||
raw: perms.raw,
|
||||
};
|
||||
return {
|
||||
details,
|
||||
lines: [
|
||||
{
|
||||
text: `Permissions (${perms.channelId}): ${perms.permissions.length ? perms.permissions.join(", ") : "none"}`,
|
||||
},
|
||||
missingRequired.length > 0
|
||||
? { text: `Missing required: ${missingRequired.join(", ")}`, tone: "warn" }
|
||||
: { text: "Missing required: none", tone: "success" },
|
||||
],
|
||||
};
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
details.permissions = { channelId: parsedTarget.id, error: message };
|
||||
return {
|
||||
details,
|
||||
lines: [{ text: `Permissions: ${message}`, tone: "error" }],
|
||||
};
|
||||
}
|
||||
},
|
||||
auditAccount: async ({ account, timeoutMs, cfg }) => {
|
||||
const { channelIds, unresolvedChannels } = collectDiscordAuditChannelIds({
|
||||
cfg,
|
||||
accountId: account.accountId,
|
||||
});
|
||||
if (!channelIds.length && unresolvedChannels === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const botToken = account.token?.trim();
|
||||
if (!botToken) {
|
||||
return {
|
||||
ok: unresolvedChannels === 0,
|
||||
checkedChannels: 0,
|
||||
unresolvedChannels,
|
||||
channels: [],
|
||||
elapsedMs: 0,
|
||||
};
|
||||
}
|
||||
const audit = await auditDiscordChannelPermissions({
|
||||
token: botToken,
|
||||
accountId: account.accountId,
|
||||
channelIds,
|
||||
timeoutMs,
|
||||
});
|
||||
return { ...audit, unresolvedChannels };
|
||||
},
|
||||
resolveAccountSnapshot: ({ account, runtime, probe, audit }) => {
|
||||
const configured =
|
||||
resolveConfiguredFromCredentialStatuses(account) ?? Boolean(account.token?.trim());
|
||||
const app = runtime?.application ?? (probe as { application?: unknown })?.application;
|
||||
const bot = runtime?.bot ?? (probe as { bot?: unknown })?.bot;
|
||||
return {
|
||||
accountId: account.accountId,
|
||||
name: account.name,
|
||||
enabled: account.enabled,
|
||||
configured,
|
||||
extra: {
|
||||
...projectCredentialSnapshotFields(account),
|
||||
connected: runtime?.connected ?? false,
|
||||
reconnectAttempts: runtime?.reconnectAttempts,
|
||||
lastConnectedAt: runtime?.lastConnectedAt ?? null,
|
||||
lastDisconnect: runtime?.lastDisconnect ?? null,
|
||||
lastEventAt: runtime?.lastEventAt ?? null,
|
||||
application: app ?? undefined,
|
||||
bot: bot ?? undefined,
|
||||
audit,
|
||||
},
|
||||
};
|
||||
},
|
||||
}),
|
||||
gateway: {
|
||||
startAccount: async (ctx) => {
|
||||
const account = ctx.account;
|
||||
const token = account.token.trim();
|
||||
let discordBotLabel = "";
|
||||
try {
|
||||
const probe = await probeDiscord(token, 2500, {
|
||||
collectStatusIssues: collectDiscordStatusIssues,
|
||||
buildChannelSummary: ({ snapshot }) =>
|
||||
buildTokenChannelStatusSummary(snapshot, { includeMode: false }),
|
||||
probeAccount: async ({ account, timeoutMs }) =>
|
||||
probeDiscord(account.token, timeoutMs, {
|
||||
includeApplication: true,
|
||||
});
|
||||
const username = probe.ok ? probe.bot?.username?.trim() : null;
|
||||
if (username) {
|
||||
discordBotLabel = ` (@${username})`;
|
||||
}),
|
||||
formatCapabilitiesProbe: ({ probe }) => {
|
||||
const discordProbe = probe as DiscordProbe | undefined;
|
||||
const lines = [];
|
||||
if (discordProbe?.bot?.username) {
|
||||
const botId = discordProbe.bot.id ? ` (${discordProbe.bot.id})` : "";
|
||||
lines.push({ text: `Bot: @${discordProbe.bot.username}${botId}` });
|
||||
}
|
||||
ctx.setStatus({
|
||||
if (discordProbe?.application?.intents) {
|
||||
lines.push({
|
||||
text: `Intents: ${formatDiscordIntents(discordProbe.application.intents)}`,
|
||||
});
|
||||
}
|
||||
return lines;
|
||||
},
|
||||
buildCapabilitiesDiagnostics: async ({ account, timeoutMs, target }) => {
|
||||
if (!target?.trim()) {
|
||||
return undefined;
|
||||
}
|
||||
const parsedTarget = parseDiscordTarget(target.trim(), { defaultKind: "channel" });
|
||||
const details: Record<string, unknown> = {
|
||||
target: {
|
||||
raw: target,
|
||||
normalized: parsedTarget?.normalized,
|
||||
kind: parsedTarget?.kind,
|
||||
channelId: parsedTarget?.kind === "channel" ? parsedTarget.id : undefined,
|
||||
},
|
||||
};
|
||||
if (!parsedTarget || parsedTarget.kind !== "channel") {
|
||||
return {
|
||||
details,
|
||||
lines: [
|
||||
{
|
||||
text: "Permissions: Target looks like a DM user; pass channel:<id> to audit channel permissions.",
|
||||
tone: "error",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
const token = account.token?.trim();
|
||||
if (!token) {
|
||||
return {
|
||||
details,
|
||||
lines: [
|
||||
{
|
||||
text: "Permissions: Discord bot token missing for permission audit.",
|
||||
tone: "error",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
try {
|
||||
const perms = await fetchChannelPermissionsDiscord(parsedTarget.id, {
|
||||
token,
|
||||
accountId: account.accountId ?? undefined,
|
||||
});
|
||||
const missingRequired = REQUIRED_DISCORD_PERMISSIONS.filter(
|
||||
(permission) => !perms.permissions.includes(permission),
|
||||
);
|
||||
details.permissions = {
|
||||
channelId: perms.channelId,
|
||||
guildId: perms.guildId,
|
||||
isDm: perms.isDm,
|
||||
channelType: perms.channelType,
|
||||
permissions: perms.permissions,
|
||||
missingRequired,
|
||||
raw: perms.raw,
|
||||
};
|
||||
return {
|
||||
details,
|
||||
lines: [
|
||||
{
|
||||
text: `Permissions (${perms.channelId}): ${perms.permissions.length ? perms.permissions.join(", ") : "none"}`,
|
||||
},
|
||||
missingRequired.length > 0
|
||||
? { text: `Missing required: ${missingRequired.join(", ")}`, tone: "warn" }
|
||||
: { text: "Missing required: none", tone: "success" },
|
||||
],
|
||||
};
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
details.permissions = { channelId: parsedTarget.id, error: message };
|
||||
return {
|
||||
details,
|
||||
lines: [{ text: `Permissions: ${message}`, tone: "error" }],
|
||||
};
|
||||
}
|
||||
},
|
||||
auditAccount: async ({ account, timeoutMs, cfg }) => {
|
||||
const { channelIds, unresolvedChannels } = collectDiscordAuditChannelIds({
|
||||
cfg,
|
||||
accountId: account.accountId,
|
||||
bot: probe.bot,
|
||||
application: probe.application,
|
||||
});
|
||||
const messageContent = probe.application?.intents?.messageContent;
|
||||
if (messageContent === "disabled") {
|
||||
ctx.log?.warn(
|
||||
`[${account.accountId}] Discord Message Content Intent is disabled; bot may not respond to channel messages. Enable it in Discord Dev Portal (Bot → Privileged Gateway Intents) or require mentions.`,
|
||||
);
|
||||
} else if (messageContent === "limited") {
|
||||
ctx.log?.info(
|
||||
`[${account.accountId}] Discord Message Content Intent is limited; bots under 100 servers can use it without verification.`,
|
||||
);
|
||||
if (!channelIds.length && unresolvedChannels === 0) {
|
||||
return undefined;
|
||||
}
|
||||
} catch (err) {
|
||||
if (getDiscordRuntime().logging.shouldLogVerbose()) {
|
||||
ctx.log?.debug?.(`[${account.accountId}] bot probe failed: ${String(err)}`);
|
||||
const botToken = account.token?.trim();
|
||||
if (!botToken) {
|
||||
return {
|
||||
ok: unresolvedChannels === 0,
|
||||
checkedChannels: 0,
|
||||
unresolvedChannels,
|
||||
channels: [],
|
||||
elapsedMs: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
ctx.log?.info(`[${account.accountId}] starting provider${discordBotLabel}`);
|
||||
return (await loadDiscordProviderRuntime()).monitorDiscordProvider({
|
||||
token,
|
||||
accountId: account.accountId,
|
||||
config: ctx.cfg,
|
||||
runtime: ctx.runtime,
|
||||
abortSignal: ctx.abortSignal,
|
||||
mediaMaxMb: account.config.mediaMaxMb,
|
||||
historyLimit: account.config.historyLimit,
|
||||
setStatus: (patch) => ctx.setStatus({ accountId: account.accountId, ...patch }),
|
||||
});
|
||||
const audit = await auditDiscordChannelPermissions({
|
||||
token: botToken,
|
||||
accountId: account.accountId,
|
||||
channelIds,
|
||||
timeoutMs,
|
||||
});
|
||||
return { ...audit, unresolvedChannels };
|
||||
},
|
||||
resolveAccountSnapshot: ({ account, runtime, probe, audit }) => {
|
||||
const configured =
|
||||
resolveConfiguredFromCredentialStatuses(account) ?? Boolean(account.token?.trim());
|
||||
const app = runtime?.application ?? (probe as { application?: unknown })?.application;
|
||||
const bot = runtime?.bot ?? (probe as { bot?: unknown })?.bot;
|
||||
return {
|
||||
accountId: account.accountId,
|
||||
name: account.name,
|
||||
enabled: account.enabled,
|
||||
configured,
|
||||
extra: {
|
||||
...projectCredentialSnapshotFields(account),
|
||||
connected: runtime?.connected ?? false,
|
||||
reconnectAttempts: runtime?.reconnectAttempts,
|
||||
lastConnectedAt: runtime?.lastConnectedAt ?? null,
|
||||
lastDisconnect: runtime?.lastDisconnect ?? null,
|
||||
lastEventAt: runtime?.lastEventAt ?? null,
|
||||
application: app ?? undefined,
|
||||
bot: bot ?? undefined,
|
||||
audit,
|
||||
},
|
||||
};
|
||||
},
|
||||
}),
|
||||
gateway: {
|
||||
startAccount: async (ctx) => {
|
||||
const account = ctx.account;
|
||||
const token = account.token.trim();
|
||||
let discordBotLabel = "";
|
||||
try {
|
||||
const probe = await probeDiscord(token, 2500, {
|
||||
includeApplication: true,
|
||||
});
|
||||
const username = probe.ok ? probe.bot?.username?.trim() : null;
|
||||
if (username) {
|
||||
discordBotLabel = ` (@${username})`;
|
||||
}
|
||||
ctx.setStatus({
|
||||
accountId: account.accountId,
|
||||
bot: probe.bot,
|
||||
application: probe.application,
|
||||
});
|
||||
const messageContent = probe.application?.intents?.messageContent;
|
||||
if (messageContent === "disabled") {
|
||||
ctx.log?.warn(
|
||||
`[${account.accountId}] Discord Message Content Intent is disabled; bot may not respond to channel messages. Enable it in Discord Dev Portal (Bot → Privileged Gateway Intents) or require mentions.`,
|
||||
);
|
||||
} else if (messageContent === "limited") {
|
||||
ctx.log?.info(
|
||||
`[${account.accountId}] Discord Message Content Intent is limited; bots under 100 servers can use it without verification.`,
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
if (getDiscordRuntime().logging.shouldLogVerbose()) {
|
||||
ctx.log?.debug?.(`[${account.accountId}] bot probe failed: ${String(err)}`);
|
||||
}
|
||||
}
|
||||
ctx.log?.info(`[${account.accountId}] starting provider${discordBotLabel}`);
|
||||
return (await loadDiscordProviderRuntime()).monitorDiscordProvider({
|
||||
token,
|
||||
accountId: account.accountId,
|
||||
config: ctx.cfg,
|
||||
runtime: ctx.runtime,
|
||||
abortSignal: ctx.abortSignal,
|
||||
mediaMaxMb: account.config.mediaMaxMb,
|
||||
historyLimit: account.config.historyLimit,
|
||||
setStatus: (patch) => ctx.setStatus({ accountId: account.accountId, ...patch }),
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
pairing: {
|
||||
text: {
|
||||
idLabel: "discordUserId",
|
||||
message: PAIRING_APPROVED_MESSAGE,
|
||||
normalizeAllowEntry: createPairingPrefixStripper(/^(discord|user):/i),
|
||||
notify: async ({ id, message }) => {
|
||||
await getDiscordRuntime().channel.discord.sendMessageDiscord(`user:${id}`, message);
|
||||
pairing: {
|
||||
text: {
|
||||
idLabel: "discordUserId",
|
||||
message: PAIRING_APPROVED_MESSAGE,
|
||||
normalizeAllowEntry: createPairingPrefixStripper(/^(discord|user):/i),
|
||||
notify: async ({ id, message }) => {
|
||||
await getDiscordRuntime().channel.discord.sendMessageDiscord(`user:${id}`, message);
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
security: {
|
||||
resolveDmPolicy: resolveDiscordDmPolicy,
|
||||
collectWarnings: collectDiscordSecurityWarnings,
|
||||
},
|
||||
threading: {
|
||||
topLevelReplyToMode: "discord",
|
||||
},
|
||||
outbound: {
|
||||
base: {
|
||||
deliveryMode: "direct",
|
||||
chunker: null,
|
||||
textChunkLimit: 2000,
|
||||
pollMaxOptions: 10,
|
||||
resolveTarget: ({ to }) => normalizeDiscordOutboundTarget(to),
|
||||
security: {
|
||||
resolveDmPolicy: resolveDiscordDmPolicy,
|
||||
collectWarnings: collectDiscordSecurityWarnings,
|
||||
},
|
||||
attachedResults: {
|
||||
channel: "discord",
|
||||
sendText: async ({ cfg, to, text, accountId, deps, replyToId, silent }) => {
|
||||
const send =
|
||||
resolveOutboundSendDep<DiscordSendFn>(deps, "discord") ??
|
||||
getDiscordRuntime().channel.discord.sendMessageDiscord;
|
||||
return await send(to, text, {
|
||||
verbose: false,
|
||||
cfg,
|
||||
replyTo: replyToId ?? undefined,
|
||||
accountId: accountId ?? undefined,
|
||||
silent: silent ?? undefined,
|
||||
});
|
||||
},
|
||||
sendMedia: async ({
|
||||
cfg,
|
||||
to,
|
||||
text,
|
||||
mediaUrl,
|
||||
mediaLocalRoots,
|
||||
accountId,
|
||||
deps,
|
||||
replyToId,
|
||||
silent,
|
||||
}) => {
|
||||
const send =
|
||||
resolveOutboundSendDep<DiscordSendFn>(deps, "discord") ??
|
||||
getDiscordRuntime().channel.discord.sendMessageDiscord;
|
||||
return await send(to, text, {
|
||||
verbose: false,
|
||||
threading: {
|
||||
topLevelReplyToMode: "discord",
|
||||
},
|
||||
outbound: {
|
||||
base: {
|
||||
deliveryMode: "direct",
|
||||
chunker: null,
|
||||
textChunkLimit: 2000,
|
||||
pollMaxOptions: 10,
|
||||
resolveTarget: ({ to }) => normalizeDiscordOutboundTarget(to),
|
||||
},
|
||||
attachedResults: {
|
||||
channel: "discord",
|
||||
sendText: async ({ cfg, to, text, accountId, deps, replyToId, silent }) => {
|
||||
const send =
|
||||
resolveOutboundSendDep<DiscordSendFn>(deps, "discord") ??
|
||||
getDiscordRuntime().channel.discord.sendMessageDiscord;
|
||||
return await send(to, text, {
|
||||
verbose: false,
|
||||
cfg,
|
||||
replyTo: replyToId ?? undefined,
|
||||
accountId: accountId ?? undefined,
|
||||
silent: silent ?? undefined,
|
||||
});
|
||||
},
|
||||
sendMedia: async ({
|
||||
cfg,
|
||||
to,
|
||||
text,
|
||||
mediaUrl,
|
||||
mediaLocalRoots,
|
||||
replyTo: replyToId ?? undefined,
|
||||
accountId: accountId ?? undefined,
|
||||
silent: silent ?? undefined,
|
||||
});
|
||||
accountId,
|
||||
deps,
|
||||
replyToId,
|
||||
silent,
|
||||
}) => {
|
||||
const send =
|
||||
resolveOutboundSendDep<DiscordSendFn>(deps, "discord") ??
|
||||
getDiscordRuntime().channel.discord.sendMessageDiscord;
|
||||
return await send(to, text, {
|
||||
verbose: false,
|
||||
cfg,
|
||||
mediaUrl,
|
||||
mediaLocalRoots,
|
||||
replyTo: replyToId ?? undefined,
|
||||
accountId: accountId ?? undefined,
|
||||
silent: silent ?? undefined,
|
||||
});
|
||||
},
|
||||
sendPoll: async ({ cfg, to, poll, accountId, silent }) =>
|
||||
await getDiscordRuntime().channel.discord.sendPollDiscord(to, poll, {
|
||||
cfg,
|
||||
accountId: accountId ?? undefined,
|
||||
silent: silent ?? undefined,
|
||||
}),
|
||||
},
|
||||
sendPoll: async ({ cfg, to, poll, accountId, silent }) =>
|
||||
await getDiscordRuntime().channel.discord.sendPollDiscord(to, poll, {
|
||||
cfg,
|
||||
accountId: accountId ?? undefined,
|
||||
silent: silent ?? undefined,
|
||||
}),
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,9 +4,11 @@ import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/exte
|
||||
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
||||
import { resolveOutboundSendDep } from "openclaw/plugin-sdk/outbound-runtime";
|
||||
import { buildOutboundBaseSessionKey, type RoutePeer } from "openclaw/plugin-sdk/routing";
|
||||
import { createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
|
||||
import {
|
||||
buildComputedAccountStatusSnapshot,
|
||||
createComputedAccountStatusAdapter,
|
||||
createDefaultChannelRuntimeState,
|
||||
} from "openclaw/plugin-sdk/status-helpers";
|
||||
import {
|
||||
collectStatusIssuesFromLastError,
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
formatTrimmedAllowFromEntries,
|
||||
@@ -107,7 +109,7 @@ function resolveIMessageOutboundSessionRoute(params: {
|
||||
}
|
||||
|
||||
export const imessagePlugin: ChannelPlugin<ResolvedIMessageAccount, IMessageProbe> =
|
||||
createChatChannelPlugin({
|
||||
createChatChannelPlugin<ResolvedIMessageAccount, IMessageProbe>({
|
||||
base: {
|
||||
...createIMessagePluginBase({
|
||||
setupWizard: imessageSetupWizard,
|
||||
@@ -150,7 +152,7 @@ export const imessagePlugin: ChannelPlugin<ResolvedIMessageAccount, IMessageProb
|
||||
},
|
||||
},
|
||||
},
|
||||
status: {
|
||||
status: createComputedAccountStatusAdapter<ResolvedIMessageAccount, IMessageProbe>({
|
||||
defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID, {
|
||||
cliPath: null,
|
||||
dbPath: null,
|
||||
@@ -163,23 +165,18 @@ export const imessagePlugin: ChannelPlugin<ResolvedIMessageAccount, IMessageProb
|
||||
}),
|
||||
probeAccount: async ({ timeoutMs }) =>
|
||||
await (await loadIMessageChannelRuntime()).probeIMessageAccount(timeoutMs),
|
||||
buildAccountSnapshot: ({ account, runtime, probe }) =>
|
||||
buildComputedAccountStatusSnapshot(
|
||||
{
|
||||
accountId: account.accountId,
|
||||
name: account.name,
|
||||
enabled: account.enabled,
|
||||
configured: account.configured,
|
||||
runtime,
|
||||
probe,
|
||||
},
|
||||
{
|
||||
cliPath: runtime?.cliPath ?? account.config.cliPath ?? null,
|
||||
dbPath: runtime?.dbPath ?? account.config.dbPath ?? null,
|
||||
},
|
||||
),
|
||||
resolveAccountSnapshot: ({ account, runtime }) => ({
|
||||
accountId: account.accountId,
|
||||
name: account.name,
|
||||
enabled: account.enabled,
|
||||
configured: account.configured,
|
||||
extra: {
|
||||
cliPath: runtime?.cliPath ?? account.config.cliPath ?? null,
|
||||
dbPath: runtime?.dbPath ?? account.config.dbPath ?? null,
|
||||
},
|
||||
}),
|
||||
resolveAccountState: ({ enabled }) => (enabled ? "enabled" : "disabled"),
|
||||
},
|
||||
}),
|
||||
gateway: {
|
||||
startAccount: async (ctx) =>
|
||||
await (await loadIMessageChannelRuntime()).startIMessageGatewayAccount(ctx),
|
||||
|
||||
@@ -22,7 +22,10 @@ import {
|
||||
import { buildTrafficStatusSummary } from "openclaw/plugin-sdk/extension-shared";
|
||||
import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
|
||||
import { createRuntimeOutboundDelegates } from "openclaw/plugin-sdk/outbound-runtime";
|
||||
import { createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
|
||||
import {
|
||||
createComputedAccountStatusAdapter,
|
||||
createDefaultChannelRuntimeState,
|
||||
} from "openclaw/plugin-sdk/status-helpers";
|
||||
import { matrixMessageActions } from "./actions.js";
|
||||
import { MatrixConfigSchema } from "./config-schema.js";
|
||||
import {
|
||||
@@ -44,7 +47,6 @@ import {
|
||||
resolveMatrixTargetIdentity,
|
||||
} from "./matrix/target-ids.js";
|
||||
import {
|
||||
buildComputedAccountStatusSnapshot,
|
||||
buildChannelConfigSchema,
|
||||
buildProbeChannelStatusSummary,
|
||||
collectStatusIssuesFromLastError,
|
||||
@@ -196,7 +198,7 @@ function matchMatrixAcpConversation(params: {
|
||||
}
|
||||
|
||||
export const matrixPlugin: ChannelPlugin<ResolvedMatrixAccount, MatrixProbe> =
|
||||
createChatChannelPlugin({
|
||||
createChatChannelPlugin<ResolvedMatrixAccount, MatrixProbe>({
|
||||
base: {
|
||||
id: "matrix",
|
||||
meta,
|
||||
@@ -319,7 +321,7 @@ export const matrixPlugin: ChannelPlugin<ResolvedMatrixAccount, MatrixProbe> =
|
||||
parentConversationId,
|
||||
}),
|
||||
},
|
||||
status: {
|
||||
status: createComputedAccountStatusAdapter<ResolvedMatrixAccount, MatrixProbe>({
|
||||
defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID),
|
||||
collectStatusIssues: (accounts) => collectStatusIssuesFromLastError("matrix", accounts),
|
||||
buildChannelSummary: ({ snapshot }) =>
|
||||
@@ -348,23 +350,18 @@ export const matrixPlugin: ChannelPlugin<ResolvedMatrixAccount, MatrixProbe> =
|
||||
};
|
||||
}
|
||||
},
|
||||
buildAccountSnapshot: ({ account, runtime, probe }) =>
|
||||
buildComputedAccountStatusSnapshot(
|
||||
{
|
||||
accountId: account.accountId,
|
||||
name: account.name,
|
||||
enabled: account.enabled,
|
||||
configured: account.configured,
|
||||
runtime,
|
||||
probe,
|
||||
},
|
||||
{
|
||||
baseUrl: account.homeserver,
|
||||
lastProbeAt: runtime?.lastProbeAt ?? null,
|
||||
...buildTrafficStatusSummary(runtime),
|
||||
},
|
||||
),
|
||||
},
|
||||
resolveAccountSnapshot: ({ account, runtime }) => ({
|
||||
accountId: account.accountId,
|
||||
name: account.name,
|
||||
enabled: account.enabled,
|
||||
configured: account.configured,
|
||||
extra: {
|
||||
baseUrl: account.homeserver,
|
||||
lastProbeAt: runtime?.lastProbeAt ?? null,
|
||||
...buildTrafficStatusSummary(runtime),
|
||||
},
|
||||
}),
|
||||
}),
|
||||
gateway: {
|
||||
startAccount: async (ctx) => {
|
||||
const account = ctx.account;
|
||||
|
||||
@@ -13,11 +13,13 @@ import {
|
||||
import { createAllowlistProviderRouteAllowlistWarningCollector } from "openclaw/plugin-sdk/channel-policy";
|
||||
import { createChatChannelPlugin } from "openclaw/plugin-sdk/core";
|
||||
import { runStoppablePassiveMonitor } from "openclaw/plugin-sdk/extension-shared";
|
||||
import { createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
|
||||
import {
|
||||
createComputedAccountStatusAdapter,
|
||||
createDefaultChannelRuntimeState,
|
||||
} from "openclaw/plugin-sdk/status-helpers";
|
||||
import {
|
||||
buildBaseChannelStatusSummary,
|
||||
buildChannelConfigSchema,
|
||||
buildRuntimeAccountStatusSnapshot,
|
||||
clearAccountEntryFields,
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
type ChannelPlugin,
|
||||
@@ -168,31 +170,25 @@ export const nextcloudTalkPlugin: ChannelPlugin<ResolvedNextcloudTalkAccount> =
|
||||
},
|
||||
},
|
||||
setup: nextcloudTalkSetupAdapter,
|
||||
status: {
|
||||
status: createComputedAccountStatusAdapter<ResolvedNextcloudTalkAccount>({
|
||||
defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID),
|
||||
buildChannelSummary: ({ snapshot }) =>
|
||||
buildBaseChannelStatusSummary(snapshot, {
|
||||
secretSource: snapshot.secretSource ?? "none",
|
||||
mode: "webhook",
|
||||
}),
|
||||
buildAccountSnapshot: ({ account, runtime }) => {
|
||||
const configured = Boolean(account.secret?.trim() && account.baseUrl?.trim());
|
||||
return buildRuntimeAccountStatusSnapshot(
|
||||
{ runtime },
|
||||
{
|
||||
accountId: account.accountId,
|
||||
name: account.name,
|
||||
enabled: account.enabled,
|
||||
configured,
|
||||
secretSource: account.secretSource,
|
||||
baseUrl: account.baseUrl ? "[set]" : "[missing]",
|
||||
mode: "webhook",
|
||||
lastInboundAt: runtime?.lastInboundAt ?? null,
|
||||
lastOutboundAt: runtime?.lastOutboundAt ?? null,
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
resolveAccountSnapshot: ({ account }) => ({
|
||||
accountId: account.accountId,
|
||||
name: account.name,
|
||||
enabled: account.enabled,
|
||||
configured: Boolean(account.secret?.trim() && account.baseUrl?.trim()),
|
||||
extra: {
|
||||
secretSource: account.secretSource,
|
||||
baseUrl: account.baseUrl ? "[set]" : "[missing]",
|
||||
mode: "webhook",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
gateway: {
|
||||
startAccount: async (ctx) => {
|
||||
const account = ctx.account;
|
||||
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
buildPassiveChannelStatusSummary,
|
||||
buildTrafficStatusSummary,
|
||||
} from "openclaw/plugin-sdk/extension-shared";
|
||||
import { createComputedAccountStatusAdapter } from "openclaw/plugin-sdk/status-helpers";
|
||||
import {
|
||||
buildComputedAccountStatusSnapshot,
|
||||
buildChannelConfigSchema,
|
||||
collectStatusIssuesFromLastError,
|
||||
createPreCryptoDirectDmAuthorizer,
|
||||
@@ -196,27 +196,25 @@ export const nostrPlugin: ChannelPlugin<ResolvedNostrAccount> = createChatChanne
|
||||
resolveOutboundSessionRoute: (params) => resolveNostrOutboundSessionRoute(params),
|
||||
},
|
||||
status: {
|
||||
defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID),
|
||||
collectStatusIssues: (accounts) => collectStatusIssuesFromLastError("nostr", accounts),
|
||||
buildChannelSummary: ({ snapshot }) =>
|
||||
buildPassiveChannelStatusSummary(snapshot, {
|
||||
publicKey: snapshot.publicKey ?? null,
|
||||
}),
|
||||
buildAccountSnapshot: ({ account, runtime }) =>
|
||||
buildComputedAccountStatusSnapshot(
|
||||
{
|
||||
accountId: account.accountId,
|
||||
name: account.name,
|
||||
enabled: account.enabled,
|
||||
configured: account.configured,
|
||||
runtime,
|
||||
},
|
||||
{
|
||||
...createComputedAccountStatusAdapter<ResolvedNostrAccount>({
|
||||
defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID),
|
||||
collectStatusIssues: (accounts) => collectStatusIssuesFromLastError("nostr", accounts),
|
||||
buildChannelSummary: ({ snapshot }) =>
|
||||
buildPassiveChannelStatusSummary(snapshot, {
|
||||
publicKey: snapshot.publicKey ?? null,
|
||||
}),
|
||||
resolveAccountSnapshot: ({ account, runtime }) => ({
|
||||
accountId: account.accountId,
|
||||
name: account.name,
|
||||
enabled: account.enabled,
|
||||
configured: account.configured,
|
||||
extra: {
|
||||
publicKey: account.publicKey,
|
||||
profile: account.profile,
|
||||
...buildTrafficStatusSummary(runtime),
|
||||
},
|
||||
),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
gateway: {
|
||||
startAccount: async (ctx) => {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { createChatChannelPlugin } from "openclaw/plugin-sdk/core";
|
||||
import { resolveOutboundSendDep } from "openclaw/plugin-sdk/outbound-runtime";
|
||||
import { resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
|
||||
import { buildOutboundBaseSessionKey, type RoutePeer } from "openclaw/plugin-sdk/routing";
|
||||
import { createComputedAccountStatusAdapter } from "openclaw/plugin-sdk/status-helpers";
|
||||
import { resolveSignalAccount, type ResolvedSignalAccount } from "./accounts.js";
|
||||
import { markdownToSignalTextChunks } from "./format.js";
|
||||
import {
|
||||
@@ -20,7 +21,6 @@ import {
|
||||
import { signalMessageActions } from "./message-actions.js";
|
||||
import type { SignalProbe } from "./probe.js";
|
||||
import {
|
||||
buildBaseAccountStatusSnapshot,
|
||||
buildBaseChannelStatusSummary,
|
||||
collectStatusIssuesFromLastError,
|
||||
createDefaultChannelRuntimeState,
|
||||
@@ -295,7 +295,7 @@ export const signalPlugin: ChannelPlugin<ResolvedSignalAccount, SignalProbe> =
|
||||
hint: "<E.164|uuid:ID|group:ID|signal:group:ID|signal:+E.164>",
|
||||
},
|
||||
},
|
||||
status: {
|
||||
status: createComputedAccountStatusAdapter<ResolvedSignalAccount, SignalProbe>({
|
||||
defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID),
|
||||
collectStatusIssues: (accounts) => collectStatusIssuesFromLastError("signal", accounts),
|
||||
buildChannelSummary: ({ snapshot }) =>
|
||||
@@ -312,9 +312,16 @@ export const signalPlugin: ChannelPlugin<ResolvedSignalAccount, SignalProbe> =
|
||||
(probe as SignalProbe | undefined)?.version
|
||||
? [{ text: `Signal daemon: ${(probe as SignalProbe).version}` }]
|
||||
: [],
|
||||
buildAccountSnapshot: ({ account, runtime, probe }) =>
|
||||
buildBaseAccountStatusSnapshot({ account, runtime, probe }, { baseUrl: account.baseUrl }),
|
||||
},
|
||||
resolveAccountSnapshot: ({ account }) => ({
|
||||
accountId: account.accountId,
|
||||
name: account.name,
|
||||
enabled: account.enabled,
|
||||
configured: account.configured,
|
||||
extra: {
|
||||
baseUrl: account.baseUrl,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
gateway: {
|
||||
startAccount: async (ctx) => {
|
||||
const account = ctx.account;
|
||||
|
||||
@@ -343,7 +343,10 @@ const collectSlackSecurityWarnings =
|
||||
},
|
||||
});
|
||||
|
||||
export const slackPlugin: ChannelPlugin<ResolvedSlackAccount> = createChatChannelPlugin({
|
||||
export const slackPlugin: ChannelPlugin<ResolvedSlackAccount, SlackProbe> = createChatChannelPlugin<
|
||||
ResolvedSlackAccount,
|
||||
SlackProbe
|
||||
>({
|
||||
base: {
|
||||
...createSlackPluginBase({
|
||||
setupWizard: slackSetupWizard,
|
||||
|
||||
@@ -5,8 +5,10 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import { createChatChannelPlugin, type ChannelPlugin } from "openclaw/plugin-sdk/core";
|
||||
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
||||
import { createRuntimeOutboundDelegates } from "openclaw/plugin-sdk/outbound-runtime";
|
||||
import { createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
|
||||
import { buildComputedAccountStatusSnapshot } from "../api.js";
|
||||
import {
|
||||
createComputedAccountStatusAdapter,
|
||||
createDefaultChannelRuntimeState,
|
||||
} from "openclaw/plugin-sdk/status-helpers";
|
||||
import { tlonChannelConfigSchema } from "./config-schema.js";
|
||||
import { resolveTlonOutboundSessionRoute } from "./session-route.js";
|
||||
import {
|
||||
@@ -109,7 +111,7 @@ export const tlonPlugin = createChatChannelPlugin({
|
||||
},
|
||||
resolveOutboundSessionRoute: (params) => resolveTlonOutboundSessionRoute(params),
|
||||
},
|
||||
status: {
|
||||
status: createComputedAccountStatusAdapter<ReturnType<typeof resolveTlonAccount>>({
|
||||
defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID),
|
||||
collectStatusIssues: (accounts) => {
|
||||
return accounts.flatMap((account) => {
|
||||
@@ -140,22 +142,17 @@ export const tlonPlugin = createChatChannelPlugin({
|
||||
}
|
||||
return await (await loadTlonChannelRuntime()).probeTlonAccount(account as never);
|
||||
},
|
||||
buildAccountSnapshot: ({ account, runtime, probe }) =>
|
||||
buildComputedAccountStatusSnapshot(
|
||||
{
|
||||
accountId: account.accountId,
|
||||
name: account.name ?? undefined,
|
||||
enabled: account.enabled,
|
||||
configured: account.configured,
|
||||
runtime,
|
||||
probe,
|
||||
},
|
||||
{
|
||||
ship: account.ship,
|
||||
url: account.url,
|
||||
},
|
||||
),
|
||||
},
|
||||
resolveAccountSnapshot: ({ account }) => ({
|
||||
accountId: account.accountId,
|
||||
name: account.name ?? undefined,
|
||||
enabled: account.enabled,
|
||||
configured: account.configured,
|
||||
extra: {
|
||||
ship: account.ship,
|
||||
url: account.url,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
gateway: {
|
||||
startAccount: async (ctx) =>
|
||||
await (await loadTlonChannelRuntime()).startTlonGatewayAccount(ctx),
|
||||
|
||||
@@ -166,16 +166,14 @@ export function createComputedAccountStatusAdapter<
|
||||
params: ComputedAccountStatusAdapterParams<ResolvedAccount, Probe, Audit>,
|
||||
) => ComputedAccountStatusBase & { extra?: TExtra };
|
||||
},
|
||||
): ChannelStatusAdapter<ResolvedAccount> {
|
||||
): ChannelStatusAdapter<ResolvedAccount, Probe, Audit> {
|
||||
return {
|
||||
defaultRuntime: options.defaultRuntime,
|
||||
buildChannelSummary: options.buildChannelSummary,
|
||||
probeAccount: options.probeAccount,
|
||||
formatCapabilitiesProbe:
|
||||
options.formatCapabilitiesProbe as ChannelStatusAdapter<ResolvedAccount>["formatCapabilitiesProbe"],
|
||||
auditAccount: options.auditAccount as ChannelStatusAdapter<ResolvedAccount>["auditAccount"],
|
||||
buildCapabilitiesDiagnostics:
|
||||
options.buildCapabilitiesDiagnostics as ChannelStatusAdapter<ResolvedAccount>["buildCapabilitiesDiagnostics"],
|
||||
formatCapabilitiesProbe: options.formatCapabilitiesProbe,
|
||||
auditAccount: options.auditAccount,
|
||||
buildCapabilitiesDiagnostics: options.buildCapabilitiesDiagnostics,
|
||||
logSelfId: options.logSelfId,
|
||||
resolveAccountState: options.resolveAccountState,
|
||||
collectStatusIssues: options.collectStatusIssues,
|
||||
|
||||
Reference in New Issue
Block a user