refactor: de-duplicate channel runtime and payload helpers

This commit is contained in:
Peter Steinberger
2026-02-23 21:25:20 +00:00
parent 0ae7f470a2
commit 0183610db3
44 changed files with 775 additions and 698 deletions

View File

@@ -1,13 +1,15 @@
import {
buildBaseAccountStatusSnapshot,
buildBaseChannelStatusSummary,
buildChannelConfigSchema,
DEFAULT_ACCOUNT_ID,
deleteAccountFromConfigSection,
formatPairingApproveHint,
getChatChannelMeta,
PAIRING_APPROVED_MESSAGE,
resolveAllowlistProviderRuntimeGroupPolicy,
resolveDefaultGroupPolicy,
setAccountEnabledInConfigSection,
deleteAccountFromConfigSection,
type ChannelPlugin,
} from "openclaw/plugin-sdk";
import {
@@ -319,37 +321,23 @@ export const ircPlugin: ChannelPlugin<ResolvedIrcAccount, IrcProbe> = {
lastError: null,
},
buildChannelSummary: ({ account, snapshot }) => ({
configured: snapshot.configured ?? false,
...buildBaseChannelStatusSummary(snapshot),
host: account.host,
port: snapshot.port,
tls: account.tls,
nick: account.nick,
running: snapshot.running ?? false,
lastStartAt: snapshot.lastStartAt ?? null,
lastStopAt: snapshot.lastStopAt ?? null,
lastError: snapshot.lastError ?? null,
probe: snapshot.probe,
lastProbeAt: snapshot.lastProbeAt ?? null,
}),
probeAccount: async ({ cfg, account, timeoutMs }) =>
probeIrc(cfg as CoreConfig, { accountId: account.accountId, timeoutMs }),
buildAccountSnapshot: ({ account, runtime, probe }) => ({
accountId: account.accountId,
name: account.name,
enabled: account.enabled,
configured: account.configured,
...buildBaseAccountStatusSnapshot({ account, runtime, probe }),
host: account.host,
port: account.port,
tls: account.tls,
nick: account.nick,
passwordSource: account.passwordSource,
running: runtime?.running ?? false,
lastStartAt: runtime?.lastStartAt ?? null,
lastStopAt: runtime?.lastStopAt ?? null,
lastError: runtime?.lastError ?? null,
probe,
lastInboundAt: runtime?.lastInboundAt ?? null,
lastOutboundAt: runtime?.lastOutboundAt ?? null,
}),
},
gateway: {

View File

@@ -4,6 +4,7 @@ import {
DmPolicySchema,
GroupPolicySchema,
MarkdownConfigSchema,
ReplyRuntimeConfigSchemaShape,
ToolPolicySchema,
requireOpenAllowFrom,
} from "openclaw/plugin-sdk";
@@ -62,15 +63,7 @@ export const IrcAccountSchemaBase = z
channels: z.array(z.string()).optional(),
mentionPatterns: z.array(z.string()).optional(),
markdown: MarkdownConfigSchema,
historyLimit: z.number().int().min(0).optional(),
dmHistoryLimit: z.number().int().min(0).optional(),
dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
textChunkLimit: z.number().int().positive().optional(),
chunkMode: z.enum(["length", "newline"]).optional(),
blockStreaming: z.boolean().optional(),
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
responsePrefix: z.string().optional(),
mediaMaxMb: z.number().positive().optional(),
...ReplyRuntimeConfigSchemaShape,
})
.strict();

View File

@@ -1,11 +1,15 @@
import {
GROUP_POLICY_BLOCKED_LABEL,
createNormalizedOutboundDeliverer,
createReplyPrefixOptions,
formatTextWithAttachmentLinks,
logInboundDrop,
resolveControlCommandGate,
resolveOutboundMediaUrls,
resolveAllowlistProviderRuntimeGroupPolicy,
resolveDefaultGroupPolicy,
warnMissingProviderGroupPolicyFallbackOnce,
type OutboundReplyPayload,
type OpenClawConfig,
type RuntimeEnv,
} from "openclaw/plugin-sdk";
@@ -27,32 +31,20 @@ const CHANNEL_ID = "irc" as const;
const escapeIrcRegexLiteral = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
async function deliverIrcReply(params: {
payload: { text?: string; mediaUrls?: string[]; mediaUrl?: string; replyToId?: string };
payload: OutboundReplyPayload;
target: string;
accountId: string;
sendReply?: (target: string, text: string, replyToId?: string) => Promise<void>;
statusSink?: (patch: { lastOutboundAt?: number }) => void;
}) {
const text = params.payload.text ?? "";
const mediaList = params.payload.mediaUrls?.length
? params.payload.mediaUrls
: params.payload.mediaUrl
? [params.payload.mediaUrl]
: [];
if (!text.trim() && mediaList.length === 0) {
const combined = formatTextWithAttachmentLinks(
params.payload.text,
resolveOutboundMediaUrls(params.payload),
);
if (!combined) {
return;
}
const mediaBlock = mediaList.length
? mediaList.map((url) => `Attachment: ${url}`).join("\n")
: "";
const combined = text.trim()
? mediaBlock
? `${text.trim()}\n\n${mediaBlock}`
: text.trim()
: mediaBlock;
if (params.sendReply) {
await params.sendReply(params.target, combined, params.payload.replyToId);
} else {
@@ -317,26 +309,22 @@ export async function handleIrcInbound(params: {
channel: CHANNEL_ID,
accountId: account.accountId,
});
const deliverReply = createNormalizedOutboundDeliverer(async (payload) => {
await deliverIrcReply({
payload,
target: peerId,
accountId: account.accountId,
sendReply: params.sendReply,
statusSink,
});
});
await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
ctx: ctxPayload,
cfg: config as OpenClawConfig,
dispatcherOptions: {
...prefixOptions,
deliver: async (payload) => {
await deliverIrcReply({
payload: payload as {
text?: string;
mediaUrls?: string[];
mediaUrl?: string;
replyToId?: string;
},
target: peerId,
accountId: account.accountId,
sendReply: params.sendReply,
statusSink,
});
},
deliver: deliverReply,
onError: (err, info) => {
runtime.error?.(`irc ${info.kind} reply failed: ${String(err)}`);
},

View File

@@ -1,4 +1,4 @@
import type { RuntimeEnv } from "openclaw/plugin-sdk";
import { createLoggerBackedRuntime, type RuntimeEnv } from "openclaw/plugin-sdk";
import { resolveIrcAccount } from "./accounts.js";
import { connectIrcClient, type IrcClient } from "./client.js";
import { buildIrcConnectOptions } from "./connect-options.js";
@@ -39,13 +39,12 @@ export async function monitorIrcProvider(opts: IrcMonitorOptions): Promise<{ sto
accountId: opts.accountId,
});
const runtime: RuntimeEnv = opts.runtime ?? {
log: (...args: unknown[]) => core.logging.getChildLogger().info(args.map(String).join(" ")),
error: (...args: unknown[]) => core.logging.getChildLogger().error(args.map(String).join(" ")),
exit: () => {
throw new Error("Runtime exit not available");
},
};
const runtime: RuntimeEnv =
opts.runtime ??
createLoggerBackedRuntime({
logger: core.logging.getChildLogger(),
exitError: () => new Error("Runtime exit not available"),
});
if (!account.configured) {
throw new Error(