Fix minor bug

- TypeError: this.client.rest.cdn.avatar is not a function
- ReferenceError: AllowedImageFormats is not defined
This commit is contained in:
March 7th 2022-03-23 18:36:58 +07:00
parent e8b87a1778
commit 6f6c986fd2
4 changed files with 373 additions and 355 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "discord.js-selfbot-v13", "name": "discord.js-selfbot-v13",
"version": "0.2.3", "version": "0.2.4",
"description": "A unofficial discord.js fork for creating selfbots [Based on discord.js v13]", "description": "A unofficial discord.js fork for creating selfbots [Based on discord.js v13]",
"main": "./src/index.js", "main": "./src/index.js",
"types": "./typings/index.d.ts", "types": "./typings/index.d.ts",

View File

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

View File

@ -200,6 +200,12 @@ exports.Events = {
GUILD_SCHEDULED_EVENT_USER_REMOVE: 'guildScheduledEventUserRemove', GUILD_SCHEDULED_EVENT_USER_REMOVE: 'guildScheduledEventUserRemove',
}; };
const AllowedImageFormats = ['webp', 'png', 'jpg', 'jpeg', 'gif'];
const AllowedImageSizes = [
16, 32, 56, 64, 96, 128, 256, 300, 512, 600, 1024, 2048, 4096,
];
function makeImageUrl(root, { format = 'webp', size } = {}) { function makeImageUrl(root, { format = 'webp', size } = {}) {
if (!['undefined', 'number'].includes(typeof size)) throw new TypeError('INVALID_TYPE', 'size', 'number'); if (!['undefined', 'number'].includes(typeof size)) throw new TypeError('INVALID_TYPE', 'size', 'number');
if (format && !AllowedImageFormats.includes(format)) throw new Error('IMAGE_FORMAT', format); if (format && !AllowedImageFormats.includes(format)) throw new Error('IMAGE_FORMAT', format);

View File

@ -101,7 +101,7 @@ class Options extends null {
$browser: 'Chrome', $browser: 'Chrome',
$device: 'ASUS ROG Phone 5', $device: 'ASUS ROG Phone 5',
}, },
version: 9, version: 10,
}, },
http: { http: {
headers: { headers: {