fix(channels): clean up discord resolve typing

This commit is contained in:
Peter Steinberger
2026-01-18 00:59:23 +00:00
parent 075ff675ac
commit 4f0771f67b
4 changed files with 35 additions and 25 deletions

View File

@@ -1,12 +1,16 @@
import type { ClawdbotConfig } from "../../../config/config.js"; import type { ClawdbotConfig } from "../../../config/config.js";
import type { DmPolicy } from "../../../config/types.js"; import type { DmPolicy } from "../../../config/types.js";
import type { DiscordGuildEntry } from "../../../config/types.discord.js";
import { import {
listDiscordAccountIds, listDiscordAccountIds,
resolveDefaultDiscordAccountId, resolveDefaultDiscordAccountId,
resolveDiscordAccount, resolveDiscordAccount,
} from "../../../discord/accounts.js"; } from "../../../discord/accounts.js";
import { normalizeDiscordSlug } from "../../../discord/monitor/allow-list.js"; import { normalizeDiscordSlug } from "../../../discord/monitor/allow-list.js";
import { resolveDiscordChannelAllowlist } from "../../../discord/resolve-channels.js"; import {
resolveDiscordChannelAllowlist,
type DiscordChannelResolution,
} from "../../../discord/resolve-channels.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../routing/session-key.js"; import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../routing/session-key.js";
import { formatDocsLink } from "../../../terminal/links.js"; import { formatDocsLink } from "../../../terminal/links.js";
import type { WizardPrompter } from "../../../wizard/prompts.js"; import type { WizardPrompter } from "../../../wizard/prompts.js";
@@ -99,14 +103,12 @@ function setDiscordGuildChannelAllowlist(
accountId === DEFAULT_ACCOUNT_ID accountId === DEFAULT_ACCOUNT_ID
? (cfg.channels?.discord?.guilds ?? {}) ? (cfg.channels?.discord?.guilds ?? {})
: (cfg.channels?.discord?.accounts?.[accountId]?.guilds ?? {}); : (cfg.channels?.discord?.accounts?.[accountId]?.guilds ?? {});
const guilds: Record<string, { channels?: Record<string, { allow: boolean }> }> = { const guilds: Record<string, DiscordGuildEntry> = { ...baseGuilds };
...baseGuilds,
};
for (const entry of entries) { for (const entry of entries) {
const guildKey = entry.guildKey || "*"; const guildKey = entry.guildKey || "*";
const existing = guilds[guildKey] ?? {}; const existing = guilds[guildKey] ?? {};
if (entry.channelKey) { if (entry.channelKey) {
const channels = { ...(existing.channels ?? {}) }; const channels = { ...existing.channels };
channels[entry.channelKey] = { allow: true }; channels[entry.channelKey] = { allow: true };
guilds[guildKey] = { ...existing, channels }; guilds[guildKey] = { ...existing, channels };
} else { } else {
@@ -298,7 +300,10 @@ export const discordOnboardingAdapter: ChannelOnboardingAdapter = {
cfg: next, cfg: next,
accountId: discordAccountId, accountId: discordAccountId,
}); });
let resolved = accessConfig.entries.map((input) => ({ input, resolved: false })); let resolved: DiscordChannelResolution[] = accessConfig.entries.map((input) => ({
input,
resolved: false,
}));
if (accountWithTokens.token && accessConfig.entries.length > 0) { if (accountWithTokens.token && accessConfig.entries.length > 0) {
try { try {
resolved = await resolveDiscordChannelAllowlist({ resolved = await resolveDiscordChannelAllowlist({

View File

@@ -156,7 +156,7 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) {
token, token,
entries: entries.map((entry) => entry.input), entries: entries.map((entry) => entry.input),
}); });
const nextGuilds = { ...(guildEntries ?? {}) }; const nextGuilds = { ...guildEntries };
const mapping: string[] = []; const mapping: string[] = [];
const unresolved: string[] = []; const unresolved: string[] = [];
for (const entry of resolved) { for (const entry of resolved) {
@@ -173,10 +173,7 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) {
: `${entry.input}${entry.guildId}`, : `${entry.input}${entry.guildId}`,
); );
const existing = nextGuilds[entry.guildId] ?? {}; const existing = nextGuilds[entry.guildId] ?? {};
const mergedChannels = { const mergedChannels = { ...sourceGuild.channels, ...existing.channels };
...(sourceGuild.channels ?? {}),
...(existing.channels ?? {}),
};
const mergedGuild = { ...sourceGuild, ...existing, channels: mergedChannels }; const mergedGuild = { ...sourceGuild, ...existing, channels: mergedChannels };
nextGuilds[entry.guildId] = mergedGuild; nextGuilds[entry.guildId] = mergedGuild;
if (source.channelKey && entry.channelId) { if (source.channelKey && entry.channelId) {
@@ -188,7 +185,7 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) {
...mergedChannels, ...mergedChannels,
[entry.channelId]: { [entry.channelId]: {
...sourceChannel, ...sourceChannel,
...(mergedChannels?.[entry.channelId] ?? {}), ...mergedChannels?.[entry.channelId],
}, },
}, },
}; };
@@ -266,7 +263,7 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) {
.filter((entry) => !entry.resolved) .filter((entry) => !entry.resolved)
.map((entry) => entry.input); .map((entry) => entry.input);
const nextGuilds = { ...(guildEntries ?? {}) }; const nextGuilds = { ...guildEntries };
for (const [guildKey, guildConfig] of Object.entries(guildEntries ?? {})) { for (const [guildKey, guildConfig] of Object.entries(guildEntries ?? {})) {
if (!guildConfig || typeof guildConfig !== "object") continue; if (!guildConfig || typeof guildConfig !== "object") continue;
const nextGuild = { ...guildConfig } as Record<string, unknown>; const nextGuild = { ...guildConfig } as Record<string, unknown>;

View File

@@ -100,13 +100,19 @@ async function listGuildChannels(
)) as RESTGetAPIGuildChannelsResult; )) as RESTGetAPIGuildChannelsResult;
return raw return raw
.filter((channel) => Boolean(channel.id) && "name" in channel) .filter((channel) => Boolean(channel.id) && "name" in channel)
.map((channel) => ({ .map((channel) => {
id: channel.id, const archived =
name: "name" in channel ? channel.name ?? "" : "", "thread_metadata" in channel
guildId, ? (channel as { thread_metadata?: { archived?: boolean } }).thread_metadata?.archived
type: channel.type, : undefined;
archived: "thread_metadata" in channel ? channel.thread_metadata?.archived : undefined, return {
})) id: channel.id,
name: "name" in channel ? channel.name ?? "" : "",
guildId,
type: channel.type,
archived,
};
})
.filter((channel) => Boolean(channel.name)); .filter((channel) => Boolean(channel.name));
} }
@@ -231,19 +237,20 @@ export async function resolveDiscordChannelAllowlist(params: {
: parsed.guild : parsed.guild
? resolveGuildByName(guilds, parsed.guild) ? resolveGuildByName(guilds, parsed.guild)
: undefined; : undefined;
if (!guild || !parsed.channel) { const channelQuery = parsed.channel?.trim();
if (!guild || !channelQuery) {
results.push({ results.push({
input, input,
resolved: false, resolved: false,
guildId: parsed.guildId, guildId: parsed.guildId,
guildName: parsed.guild, guildName: parsed.guild,
channelName: parsed.channel, channelName: channelQuery ?? parsed.channel,
}); });
continue; continue;
} }
const channels = await getChannels(guild.id); const channels = await getChannels(guild.id);
const matches = channels.filter( const matches = channels.filter(
(channel) => normalizeDiscordSlug(channel.name) === normalizeDiscordSlug(parsed.channel), (channel) => normalizeDiscordSlug(channel.name) === normalizeDiscordSlug(channelQuery),
); );
const match = preferActiveMatch(matches); const match = preferActiveMatch(matches);
if (match) { if (match) {

View File

@@ -127,10 +127,11 @@ export async function resolveDiscordUserAllowlist(params: {
continue; continue;
} }
const guildName = parsed.guildName?.trim();
const guildList = parsed.guildId const guildList = parsed.guildId
? guilds.filter((g) => g.id === parsed.guildId) ? guilds.filter((g) => g.id === parsed.guildId)
: parsed.guildName : guildName
? guilds.filter((g) => g.slug === normalizeDiscordSlug(parsed.guildName)) ? guilds.filter((g) => g.slug === normalizeDiscordSlug(guildName))
: guilds; : guilds;
let best: { member: DiscordMember; guild: DiscordGuildSummary; score: number } | null = null; let best: { member: DiscordMember; guild: DiscordGuildSummary; score: number } | null = null;