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
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;
|
|
|