refactor!: rename chat providers to channels

This commit is contained in:
Peter Steinberger
2026-01-13 06:16:43 +00:00
parent 0cd632ba84
commit 90342a4f3a
393 changed files with 8004 additions and 6737 deletions

View File

@@ -0,0 +1,203 @@
import type { ClawdbotConfig } from "../../../config/config.js";
import type { DmPolicy } from "../../../config/types.js";
import {
listDiscordAccountIds,
resolveDefaultDiscordAccountId,
resolveDiscordAccount,
} from "../../../discord/accounts.js";
import {
DEFAULT_ACCOUNT_ID,
normalizeAccountId,
} from "../../../routing/session-key.js";
import { formatDocsLink } from "../../../terminal/links.js";
import type { WizardPrompter } from "../../../wizard/prompts.js";
import type {
ChannelOnboardingAdapter,
ChannelOnboardingDmPolicy,
} from "../onboarding-types.js";
import { addWildcardAllowFrom, promptAccountId } from "./helpers.js";
const channel = "discord" as const;
function setDiscordDmPolicy(cfg: ClawdbotConfig, dmPolicy: DmPolicy) {
const allowFrom =
dmPolicy === "open"
? addWildcardAllowFrom(cfg.channels?.discord?.dm?.allowFrom)
: undefined;
return {
...cfg,
channels: {
...cfg.channels,
discord: {
...cfg.channels?.discord,
dm: {
...cfg.channels?.discord?.dm,
enabled: cfg.channels?.discord?.dm?.enabled ?? true,
policy: dmPolicy,
...(allowFrom ? { allowFrom } : {}),
},
},
},
};
}
async function noteDiscordTokenHelp(prompter: WizardPrompter): Promise<void> {
await prompter.note(
[
"1) Discord Developer Portal → Applications → New Application",
"2) Bot → Add Bot → Reset Token → copy token",
"3) OAuth2 → URL Generator → scope 'bot' → invite to your server",
"Tip: enable Message Content Intent if you need message text.",
`Docs: ${formatDocsLink("/discord", "discord")}`,
].join("\n"),
"Discord bot token",
);
}
const dmPolicy: ChannelOnboardingDmPolicy = {
label: "Discord",
channel,
policyKey: "channels.discord.dm.policy",
allowFromKey: "channels.discord.dm.allowFrom",
getCurrent: (cfg) => cfg.channels?.discord?.dm?.policy ?? "pairing",
setPolicy: (cfg, policy) => setDiscordDmPolicy(cfg, policy),
};
export const discordOnboardingAdapter: ChannelOnboardingAdapter = {
channel,
getStatus: async ({ cfg }) => {
const configured = listDiscordAccountIds(cfg).some((accountId) =>
Boolean(resolveDiscordAccount({ cfg, accountId }).token),
);
return {
channel,
configured,
statusLines: [`Discord: ${configured ? "configured" : "needs token"}`],
selectionHint: configured ? "configured" : "needs token",
quickstartScore: configured ? 2 : 1,
};
},
configure: async ({
cfg,
prompter,
accountOverrides,
shouldPromptAccountIds,
}) => {
const discordOverride = accountOverrides.discord?.trim();
const defaultDiscordAccountId = resolveDefaultDiscordAccountId(cfg);
let discordAccountId = discordOverride
? normalizeAccountId(discordOverride)
: defaultDiscordAccountId;
if (shouldPromptAccountIds && !discordOverride) {
discordAccountId = await promptAccountId({
cfg,
prompter,
label: "Discord",
currentId: discordAccountId,
listAccountIds: listDiscordAccountIds,
defaultAccountId: defaultDiscordAccountId,
});
}
let next = cfg;
const resolvedAccount = resolveDiscordAccount({
cfg: next,
accountId: discordAccountId,
});
const accountConfigured = Boolean(resolvedAccount.token);
const allowEnv = discordAccountId === DEFAULT_ACCOUNT_ID;
const canUseEnv =
allowEnv && Boolean(process.env.DISCORD_BOT_TOKEN?.trim());
const hasConfigToken = Boolean(resolvedAccount.config.token);
let token: string | null = null;
if (!accountConfigured) {
await noteDiscordTokenHelp(prompter);
}
if (canUseEnv && !resolvedAccount.config.token) {
const keepEnv = await prompter.confirm({
message: "DISCORD_BOT_TOKEN detected. Use env var?",
initialValue: true,
});
if (keepEnv) {
next = {
...next,
channels: {
...next.channels,
discord: { ...next.channels?.discord, enabled: true },
},
};
} else {
token = String(
await prompter.text({
message: "Enter Discord bot token",
validate: (value) => (value?.trim() ? undefined : "Required"),
}),
).trim();
}
} else if (hasConfigToken) {
const keep = await prompter.confirm({
message: "Discord token already configured. Keep it?",
initialValue: true,
});
if (!keep) {
token = String(
await prompter.text({
message: "Enter Discord bot token",
validate: (value) => (value?.trim() ? undefined : "Required"),
}),
).trim();
}
} else {
token = String(
await prompter.text({
message: "Enter Discord bot token",
validate: (value) => (value?.trim() ? undefined : "Required"),
}),
).trim();
}
if (token) {
if (discordAccountId === DEFAULT_ACCOUNT_ID) {
next = {
...next,
channels: {
...next.channels,
discord: { ...next.channels?.discord, enabled: true, token },
},
};
} else {
next = {
...next,
channels: {
...next.channels,
discord: {
...next.channels?.discord,
enabled: true,
accounts: {
...next.channels?.discord?.accounts,
[discordAccountId]: {
...next.channels?.discord?.accounts?.[discordAccountId],
enabled:
next.channels?.discord?.accounts?.[discordAccountId]
?.enabled ?? true,
token,
},
},
},
},
};
}
}
return { cfg: next, accountId: discordAccountId };
},
dmPolicy,
disable: (cfg) => ({
...cfg,
channels: {
...cfg.channels,
discord: { ...cfg.channels?.discord, enabled: false },
},
}),
};