const { getSetting } = require("./settings"); const YOUTUBE_SCOPES = [ "https://www.googleapis.com/auth/youtube", "https://www.googleapis.com/auth/youtube.force-ssl", "https://www.googleapis.com/auth/youtube.readonly", "https://www.googleapis.com/auth/youtube.channel-memberships.creator" ]; function getDiscordRedirectUri(override) { return override || getSetting("discord_redirect_uri", null); } function buildDiscordAuthUrl(state, redirectOverride) { const clientId = getSetting("discord_client_id"); const redirectUri = getDiscordRedirectUri(redirectOverride); const params = new URLSearchParams({ client_id: clientId || "", redirect_uri: redirectUri || "", response_type: "code", scope: "identify guilds guilds.members.read", state }); return `https://discord.com/api/oauth2/authorize?${params.toString()}`; } async function exchangeDiscordCode(code, redirectOverride) { const clientId = getSetting("discord_client_id"); const clientSecret = getSetting("discord_client_secret"); const redirectUri = getDiscordRedirectUri(redirectOverride); const body = new URLSearchParams({ client_id: clientId || "", client_secret: clientSecret || "", grant_type: "authorization_code", code, redirect_uri: redirectUri || "" }); const response = await fetch("https://discord.com/api/oauth2/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body }); if (!response.ok) { throw new Error(`Discord token exchange failed: ${response.status}`); } return response.json(); } async function fetchDiscordUser(accessToken) { const response = await fetch("https://discord.com/api/users/@me", { headers: { Authorization: `Bearer ${accessToken}` } }); if (!response.ok) { throw new Error(`Discord user fetch failed: ${response.status}`); } return response.json(); } async function fetchDiscordGuildMember(accessToken, guildId) { const response = await fetch( `https://discord.com/api/users/@me/guilds/${guildId}/member`, { headers: { Authorization: `Bearer ${accessToken}` } } ); if (!response.ok) { return null; } return response.json(); } function buildTwitchAuthUrl(state, redirectOverride) { const clientId = getSetting("twitch_client_id"); const redirectUri = redirectOverride || getSetting("twitch_redirect_uri"); const params = new URLSearchParams({ client_id: clientId || "", redirect_uri: redirectUri || "", response_type: "code", scope: "user:read:email", state }); return `https://id.twitch.tv/oauth2/authorize?${params.toString()}`; } async function exchangeTwitchCode(code, redirectOverride) { const clientId = getSetting("twitch_client_id"); const clientSecret = getSetting("twitch_client_secret"); const redirectUri = redirectOverride || getSetting("twitch_redirect_uri"); const params = new URLSearchParams({ client_id: clientId || "", client_secret: clientSecret || "", code, grant_type: "authorization_code", redirect_uri: redirectUri || "" }); const response = await fetch( `https://id.twitch.tv/oauth2/token?${params.toString()}`, { method: "POST" } ); if (!response.ok) { throw new Error(`Twitch token exchange failed: ${response.status}`); } return response.json(); } async function fetchTwitchUser(accessToken) { const clientId = getSetting("twitch_client_id"); const response = await fetch("https://api.twitch.tv/helix/users", { headers: { "Client-Id": clientId || "", Authorization: `Bearer ${accessToken}` } }); if (!response.ok) { throw new Error(`Twitch user fetch failed: ${response.status}`); } const data = await response.json(); return data.data?.[0] || null; } function buildYouTubeAuthUrl(state, redirectOverride, options = {}) { const clientId = getSetting("youtube_client_id"); const redirectUri = redirectOverride || getSetting("youtube_redirect_uri"); const scopes = options.scopes || YOUTUBE_SCOPES; const params = new URLSearchParams({ client_id: clientId || "", redirect_uri: redirectUri || "", response_type: "code", scope: scopes.join(" "), state, access_type: "offline", include_granted_scopes: "true" }); if (options.prompt) { params.set("prompt", options.prompt); } return `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`; } async function exchangeYouTubeCode(code, redirectOverride) { const clientId = getSetting("youtube_client_id"); const clientSecret = getSetting("youtube_client_secret"); const redirectUri = redirectOverride || getSetting("youtube_redirect_uri"); const body = new URLSearchParams({ client_id: clientId || "", client_secret: clientSecret || "", grant_type: "authorization_code", code, redirect_uri: redirectUri || "" }); const response = await fetch("https://oauth2.googleapis.com/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body }); if (!response.ok) { throw new Error(`YouTube token exchange failed: ${response.status}`); } return response.json(); } async function fetchYouTubeChannel(accessToken) { const response = await fetch( "https://www.googleapis.com/youtube/v3/channels?part=snippet&mine=true", { headers: { Authorization: `Bearer ${accessToken}` } } ); if (!response.ok) { throw new Error(`YouTube channel fetch failed: ${response.status}`); } const data = await response.json(); return data.items?.[0] || null; } module.exports = { buildDiscordAuthUrl, exchangeDiscordCode, fetchDiscordUser, fetchDiscordGuildMember, buildTwitchAuthUrl, exchangeTwitchCode, fetchTwitchUser, buildYouTubeAuthUrl, exchangeYouTubeCode, fetchYouTubeChannel };