update
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
const { Presence } = require('./Presence');
|
||||
const { TypeError } = require('../errors');
|
||||
const { Opcodes, ActivityTypes } = require('../util/Constants');
|
||||
const { ActivityTypes, Opcodes } = require('../util/Constants');
|
||||
|
||||
const CustomStatusActivityTypes = [ActivityTypes.CUSTOM, ActivityTypes[ActivityTypes.CUSTOM]];
|
||||
|
||||
@@ -22,7 +22,6 @@ class ClientPresence extends Presence {
|
||||
*/
|
||||
set(presence) {
|
||||
const packet = this._parse(presence);
|
||||
// Parse with custom class
|
||||
this._patch(packet, true);
|
||||
if (typeof presence.shardId === 'undefined') {
|
||||
this.client.ws.broadcast({ op: Opcodes.STATUS_UPDATE, d: packet });
|
||||
@@ -33,8 +32,6 @@ class ClientPresence extends Presence {
|
||||
} else {
|
||||
this.client.ws.shards.get(presence.shardId).send({ op: Opcodes.STATUS_UPDATE, d: packet });
|
||||
}
|
||||
// Parse with default class
|
||||
// this._patch(packet, false);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -48,14 +45,13 @@ class ClientPresence extends Presence {
|
||||
const data = {
|
||||
activities: [],
|
||||
afk: typeof afk === 'boolean' ? afk : false,
|
||||
since: 0,
|
||||
since: typeof since === 'number' && !Number.isNaN(since) ? since : null,
|
||||
status: status ?? this.status,
|
||||
};
|
||||
if (activities?.length) {
|
||||
for (const [i, activity] of activities.entries()) {
|
||||
if (![ActivityTypes.CUSTOM, 'CUSTOM'].includes(activity.type) && typeof activity.name !== 'string') {
|
||||
throw new TypeError('INVALID_TYPE', `activities[${i}].name`, 'string');
|
||||
}
|
||||
if (typeof activity.name !== 'string') throw new TypeError('INVALID_TYPE', `activities[${i}].name`, 'string');
|
||||
|
||||
activity.type ??= ActivityTypes.PLAYING;
|
||||
|
||||
if (CustomStatusActivityTypes.includes(activity.type) && !activity.state) {
|
||||
|
@@ -3,19 +3,24 @@
|
||||
const { setInterval } = require('node:timers');
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const Invite = require('./Invite');
|
||||
const { Message } = require('./Message');
|
||||
const User = require('./User');
|
||||
const { Util } = require('..');
|
||||
const { Error: Error_ } = require('../errors');
|
||||
const { Opcodes, NitroType, HypeSquadType } = require('../util/Constants');
|
||||
const DataResolver = require('../util/DataResolver');
|
||||
const PremiumUsageFlags = require('../util/PremiumUsageFlags');
|
||||
const PurchasedFlags = require('../util/PurchasedFlags');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* Represents the logged in client's Discord user.
|
||||
* @extends {User}
|
||||
*/
|
||||
class ClientUser extends User {
|
||||
#packageName = null;
|
||||
#intervalSamsungPresence = setInterval(() => {
|
||||
this.client.emit('debug', `[UPDATE] Samsung Presence: ${this.#packageName}`);
|
||||
if (!this.#packageName) return;
|
||||
this.setSamsungActivity(this.#packageName, 'UPDATE');
|
||||
}, 1000 * 60 * 10).unref();
|
||||
|
||||
_patch(data) {
|
||||
super._patch(data);
|
||||
|
||||
@@ -29,7 +34,7 @@ class ClientUser extends User {
|
||||
|
||||
if ('mfa_enabled' in data) {
|
||||
/**
|
||||
* If the bot's {@link ClientApplication#owner Owner} has MFA enabled on their account
|
||||
* If the bot's {@link Application#owner Owner} has MFA enabled on their account
|
||||
* @type {?boolean}
|
||||
*/
|
||||
this.mfaEnabled = typeof data.mfa_enabled === 'boolean' ? data.mfa_enabled : null;
|
||||
@@ -39,16 +44,6 @@ class ClientUser extends User {
|
||||
|
||||
if ('token' in data) this.client.token = data.token;
|
||||
|
||||
// Todo: Add (Selfbot)
|
||||
if ('premium_type' in data) {
|
||||
const nitro = NitroType[data.premium_type ?? 0];
|
||||
/**
|
||||
* Nitro type of the client user.
|
||||
* @type {NitroType}
|
||||
*/
|
||||
this.nitroType = nitro ?? `UNKNOWN_TYPE_${data.premium_type}`;
|
||||
}
|
||||
|
||||
if ('purchased_flags' in data) {
|
||||
/**
|
||||
* Purchased state of the client user.
|
||||
@@ -70,7 +65,7 @@ class ClientUser extends User {
|
||||
* Phone number of the client user.
|
||||
* @type {?string}
|
||||
*/
|
||||
this.phoneNumber = data.phone;
|
||||
this.phone = data.phone;
|
||||
}
|
||||
|
||||
if ('nsfw_allowed' in data) {
|
||||
@@ -86,48 +81,35 @@ class ClientUser extends User {
|
||||
* Email address of the client user.
|
||||
* @type {?string}
|
||||
*/
|
||||
this.emailAddress = data.email;
|
||||
this.email = data.email;
|
||||
}
|
||||
|
||||
if ('bio' in data) {
|
||||
/**
|
||||
* About me (User)
|
||||
* <info>The user must be force fetched for this property to be present or be updated</info>
|
||||
* @type {?string}
|
||||
*/
|
||||
this.bio = data.bio;
|
||||
}
|
||||
|
||||
if ('pronouns' in data) {
|
||||
/**
|
||||
* Pronouns (User)
|
||||
* <info>The user must be force fetched for this property to be present or be updated</info>
|
||||
* @type {?string}
|
||||
*/
|
||||
this.pronouns = data.pronouns;
|
||||
}
|
||||
|
||||
if (!this.friendNicknames?.size) {
|
||||
if ('premium_type' in data) {
|
||||
/**
|
||||
* The friend nicknames cache of the client user.
|
||||
* @type {Collection<Snowflake, string>}
|
||||
* @private
|
||||
* Premium types denote the level of premium a user has.
|
||||
* @type {number}
|
||||
* @see {@link https://discord-userdoccers.vercel.app/resources/user#premium-type}
|
||||
*/
|
||||
this.friendNicknames = new Collection();
|
||||
this.premiumType = data.premium_type;
|
||||
}
|
||||
|
||||
if (!this._intervalSamsungPresence) {
|
||||
this._intervalSamsungPresence = setInterval(() => {
|
||||
this.client.emit('debug', `Samsung Presence: ${this._packageName}`);
|
||||
if (!this._packageName) return;
|
||||
this.setSamsungActivity(this._packageName, 'UPDATE');
|
||||
}, 1000 * 60 * 10).unref();
|
||||
// 20 minutes max
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch note
|
||||
* @param {Object} data Note data
|
||||
* @private
|
||||
*/
|
||||
_patchNote(data) {
|
||||
/**
|
||||
* The notes cache of the client user.
|
||||
* @type {Collection<Snowflake, string>}
|
||||
* @private
|
||||
*/
|
||||
this.notes = data ? new Collection(Object.entries(data)) : new Collection();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,7 +147,6 @@ class ClientUser extends User {
|
||||
* <info>Changing usernames in Discord is heavily rate limited, with only 2 requests
|
||||
* every hour. Use this sparingly!</info>
|
||||
* @param {string} username The new username
|
||||
* @param {string} password The password of the account
|
||||
* @returns {Promise<ClientUser>}
|
||||
* @example
|
||||
* // Set username
|
||||
@@ -173,14 +154,8 @@ class ClientUser extends User {
|
||||
* .then(user => console.log(`My new username is ${user.username}`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
setUsername(username, password) {
|
||||
if (!password && !this.client.password) {
|
||||
throw new Error('A password is required to change a username.');
|
||||
}
|
||||
return this.edit({
|
||||
username,
|
||||
password: this.client.password ? this.client.password : password,
|
||||
});
|
||||
setUsername(username) {
|
||||
return this.edit({ username });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -197,186 +172,6 @@ class ClientUser extends User {
|
||||
avatar = avatar && (await DataResolver.resolveImage(avatar));
|
||||
return this.edit({ avatar });
|
||||
}
|
||||
/**
|
||||
* Sets the banner of the logged in client.
|
||||
* @param {?(BufferResolvable|Base64Resolvable)} banner The new banner
|
||||
* @returns {Promise<ClientUser>}
|
||||
* @example
|
||||
* // Set banner
|
||||
* client.user.setBanner('./banner.png')
|
||||
* .then(user => console.log(`New banner set!`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async setBanner(banner) {
|
||||
if (this.nitroType !== 'NITRO_BOOST') {
|
||||
throw new Error('You must be a Nitro Boosted User to change your banner.');
|
||||
}
|
||||
banner = banner && (await DataResolver.resolveImage(banner));
|
||||
return this.edit({ banner });
|
||||
}
|
||||
|
||||
/**
|
||||
* Set HyperSquad House
|
||||
* @param {HypeSquadType} type
|
||||
* * `LEAVE`: 0
|
||||
* * `HOUSE_BRAVERY`: 1
|
||||
* * `HOUSE_BRILLIANCE`: 2
|
||||
* * `HOUSE_BALANCE`: 3
|
||||
* @returns {Promise<void>}
|
||||
* @example
|
||||
* // Set HyperSquad HOUSE_BRAVERY
|
||||
* client.user.setHypeSquad(1); || client.user.setHypeSquad('HOUSE_BRAVERY');
|
||||
* // Leave
|
||||
* client.user.setHypeSquad(0);
|
||||
*/
|
||||
async setHypeSquad(type) {
|
||||
const id = typeof type === 'string' ? HypeSquadType[type] : type;
|
||||
if (!id && id !== 0) throw new Error('Invalid HypeSquad type.');
|
||||
if (id !== 0) {
|
||||
const data = await this.client.api.hypesquad.online.post({
|
||||
data: { house_id: id },
|
||||
});
|
||||
return data;
|
||||
} else {
|
||||
const data = await this.client.api.hypesquad.online.delete();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Accent color
|
||||
* @param {ColorResolvable} color Color to set
|
||||
* @returns {Promise<ClientUser>}
|
||||
*/
|
||||
setAccentColor(color = null) {
|
||||
return this.edit({ accent_color: color ? Util.resolveColor(color) : null });
|
||||
}
|
||||
|
||||
/**
|
||||
* Set discriminator
|
||||
* @param {User.discriminator} discriminator It is #1234
|
||||
* @param {string} password The password of the account
|
||||
* @returns {Promise<ClientUser>}
|
||||
*/
|
||||
setDiscriminator(discriminator, password) {
|
||||
if (this.nitroType == 'NONE') throw new Error('You must be a Nitro User to change your discriminator.');
|
||||
if (!password && !this.client.password) {
|
||||
throw new Error('A password is required to change a discriminator.');
|
||||
}
|
||||
return this.edit({
|
||||
discriminator,
|
||||
username: this.username,
|
||||
password: this.client.password ? this.client.password : password,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set About me
|
||||
* @param {string | null} bio Bio to set
|
||||
* @returns {Promise<ClientUser>}
|
||||
*/
|
||||
setAboutMe(bio = null) {
|
||||
return this.edit({
|
||||
bio,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the email
|
||||
* @param {Email<string>} email Email to change
|
||||
* @param {string} password Password of the account
|
||||
* @returns {Promise<ClientUser>}
|
||||
*/
|
||||
setEmail(email, password) {
|
||||
throw new Error('This method is not available yet. Please use the official Discord client to change your email.');
|
||||
// eslint-disable-next-line no-unreachable
|
||||
if (!password && !this.client.password) {
|
||||
throw new Error('A password is required to change a email.');
|
||||
}
|
||||
return this.edit({
|
||||
email,
|
||||
password: this.client.password ? this.client.password : password,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new password
|
||||
* @param {string} oldPassword Old password
|
||||
* @param {string} newPassword New password to set
|
||||
* @returns {Promise<ClientUser>}
|
||||
*/
|
||||
setPassword(oldPassword, newPassword) {
|
||||
if (!oldPassword && !this.client.password) {
|
||||
throw new Error('A password is required to change a password.');
|
||||
}
|
||||
if (!newPassword) throw new Error('New password is required.');
|
||||
return this.edit({
|
||||
password: this.client.password ? this.client.password : oldPassword,
|
||||
new_password: newPassword,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable account
|
||||
* @param {string} password Password of the account
|
||||
* @returns {Promise<ClientUser>}
|
||||
*/
|
||||
async disableAccount(password) {
|
||||
if (!password && !this.client.password) {
|
||||
throw new Error('A password is required to disable an account.');
|
||||
}
|
||||
const data = await this.client.api.users['@me'].disable.post({
|
||||
data: {
|
||||
password: this.client.password ? this.client.password : password,
|
||||
},
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set selfdeaf (Global)
|
||||
* @param {boolean} status Whether or not the ClientUser is deafened
|
||||
* @returns {boolean}
|
||||
*/
|
||||
setDeaf(status) {
|
||||
if (typeof status !== 'boolean') throw new Error('Deaf status must be a boolean.');
|
||||
this.client.ws.broadcast({
|
||||
op: Opcodes.VOICE_STATE_UPDATE,
|
||||
d: { self_deaf: status },
|
||||
});
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set selfmute (Global)
|
||||
* @param {boolean} status Whether or not the ClientUser is muted
|
||||
* @returns {boolean}
|
||||
*/
|
||||
setMute(status) {
|
||||
if (typeof status !== 'boolean') throw new Error('Mute status must be a boolean.');
|
||||
this.client.ws.broadcast({
|
||||
op: Opcodes.VOICE_STATE_UPDATE,
|
||||
d: { self_mute: status },
|
||||
});
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete account. Warning: Cannot be changed once used!
|
||||
* @param {string} password Password of the account
|
||||
* @returns {Promise<ClientUser>}
|
||||
*/
|
||||
async deleteAccount(password) {
|
||||
if (!password && !this.client.password) {
|
||||
throw new Error('A password is required to delete an account.');
|
||||
}
|
||||
const data = await this.client.api.users['@me/delete'].post({
|
||||
data: {
|
||||
password: this.client.password ? this.client.password : password,
|
||||
},
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for setting activities
|
||||
@@ -392,7 +187,7 @@ class ClientUser extends User {
|
||||
* @typedef {Object} PresenceData
|
||||
* @property {PresenceStatusData} [status] Status of the user
|
||||
* @property {boolean} [afk] Whether the user is AFK
|
||||
* @property {ActivitiesOptions[]|CustomStatus[]|RichPresence[]|SpotifyRPC[]} [activities] Activity the user is playing
|
||||
* @property {(ActivitiesOptions|CustomStatus|RichPresence|SpotifyRPC)[]} [activities] Activity the user is playing
|
||||
* @property {number|number[]} [shardId] Shard id(s) to have the activity set on
|
||||
*/
|
||||
|
||||
@@ -451,15 +246,10 @@ class ClientUser extends User {
|
||||
* @see {@link https://github.com/aiko-chan-ai/discord.js-selfbot-v13/blob/main/Document/RichPresence.md}
|
||||
*/
|
||||
setActivity(name, options = {}) {
|
||||
if (!name) {
|
||||
return this.setPresence({ activities: [], shardId: options.shardId });
|
||||
}
|
||||
if (!name) return this.setPresence({ activities: [], shardId: options.shardId });
|
||||
|
||||
const activity = Object.assign({}, options, typeof name === 'object' ? name : { name });
|
||||
return this.setPresence({
|
||||
activities: [activity],
|
||||
shardId: activity.shardId,
|
||||
});
|
||||
return this.setPresence({ activities: [activity], shardId: activity.shardId });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -472,6 +262,81 @@ class ClientUser extends User {
|
||||
return this.setPresence({ afk, shardId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the banner of the logged in client.
|
||||
* @param {?(BufferResolvable|Base64Resolvable)} banner The new banner
|
||||
* @returns {Promise<ClientUser>}
|
||||
* @example
|
||||
* // Set banner
|
||||
* client.user.setBanner('./banner.png')
|
||||
* .then(user => console.log(`New banner set!`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async setBanner(banner) {
|
||||
banner = banner && (await DataResolver.resolveImage(banner));
|
||||
return this.edit({ banner });
|
||||
}
|
||||
|
||||
/**
|
||||
* Set HyperSquad House
|
||||
* @param {string|number} type
|
||||
* * `LEAVE`: 0
|
||||
* * `HOUSE_BRAVERY`: 1
|
||||
* * `HOUSE_BRILLIANCE`: 2
|
||||
* * `HOUSE_BALANCE`: 3
|
||||
* @returns {Promise<void>}
|
||||
* @example
|
||||
* // Set HyperSquad HOUSE_BRAVERY
|
||||
* client.user.setHypeSquad(1); || client.user.setHypeSquad('HOUSE_BRAVERY');
|
||||
* // Leave
|
||||
* client.user.setHypeSquad(0);
|
||||
*/
|
||||
setHypeSquad(type) {
|
||||
switch (type) {
|
||||
case 'LEAVE': {
|
||||
type = 0;
|
||||
break;
|
||||
}
|
||||
case 'HOUSE_BRAVERY': {
|
||||
type = 1;
|
||||
break;
|
||||
}
|
||||
case 'HOUSE_BRILLIANCE': {
|
||||
type = 2;
|
||||
break;
|
||||
}
|
||||
case 'HOUSE_BALANCE': {
|
||||
type = 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (type == 0) {
|
||||
return this.client.api.hypesquad.online.delete();
|
||||
} else {
|
||||
return this.client.api.hypesquad.online.post({
|
||||
data: { house_id: type },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Accent color
|
||||
* @param {ColorResolvable} color Color to set
|
||||
* @returns {Promise<ClientUser>}
|
||||
*/
|
||||
setAccentColor(color = null) {
|
||||
return this.edit({ accent_color: color ? Util.resolveColor(color) : null });
|
||||
}
|
||||
|
||||
/**
|
||||
* Set About me
|
||||
* @param {string} [bio=null] Bio to set
|
||||
* @returns {Promise<ClientUser>}
|
||||
*/
|
||||
setAboutMe(bio = null) {
|
||||
return this.edit({ bio });
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an invite [Friend Invites]
|
||||
* maxAge: 604800 | maxUses: 1
|
||||
@@ -505,63 +370,10 @@ class ClientUser extends User {
|
||||
|
||||
/**
|
||||
* Revoke all friend invites
|
||||
* @returns {Promise<Collection<string, Invite>>}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async revokeAllFriendInvites() {
|
||||
const data = await this.client.api.users['@me'].invites.delete();
|
||||
const collection = new Collection();
|
||||
for (const invite of data) {
|
||||
collection.set(invite.code, new Invite(this.client, invite));
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection of messages mentioning clientUser
|
||||
* @param {number} [limit=25] Maximum number of messages to get
|
||||
* @param {boolean} [mentionRoles=true] Whether or not to mention roles
|
||||
* @param {boolean} [mentionEveryone=true] Whether or not to mention `@everyone`
|
||||
* @returns {Promise<Collection<Snowflake, Message>>}
|
||||
*/
|
||||
async getMentions(limit = 25, mentionRoles = true, mentionEveryone = true) {
|
||||
// https://canary.discord.com/api/v9/users/@me/mentions?limit=25&roles=true&everyone=true
|
||||
const data = await this.client.api.users['@me'].mentions.get({
|
||||
query: {
|
||||
limit,
|
||||
roles: mentionRoles,
|
||||
everyone: mentionEveryone,
|
||||
},
|
||||
});
|
||||
const collection = new Collection();
|
||||
for (const msg of data) {
|
||||
collection.set(msg.id, new Message(this.client, msg));
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change Theme color
|
||||
* @param {ColorResolvable} primary The primary color of the user's profile
|
||||
* @param {ColorResolvable} accent The accent color of the user's profile
|
||||
* @returns {Promise<ClientUser>}
|
||||
*/
|
||||
async setThemeColors(primary, accent) {
|
||||
if (!primary || !accent) throw new Error('PRIMARY_COLOR or ACCENT_COLOR are required.');
|
||||
// Check nitro
|
||||
if (this.nitroType !== 'NITRO_BOOST') {
|
||||
throw new Error_('NITRO_BOOST_REQUIRED', 'themeColors');
|
||||
}
|
||||
primary = Util.resolveColor(primary) || this.themeColors[0];
|
||||
accent = Util.resolveColor(accent) || this.themeColors[1];
|
||||
const data_ = await this.client.api.users['@me'].profile.patch({
|
||||
data: {
|
||||
theme_colors: [primary, accent],
|
||||
},
|
||||
});
|
||||
this._ProfilePatch({
|
||||
user_profile: data_,
|
||||
});
|
||||
return this;
|
||||
revokeAllFriendInvites() {
|
||||
return this.client.api.users['@me'].invites.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -587,8 +399,8 @@ class ClientUser extends User {
|
||||
update: type,
|
||||
},
|
||||
});
|
||||
if (type !== 'STOP') this._packageName = packageName;
|
||||
else this._packageName = null;
|
||||
if (type !== 'STOP') this.#packageName = packageName;
|
||||
else this.#packageName = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -598,9 +410,7 @@ class ClientUser extends User {
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
stopRinging(channel) {
|
||||
const id = this.client.channels.resolveId(channel);
|
||||
if (!channel) return false;
|
||||
return this.client.api.channels(id).call['stop-ringing'].post({
|
||||
return this.client.api.channels(this.client.channels.resolveId(channel)).call['stop-ringing'].post({
|
||||
data: {},
|
||||
});
|
||||
}
|
||||
|
@@ -8,7 +8,6 @@ const { Error } = require('../errors');
|
||||
const GuildMemberRoleManager = require('../managers/GuildMemberRoleManager');
|
||||
const GuildMemberFlags = require('../util/GuildMemberFlags');
|
||||
const Permissions = require('../util/Permissions');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* @type {WeakSet<GuildMember>}
|
||||
@@ -102,7 +101,6 @@ class GuildMember extends Base {
|
||||
this.communicationDisabledUntilTimestamp =
|
||||
data.communication_disabled_until && Date.parse(data.communication_disabled_until);
|
||||
}
|
||||
|
||||
if ('flags' in data) {
|
||||
/**
|
||||
* The flags of this member
|
||||
@@ -114,51 +112,6 @@ class GuildMember extends Base {
|
||||
}
|
||||
}
|
||||
|
||||
_ProfilePatch(data) {
|
||||
if ('accent_color' in data) {
|
||||
/**
|
||||
* The member's accent color
|
||||
* <info>The user must be force fetched for this property to be present or be updated</info>
|
||||
* @type {?number}
|
||||
*/
|
||||
this.accentColor = data.accent_color;
|
||||
}
|
||||
if ('banner' in data) {
|
||||
/**
|
||||
* The member's banner hash
|
||||
* <info>The user must be force fetched for this property to be present or be updated</info>
|
||||
* @type {?string}
|
||||
*/
|
||||
this.banner = data.banner;
|
||||
}
|
||||
if ('bio' in data) {
|
||||
/**
|
||||
* The member's biography (About me)
|
||||
* <info>The user must be force fetched for this property to be present or be updated</info>
|
||||
* @type {?string}
|
||||
*/
|
||||
this.bio = data.bio;
|
||||
}
|
||||
if ('theme_colors' in data) {
|
||||
/**
|
||||
* The member's theme colors (Profile theme) [Primary, Accent]
|
||||
* <info>The user must be force fetched for this property to be present or be updated</info>
|
||||
* @type {?Array<number>}
|
||||
*/
|
||||
this.themeColors = data.theme_colors;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The hexadecimal version of the user theme color, with a leading hash [Primary, Accent]
|
||||
* <info>The user must be force fetched for this property to be present or be updated</info>
|
||||
* @type {?Array<string>}
|
||||
* @readonly
|
||||
*/
|
||||
get hexThemeColor() {
|
||||
return this.themeColors?.map(c => `#${c.toString(16).padStart(6, '0')}`) || null;
|
||||
}
|
||||
|
||||
_clone() {
|
||||
const clone = super._clone();
|
||||
clone._roles = this._roles.slice();
|
||||
@@ -232,21 +185,6 @@ class GuildMember extends Base {
|
||||
return this.client.rest.cdn.GuildMemberAvatar(this.guild.id, this.id, this.avatar, format, size, dynamic);
|
||||
}
|
||||
|
||||
/**
|
||||
* A link to the user's banner.
|
||||
* <info>This method will throw an error if called before the user is force fetched Profile.
|
||||
* See {@link GuildMember#banner} for more info</info>
|
||||
* @param {ImageURLOptions} [options={}] Options for the Image URL
|
||||
* @returns {?string}
|
||||
*/
|
||||
bannerURL({ format, size, dynamic } = {}) {
|
||||
if (typeof this.banner === 'undefined') {
|
||||
throw new Error('USER_BANNER_NOT_FETCHED');
|
||||
}
|
||||
if (!this.banner) return null;
|
||||
return this.client.rest.cdn.GuildMemberBanner(this.guild.id, this.id, this.banner, format, size, dynamic);
|
||||
}
|
||||
|
||||
/**
|
||||
* A link to the member's guild avatar if they have one.
|
||||
* Otherwise, a link to their {@link User#displayAvatarURL} will be returned.
|
||||
@@ -444,79 +382,6 @@ class GuildMember extends Base {
|
||||
return this.edit({ flags, reason });
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the guild avatar of the logged in client.
|
||||
* @param {?(BufferResolvable|Base64Resolvable)} avatar The new avatar
|
||||
* @returns {Promise<GuildMember>}
|
||||
*/
|
||||
setAvatar(avatar) {
|
||||
if (this.user.id !== this.client.user.id) {
|
||||
throw new Error('ONLY_ME');
|
||||
}
|
||||
if (this.client.user.nitroType !== 'NITRO_BOOST') {
|
||||
throw new Error('NITRO_BOOST_REQUIRED', 'avatar');
|
||||
}
|
||||
return this.edit({ avatar });
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the guild banner of the logged in client.
|
||||
* @param {?(BufferResolvable|Base64Resolvable)} banner The new banner
|
||||
* @returns {Promise<GuildMember>}
|
||||
*/
|
||||
setBanner(banner) {
|
||||
if (this.user.id !== this.client.user.id) {
|
||||
throw new Error('ONLY_ME');
|
||||
}
|
||||
if (this.client.user.nitroType !== 'NITRO_BOOST') {
|
||||
throw new Error('NITRO_BOOST_REQUIRED', 'banner');
|
||||
}
|
||||
return this.edit({ banner });
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Guild About me
|
||||
* @param {string | null} bio Bio to set
|
||||
* @returns {Promise<GuildMember>}
|
||||
*/
|
||||
setAboutMe(bio = null) {
|
||||
if (this.user.id !== this.client.user.id) {
|
||||
throw new Error('ONLY_ME');
|
||||
}
|
||||
if (this.client.user.nitroType !== 'NITRO_BOOST') {
|
||||
throw new Error('NITRO_BOOST_REQUIRED', 'bio');
|
||||
}
|
||||
return this.edit({ bio });
|
||||
}
|
||||
|
||||
/**
|
||||
* Change Theme color
|
||||
* @param {ColorResolvable} primary The primary color of the user's profile
|
||||
* @param {ColorResolvable} accent The accent color of the user's profile
|
||||
* @returns {Promise<GuildMember>}
|
||||
*/
|
||||
async setThemeColors(primary, accent) {
|
||||
if (this.user.id !== this.client.user.id) {
|
||||
throw new Error('ONLY_ME');
|
||||
}
|
||||
if (!primary || !accent) throw new Error('PRIMARY_COLOR or ACCENT_COLOR are required.');
|
||||
// Check nitro
|
||||
if (this.nitroType !== 'NITRO_BOOST') {
|
||||
throw new Error('NITRO_BOOST_REQUIRED', 'themeColors');
|
||||
}
|
||||
primary = Util.resolveColor(primary) || this.themeColors ? this.themeColors[0] : 0;
|
||||
accent = Util.resolveColor(accent) || this.themeColors ? this.themeColors[1] : 0;
|
||||
const data_ = await this.client.api.guilds[this.guild.id].profile['@me'].patch({
|
||||
data: {
|
||||
theme_colors: [primary, accent],
|
||||
},
|
||||
});
|
||||
this._ProfilePatch({
|
||||
guild_member_profile: data_,
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DM channel between the client and this member.
|
||||
* @param {boolean} [force=false] Whether to skip the cache check and request the API
|
||||
@@ -619,8 +484,6 @@ class GuildMember extends Base {
|
||||
this.joinedTimestamp === member.joinedTimestamp &&
|
||||
this.nickname === member.nickname &&
|
||||
this.avatar === member.avatar &&
|
||||
this.accentColor === member.accentColor &&
|
||||
this.bio === member.bio &&
|
||||
this.pending === member.pending &&
|
||||
this.communicationDisabledUntilTimestamp === member.communicationDisabledUntilTimestamp &&
|
||||
this.flags.equals(member.flags) &&
|
||||
@@ -629,14 +492,6 @@ class GuildMember extends Base {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get profile guildMember
|
||||
* @returns {Promise<User>}
|
||||
*/
|
||||
getProfile() {
|
||||
return this.user.getProfile(this.guild.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* When concatenated with a string, this automatically returns the user's mention instead of the GuildMember object.
|
||||
* @returns {string}
|
||||
@@ -659,6 +514,33 @@ class GuildMember extends Base {
|
||||
json.displayAvatarURL = this.displayAvatarURL();
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the guild avatar of the logged in client.
|
||||
* @param {?(BufferResolvable|Base64Resolvable)} avatar The new avatar
|
||||
* @returns {Promise<GuildMember>}
|
||||
*/
|
||||
setAvatar(avatar) {
|
||||
return this.edit({ avatar });
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the guild banner of the logged in client.
|
||||
* @param {?(BufferResolvable|Base64Resolvable)} banner The new banner
|
||||
* @returns {Promise<GuildMember>}
|
||||
*/
|
||||
setBanner(banner) {
|
||||
return this.edit({ banner });
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Guild About me
|
||||
* @param {string | null} bio Bio to set
|
||||
* @returns {Promise<GuildMember>}
|
||||
*/
|
||||
setAboutMe(bio = null) {
|
||||
return this.edit({ bio });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,12 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const Buffer = require('node:buffer').Buffer;
|
||||
const Base = require('./Base');
|
||||
const { GuildScheduledEvent } = require('./GuildScheduledEvent');
|
||||
const IntegrationApplication = require('./IntegrationApplication');
|
||||
const InviteStageInstance = require('./InviteStageInstance');
|
||||
const { Error } = require('../errors');
|
||||
const { ChannelTypes, Endpoints } = require('../util/Constants');
|
||||
const { Endpoints } = require('../util/Constants');
|
||||
const Permissions = require('../util/Permissions');
|
||||
|
||||
// TODO: Convert `inviter` and `channel` in this class to a getter.
|
||||
@@ -179,7 +178,7 @@ class Invite extends Base {
|
||||
this.channel = this.client.channels.cache.get(data.channel_id);
|
||||
}
|
||||
|
||||
if ('channel' in data && data.channel) {
|
||||
if ('channel' in data) {
|
||||
/**
|
||||
* The channel this invite is for
|
||||
* @type {Channel}
|
||||
@@ -317,53 +316,6 @@ class Invite extends Base {
|
||||
valueOf() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join this Guild using this invite.
|
||||
* @param {boolean} [autoVerify] Whether to automatically verify member
|
||||
* @returns {Promise<Guild>}
|
||||
* @example
|
||||
* await client.fetchInvite('code').then(async invite => {
|
||||
* await invite.acceptInvite();
|
||||
* });
|
||||
*/
|
||||
async acceptInvite(autoVerify = false) {
|
||||
if (!this.guild) throw new Error('INVITE_NO_GUILD');
|
||||
if (this.client.guilds.cache.get(this.guild.id)) return this.guild;
|
||||
const dataHeader = {
|
||||
location: 'Join Guild',
|
||||
location_guild_id: this.guild?.id,
|
||||
location_channel_id: this.channelId,
|
||||
location_channel_type: ChannelTypes[this.channel?.type] ?? 0,
|
||||
};
|
||||
await this.client.api.invites(this.code).post({
|
||||
data: {
|
||||
session_id: this.client.sessionId,
|
||||
},
|
||||
headers: {
|
||||
'X-Context-Properties': Buffer.from(JSON.stringify(dataHeader), 'utf8').toString('base64'),
|
||||
},
|
||||
});
|
||||
const guild = this.client.guilds.cache.get(this.guild.id);
|
||||
/*
|
||||
//
|
||||
if (autoVerify) {
|
||||
console.warn('Feature is under maintenance - Invite#acceptInvite(true)');
|
||||
}
|
||||
*/
|
||||
if (autoVerify) {
|
||||
const getForm = await this.client.api
|
||||
.guilds(this.guild.id)
|
||||
['member-verification'].get({ query: { with_guild: false, invite_code: this.code } })
|
||||
.catch(() => {});
|
||||
if (!getForm) return guild;
|
||||
const form = Object.assign(getForm.form_fields[0], { response: true });
|
||||
// Respond to the form
|
||||
// https://discord.com/api/v9/guilds/:id/requests/@me
|
||||
await this.client.api.guilds(this.guild.id).requests['@me'].put({ data: { form_fields: [form] } });
|
||||
}
|
||||
return guild;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -91,7 +91,7 @@ class Presence extends Base {
|
||||
if ('activities' in data) {
|
||||
/**
|
||||
* The activities of this presence
|
||||
* @type {Activity[]}
|
||||
* @type {(Activity|CustomStatus|SpotifyRPC|RichPresence)[]}
|
||||
*/
|
||||
this.activities = data.activities.map(activity => {
|
||||
if (fromClient === true) {
|
||||
|
@@ -1,11 +1,8 @@
|
||||
'use strict';
|
||||
const { randomUUID } = require('node:crypto');
|
||||
const { ActivityTypes } = require('../util/Constants');
|
||||
const { resolvePartialEmoji } = require('../util/Util');
|
||||
|
||||
// eslint-disable-next-line
|
||||
const getUUID = () =>
|
||||
([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, a => (a ^ ((Math.random() * 16) >> (a / 4))).toString(16));
|
||||
// Function check url valid (ok copilot)
|
||||
// eslint-disable-next-line
|
||||
const checkUrl = url => {
|
||||
try {
|
||||
@@ -351,7 +348,7 @@ https://github.com/aiko-chan-ai/discord.js-selfbot-v13/blob/main/Documents/RichP
|
||||
if (!party.max || typeof party.max != 'number') throw new Error('Party must have max number');
|
||||
if (!party.current || typeof party.current != 'number') throw new Error('Party must have current');
|
||||
if (party.current > party.max) throw new Error('Party current must be less than max number');
|
||||
if (!party.id || typeof party.id != 'string') party.id = getUUID();
|
||||
if (!party.id || typeof party.id != 'string') party.id = randomUUID();
|
||||
this.party = {
|
||||
size: [party.current, party.max],
|
||||
id: party.id,
|
||||
@@ -488,16 +485,6 @@ https://github.com/aiko-chan-ai/discord.js-selfbot-v13/blob/main/Documents/RichP
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get random UUID string (Util)
|
||||
* @returns {string}
|
||||
*/
|
||||
static getUUID() {
|
||||
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, a =>
|
||||
(a ^ ((Math.random() * 16) >> (a / 4))).toString(16),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Assets from a RichPresence (Util)
|
||||
* @param {Client} client Discord Client
|
||||
@@ -718,5 +705,4 @@ module.exports = {
|
||||
CustomStatus,
|
||||
RichPresence,
|
||||
SpotifyRPC,
|
||||
getUUID,
|
||||
};
|
||||
|
@@ -1,12 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const Base = require('./Base');
|
||||
const ClientApplication = require('./ClientApplication');
|
||||
const VoiceState = require('./VoiceState');
|
||||
const TextBasedChannel = require('./interfaces/TextBasedChannel');
|
||||
const { Error } = require('../errors');
|
||||
const { RelationshipTypes, NitroType } = require('../util/Constants');
|
||||
const { RelationshipTypes } = require('../util/Constants');
|
||||
const SnowflakeUtil = require('../util/SnowflakeUtil');
|
||||
const UserFlags = require('../util/UserFlags');
|
||||
const Util = require('../util/Util');
|
||||
@@ -17,8 +15,9 @@ const Util = require('../util/Util');
|
||||
* @extends {Base}
|
||||
*/
|
||||
class User extends Base {
|
||||
constructor(client, data, application) {
|
||||
constructor(client, data) {
|
||||
super(client);
|
||||
|
||||
/**
|
||||
* The user's id
|
||||
* @type {Snowflake}
|
||||
@@ -31,52 +30,6 @@ class User extends Base {
|
||||
|
||||
this.flags = null;
|
||||
|
||||
/**
|
||||
* An array of object (connected accounts), containing the following properties:
|
||||
* @property {string} type The account type (twitch, youtube, etc)
|
||||
* @property {string} name The account name
|
||||
* @property {string} id The account id
|
||||
* @property {boolean} verified Whether the account is verified
|
||||
* @see {@link https://discord.com/developers/docs/resources/user#connection-object}
|
||||
* @typedef {Object} ConnectionAccount
|
||||
*/
|
||||
|
||||
/**
|
||||
* Accounts connected to this user
|
||||
* <info>The user must be force fetched for this property to be present or be updated</info>
|
||||
* @type {?ConnectionAccount[]}
|
||||
*/
|
||||
this.connectedAccounts = [];
|
||||
/**
|
||||
* Time that User has nitro (Unix Timestamp)
|
||||
* <info>The user must be force fetched for this property to be present or be updated</info>
|
||||
* @type {?number}
|
||||
*/
|
||||
this.premiumSince = null;
|
||||
/**
|
||||
* Time that User has nitro and boost server (Unix Timestamp)
|
||||
* @type {?number}
|
||||
*/
|
||||
this.premiumGuildSince = null;
|
||||
/**
|
||||
* About me (User)
|
||||
* <info>The user must be force fetched for this property to be present or be updated</info>
|
||||
* @type {?string}
|
||||
*/
|
||||
this.bio = null;
|
||||
/**
|
||||
* Pronouns (User)
|
||||
* <info>The user must be force fetched for this property to be present or be updated</info>
|
||||
* @type {?string}
|
||||
*/
|
||||
this.pronouns = null;
|
||||
this._mutualGuilds = [];
|
||||
/**
|
||||
* [Bot] Application
|
||||
* @type {?ClientApplication}
|
||||
*/
|
||||
this.application = application ? new ClientApplication(this.client, application, this) : null;
|
||||
this._partial = true;
|
||||
this._patch(data);
|
||||
}
|
||||
|
||||
@@ -107,10 +60,6 @@ class User extends Base {
|
||||
* @type {?boolean}
|
||||
*/
|
||||
this.bot = Boolean(data.bot);
|
||||
if (this.bot === true && !this.application) {
|
||||
this.application = new ClientApplication(this.client, { id: this.id }, this);
|
||||
this.botInGuildsCount = null;
|
||||
}
|
||||
} else if (!this.partial && typeof this.bot !== 'boolean') {
|
||||
this.bot = false;
|
||||
}
|
||||
@@ -147,6 +96,17 @@ class User extends Base {
|
||||
this.banner ??= undefined;
|
||||
}
|
||||
|
||||
if ('banner_color' in data) {
|
||||
/**
|
||||
* The user banner's hex
|
||||
* <info>The user must be force fetched for this property to be present or be updated</info>
|
||||
* @type {?string}
|
||||
*/
|
||||
this.bannerColor = data.banner_color;
|
||||
} else if (this.bannerColor !== null) {
|
||||
this.bannerColor ??= undefined;
|
||||
}
|
||||
|
||||
if ('accent_color' in data) {
|
||||
/**
|
||||
* The base 10 accent color of the user's banner
|
||||
@@ -176,252 +136,23 @@ class User extends Base {
|
||||
this.flags = new UserFlags(data.public_flags);
|
||||
}
|
||||
|
||||
if ('approximate_guild_count' in data) {
|
||||
/**
|
||||
* Check how many guilds the bot is in (Probably only approximate) (application.fetch() first)
|
||||
* @type {?number}
|
||||
*/
|
||||
this.botInGuildsCount = data.approximate_guild_count;
|
||||
}
|
||||
|
||||
if ('avatar_decoration' in data) {
|
||||
if ('avatar_decoration_data' in data) {
|
||||
/**
|
||||
* The user avatar decoration's hash
|
||||
* @type {?string}
|
||||
*/
|
||||
this.avatarDecoration = data.avatar_decoration;
|
||||
this.avatarDecoration = data.avatar_decoration_data?.asset;
|
||||
/**
|
||||
* The ID of the avatar decoration's SKU
|
||||
* @type {?Snowflake}
|
||||
*/
|
||||
this.avatarDecorationSKUId = data.avatar_decoration_data?.sku_id;
|
||||
} else {
|
||||
this.avatarDecoration ??= null;
|
||||
this.avatarDecorationSKUId ??= null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This user is on the same servers as Client User
|
||||
* <info>The user must be force fetched for this property to be present or be updated</info>
|
||||
* @type {Collection<Snowflake, Guild>}
|
||||
* @readonly
|
||||
*/
|
||||
get mutualGuilds() {
|
||||
return new Collection(this._mutualGuilds.map(obj => [obj.id, obj]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all mutual friends (Client -> User)
|
||||
* @type {Promise<Collection<Snowflake, User>>}
|
||||
* @readonly
|
||||
*/
|
||||
get mutualFriends() {
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
return new Promise(async resolve => {
|
||||
const all = new Collection();
|
||||
if (this.bot || this.client.user.id === this.id) return resolve(all);
|
||||
const data = await this.client.api.users(this.id).relationships.get();
|
||||
for (const u of data) {
|
||||
all.set(u.id, this.client.users._add(u));
|
||||
}
|
||||
return resolve(all);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check relationship status (Client -> User)
|
||||
* @type {RelationshipTypes}
|
||||
* @readonly
|
||||
*/
|
||||
get relationships() {
|
||||
const i = this.client.relationships.cache.get(this.id) ?? 0;
|
||||
return RelationshipTypes[parseInt(i)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check note
|
||||
* @type {?string}
|
||||
* @readonly
|
||||
*/
|
||||
get note() {
|
||||
return this.client.user.notes.get(this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get friend nickname
|
||||
* @type {?string}
|
||||
* @readonly
|
||||
*/
|
||||
get nickname() {
|
||||
return this.client.user.friendNicknames.get(this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* The voice state of this member
|
||||
* @type {VoiceState}
|
||||
* @readonly
|
||||
*/
|
||||
get voice() {
|
||||
return (
|
||||
this.client.voiceStates.cache.get(this.id) ??
|
||||
this.client.guilds.cache.find(g => g?.voiceStates?.cache?.get(this.id))?.voiceStates?.cache?.get(this.id) ??
|
||||
new VoiceState({ client: this.client }, { user_id: this.id })
|
||||
);
|
||||
}
|
||||
|
||||
_ProfilePatch(data) {
|
||||
if (!data) return;
|
||||
|
||||
this._partial = false;
|
||||
|
||||
if (data.connected_accounts.length > 0) {
|
||||
this.connectedAccounts = 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();
|
||||
}
|
||||
|
||||
if ('premium_type' in data) {
|
||||
const nitro = NitroType[data.premium_type ?? 0];
|
||||
/**
|
||||
* Nitro type of the user.
|
||||
* @type {NitroType}
|
||||
*/
|
||||
this.nitroType = nitro ?? `UNKNOWN_TYPE_${data.premium_type}`;
|
||||
}
|
||||
|
||||
if ('user_profile' in data) {
|
||||
this.bio = data.user_profile.bio;
|
||||
/**
|
||||
* The user's theme colors (Profile theme) [Primary, Accent]
|
||||
* <info>The user must be force fetched for this property to be present or be updated</info>
|
||||
* @type {?Array<number>}
|
||||
*/
|
||||
this.themeColors = data.user_profile.theme_colors;
|
||||
|
||||
this.pronouns = data.user_profile.pronouns;
|
||||
}
|
||||
|
||||
if ('guild_member_profile' in data && 'guild_member' in data) {
|
||||
const guild = this.client.guilds.cache.get(data.guild_member_profile.guild_id);
|
||||
const member = guild?.members._add(data.guild_member);
|
||||
member._ProfilePatch(data.guild_member_profile);
|
||||
}
|
||||
|
||||
if ('application' in data) {
|
||||
this.application = new ClientApplication(this.client, data.application, this);
|
||||
}
|
||||
|
||||
if ('badges' in data) {
|
||||
/**
|
||||
* @callback BadgeIcon
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} UserBadge
|
||||
* @property {string} id The id of the badge
|
||||
* @property {string} description The description of the badge
|
||||
* @property {string} icon The icon hash of the badge
|
||||
* @property {?string} link The link of the badge
|
||||
* @property {BadgeIcon} iconURL The iconURL of the badge
|
||||
*/
|
||||
|
||||
/**
|
||||
* User badges (Boost, Slash, AutoMod, etc.)
|
||||
* @type {?Array<UserBadge>}
|
||||
*/
|
||||
this.badges = data.badges.map(o => ({ ...o, iconURL: () => this.client.rest.cdn.BadgeIcon(o.icon) }));
|
||||
}
|
||||
|
||||
if ('guild_badges' in data) {
|
||||
// Unknown
|
||||
}
|
||||
|
||||
if ('mutual_guilds' in data) {
|
||||
this._mutualGuilds = data.mutual_guilds;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get profile from Discord, if client is in a server with the target.
|
||||
* @type {User}
|
||||
* @param {Snowflake | null} guildId The guild id to get the profile from
|
||||
* @returns {Promise<User>}
|
||||
*/
|
||||
async getProfile(guildId) {
|
||||
if (this.client.bot) throw new Error('INVALID_BOT_METHOD');
|
||||
const query = guildId
|
||||
? {
|
||||
with_mutual_guilds: true,
|
||||
guild_id: guildId,
|
||||
}
|
||||
: {
|
||||
with_mutual_guilds: true,
|
||||
};
|
||||
const data = await this.client.api.users(this.id).profile.get({
|
||||
query,
|
||||
});
|
||||
this._ProfilePatch(data);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Friends the user [If incoming request]
|
||||
* @type {boolean}
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
setFriend() {
|
||||
return this.client.relationships.addFriend(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the nickname of the friend
|
||||
* @param {?string} nickname The nickname to change
|
||||
* @type {boolean}
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
setNickname(nickname) {
|
||||
return this.client.relationships.setNickname(this.id, nickname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Friend Request to the user
|
||||
* @type {boolean}
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
sendFriendRequest() {
|
||||
return this.client.relationships.sendFriendRequest(this.username, this.discriminator);
|
||||
}
|
||||
/**
|
||||
* Blocks the user
|
||||
* @type {boolean}
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
setBlock() {
|
||||
return this.client.relationships.addBlocked(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the user from your blocks list
|
||||
* @type {boolean}
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
unBlock() {
|
||||
return this.client.relationships.deleteBlocked(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the user from your friends list
|
||||
* @type {boolean}
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
unFriend() {
|
||||
return this.client.relationships.deleteFriend(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this User is a partial
|
||||
* @type {boolean}
|
||||
@@ -508,40 +239,11 @@ class User extends Base {
|
||||
* @returns {?string}
|
||||
*/
|
||||
bannerURL({ format, size, dynamic } = {}) {
|
||||
if (typeof this.banner === 'undefined') {
|
||||
throw new Error('USER_BANNER_NOT_FETCHED');
|
||||
}
|
||||
if (typeof this.banner === 'undefined') throw new Error('USER_BANNER_NOT_FETCHED');
|
||||
if (!this.banner) return null;
|
||||
return this.client.rest.cdn.Banner(this.id, this.banner, format, size, dynamic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ring the user's phone / PC (call)
|
||||
* @returns {Promise<any>}
|
||||
* @deprecated
|
||||
*/
|
||||
ring() {
|
||||
if (this.relationships !== 'FRIEND') return Promise.reject(new Error('USER_NOT_FRIEND'));
|
||||
if (!this.client.user.voice?.channelId || !this.client.callVoice) {
|
||||
return Promise.reject(new Error('CLIENT_NO_CALL'));
|
||||
}
|
||||
return this.client.api.channels(this.dmChannel.id).call.ring.post({
|
||||
data: {
|
||||
recipients: [this.id],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The hexadecimal version of the user theme color, with a leading hash [Primary, Accent]
|
||||
* <info>The user must be force fetched for this property to be present or be updated</info>
|
||||
* @type {?Array<string>}
|
||||
* @readonly
|
||||
*/
|
||||
get hexThemeColor() {
|
||||
return this.themeColors?.map(c => `#${c.toString(16).padStart(6, '0')}`) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The tag of this user
|
||||
* <info>This user's username, or their legacy tag (e.g. `hydrabolt#0001`)
|
||||
@@ -609,8 +311,7 @@ class User extends Base {
|
||||
this.avatar === user.avatar &&
|
||||
this.flags?.bitfield === user.flags?.bitfield &&
|
||||
this.banner === user.banner &&
|
||||
this.accentColor === user.accentColor &&
|
||||
this.bio === user.bio
|
||||
this.accentColor === user.accentColor
|
||||
);
|
||||
}
|
||||
|
||||
@@ -652,6 +353,28 @@ class User extends Base {
|
||||
return this.client.users.fetch(this.id, { force });
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a user profile object for a given user ID.
|
||||
* <info>This endpoint requires one of the following:
|
||||
* - The user is a bot
|
||||
* - The user shares a mutual guild with the current user
|
||||
* - The user is a friend of the current user
|
||||
* - The user is a friend suggestion of the current user
|
||||
* - The user has an outgoing friend request to the current user</info>
|
||||
* @param {Snowflake} [guildId] The guild ID to get the user's member profile in
|
||||
* @returns {Promise<Object>}
|
||||
* @see {@link https://discord-userdoccers.vercel.app/resources/user#response-body}
|
||||
*/
|
||||
getProfile(guildId) {
|
||||
return this.client.api.users(this.id).profile.get({
|
||||
query: {
|
||||
with_mutual_guilds: true,
|
||||
with_mutual_friends_count: true,
|
||||
guild_id: guildId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* When concatenated with a string, this automatically returns the user's mention instead of the User object.
|
||||
* @returns {string}
|
||||
@@ -680,29 +403,85 @@ class User extends Base {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set note to user
|
||||
* @param {string} note Note to set
|
||||
* @returns {Promise<User>}
|
||||
* The function updates the note of a user and returns the updated user.
|
||||
* @param {string|null|undefined} [note=null] - The `note` parameter is the new value that you want to set for the note of the
|
||||
* user. It is an optional parameter and its default value is `null`.
|
||||
* @returns {Promise<User>} The `setNote` method is returning the `User` object.
|
||||
*/
|
||||
async setNote(note = null) {
|
||||
await this.client.api.users['@me'].notes(this.id).put({ data: { note } });
|
||||
await this.client.notes.updateNote(this.id, note);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get presence (~ v12)
|
||||
* @returns {Promise<Presence | null>}
|
||||
* The function returns the note associated with a specific client ID from a cache.
|
||||
* @type {?string} The note that corresponds to the given id.
|
||||
*/
|
||||
async presenceFetch() {
|
||||
let data = null;
|
||||
await Promise.all(
|
||||
this.client.guilds.cache.map(async guild => {
|
||||
const res_ = await guild.presences.resolve(this.id);
|
||||
if (res_) return (data = res_);
|
||||
return true;
|
||||
}),
|
||||
get note() {
|
||||
return this.client.notes.cache.get(this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* The voice state of this member
|
||||
* @type {VoiceState}
|
||||
* @readonly
|
||||
*/
|
||||
get voice() {
|
||||
return (
|
||||
this.client.voiceStates.cache.get(this.id) ??
|
||||
this.client.guilds.cache.find(g => g?.voiceStates?.cache?.get(this.id))?.voiceStates?.cache?.get(this.id) ??
|
||||
new VoiceState({ client: this.client }, { user_id: this.id })
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ring the user's phone / PC (call)
|
||||
* @returns {Promise<void>}
|
||||
* @deprecated
|
||||
*/
|
||||
ring() {
|
||||
return this.client.api.channels(this.dmChannel.id).call.ring.post({
|
||||
data: {
|
||||
recipients: [this.id],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Friend Request to the user
|
||||
* @type {boolean}
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
sendFriendRequest() {
|
||||
return this.client.relationships.sendFriendRequest({ user: this });
|
||||
}
|
||||
|
||||
/**
|
||||
* Unblock / Unfriend / Cancels a friend request
|
||||
* @type {boolean}
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
deleteRelationship() {
|
||||
return this.client.relationships.deleteRelationship(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check relationship status (Client -> User)
|
||||
* @type {RelationshipTypes}
|
||||
* @readonly
|
||||
*/
|
||||
get relationship() {
|
||||
const i = this.client.relationships.cache.get(this.id) ?? 0;
|
||||
return RelationshipTypes[parseInt(i)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get friend nickname
|
||||
* @type {?string}
|
||||
* @readonly
|
||||
*/
|
||||
get friendNickname() {
|
||||
return this.client.relationships.friendNicknames.get(this.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user