From 4734c985c8a69f3610d82e37c681efddd824a74d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 14 Feb 2026 14:45:10 +0000 Subject: [PATCH] refactor(discord): share client rest helpers --- src/discord/client.ts | 60 +++++++++++++++++++++++++++++++++ src/discord/send.permissions.ts | 44 ++---------------------- src/discord/send.shared.ts | 51 ++-------------------------- 3 files changed, 64 insertions(+), 91 deletions(-) create mode 100644 src/discord/client.ts diff --git a/src/discord/client.ts b/src/discord/client.ts new file mode 100644 index 0000000000..6829dcc35d --- /dev/null +++ b/src/discord/client.ts @@ -0,0 +1,60 @@ +import { RequestClient } from "@buape/carbon"; +import type { RetryConfig } from "../infra/retry.js"; +import { loadConfig } from "../config/config.js"; +import { createDiscordRetryRunner, type RetryRunner } from "../infra/retry-policy.js"; +import { resolveDiscordAccount } from "./accounts.js"; +import { normalizeDiscordToken } from "./token.js"; + +export type DiscordClientOpts = { + token?: string; + accountId?: string; + rest?: RequestClient; + retry?: RetryConfig; + verbose?: boolean; +}; + +function resolveToken(params: { explicit?: string; accountId: string; fallbackToken?: string }) { + const explicit = normalizeDiscordToken(params.explicit); + if (explicit) { + return explicit; + } + const fallback = normalizeDiscordToken(params.fallbackToken); + if (!fallback) { + throw new Error( + `Discord bot token missing for account "${params.accountId}" (set discord.accounts.${params.accountId}.token or DISCORD_BOT_TOKEN for default).`, + ); + } + return fallback; +} + +function resolveRest(token: string, rest?: RequestClient) { + return rest ?? new RequestClient(token); +} + +export function createDiscordRestClient(opts: DiscordClientOpts, cfg = loadConfig()) { + const account = resolveDiscordAccount({ cfg, accountId: opts.accountId }); + const token = resolveToken({ + explicit: opts.token, + accountId: account.accountId, + fallbackToken: account.token, + }); + const rest = resolveRest(token, opts.rest); + return { token, rest, account }; +} + +export function createDiscordClient( + opts: DiscordClientOpts, + cfg = loadConfig(), +): { token: string; rest: RequestClient; request: RetryRunner } { + const { token, rest, account } = createDiscordRestClient(opts, cfg); + const request = createDiscordRetryRunner({ + retry: opts.retry, + configRetry: account.config.retry, + verbose: opts.verbose, + }); + return { token, rest, request }; +} + +export function resolveDiscordRest(opts: DiscordClientOpts) { + return createDiscordRestClient(opts).rest; +} diff --git a/src/discord/send.permissions.ts b/src/discord/send.permissions.ts index ae09e4c29f..fa95aa6604 100644 --- a/src/discord/send.permissions.ts +++ b/src/discord/send.permissions.ts @@ -1,11 +1,8 @@ +import type { RequestClient } from "@buape/carbon"; import type { APIChannel, APIGuild, APIGuildMember, APIRole } from "discord-api-types/v10"; -import { RequestClient } from "@buape/carbon"; import { ChannelType, PermissionFlagsBits, Routes } from "discord-api-types/v10"; -import type { RetryConfig } from "../infra/retry.js"; import type { DiscordPermissionsSummary, DiscordReactOpts } from "./send.types.js"; -import { loadConfig } from "../config/config.js"; -import { resolveDiscordAccount } from "./accounts.js"; -import { normalizeDiscordToken } from "./token.js"; +import { resolveDiscordRest } from "./client.js"; const PERMISSION_ENTRIES = Object.entries(PermissionFlagsBits).filter( ([, value]) => typeof value === "bigint", @@ -13,43 +10,6 @@ const PERMISSION_ENTRIES = Object.entries(PermissionFlagsBits).filter( const ALL_PERMISSIONS = PERMISSION_ENTRIES.reduce((acc, [, value]) => acc | value, 0n); const ADMINISTRATOR_BIT = PermissionFlagsBits.Administrator; -type DiscordClientOpts = { - token?: string; - accountId?: string; - rest?: RequestClient; - retry?: RetryConfig; - verbose?: boolean; -}; - -function resolveToken(params: { explicit?: string; accountId: string; fallbackToken?: string }) { - const explicit = normalizeDiscordToken(params.explicit); - if (explicit) { - return explicit; - } - const fallback = normalizeDiscordToken(params.fallbackToken); - if (!fallback) { - throw new Error( - `Discord bot token missing for account "${params.accountId}" (set discord.accounts.${params.accountId}.token or DISCORD_BOT_TOKEN for default).`, - ); - } - return fallback; -} - -function resolveRest(token: string, rest?: RequestClient) { - return rest ?? new RequestClient(token); -} - -function resolveDiscordRest(opts: DiscordClientOpts) { - const cfg = loadConfig(); - const account = resolveDiscordAccount({ cfg, accountId: opts.accountId }); - const token = resolveToken({ - explicit: opts.token, - accountId: account.accountId, - fallbackToken: account.token, - }); - return resolveRest(token, opts.rest); -} - function addPermissionBits(base: bigint, add?: string) { if (!add) { return base; diff --git a/src/discord/send.shared.ts b/src/discord/send.shared.ts index 872cfb9668..2f62c754e3 100644 --- a/src/discord/send.shared.ts +++ b/src/discord/send.shared.ts @@ -3,17 +3,16 @@ import { RequestClient } from "@buape/carbon"; import { PollLayoutType } from "discord-api-types/payloads/v10"; import { Routes } from "discord-api-types/v10"; import type { ChunkMode } from "../auto-reply/chunk.js"; -import type { RetryConfig } from "../infra/retry.js"; +import type { RetryRunner } from "../infra/retry-policy.js"; import { loadConfig } from "../config/config.js"; -import { createDiscordRetryRunner, type RetryRunner } from "../infra/retry-policy.js"; import { normalizePollDurationHours, normalizePollInput, type PollInput } from "../polls.js"; import { loadWebMedia } from "../web/media.js"; import { resolveDiscordAccount } from "./accounts.js"; import { chunkDiscordTextWithMode } from "./chunk.js"; +import { createDiscordClient, resolveDiscordRest } from "./client.js"; import { fetchChannelPermissionsDiscord, isThreadChannelType } from "./send.permissions.js"; import { DiscordSendError } from "./send.types.js"; import { parseDiscordTarget, resolveDiscordTarget } from "./targets.js"; -import { normalizeDiscordToken } from "./token.js"; const DISCORD_TEXT_LIMIT = 2000; const DISCORD_MAX_STICKERS = 3; @@ -34,52 +33,6 @@ type DiscordRecipient = id: string; }; -type DiscordClientOpts = { - token?: string; - accountId?: string; - rest?: RequestClient; - retry?: RetryConfig; - verbose?: boolean; -}; - -function resolveToken(params: { explicit?: string; accountId: string; fallbackToken?: string }) { - const explicit = normalizeDiscordToken(params.explicit); - if (explicit) { - return explicit; - } - const fallback = normalizeDiscordToken(params.fallbackToken); - if (!fallback) { - throw new Error( - `Discord bot token missing for account "${params.accountId}" (set discord.accounts.${params.accountId}.token or DISCORD_BOT_TOKEN for default).`, - ); - } - return fallback; -} - -function resolveRest(token: string, rest?: RequestClient) { - return rest ?? new RequestClient(token); -} - -function createDiscordClient(opts: DiscordClientOpts, cfg = loadConfig()) { - const account = resolveDiscordAccount({ cfg, accountId: opts.accountId }); - const token = resolveToken({ - explicit: opts.token, - accountId: account.accountId, - fallbackToken: account.token, - }); - const rest = resolveRest(token, opts.rest); - const request = createDiscordRetryRunner({ - retry: opts.retry, - configRetry: account.config.retry, - verbose: opts.verbose, - }); - return { token, rest, request }; -} - -function resolveDiscordRest(opts: DiscordClientOpts) { - return createDiscordClient(opts).rest; -} - function normalizeReactionEmoji(raw: string) { const trimmed = raw.trim(); if (!trimmed) {