openclaw/extensions/irc/src/setup-core.ts

148 lines
4.2 KiB
TypeScript

import {
applyAccountNameToChannelSection,
patchScopedAccountConfig,
} from "../../../src/channels/plugins/setup-helpers.js";
import {
setTopLevelChannelAllowFrom,
setTopLevelChannelDmPolicyWithAllowFrom,
} from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelSetupAdapter } from "../../../src/channels/plugins/types.adapters.js";
import type { ChannelSetupInput } from "../../../src/channels/plugins/types.core.js";
import type { DmPolicy } from "../../../src/config/types.js";
import { normalizeAccountId } from "../../../src/routing/session-key.js";
import type { CoreConfig, IrcAccountConfig, IrcNickServConfig } from "./types.js";
const channel = "irc" as const;
type IrcSetupInput = ChannelSetupInput & {
host?: string;
port?: number | string;
tls?: boolean;
nick?: string;
username?: string;
realname?: string;
channels?: string[];
password?: string;
};
export function parsePort(raw: string, fallback: number): number {
const trimmed = raw.trim();
if (!trimmed) {
return fallback;
}
const parsed = Number.parseInt(trimmed, 10);
if (!Number.isFinite(parsed) || parsed < 1 || parsed > 65535) {
return fallback;
}
return parsed;
}
export function updateIrcAccountConfig(
cfg: CoreConfig,
accountId: string,
patch: Partial<IrcAccountConfig>,
): CoreConfig {
return patchScopedAccountConfig({
cfg,
channelKey: channel,
accountId,
patch,
ensureChannelEnabled: false,
ensureAccountEnabled: false,
}) as CoreConfig;
}
export function setIrcDmPolicy(cfg: CoreConfig, dmPolicy: DmPolicy): CoreConfig {
return setTopLevelChannelDmPolicyWithAllowFrom({
cfg,
channel,
dmPolicy,
}) as CoreConfig;
}
export function setIrcAllowFrom(cfg: CoreConfig, allowFrom: string[]): CoreConfig {
return setTopLevelChannelAllowFrom({
cfg,
channel,
allowFrom,
}) as CoreConfig;
}
export function setIrcNickServ(
cfg: CoreConfig,
accountId: string,
nickserv?: IrcNickServConfig,
): CoreConfig {
return updateIrcAccountConfig(cfg, accountId, { nickserv });
}
export function setIrcGroupAccess(
cfg: CoreConfig,
accountId: string,
policy: "open" | "allowlist" | "disabled",
entries: string[],
normalizeGroupEntry: (raw: string) => string | null,
): CoreConfig {
if (policy !== "allowlist") {
return updateIrcAccountConfig(cfg, accountId, { enabled: true, groupPolicy: policy });
}
const normalizedEntries = [
...new Set(entries.map((entry) => normalizeGroupEntry(entry)).filter(Boolean)),
];
const groups = Object.fromEntries(normalizedEntries.map((entry) => [entry, {}]));
return updateIrcAccountConfig(cfg, accountId, {
enabled: true,
groupPolicy: "allowlist",
groups,
});
}
export const ircSetupAdapter: ChannelSetupAdapter = {
resolveAccountId: ({ accountId }) => normalizeAccountId(accountId),
applyAccountName: ({ cfg, accountId, name }) =>
applyAccountNameToChannelSection({
cfg,
channelKey: channel,
accountId,
name,
}),
validateInput: ({ input }) => {
const setupInput = input as IrcSetupInput;
if (!setupInput.host?.trim()) {
return "IRC requires host.";
}
if (!setupInput.nick?.trim()) {
return "IRC requires nick.";
}
return null;
},
applyAccountConfig: ({ cfg, accountId, input }) => {
const setupInput = input as IrcSetupInput;
const namedConfig = applyAccountNameToChannelSection({
cfg,
channelKey: channel,
accountId,
name: setupInput.name,
});
const portInput =
typeof setupInput.port === "number" ? String(setupInput.port) : String(setupInput.port ?? "");
const patch: Partial<IrcAccountConfig> = {
enabled: true,
host: setupInput.host?.trim(),
port: portInput ? parsePort(portInput, setupInput.tls === false ? 6667 : 6697) : undefined,
tls: setupInput.tls,
nick: setupInput.nick?.trim(),
username: setupInput.username?.trim(),
realname: setupInput.realname?.trim(),
password: setupInput.password?.trim(),
channels: setupInput.channels,
};
return patchScopedAccountConfig({
cfg: namedConfig,
channelKey: channel,
accountId,
patch,
}) as CoreConfig;
},
};