mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-30 01:06:11 +00:00
IRC: split setup adapter helpers
This commit is contained in:
@@ -36,7 +36,8 @@ import { resolveIrcGroupMatch, resolveIrcRequireMention } from "./policy.js";
|
||||
import { probeIrc } from "./probe.js";
|
||||
import { getIrcRuntime } from "./runtime.js";
|
||||
import { sendMessageIrc } from "./send.js";
|
||||
import { ircSetupAdapter, ircSetupWizard } from "./setup-surface.js";
|
||||
import { ircSetupAdapter } from "./setup-core.js";
|
||||
import { ircSetupWizard } from "./setup-surface.js";
|
||||
import type { CoreConfig, IrcProbe } from "./types.js";
|
||||
|
||||
const meta = getChatChannelMeta("irc");
|
||||
|
||||
147
extensions/irc/src/setup-core.ts
Normal file
147
extensions/irc/src/setup-core.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
import {
|
||||
setTopLevelChannelAllowFrom,
|
||||
setTopLevelChannelDmPolicyWithAllowFrom,
|
||||
} from "../../../src/channels/plugins/onboarding/helpers.js";
|
||||
import {
|
||||
applyAccountNameToChannelSection,
|
||||
patchScopedAccountConfig,
|
||||
} from "../../../src/channels/plugins/setup-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;
|
||||
},
|
||||
};
|
||||
@@ -2,18 +2,10 @@ import type { ChannelOnboardingDmPolicy } from "../../../src/channels/plugins/on
|
||||
import {
|
||||
resolveOnboardingAccountId,
|
||||
setOnboardingChannelEnabled,
|
||||
setTopLevelChannelAllowFrom,
|
||||
setTopLevelChannelDmPolicyWithAllowFrom,
|
||||
} from "../../../src/channels/plugins/onboarding/helpers.js";
|
||||
import {
|
||||
applyAccountNameToChannelSection,
|
||||
patchScopedAccountConfig,
|
||||
} from "../../../src/channels/plugins/setup-helpers.js";
|
||||
import type { ChannelSetupWizard } from "../../../src/channels/plugins/setup-wizard.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 { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../src/routing/session-key.js";
|
||||
import { DEFAULT_ACCOUNT_ID } from "../../../src/routing/session-key.js";
|
||||
import { formatDocsLink } from "../../../src/terminal/links.js";
|
||||
import type { WizardPrompter } from "../../../src/wizard/prompts.js";
|
||||
import { listIrcAccountIds, resolveDefaultIrcAccountId, resolveIrcAccount } from "./accounts.js";
|
||||
@@ -22,23 +14,21 @@ import {
|
||||
normalizeIrcAllowEntry,
|
||||
normalizeIrcMessagingTarget,
|
||||
} from "./normalize.js";
|
||||
import {
|
||||
ircSetupAdapter,
|
||||
parsePort,
|
||||
setIrcAllowFrom,
|
||||
setIrcDmPolicy,
|
||||
setIrcGroupAccess,
|
||||
setIrcNickServ,
|
||||
updateIrcAccountConfig,
|
||||
} from "./setup-core.js";
|
||||
import type { CoreConfig, IrcAccountConfig, IrcNickServConfig } from "./types.js";
|
||||
|
||||
const channel = "irc" as const;
|
||||
const USE_ENV_FLAG = "__ircUseEnv";
|
||||
const TLS_FLAG = "__ircTls";
|
||||
|
||||
type IrcSetupInput = ChannelSetupInput & {
|
||||
host?: string;
|
||||
port?: number | string;
|
||||
tls?: boolean;
|
||||
nick?: string;
|
||||
username?: string;
|
||||
realname?: string;
|
||||
channels?: string[];
|
||||
password?: string;
|
||||
};
|
||||
|
||||
function parseListInput(raw: string): string[] {
|
||||
return raw
|
||||
.split(/[\n,;]+/g)
|
||||
@@ -46,18 +36,6 @@ function parseListInput(raw: string): string[] {
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
function normalizeGroupEntry(raw: string): string | null {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) {
|
||||
@@ -73,65 +51,6 @@ function normalizeGroupEntry(raw: string): string | null {
|
||||
return `#${normalized.replace(/^#+/, "")}`;
|
||||
}
|
||||
|
||||
function updateIrcAccountConfig(
|
||||
cfg: CoreConfig,
|
||||
accountId: string,
|
||||
patch: Partial<IrcAccountConfig>,
|
||||
): CoreConfig {
|
||||
return patchScopedAccountConfig({
|
||||
cfg,
|
||||
channelKey: channel,
|
||||
accountId,
|
||||
patch,
|
||||
ensureChannelEnabled: false,
|
||||
ensureAccountEnabled: false,
|
||||
}) as CoreConfig;
|
||||
}
|
||||
|
||||
function setIrcDmPolicy(cfg: CoreConfig, dmPolicy: DmPolicy): CoreConfig {
|
||||
return setTopLevelChannelDmPolicyWithAllowFrom({
|
||||
cfg,
|
||||
channel,
|
||||
dmPolicy,
|
||||
}) as CoreConfig;
|
||||
}
|
||||
|
||||
function setIrcAllowFrom(cfg: CoreConfig, allowFrom: string[]): CoreConfig {
|
||||
return setTopLevelChannelAllowFrom({
|
||||
cfg,
|
||||
channel,
|
||||
allowFrom,
|
||||
}) as CoreConfig;
|
||||
}
|
||||
|
||||
function setIrcNickServ(
|
||||
cfg: CoreConfig,
|
||||
accountId: string,
|
||||
nickserv?: IrcNickServConfig,
|
||||
): CoreConfig {
|
||||
return updateIrcAccountConfig(cfg, accountId, { nickserv });
|
||||
}
|
||||
|
||||
function setIrcGroupAccess(
|
||||
cfg: CoreConfig,
|
||||
accountId: string,
|
||||
policy: "open" | "allowlist" | "disabled",
|
||||
entries: string[],
|
||||
): 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,
|
||||
});
|
||||
}
|
||||
|
||||
async function promptIrcAllowFrom(params: {
|
||||
cfg: CoreConfig;
|
||||
prompter: WizardPrompter;
|
||||
@@ -264,55 +183,6 @@ const ircDmPolicy: ChannelOnboardingDmPolicy = {
|
||||
}),
|
||||
};
|
||||
|
||||
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;
|
||||
},
|
||||
};
|
||||
|
||||
export const ircSetupWizard: ChannelSetupWizard = {
|
||||
channel,
|
||||
status: {
|
||||
@@ -509,11 +379,17 @@ export const ircSetupWizard: ChannelSetupWizard = {
|
||||
updatePrompt: ({ cfg, accountId }) =>
|
||||
Boolean(resolveIrcAccount({ cfg: cfg as CoreConfig, accountId }).config.groups),
|
||||
setPolicy: ({ cfg, accountId, policy }) =>
|
||||
setIrcGroupAccess(cfg as CoreConfig, accountId, policy, []),
|
||||
setIrcGroupAccess(cfg as CoreConfig, accountId, policy, [], normalizeGroupEntry),
|
||||
resolveAllowlist: async ({ entries }) =>
|
||||
[...new Set(entries.map((entry) => normalizeGroupEntry(entry)).filter(Boolean))] as string[],
|
||||
applyAllowlist: ({ cfg, accountId, resolved }) =>
|
||||
setIrcGroupAccess(cfg as CoreConfig, accountId, "allowlist", resolved as string[]),
|
||||
setIrcGroupAccess(
|
||||
cfg as CoreConfig,
|
||||
accountId,
|
||||
"allowlist",
|
||||
resolved as string[],
|
||||
normalizeGroupEntry,
|
||||
),
|
||||
},
|
||||
allowFrom: {
|
||||
helpTitle: "IRC allowlist",
|
||||
@@ -584,3 +460,5 @@ export const ircSetupWizard: ChannelSetupWizard = {
|
||||
dmPolicy: ircDmPolicy,
|
||||
disable: (cfg) => setOnboardingChannelEnabled(cfg, channel, false),
|
||||
};
|
||||
|
||||
export { ircSetupAdapter };
|
||||
|
||||
Reference in New Issue
Block a user