From 97dc493e2a5a84fef81d0ebef821a2451677eca9 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 14 Mar 2026 02:07:04 +0000 Subject: [PATCH] refactor: share extension channel status summaries --- extensions/googlechat/src/channel.ts | 23 ++++++-------- extensions/imessage/src/channel.ts | 17 ++++------- extensions/mattermost/src/channel.ts | 19 +++++------- extensions/nostr/src/channel.ts | 13 +++----- extensions/shared/channel-status-summary.ts | 34 +++++++++++++++++++++ extensions/slack/src/channel.ts | 17 ++++------- extensions/twitch/src/plugin.ts | 12 ++------ extensions/zalouser/src/channel.ts | 11 ++----- 8 files changed, 72 insertions(+), 74 deletions(-) create mode 100644 extensions/shared/channel-status-summary.ts diff --git a/extensions/googlechat/src/channel.ts b/extensions/googlechat/src/channel.ts index 47980f97d92..3ae992d3e9e 100644 --- a/extensions/googlechat/src/channel.ts +++ b/extensions/googlechat/src/channel.ts @@ -30,6 +30,7 @@ import { type OpenClawConfig, } from "openclaw/plugin-sdk/googlechat"; import { GoogleChatConfigSchema } from "openclaw/plugin-sdk/googlechat"; +import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js"; import { listGoogleChatAccountIds, resolveDefaultGoogleChatAccountId, @@ -473,20 +474,14 @@ export const googlechatPlugin: ChannelPlugin = { } return issues; }), - buildChannelSummary: ({ snapshot }) => ({ - configured: snapshot.configured ?? false, - credentialSource: snapshot.credentialSource ?? "none", - audienceType: snapshot.audienceType ?? null, - audience: snapshot.audience ?? null, - webhookPath: snapshot.webhookPath ?? null, - webhookUrl: snapshot.webhookUrl ?? null, - running: snapshot.running ?? false, - lastStartAt: snapshot.lastStartAt ?? null, - lastStopAt: snapshot.lastStopAt ?? null, - lastError: snapshot.lastError ?? null, - probe: snapshot.probe, - lastProbeAt: snapshot.lastProbeAt ?? null, - }), + buildChannelSummary: ({ snapshot }) => + buildPassiveProbedChannelStatusSummary(snapshot, { + credentialSource: snapshot.credentialSource ?? "none", + audienceType: snapshot.audienceType ?? null, + audience: snapshot.audience ?? null, + webhookPath: snapshot.webhookPath ?? null, + webhookUrl: snapshot.webhookUrl ?? null, + }), probeAccount: async ({ account }) => probeGoogleChat(account), buildAccountSnapshot: ({ account, runtime, probe }) => { const base = buildComputedAccountStatusSnapshot({ diff --git a/extensions/imessage/src/channel.ts b/extensions/imessage/src/channel.ts index 22c45cf6072..17023599eb1 100644 --- a/extensions/imessage/src/channel.ts +++ b/extensions/imessage/src/channel.ts @@ -29,6 +29,7 @@ import { type ChannelPlugin, type ResolvedIMessageAccount, } from "openclaw/plugin-sdk/imessage"; +import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js"; import { getIMessageRuntime } from "./runtime.js"; const meta = getChatChannelMeta("imessage"); @@ -264,17 +265,11 @@ export const imessagePlugin: ChannelPlugin = { dbPath: null, }, collectStatusIssues: (accounts) => collectStatusIssuesFromLastError("imessage", accounts), - buildChannelSummary: ({ snapshot }) => ({ - configured: snapshot.configured ?? false, - running: snapshot.running ?? false, - lastStartAt: snapshot.lastStartAt ?? null, - lastStopAt: snapshot.lastStopAt ?? null, - lastError: snapshot.lastError ?? null, - cliPath: snapshot.cliPath ?? null, - dbPath: snapshot.dbPath ?? null, - probe: snapshot.probe, - lastProbeAt: snapshot.lastProbeAt ?? null, - }), + buildChannelSummary: ({ snapshot }) => + buildPassiveProbedChannelStatusSummary(snapshot, { + cliPath: snapshot.cliPath ?? null, + dbPath: snapshot.dbPath ?? null, + }), probeAccount: async ({ timeoutMs }) => getIMessageRuntime().channel.imessage.probeIMessage(timeoutMs), buildAccountSnapshot: ({ account, runtime, probe }) => ({ diff --git a/extensions/mattermost/src/channel.ts b/extensions/mattermost/src/channel.ts index f8116e127b3..c872b8d5085 100644 --- a/extensions/mattermost/src/channel.ts +++ b/extensions/mattermost/src/channel.ts @@ -21,6 +21,7 @@ import { type ChannelMessageActionName, type ChannelPlugin, } from "openclaw/plugin-sdk/mattermost"; +import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js"; import { MattermostConfigSchema } from "./config-schema.js"; import { resolveMattermostGroupRequireMention } from "./group-mentions.js"; import { @@ -419,18 +420,12 @@ export const mattermostPlugin: ChannelPlugin = { lastStopAt: null, lastError: null, }, - buildChannelSummary: ({ snapshot }) => ({ - configured: snapshot.configured ?? false, - botTokenSource: snapshot.botTokenSource ?? "none", - running: snapshot.running ?? false, - connected: snapshot.connected ?? false, - lastStartAt: snapshot.lastStartAt ?? null, - lastStopAt: snapshot.lastStopAt ?? null, - lastError: snapshot.lastError ?? null, - baseUrl: snapshot.baseUrl ?? null, - probe: snapshot.probe, - lastProbeAt: snapshot.lastProbeAt ?? null, - }), + buildChannelSummary: ({ snapshot }) => + buildPassiveProbedChannelStatusSummary(snapshot, { + botTokenSource: snapshot.botTokenSource ?? "none", + connected: snapshot.connected ?? false, + baseUrl: snapshot.baseUrl ?? null, + }), probeAccount: async ({ account, timeoutMs }) => { const token = account.botToken?.trim(); const baseUrl = account.baseUrl?.trim(); diff --git a/extensions/nostr/src/channel.ts b/extensions/nostr/src/channel.ts index 20de320a3d1..43137b23827 100644 --- a/extensions/nostr/src/channel.ts +++ b/extensions/nostr/src/channel.ts @@ -7,6 +7,7 @@ import { mapAllowFromEntries, type ChannelPlugin, } from "openclaw/plugin-sdk/nostr"; +import { buildPassiveChannelStatusSummary } from "../../shared/channel-status-summary.js"; import type { NostrProfile } from "./config-schema.js"; import { NostrConfigSchema } from "./config-schema.js"; import type { MetricEvent, MetricsSnapshot } from "./metrics.js"; @@ -160,14 +161,10 @@ export const nostrPlugin: ChannelPlugin = { status: { defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID), collectStatusIssues: (accounts) => collectStatusIssuesFromLastError("nostr", accounts), - buildChannelSummary: ({ snapshot }) => ({ - configured: snapshot.configured ?? false, - publicKey: snapshot.publicKey ?? null, - running: snapshot.running ?? false, - lastStartAt: snapshot.lastStartAt ?? null, - lastStopAt: snapshot.lastStopAt ?? null, - lastError: snapshot.lastError ?? null, - }), + buildChannelSummary: ({ snapshot }) => + buildPassiveChannelStatusSummary(snapshot, { + publicKey: snapshot.publicKey ?? null, + }), buildAccountSnapshot: ({ account, runtime }) => ({ accountId: account.accountId, name: account.name, diff --git a/extensions/shared/channel-status-summary.ts b/extensions/shared/channel-status-summary.ts new file mode 100644 index 00000000000..f2671704ac5 --- /dev/null +++ b/extensions/shared/channel-status-summary.ts @@ -0,0 +1,34 @@ +type PassiveChannelStatusSnapshot = { + configured?: boolean; + running?: boolean; + lastStartAt?: number | null; + lastStopAt?: number | null; + lastError?: string | null; + probe?: unknown; + lastProbeAt?: number | null; +}; + +export function buildPassiveChannelStatusSummary( + snapshot: PassiveChannelStatusSnapshot, + extra?: TExtra, +) { + return { + configured: snapshot.configured ?? false, + ...(extra ?? ({} as TExtra)), + running: snapshot.running ?? false, + lastStartAt: snapshot.lastStartAt ?? null, + lastStopAt: snapshot.lastStopAt ?? null, + lastError: snapshot.lastError ?? null, + }; +} + +export function buildPassiveProbedChannelStatusSummary( + snapshot: PassiveChannelStatusSnapshot, + extra?: TExtra, +) { + return { + ...buildPassiveChannelStatusSummary(snapshot, extra), + probe: snapshot.probe, + lastProbeAt: snapshot.lastProbeAt ?? null, + }; +} diff --git a/extensions/slack/src/channel.ts b/extensions/slack/src/channel.ts index 73c844a1cc0..17209b6e4d1 100644 --- a/extensions/slack/src/channel.ts +++ b/extensions/slack/src/channel.ts @@ -38,6 +38,7 @@ import { type ChannelPlugin, type ResolvedSlackAccount, } from "openclaw/plugin-sdk/slack"; +import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js"; import { getSlackRuntime } from "./runtime.js"; const meta = getChatChannelMeta("slack"); @@ -421,17 +422,11 @@ export const slackPlugin: ChannelPlugin = { lastStopAt: null, lastError: null, }, - buildChannelSummary: ({ snapshot }) => ({ - configured: snapshot.configured ?? false, - botTokenSource: snapshot.botTokenSource ?? "none", - appTokenSource: snapshot.appTokenSource ?? "none", - running: snapshot.running ?? false, - lastStartAt: snapshot.lastStartAt ?? null, - lastStopAt: snapshot.lastStopAt ?? null, - lastError: snapshot.lastError ?? null, - probe: snapshot.probe, - lastProbeAt: snapshot.lastProbeAt ?? null, - }), + buildChannelSummary: ({ snapshot }) => + buildPassiveProbedChannelStatusSummary(snapshot, { + botTokenSource: snapshot.botTokenSource ?? "none", + appTokenSource: snapshot.appTokenSource ?? "none", + }), probeAccount: async ({ account, timeoutMs }) => { const token = account.botToken?.trim(); if (!token) { diff --git a/extensions/twitch/src/plugin.ts b/extensions/twitch/src/plugin.ts index f6cf576b6a0..11cf90b8893 100644 --- a/extensions/twitch/src/plugin.ts +++ b/extensions/twitch/src/plugin.ts @@ -7,6 +7,7 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/twitch"; import { buildChannelConfigSchema } from "openclaw/plugin-sdk/twitch"; +import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js"; import { twitchMessageActions } from "./actions.js"; import { removeClientManager } from "./client-manager-registry.js"; import { TwitchConfigSchema } from "./config-schema.js"; @@ -169,15 +170,8 @@ export const twitchPlugin: ChannelPlugin = { }, /** Build channel summary from snapshot */ - buildChannelSummary: ({ snapshot }: { snapshot: ChannelAccountSnapshot }) => ({ - configured: snapshot.configured ?? false, - running: snapshot.running ?? false, - lastStartAt: snapshot.lastStartAt ?? null, - lastStopAt: snapshot.lastStopAt ?? null, - lastError: snapshot.lastError ?? null, - probe: snapshot.probe, - lastProbeAt: snapshot.lastProbeAt ?? null, - }), + buildChannelSummary: ({ snapshot }: { snapshot: ChannelAccountSnapshot }) => + buildPassiveProbedChannelStatusSummary(snapshot), /** Probe account connection */ probeAccount: async ({ diff --git a/extensions/zalouser/src/channel.ts b/extensions/zalouser/src/channel.ts index d2f7a714537..81fce5e3ab9 100644 --- a/extensions/zalouser/src/channel.ts +++ b/extensions/zalouser/src/channel.ts @@ -29,6 +29,7 @@ import { sendPayloadWithChunkedTextAndMedia, setAccountEnabledInConfigSection, } from "openclaw/plugin-sdk/zalouser"; +import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js"; import { listZalouserAccountIds, resolveDefaultZalouserAccountId, @@ -652,15 +653,7 @@ export const zalouserPlugin: ChannelPlugin = { lastError: null, }, collectStatusIssues: collectZalouserStatusIssues, - buildChannelSummary: ({ snapshot }) => ({ - configured: snapshot.configured ?? false, - running: snapshot.running ?? false, - lastStartAt: snapshot.lastStartAt ?? null, - lastStopAt: snapshot.lastStopAt ?? null, - lastError: snapshot.lastError ?? null, - probe: snapshot.probe, - lastProbeAt: snapshot.lastProbeAt ?? null, - }), + buildChannelSummary: ({ snapshot }) => buildPassiveProbedChannelStatusSummary(snapshot), probeAccount: async ({ account, timeoutMs }) => probeZalouser(account.profile, timeoutMs), buildAccountSnapshot: async ({ account, runtime }) => { const configured = await checkZcaAuthenticated(account.profile);