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, ): 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 = { 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; }, };