refactor(extensions): reuse shared helper primitives

This commit is contained in:
Peter Steinberger
2026-03-07 10:40:57 +00:00
parent 3c71e2bd48
commit 1aa77e4603
58 changed files with 1567 additions and 2195 deletions

View File

@@ -1,6 +1,9 @@
import {
applyAccountNameToChannelSection,
buildBaseChannelStatusSummary,
buildChannelConfigSchema,
buildRuntimeAccountStatusSnapshot,
clearAccountEntryFields,
DEFAULT_ACCOUNT_ID,
deleteAccountFromConfigSection,
formatPairingApproveHint,
@@ -288,17 +291,21 @@ export const nextcloudTalkPlugin: ChannelPlugin<ResolvedNextcloudTalkAccount> =
lastStopAt: null,
lastError: null,
},
buildChannelSummary: ({ snapshot }) => ({
configured: snapshot.configured ?? false,
secretSource: snapshot.secretSource ?? "none",
running: snapshot.running ?? false,
mode: "webhook",
lastStartAt: snapshot.lastStartAt ?? null,
lastStopAt: snapshot.lastStopAt ?? null,
lastError: snapshot.lastError ?? null,
}),
buildChannelSummary: ({ snapshot }) => {
const base = buildBaseChannelStatusSummary(snapshot);
return {
configured: base.configured,
secretSource: snapshot.secretSource ?? "none",
running: base.running,
mode: "webhook",
lastStartAt: base.lastStartAt,
lastStopAt: base.lastStopAt,
lastError: base.lastError,
};
},
buildAccountSnapshot: ({ account, runtime }) => {
const configured = Boolean(account.secret?.trim() && account.baseUrl?.trim());
const runtimeSnapshot = buildRuntimeAccountStatusSnapshot({ runtime });
return {
accountId: account.accountId,
name: account.name,
@@ -306,10 +313,10 @@ export const nextcloudTalkPlugin: ChannelPlugin<ResolvedNextcloudTalkAccount> =
configured,
secretSource: account.secretSource,
baseUrl: account.baseUrl ? "[set]" : "[missing]",
running: runtime?.running ?? false,
lastStartAt: runtime?.lastStartAt ?? null,
lastStopAt: runtime?.lastStopAt ?? null,
lastError: runtime?.lastError ?? null,
running: runtimeSnapshot.running,
lastStartAt: runtimeSnapshot.lastStartAt,
lastStopAt: runtimeSnapshot.lastStopAt,
lastError: runtimeSnapshot.lastError,
mode: "webhook",
lastInboundAt: runtime?.lastInboundAt ?? null,
lastOutboundAt: runtime?.lastOutboundAt ?? null,
@@ -353,36 +360,20 @@ export const nextcloudTalkPlugin: ChannelPlugin<ResolvedNextcloudTalkAccount> =
cleared = true;
changed = true;
}
const accounts =
nextSection.accounts && typeof nextSection.accounts === "object"
? { ...nextSection.accounts }
: undefined;
if (accounts && accountId in accounts) {
const entry = accounts[accountId];
if (entry && typeof entry === "object") {
const nextEntry = { ...entry } as Record<string, unknown>;
if ("botSecret" in nextEntry) {
const secret = nextEntry.botSecret;
if (typeof secret === "string" ? secret.trim() : secret) {
cleared = true;
}
delete nextEntry.botSecret;
changed = true;
}
if (Object.keys(nextEntry).length === 0) {
delete accounts[accountId];
changed = true;
} else {
accounts[accountId] = nextEntry as typeof entry;
}
const accountCleanup = clearAccountEntryFields({
accounts: nextSection.accounts,
accountId,
fields: ["botSecret"],
});
if (accountCleanup.changed) {
changed = true;
if (accountCleanup.cleared) {
cleared = true;
}
}
if (accounts) {
if (Object.keys(accounts).length === 0) {
delete nextSection.accounts;
changed = true;
if (accountCleanup.nextAccounts) {
nextSection.accounts = accountCleanup.nextAccounts;
} else {
nextSection.accounts = accounts;
delete nextSection.accounts;
}
}
}

View File

@@ -1,8 +1,7 @@
import {
GROUP_POLICY_BLOCKED_LABEL,
createScopedPairingAccess,
createNormalizedOutboundDeliverer,
createReplyPrefixOptions,
dispatchInboundReplyWithBase,
formatTextWithAttachmentLinks,
logInboundDrop,
readStoreAllowFromForDmPolicy,
@@ -291,43 +290,30 @@ export async function handleNextcloudTalkInbound(params: {
CommandAuthorized: commandAuthorized,
});
await core.channel.session.recordInboundSession({
await dispatchInboundReplyWithBase({
cfg: config as OpenClawConfig,
channel: CHANNEL_ID,
accountId: account.accountId,
route,
storePath,
sessionKey: ctxPayload.SessionKey ?? route.sessionKey,
ctx: ctxPayload,
ctxPayload,
core,
deliver: async (payload) => {
await deliverNextcloudTalkReply({
payload,
roomToken,
accountId: account.accountId,
statusSink,
});
},
onRecordError: (err) => {
runtime.error?.(`nextcloud-talk: failed updating session meta: ${String(err)}`);
},
});
const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
cfg: config as OpenClawConfig,
agentId: route.agentId,
channel: CHANNEL_ID,
accountId: account.accountId,
});
const deliverReply = createNormalizedOutboundDeliverer(async (payload) => {
await deliverNextcloudTalkReply({
payload,
roomToken,
accountId: account.accountId,
statusSink,
});
});
await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
ctx: ctxPayload,
cfg: config as OpenClawConfig,
dispatcherOptions: {
...prefixOptions,
deliver: deliverReply,
onError: (err, info) => {
runtime.error?.(`nextcloud-talk ${info.kind} reply failed: ${String(err)}`);
},
onDispatchError: (err, info) => {
runtime.error?.(`nextcloud-talk ${info.kind} reply failed: ${String(err)}`);
},
replyOptions: {
skillFilter: roomConfig?.skills,
onModelSelected,
disableBlockStreaming:
typeof account.config.blockStreaming === "boolean"
? !account.config.blockStreaming

View File

@@ -43,6 +43,45 @@ function setNextcloudTalkDmPolicy(cfg: CoreConfig, dmPolicy: DmPolicy): CoreConf
} as CoreConfig;
}
function setNextcloudTalkAccountConfig(
cfg: CoreConfig,
accountId: string,
updates: Record<string, unknown>,
): CoreConfig {
if (accountId === DEFAULT_ACCOUNT_ID) {
return {
...cfg,
channels: {
...cfg.channels,
"nextcloud-talk": {
...cfg.channels?.["nextcloud-talk"],
enabled: true,
...updates,
},
},
};
}
return {
...cfg,
channels: {
...cfg.channels,
"nextcloud-talk": {
...cfg.channels?.["nextcloud-talk"],
enabled: true,
accounts: {
...cfg.channels?.["nextcloud-talk"]?.accounts,
[accountId]: {
...cfg.channels?.["nextcloud-talk"]?.accounts?.[accountId],
enabled: cfg.channels?.["nextcloud-talk"]?.accounts?.[accountId]?.enabled ?? true,
...updates,
},
},
},
},
};
}
async function noteNextcloudTalkSecretHelp(prompter: WizardPrompter): Promise<void> {
await prompter.note(
[
@@ -105,40 +144,10 @@ async function promptNextcloudTalkAllowFrom(params: {
];
const unique = mergeAllowFromEntries(undefined, merged);
if (accountId === DEFAULT_ACCOUNT_ID) {
return {
...cfg,
channels: {
...cfg.channels,
"nextcloud-talk": {
...cfg.channels?.["nextcloud-talk"],
enabled: true,
dmPolicy: "allowlist",
allowFrom: unique,
},
},
};
}
return {
...cfg,
channels: {
...cfg.channels,
"nextcloud-talk": {
...cfg.channels?.["nextcloud-talk"],
enabled: true,
accounts: {
...cfg.channels?.["nextcloud-talk"]?.accounts,
[accountId]: {
...cfg.channels?.["nextcloud-talk"]?.accounts?.[accountId],
enabled: cfg.channels?.["nextcloud-talk"]?.accounts?.[accountId]?.enabled ?? true,
dmPolicy: "allowlist",
allowFrom: unique,
},
},
},
},
};
return setNextcloudTalkAccountConfig(cfg, accountId, {
dmPolicy: "allowlist",
allowFrom: unique,
});
}
async function promptNextcloudTalkAllowFromForAccount(params: {
@@ -265,41 +274,10 @@ export const nextcloudTalkOnboardingAdapter: ChannelOnboardingAdapter = {
}
if (secretResult.action === "use-env" || secret || baseUrl !== resolvedAccount.baseUrl) {
if (accountId === DEFAULT_ACCOUNT_ID) {
next = {
...next,
channels: {
...next.channels,
"nextcloud-talk": {
...next.channels?.["nextcloud-talk"],
enabled: true,
baseUrl,
...(secret ? { botSecret: secret } : {}),
},
},
};
} else {
next = {
...next,
channels: {
...next.channels,
"nextcloud-talk": {
...next.channels?.["nextcloud-talk"],
enabled: true,
accounts: {
...next.channels?.["nextcloud-talk"]?.accounts,
[accountId]: {
...next.channels?.["nextcloud-talk"]?.accounts?.[accountId],
enabled:
next.channels?.["nextcloud-talk"]?.accounts?.[accountId]?.enabled ?? true,
baseUrl,
...(secret ? { botSecret: secret } : {}),
},
},
},
},
};
}
next = setNextcloudTalkAccountConfig(next, accountId, {
baseUrl,
...(secret ? { botSecret: secret } : {}),
});
}
const existingApiUser = resolvedAccount.config.apiUser?.trim();
@@ -333,41 +311,10 @@ export const nextcloudTalkOnboardingAdapter: ChannelOnboardingAdapter = {
preferredEnvVar: "NEXTCLOUD_TALK_API_PASSWORD",
});
const apiPassword = apiPasswordResult.action === "set" ? apiPasswordResult.value : undefined;
if (accountId === DEFAULT_ACCOUNT_ID) {
next = {
...next,
channels: {
...next.channels,
"nextcloud-talk": {
...next.channels?.["nextcloud-talk"],
enabled: true,
apiUser,
...(apiPassword ? { apiPassword } : {}),
},
},
};
} else {
next = {
...next,
channels: {
...next.channels,
"nextcloud-talk": {
...next.channels?.["nextcloud-talk"],
enabled: true,
accounts: {
...next.channels?.["nextcloud-talk"]?.accounts,
[accountId]: {
...next.channels?.["nextcloud-talk"]?.accounts?.[accountId],
enabled:
next.channels?.["nextcloud-talk"]?.accounts?.[accountId]?.enabled ?? true,
apiUser,
...(apiPassword ? { apiPassword } : {}),
},
},
},
},
};
}
next = setNextcloudTalkAccountConfig(next, accountId, {
apiUser,
...(apiPassword ? { apiPassword } : {}),
});
}
if (forceAllowFrom) {