The backend server for the ngsplanner app.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

218 lines
7.6 KiB

"use strict";
const RequestHandler = require("./eris/rest/RequestHandler");
/**
* Make requests to discord's OAuth2 API
* @extends requestHandler
*/
class OAuth extends RequestHandler {
/**
*
* @arg {Object} opts
* @arg {String?} opts.version The version of the Discord API to use. Defaults to v7.
* @arg {Number} [opts.requestTimeout=15000] A number of milliseconds before requests are considered timed out
* @arg {Number} [opts.latencyThreshold=30000] The average request latency at which the RequestHandler will start emitting latency errors
* @arg {Number} [opts.ratelimiterOffset=0] A number of milliseconds to offset the ratelimit timing calculations by
* @arg {String?} opts.clientId Your application's client id
* @arg {String?} opts.clientSecret Your application's client secret
* @arg {String?} opts.redirectUri Your URL redirect uri
* @arg {String?} opts.credentials Base64 encoding of the UTF-8 encoded credentials string of your application
*/
constructor(opts = {}) {
super({
version: opts.version || "v7",
requestTimeout: opts.requestTimeout || 15000,
latencyThreshold: opts.latencyThreshold || 30000,
ratelimiterOffset: opts.ratelimiterOffset || 0,
});
this.client_id = opts.clientId;
this.client_secret = opts.clientSecret;
this.redirect_uri = opts.redirectUri;
this.credentials = opts.credentials;
}
_encode(obj) {
let string = "";
for (const [key, value] of Object.entries(obj)) {
if (!value) continue;
string += `&${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
}
return string.substring(1);
}
/**
* Exchange the code returned by discord in the query for the user access token
* If specified, can also use refresh_token to get a new valid token
* Read discord's OAuth2 documentation for a full example (https://discord.com/developers/docs/topics/oauth2)
* @arg {Object} opts The object containing the parameters for the request
* @arg {String?} opts.clientId Your application's client id
* @arg {String?} opts.clientSecret Your application's client secret
* @arg {String} opts.grantType Either authorization_code or refresh_token
* @arg {String?} opts.code The code from the querystring
* @arg {String?} opts.refreshToken The user's refresh token
* @arg {String?} opts.redirectUri Your URL redirect uri
* @arg {String} opts.scope The scopes requested in your authorization url, space-delimited
* @returns {Promise<Object>}
*/
tokenRequest(opts = {}) {
const obj = {
client_id: opts.clientId || this.client_id,
client_secret: opts.clientSecret || this.client_secret,
grant_type: undefined,
code: undefined,
refresh_token: undefined,
redirect_uri: opts.redirectUri || this.redirect_uri,
scope: opts.scope instanceof Array ? opts.scope.join(" ") : opts.scope,
};
if (opts.grantType === "authorization_code") {
obj.code = opts.code;
obj.grant_type = opts.grantType;
}
else if (opts.grantType === "refresh_token") {
obj.refresh_token = opts.refreshToken;
obj.grant_type = opts.grantType;
}
else throw new Error("Invalid grant_type provided, it must be either authorization_code or refresh_token");
const encoded_string = this._encode(obj);
return this.request("POST", "/oauth2/token", encoded_string, {
contentType: "application/x-www-form-urlencoded",
});
}
/**
* Revoke the user access token
* @arg {String} access_token The user access token
* @arg {String} credentials Base64 encoding of the UTF-8 encoded credentials string of your application
* @returns {Promise<String>}
*/
revokeToken(access_token, credentials) {
if (!credentials && !this.credentials) throw new Error("Missing credentials for revokeToken method");
return this.request("POST", "/oauth2/token/revoke", `token=${access_token}`, {
auth: {
type: "Basic",
creds: credentials || this.credentials,
},
contentType: "application/x-www-form-urlencoded",
});
}
/**
* Request basic user data
* Requires the `identify` scope
* @arg {String} access_token The user access token
* @returns {Promise<Object>}
*/
getUser(access_token) {
return this.request("GET", "/users/@me", undefined, {
auth: {
type: "Bearer",
creds: access_token,
},
contentType: "application/json",
});
}
/**
* Request all the guilds the user is in
* Requires the `guilds` scope
* @arg {String} access_token The user access token
* @returns {Promise<Object[]>}
*/
getUserGuilds(access_token) {
return this.request("GET", "/users/@me/guilds", undefined, {
auth: {
type: "Bearer",
creds: access_token,
},
contentType: "application/json",
});
}
/**
* Request a user's connections
* Requires the `connections` scope
* @arg {String} access_token The user access token
* @returns {Promise<Object[]>}
*/
getUserConnections(access_token) {
return this.request("GET", "/users/@me/connections", undefined, {
auth: {
type: "Bearer",
creds: access_token,
},
contentType: "application/json",
});
}
/**
* Force a user to join a guild
* Requires the `guilds.join` scope
* @arg {Object} opts
* @arg {String} opts.guildId The ID of the guild to join
* @arg {String} opts.userId The ID of the user to be added to the guild
* @arg {Boolean?} opts.deaf Whether the user is deafened in voice channels
* @arg {Boolean?} opts.mute Whether the user is muted in voice channels
* @arg {String?} opts.nickname Value to set users nickname to
* @arg {String[]?} opts.roles Array of role ids the member is assigned
* @arg {String} opts.accessToken The user access token
* @arg {String} opts.botToken The token of the bot used to authenticate
* @returns {Promise<Object | String>}
*/
addMember(opts) {
return this.request("PUT", `/guilds/${opts.guildId}/members/${opts.userId}`, {
deaf: opts.deaf,
mute: opts.mute,
nick: opts.nickname,
roles: opts.roles,
access_token: opts.accessToken,
}, {
auth: {
type: "Bot",
creds: opts.botToken,
},
contentType: "application/json",
});
}
/**
*
* @arg {Object} opts
* @arg {String} opts.clientId Your application's client id
* @arg {String?} opts.prompt Controls how existing authorizations are handled, either consent or none (for passthrough scopes authorization is always required).
* @arg {String?} opts.redirectUri Your URL redirect uri
* @arg {String?} opts.responseType The response type, either code or token (token is for client-side web applications only). Defaults to code
* @arg {String | Array} opts.scope The scopes for your URL
* @arg {String?} opts.state A unique cryptographically secure string (https://discord.com/developers/docs/topics/oauth2#state-and-security)
* @arg {Number?} opts.permissions The permissions number for the bot invite (only with bot scope) (https://discord.com/developers/docs/topics/permissions)
* @arg {String?} opts.guildId The guild id to pre-fill the bot invite (only with bot scope)
* @arg {Boolean?} opts.disableGuildSelect Disallows the user from changing the guild for the bot invite, either true or false (only with bot scope)
* @returns {String}
*/
generateAuthUrl(opts = {}) {
const obj = {
client_id: opts.clientId || this.client_id,
prompt: opts.prompt || undefined,
redirect_uri: opts.redirectUri || this.redirect_uri,
response_type: opts.responseType || "code",
scope: opts.scope instanceof Array ? opts.scope.join(" ") : opts.scope,
permissions: opts.permissions || undefined,
guild_id: opts.guildId || undefined,
disable_guild_select: opts.disableGuildSelect || undefined,
state: opts.state || undefined,
};
const encoded_string = this._encode(obj);
return `https://discord.com/api/oauth2/authorize?${encoded_string}`;
}
}
module.exports = OAuth;