discord.js-selfbot-v13/src/structures/User.js

448 lines
10 KiB
JavaScript
Raw Normal View History

2022-03-19 10:37:45 +00:00
'use strict';
const Base = require('./Base');
const { Error } = require('../errors/DJSError');
const { DiscordSnowflake } = require('@sapphire/snowflake');
const UserFlagsBitField = require('../util/UserFlagsBitField');
const { default: Collection } = require('@discordjs/collection');
const TextBasedChannel = require('./interfaces/TextBasedChannel');
/**
* Represents a user on Discord.
* @implements {TextBasedChannel}
* @extends {Base}
*/
class User extends Base {
constructor(client, data) {
super(client);
/**
* The user's id
* @type {Snowflake}
*/
this.id = data.id;
this.bot = null;
this.system = null;
this.flags = null;
this.friend = client.friends.cache.has(this.id);
this.blocked = client.blocked.cache.has(this.id);
// Code written by https://github.com/aiko-chan-ai
this.connectedAccounds = [];
this.premiumSince = null;
this.premiumGuildSince = null;
this.mutualGuilds = new Collection();
this._patch(data);
}
_patch(data) {
if ('username' in data) {
/**
* The username of the user
* @type {?string}
*/
this.username = data.username;
} else {
this.username ??= null;
}
if ('bot' in data) {
/**
* Whether or not the user is a bot
* @type {?boolean}
*/
this.bot = Boolean(data.bot);
} else if (!this.partial && typeof this.bot !== 'boolean') {
this.bot = false;
}
if ('discriminator' in data) {
/**
* A discriminator based on username for the user
* @type {?string}
*/
this.discriminator = data.discriminator;
} else {
this.discriminator ??= null;
}
if ('avatar' in data) {
/**
* The user avatar's hash
* @type {?string}
*/
this.avatar = data.avatar;
} else {
this.avatar ??= null;
}
if ('banner' in data) {
/**
* The user banner's hash
* <info>The user must be force fetched for this property to be present or be updated</info>
* @type {?string}
*/
this.banner = data.banner;
} else if (this.banner !== null) {
this.banner ??= undefined;
}
if ('accent_color' in data) {
/**
* The base 10 accent color of the user's banner
* <info>The user must be force fetched for this property to be present or be updated</info>
* @type {?number}
*/
this.accentColor = data.accent_color;
} else if (this.accentColor !== null) {
this.accentColor ??= undefined;
}
if ('system' in data) {
/**
* Whether the user is an Official Discord System user (part of the urgent message system)
* @type {?boolean}
*/
this.system = Boolean(data.system);
} else if (!this.partial && typeof this.system !== 'boolean') {
this.system = false;
}
if ('public_flags' in data) {
/**
* The flags for this user
* @type {?UserFlagsBitField}
*/
this.flags = new UserFlagsBitField(data.public_flags);
}
}
// Code written by https://github.com/aiko-chan-ai
_ProfilePatch(data) {
if (!data) return;
if (data.connected_accounts.length > 0)
this.connectedAccounds = data.connected_accounts;
if ('premium_since' in data) {
const date = new Date(data.premium_since);
this.premiumSince = date.getTime();
}
if ('premium_guild_since' in data) {
const date = new Date(data.premium_guild_since);
this.premiumGuildSince = date.getTime();
}
this.mutualGuilds = new Collection(
data.mutual_guilds.map((obj) => [obj.id, obj]),
);
}
/**
* Get profile from Discord, if client is in a server with the target.
* <br>Code written by https://github.com/aiko-chan-ai
*/
async getProfile() {
if (this.client.bot) throw new Error('INVALID_BOT_METHOD');
try {
const data = await this.client.api.users(this.id).profile.get();
this._ProfilePatch(data);
return this;
} catch (e) {
throw e;
}
}
/**
* Friends the user and send Request [If no request]
* @returns {Promise<User>} the user object
*/
async setFriend() {
return await this.client.api
2022-03-21 11:15:44 +00:00
.user('@me')
.relationships[this.id].put({ data: { type: 1 } })
.then((_) => _);
}
/**
* Send Friend Request to the user
* @returns {Promise<User>} the user object
*/
async sendFriendRequest() {
return await this.client.api
2022-03-21 11:15:44 +00:00
.users('@me')
.relationships.post({
data: {
username: this.username,
discriminator: parseInt(this.discriminator),
},
})
.then((_) => _);
}
/**
* Blocks the user
* @returns {Promise<User>} the user object
*/
async setBlock() {
return this.client.api
.users('@me')
.relationships[this.id].put({ data: { type: 2 } })
.then((_) => _);
}
/**
* Removes the user from your blocks list
* @returns {Promise<User>} the user object
*/
async unBlock() {
return this.client.api
.users('@me')
.relationships[this.id].delete.then((_) => _);
}
/**
* Removes the user from your friends list
* @returns {Promise<User>} the user object
*/
async unFriend() {
return this.client.api
.users('@me')
.relationships[this.id].delete.then((_) => _);
}
/**
* Whether this User is a partial
* @type {boolean}
* @readonly
*/
get partial() {
return typeof this.username !== 'string';
}
/**
* The timestamp the user was created at
* @type {number}
* @readonly
*/
get createdTimestamp() {
return DiscordSnowflake.timestampFrom(this.id);
}
/**
* The time the user was created at
* @type {Date}
* @readonly
*/
get createdAt() {
return new Date(this.createdTimestamp);
}
/**
* A link to the user's avatar.
* @param {ImageURLOptions} [options={}] Options for the image URL
* @returns {?string}
*/
avatarURL({ format, size, dynamic } = {}) {
if (!this.avatar) return null;
return this.client.rest.cdn.Avatar(
this.id,
this.avatar,
format,
size,
dynamic,
);
}
/**
* If the user is a bot then it'll return the slash commands else return null
* @readonly
*/
get slashCommands() {
if (this.bot) {
return this.client.api.applications(this.id).commands.get();
} else return null;
}
/**
* A link to the user's default avatar
* @type {string}
* @readonly
*/
get defaultAvatarURL() {
return this.client.rest.cdn.defaultAvatar(this.discriminator % 5);
}
/**
* A link to the user's avatar if they have one.
* Otherwise a link to their default avatar will be returned.
* @param {ImageURLOptions} [options={}] Options for the Image URL
* @returns {string}
*/
displayAvatarURL(options) {
return this.avatarURL(options) ?? this.defaultAvatarURL;
}
/**
* The hexadecimal version of the user accent color, with a leading hash
* <info>The user must be force fetched for this property to be present</info>
* @type {?string}
* @readonly
*/
get hexAccentColor() {
if (typeof this.accentColor !== 'number') return this.accentColor;
return `#${this.accentColor.toString(16).padStart(6, '0')}`;
}
/**
* A link to the user's banner. See {@link User#banner} for more info
* @param {ImageURLOptions} [options={}] Options for the image URL
* @returns {?string}
*/
bannerURL(options = {}) {
return (
this.banner && this.client.rest.cdn.banner(this.id, this.banner, options)
);
}
/**
* The Discord "tag" (e.g. `hydrabolt#0001`) for this user
* @type {?string}
* @readonly
*/
get tag() {
return typeof this.username === 'string'
? `${this.username}#${this.discriminator}`
: null;
}
/**
* The DM between the client's user and this user
* @type {?DMChannel}
* @readonly
*/
get dmChannel() {
return this.client.users.dmChannel(this.id);
}
/**
* Creates a DM channel between the client and the user.
* @param {boolean} [force=false] Whether to skip the cache check and request the API
* @returns {Promise<DMChannel>}
*/
createDM(force = false) {
return this.client.users.createDM(this.id, force);
}
/**
* Deletes a DM channel (if one exists) between the client and the user. Resolves with the channel if successful.
* @returns {Promise<DMChannel>}
*/
deleteDM() {
return this.client.users.deleteDM(this.id);
}
/**
* Checks if the user is equal to another.
* It compares id, username, discriminator, avatar, banner, accent color, and bot flags.
* It is recommended to compare equality by using `user.id === user2.id` unless you want to compare all properties.
* @param {User} user User to compare with
* @returns {boolean}
*/
equals(user) {
return (
user &&
this.id === user.id &&
this.username === user.username &&
this.discriminator === user.discriminator &&
this.avatar === user.avatar &&
this.flags?.bitfield === user.flags?.bitfield &&
this.banner === user.banner &&
this.accentColor === user.accentColor
);
}
/**
* Compares the user with an API user object
* @param {APIUser} user The API user object to compare
* @returns {boolean}
* @private
*/
_equals(user) {
return (
user &&
this.id === user.id &&
this.username === user.username &&
this.discriminator === user.discriminator &&
this.avatar === user.avatar &&
this.flags?.bitfield === user.public_flags &&
('banner' in user ? this.banner === user.banner : true) &&
('accent_color' in user ? this.accentColor === user.accent_color : true)
);
}
/**
* Fetches this user's flags.
* @param {boolean} [force=false] Whether to skip the cache check and request the API
* @returns {Promise<UserFlagsBitField>}
*/
fetchFlags(force = false) {
return this.client.users.fetchFlags(this.id, { force });
}
/**
* Fetches this user.
* @param {boolean} [force=true] Whether to skip the cache check and request the API
* @returns {Promise<User>}
*/
fetch(force = true) {
return this.client.users.fetch(this.id, { force });
}
/**
* When concatenated with a string, this automatically returns the user's mention instead of the User object.
* @returns {string}
* @example
* // Logs: Hello from <@123456789012345678>!
* console.log(`Hello from ${user}!`);
*/
toString() {
return `<@${this.id}>`;
}
toJSON(...props) {
const json = super.toJSON(
{
createdTimestamp: true,
defaultAvatarURL: true,
hexAccentColor: true,
tag: true,
},
...props,
);
json.avatarURL = this.avatarURL();
json.displayAvatarURL = this.displayAvatarURL();
json.bannerURL = this.banner ? this.bannerURL() : this.banner;
return json;
}
// These are here only for documentation purposes - they are implemented by TextBasedChannel
/* eslint-disable no-empty-function */
send() {}
2022-03-19 10:37:45 +00:00
}
TextBasedChannel.applyToClass(User);
module.exports = User;
/**
* @external APIUser
* @see {@link https://discord.com/developers/docs/resources/user#user-object}
*/