UNPKG

@osiris-ai/discord-sdk

Version:
199 lines (196 loc) 6.98 kB
import { OAuthAuthenticator } from '@osiris-ai/sdk'; import axios from 'axios'; // @osiris-ai/discord-sdk - Discord SDK for Authentication and API Clients var DiscordAuthenticator = class extends OAuthAuthenticator { config; allowedScopes; constructor(allowedScopes, config) { const authUrl = "https://discord.com/api/oauth2/authorize"; const tokenUrl = "https://discord.com/api/oauth2/token"; const baseApiUrl = "https://discord.com/api/v10"; super("discord", baseApiUrl, authUrl, tokenUrl, allowedScopes, config); this.config = config; this.allowedScopes = allowedScopes; } getAuthenticationURL(scopes, options) { const invalidScopes = scopes.filter((scope) => !this.allowedScopes.includes(scope)); if (invalidScopes.length > 0) { throw new Error(`Invalid scopes: ${invalidScopes.join(", ")}`); } if (!options.state || !options.prompt) { throw new Error("Missing required options"); } const url = new URL(this.authUrl); url.searchParams.set("client_id", this.config.clientId); url.searchParams.set("redirect_uri", this.config.redirectUri); url.searchParams.set("scope", scopes.join(" ")); url.searchParams.set("response_type", "code"); if (options.state) { url.searchParams.set("state", options.state); } if (options.prompt) { url.searchParams.set("prompt", options.prompt); } if (scopes.includes("bot") && options.permissions) { url.searchParams.set("permissions", options.permissions.toString()); } if (options.guildId) { url.searchParams.set("guild_id", options.guildId); } return url.toString(); } async callback(params) { try { if (!params.code) { throw new Error("Missing code"); } const formData = new URLSearchParams(); formData.append("client_id", this.config.clientId); formData.append("client_secret", this.config.clientSecret); formData.append("grant_type", params.grantType); formData.append("redirect_uri", this.config.redirectUri); formData.append("code", params.code); const response = await axios.post(this.tokenUrl, formData, { headers: { "Content-Type": "application/x-www-form-urlencoded" } }); const data = response.data; if (data.error) { throw new Error(`Discord OAuth error: ${data.error_description || data.error}`); } const userInfo = await this.getUserInfo(data.access_token); const transformedData = { accessToken: data.access_token, refreshToken: data.refresh_token, expiresIn: data.expires_in, tokenType: data.token_type || "Bearer", scopes: data.scope ? data.scope.split(" ") : [], guild: data.guild, user: userInfo }; return transformedData; } catch (error) { throw new Error( `Failed to exchange token: ${error instanceof Error ? error.message : "Unknown error"}` ); } } async refreshToken(refreshToken) { try { const formData = new URLSearchParams(); formData.append("client_id", this.config.clientId); formData.append("client_secret", this.config.clientSecret); formData.append("grant_type", "refresh_token"); formData.append("refresh_token", refreshToken); const response = await axios.post(this.tokenUrl, formData, { headers: { "Content-Type": "application/x-www-form-urlencoded" } }); const data = response.data; if (data.error) { throw new Error(`Discord OAuth error: ${data.error_description || data.error}`); } const transformedData = { accessToken: data.access_token, refreshToken: data.refresh_token || refreshToken, expiresIn: data.expires_in, tokenType: data.token_type || "Bearer", scopes: data.scope ? data.scope.split(" ") : [] }; return transformedData; } catch (error) { throw new Error( `Failed to refresh token: ${error instanceof Error ? error.message : "Unknown error"}` ); } } async getUserInfo(accessToken) { try { const response = await axios.get(`${this.baseApiUrl}/users/@me`, { headers: { Authorization: `Bearer ${accessToken}` } }); return { uniqueId: response.data.id, id: response.data.id, username: response.data.username, avatar: response.data.avatar, discriminator: response.data.discriminator, public_flags: response.data.public_flags, email: response.data.email }; } catch (error) { throw new Error( `Failed to fetch user info: ${error instanceof Error ? error.message : "Unknown error"}` ); } } async action(params, accessToken, refreshToken, retry = false) { try { const useBot = params.data?.useBot === true; const { useBot: _useBot, ...data } = params.data || {}; const finalData = Object.keys(data).length === 0 ? void 0 : data; const tokenType = useBot ? "Bot" : "Bearer"; const request = await axios({ url: `${this.baseApiUrl}${params.url}`, method: params.method, data: finalData, headers: { "Content-Type": "application/json", Authorization: `${tokenType} ${accessToken}` } }); const response = { data: request.data, headers: request.headers, status: request.status, statusText: request.statusText }; return response; } catch (error) { console.log(error); const isAxiosError = error?.isAxiosError || error?.response; if (isAxiosError && error.response) { if (error.response.status === 429) { const retryAfter = error.response.headers["retry-after"]; return { status: 429, statusText: `Rate limited. Retry after ${retryAfter} seconds.`, data: error.response.data }; } if (error.response.status === 401 && refreshToken && !retry) { try { const newTokens = await this.refreshToken(refreshToken); return await this.action(params, newTokens.accessToken, refreshToken, true); } catch (refreshError) { return { status: 401, statusText: `Failed to refresh token: ${refreshError.message}`, data: null }; } } return { status: error.response.status, statusText: error.response.data?.message || "Error contacting Discord API", data: error.response.data }; } return { status: 500, statusText: "We are facing issues contacting Discord API, please try again later", data: null }; } } }; export { DiscordAuthenticator }; //# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map