@osiris-ai/discord-sdk
Version:
Osiris Discord SDK
199 lines (196 loc) • 6.98 kB
JavaScript
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