import { createAuthenticatedFetch } from 'kody:runtime'
import {
DISCORD_API_BASE_URL,
DISCORD_USER_AGENT,
missingOauthIntegrationError,
throwDiscordApiError,
} from './errors.ts'
import { integrationName, type InputRecord } from './validation.ts'
const fetchers = new Map<string, typeof fetch>()
async function oauthFetch(integration: string): Promise<typeof fetch> {
const cached = fetchers.get(integration)
if (cached) return cached
try {
const authed = await createAuthenticatedFetch(integration)
fetchers.set(integration, authed)
return authed
} catch (error) {
throw missingOauthIntegrationError(integration, error)
}
}
export async function discordOauthRequest(
path: string,
input: InputRecord,
init: { method?: string; query?: Record<string, string | number | undefined> } = {},
): Promise<unknown> {
const integration = integrationName(input)
const url = new URL(DISCORD_API_BASE_URL + path)
for (const [key, value] of Object.entries(init.query ?? {})) {
if (value === undefined) continue
url.searchParams.set(key, String(value))
}
const authedFetch = await oauthFetch(integration)
const response = await authedFetch(url.toString(), {
method: init.method ?? 'GET',
headers: {
accept: 'application/json',
'User-Agent': DISCORD_USER_AGENT,
},
})
const text = await response.text()
let json: unknown = null
if (text) {
try {
json = JSON.parse(text)
} catch {
json = text
}
}
if (!response.ok) {
throwDiscordApiError({
status: response.status,
path: url.pathname + url.search,
lane: 'oauth',
details: json ?? text,
integration,
})
}
return json
}