import { container } from "@sapphire/framework"; import config from "@/config"; import type { BanResponse, RobloxUser } from "./types"; const COOKIE = config.roblox.cookie; let CSRF_TOKEN: string | undefined; const request = (url: URL, options?: RequestInit) => fetch(url, { headers: { Cookie: COOKIE, }, ...options, }); export async function getUser(id: string): Promise { const url = new URL(`https://users.roblox.com/v1/users/${id}`); const response = await request(url); return (await response.json()) as RobloxUser; } export async function ban( userId: string, universeId: string, options: { duration: string | undefined; excludeAltAccounts: boolean; displayReason: string; privateReason: string; }, ): Promise { container.logger.debug(`banning ${userId} in universe ${universeId}`, { name: "ban", ...options, }); const makeRequest = async () => { const res = await fetch( `https://apis.roblox.com/user/cloud/v2/universes/${universeId}/user-restrictions/${userId}`, { method: "PATCH", headers: { "Content-Type": "application/json", Cookie: config.roblox.cookie, Host: "apis.roblox.com", Origin: "https://create.roblox.com", Referer: "https://create.roblox.com", "x-csrf-token": CSRF_TOKEN ?? "", }, body: JSON.stringify({ gameJoinRestriction: { active: true, ...options, }, }), }, ); const data = (await res.json()) as Record; container.logger.debug("ban response", { ...data }); if (res.status === 403) { const csrfHeader = res.headers.get("x-csrf-token"); if (csrfHeader && csrfHeader !== "") { CSRF_TOKEN = csrfHeader; return await makeRequest(); } } if (!res.ok) { throw new Error(`failed to ban user: ${res.status} ${res.statusText}`); } return data as BanResponse; }; return await makeRequest(); } export * from "./types";