import { createAuthenticatedFetch } from 'kody:runtime' export const SLACK_API_ORIGIN = 'https://slack.com' export const SLACK_INTEGRATION = 'slack' export type SlackResponse = { ok: boolean error?: string needed?: string provided?: string response_metadata?: { next_cursor?: string } [key: string]: unknown } export class SlackApiError extends Error { readonly method: string readonly status: number readonly details: SlackResponse constructor(method: string, status: number, details: SlackResponse) { super('Slack API ' + method + ' failed: ' + (details.error || String(status))) this.name = 'SlackApiError' this.method = method this.status = status this.details = details } } let authenticatedFetch: typeof fetch | null = null async function getAuthenticatedFetch(): Promise { if (!authenticatedFetch) { authenticatedFetch = await createAuthenticatedFetch(SLACK_INTEGRATION) } return authenticatedFetch } export async function slackRequest( method: string, params: Record = {}, ): Promise { if (!/^[a-z][a-z0-9_.]+$/.test(method)) { throw new Error('Invalid Slack Web API method name.') } const body = new URLSearchParams() for (const [key, value] of Object.entries(params)) { if (value === undefined || value === null || value === '') continue body.set(key, typeof value === 'object' ? JSON.stringify(value) : String(value)) } const url = new URL('/api/' + method, SLACK_API_ORIGIN) const authedFetch = await getAuthenticatedFetch() const response = await authedFetch(url.toString(), { method: 'POST', headers: { accept: 'application/json', 'content-type': 'application/x-www-form-urlencoded; charset=utf-8', }, body, }) const result = (await response.json()) as SlackResponse if (!response.ok || result.ok !== true) { throw new SlackApiError(method, response.status, result) } return result as T } export function nextCursor(response: SlackResponse): string | null { const cursor = response.response_metadata?.next_cursor return typeof cursor === 'string' && cursor.length > 0 ? cursor : null }