Group DM channel
Feature list: Create, Add member, Remove member, fetchInvite, createInvite, setName, setIcon, send Message, leave .-.
This commit is contained in:
parent
ed996371b0
commit
54d0e9d272
36
DOCUMENT.md
36
DOCUMENT.md
@ -117,8 +117,6 @@ User {
|
|||||||
bot: false,
|
bot: false,
|
||||||
system: false,
|
system: false,
|
||||||
flags: UserFlagsBitField { bitfield: 256 },
|
flags: UserFlagsBitField { bitfield: 256 },
|
||||||
friend: false,
|
|
||||||
blocked: false,
|
|
||||||
note: null,
|
note: null,
|
||||||
connectedAccounts: [],
|
connectedAccounts: [],
|
||||||
premiumSince: 1623357181151,
|
premiumSince: 1623357181151,
|
||||||
@ -156,6 +154,40 @@ Guild {}
|
|||||||
```
|
```
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
## Group DM
|
||||||
|
<details>
|
||||||
|
<summary><strong>Click to show</strong></summary>
|
||||||
|
|
||||||
|
Code:
|
||||||
|
```js
|
||||||
|
/* Create */
|
||||||
|
const memberAdd = [
|
||||||
|
client.users.cache.get('id1'),
|
||||||
|
client.users.cache.get('id2'),
|
||||||
|
...
|
||||||
|
client.users.cache.get('id9')
|
||||||
|
]
|
||||||
|
// Max member add to Group: 9, Min: 2
|
||||||
|
await client.channels.createGroupDM(memberAdd);
|
||||||
|
/* Edit */
|
||||||
|
const groupDM = client.channels.cache.get('id');
|
||||||
|
await groupDM.setName('New Name');
|
||||||
|
await groupDM.setIcon('iconURL');
|
||||||
|
await groupDM.getInvite();
|
||||||
|
await groupDM.fetchInvite();
|
||||||
|
await groupDM.removeInvite(invite);
|
||||||
|
await groupDM.addMember(user);
|
||||||
|
await groupDM.removeMember(user);
|
||||||
|
/* Text Channel not Bulk delete */
|
||||||
|
await groupDM.send('Hello World');
|
||||||
|
await groupDM.delete(); // Leave
|
||||||
|
```
|
||||||
|
Response
|
||||||
|
```js
|
||||||
|
Guild {}
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
## Custom Status and RPC
|
## Custom Status and RPC
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "discord.js-selfbot-v13",
|
"name": "discord.js-selfbot-v13",
|
||||||
"version": "1.2.6",
|
"version": "1.2.7",
|
||||||
"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",
|
||||||
@ -47,6 +47,7 @@
|
|||||||
"@types/ws": "^8.5.2",
|
"@types/ws": "^8.5.2",
|
||||||
"axios": "^0.26.1",
|
"axios": "^0.26.1",
|
||||||
"bignumber.js": "^9.0.2",
|
"bignumber.js": "^9.0.2",
|
||||||
|
"bufferutil": "^4.0.6",
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
"discord-api-types": "^0.27.3",
|
"discord-api-types": "^0.27.3",
|
||||||
"discord-bettermarkdown": "^1.1.0",
|
"discord-bettermarkdown": "^1.1.0",
|
||||||
@ -59,6 +60,7 @@
|
|||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
"string-similarity": "^4.0.4",
|
"string-similarity": "^4.0.4",
|
||||||
"undici": "^4.15.0",
|
"undici": "^4.15.0",
|
||||||
|
"utf-8-validate": "^5.0.9",
|
||||||
"ws": "^8.5.0"
|
"ws": "^8.5.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -56,7 +56,7 @@ const Messages = {
|
|||||||
EMBED_FOOTER_TEXT: 'MessageEmbed footer text must be a string.',
|
EMBED_FOOTER_TEXT: 'MessageEmbed footer text must be a string.',
|
||||||
EMBED_DESCRIPTION: 'MessageEmbed description must be a string.',
|
EMBED_DESCRIPTION: 'MessageEmbed description must be a string.',
|
||||||
EMBED_AUTHOR_NAME: 'MessageEmbed author name must be a string.',
|
EMBED_AUTHOR_NAME: 'MessageEmbed author name must be a string.',
|
||||||
/* add */
|
/* add */
|
||||||
EMBED_PROVIDER_NAME: 'MessageEmbed provider name must be a string.',
|
EMBED_PROVIDER_NAME: 'MessageEmbed provider name must be a string.',
|
||||||
|
|
||||||
BUTTON_LABEL: 'MessageButton label must be a string',
|
BUTTON_LABEL: 'MessageButton label must be a string',
|
||||||
@ -152,6 +152,10 @@ const Messages = {
|
|||||||
|
|
||||||
INVITE_NOT_FOUND: 'Could not find the requested invite.',
|
INVITE_NOT_FOUND: 'Could not find the requested invite.',
|
||||||
|
|
||||||
|
NOT_OWNER_GROUP_DM_CHANNEL: "You can't do this action [Missing Permission]",
|
||||||
|
USER_ALREADY_IN_GROUP_DM_CHANNEL: 'User is already in the channel.',
|
||||||
|
USER_NOT_IN_GROUP_DM_CHANNEL: 'User is not in the channel.',
|
||||||
|
|
||||||
DELETE_GROUP_DM_CHANNEL:
|
DELETE_GROUP_DM_CHANNEL:
|
||||||
"Bots don't have access to Group DM Channels and cannot delete them",
|
"Bots don't have access to Group DM Channels and cannot delete them",
|
||||||
FETCH_GROUP_DM_CHANNEL:
|
FETCH_GROUP_DM_CHANNEL:
|
||||||
@ -200,7 +204,8 @@ const Messages = {
|
|||||||
APPLICATION_ID_INVALID: "The application isn't BOT",
|
APPLICATION_ID_INVALID: "The application isn't BOT",
|
||||||
INVALID_NITRO: 'Invalid Nitro Code',
|
INVALID_NITRO: 'Invalid Nitro Code',
|
||||||
MESSAGE_ID_NOT_FOUND: 'Message ID not found',
|
MESSAGE_ID_NOT_FOUND: 'Message ID not found',
|
||||||
MESSAGE_EMBED_LINK_LENGTH: 'Message content with embed link length is too long',
|
MESSAGE_EMBED_LINK_LENGTH:
|
||||||
|
'Message content with embed link length is too long',
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const [name, message] of Object.entries(Messages)) register(name, message);
|
for (const [name, message] of Object.entries(Messages)) register(name, message);
|
||||||
|
@ -4,6 +4,8 @@ const process = require('node:process');
|
|||||||
const CachedManager = require('./CachedManager');
|
const CachedManager = require('./CachedManager');
|
||||||
const { Channel } = require('../structures/Channel');
|
const { Channel } = require('../structures/Channel');
|
||||||
const { Events, ThreadChannelTypes } = require('../util/Constants');
|
const { Events, ThreadChannelTypes } = require('../util/Constants');
|
||||||
|
const User = require('../structures/User');
|
||||||
|
const PartialGroupDMChannel = require('../structures/PartialGroupDMChannel');
|
||||||
|
|
||||||
let cacheWarningEmitted = false;
|
let cacheWarningEmitted = false;
|
||||||
|
|
||||||
@ -113,8 +115,27 @@ class ChannelManager extends CachedManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const data = await this.client.api.channels(id).get();
|
const data = await this.client.api.channels(id).get();
|
||||||
|
// delete in cache
|
||||||
|
this._remove(id);
|
||||||
return this._add(data, null, { cache, allowUnknownGuild });
|
return this._add(data, null, { cache, allowUnknownGuild });
|
||||||
}
|
}
|
||||||
|
// Create Group DM
|
||||||
|
/**
|
||||||
|
* Create Group DM
|
||||||
|
* @param {Array<Discord.User>} recipients Array of recipients
|
||||||
|
* @returns {PartialGroupDMChannel} Channel
|
||||||
|
*/
|
||||||
|
async createGroupDM(recipients) {
|
||||||
|
// Check
|
||||||
|
if (!recipients || !Array.isArray(recipients)) throw new Error('No recipients || Invalid Type (Array)');
|
||||||
|
recipients = recipients.filter(r => r instanceof User && r.id && r.friend);
|
||||||
|
console.log(recipients);
|
||||||
|
if (recipients.length < 2 || recipients.length > 9) throw new Error('Invalid Users length (2 - 9)');
|
||||||
|
const data = await this.client.api.users['@me'].channels.post({
|
||||||
|
data: { recipients: recipients.map((r) => r.id) },
|
||||||
|
});
|
||||||
|
return this._add(data, null, { cache: true, allowUnknownGuild: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ChannelManager;
|
module.exports = ChannelManager;
|
||||||
|
@ -1 +0,0 @@
|
|||||||
// Todo: create, add, remove, update
|
|
@ -2,6 +2,14 @@
|
|||||||
|
|
||||||
const { Channel } = require('./Channel');
|
const { Channel } = require('./Channel');
|
||||||
const { Error } = require('../errors');
|
const { Error } = require('../errors');
|
||||||
|
const { Collection } = require('discord.js');
|
||||||
|
const { Message } = require('./Message');
|
||||||
|
const MessageManager = require('../managers/MessageManager');
|
||||||
|
const User = require('./User');
|
||||||
|
const DataResolver = require('../util/DataResolver');
|
||||||
|
const TextBasedChannel = require('./interfaces/TextBasedChannel');
|
||||||
|
const Invite = require('./Invite');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a Partial Group DM Channel on Discord.
|
* Represents a Partial Group DM Channel on Discord.
|
||||||
@ -10,7 +18,6 @@ const { Error } = require('../errors');
|
|||||||
class PartialGroupDMChannel extends Channel {
|
class PartialGroupDMChannel extends Channel {
|
||||||
constructor(client, data) {
|
constructor(client, data) {
|
||||||
super(client, data);
|
super(client, data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of this Group DM Channel
|
* The name of this Group DM Channel
|
||||||
* @type {?string}
|
* @type {?string}
|
||||||
@ -33,7 +40,84 @@ class PartialGroupDMChannel extends Channel {
|
|||||||
* The recipients of this Group DM Channel.
|
* The recipients of this Group DM Channel.
|
||||||
* @type {PartialRecipient[]}
|
* @type {PartialRecipient[]}
|
||||||
*/
|
*/
|
||||||
this.recipients = data.recipients;
|
this.recipients = new Collection();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Messages data
|
||||||
|
* @type {Collection}
|
||||||
|
*/
|
||||||
|
this.messages = new MessageManager(this);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last Message ID
|
||||||
|
* @type {?snowflake<Message.id>}
|
||||||
|
*/
|
||||||
|
this.lastMessageId = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last Pin Timestamp
|
||||||
|
* @type {UnixTimestamp}
|
||||||
|
*/
|
||||||
|
this.lastPinTimestamp = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The owner of this Group DM Channel
|
||||||
|
* @type {?User}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
this.owner = client.users.cache.get(data.owner_id);
|
||||||
|
this.ownerId = data.owner_id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invites fetch
|
||||||
|
*/
|
||||||
|
this.invites = new Collection();
|
||||||
|
|
||||||
|
this._setup(client, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Discord.Client} client
|
||||||
|
* @param {object} data
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_setup(client, data) {
|
||||||
|
if ('recipients' in data) {
|
||||||
|
Promise.all(
|
||||||
|
data.recipients.map((recipient) => {
|
||||||
|
this.recipients.set(
|
||||||
|
recipient.id,
|
||||||
|
client.users.cache.get(data.owner_id) || recipient,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ('last_pin_timestamp' in data) {
|
||||||
|
const date = new Date(data.last_pin_timestamp);
|
||||||
|
this.lastPinTimestamp = date.getTime();
|
||||||
|
}
|
||||||
|
if ('last_message_id' in data) {
|
||||||
|
this.lastMessageId = data.last_message_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Object} data name, icon
|
||||||
|
* @returns
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
async edit(data) {
|
||||||
|
const _data = {};
|
||||||
|
if ('name' in data) _data.name = data.name?.trim() ?? null;
|
||||||
|
if (typeof data.icon !== 'undefined')
|
||||||
|
_data.icon = await DataResolver.resolveImage(data.icon);
|
||||||
|
const newData = await this.client.api.channels(this.id).patch({
|
||||||
|
data: _data,
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.client.actions.ChannelUpdate.handle(newData).updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,16 +126,85 @@ class PartialGroupDMChannel extends Channel {
|
|||||||
* @returns {?string}
|
* @returns {?string}
|
||||||
*/
|
*/
|
||||||
iconURL({ format, size } = {}) {
|
iconURL({ format, size } = {}) {
|
||||||
return this.icon && this.client.rest.cdn.GDMIcon(this.id, this.icon, format, size);
|
return (
|
||||||
|
this.icon &&
|
||||||
|
this.client.rest.cdn.GDMIcon(this.id, this.icon, format, size)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete() {
|
async addMember(user) {
|
||||||
return Promise.reject(new Error('DELETE_GROUP_DM_CHANNEL'));
|
if (this.ownerId !== this.client.user.id)
|
||||||
|
return Promise.reject(new Error('NOT_OWNER_GROUP_DM_CHANNEL'));
|
||||||
|
if (!user instanceof User)
|
||||||
|
return Promise.reject(
|
||||||
|
new TypeError('User is not an instance of Discord.User'),
|
||||||
|
);
|
||||||
|
if (this.recipients.get(user.id)) return Promise.reject(new Error('USER_ALREADY_IN_GROUP_DM_CHANNEL'));
|
||||||
|
//
|
||||||
|
await this.client.api.channels[this.id].recipients[user.id].put();
|
||||||
|
this.recipients.set(user.id, user);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch() {
|
async removeMember(user) {
|
||||||
return Promise.reject(new Error('FETCH_GROUP_DM_CHANNEL'));
|
if (this.ownerId !== this.client.user.id)
|
||||||
|
return Promise.reject(new Error('NOT_OWNER_GROUP_DM_CHANNEL'));
|
||||||
|
if (!user instanceof User)
|
||||||
|
return Promise.reject(
|
||||||
|
new TypeError('User is not an instance of Discord.User'),
|
||||||
|
);
|
||||||
|
if (!this.recipients.get(user.id)) return Promise.reject(new Error('USER_NOT_IN_GROUP_DM_CHANNEL'));
|
||||||
|
await this.client.api.channels[this.id].recipients[user.id].delete();
|
||||||
|
this.recipients.delete(user.id);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setName(name) {
|
||||||
|
return this.edit({ name });
|
||||||
|
}
|
||||||
|
|
||||||
|
setIcon(icon) {
|
||||||
|
return this.edit({ icon });
|
||||||
|
}
|
||||||
|
|
||||||
|
async getInvite() {
|
||||||
|
const inviteCode = await this.client.api.channels(this.id).invites.post({
|
||||||
|
data: {
|
||||||
|
max_age: 86400,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const invite = new Invite(this.client, inviteCode);
|
||||||
|
this.invites.set(invite.code, invite);
|
||||||
|
return invite;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchInvite(force = false) {
|
||||||
|
if (this.ownerId !== this.client.user.id)
|
||||||
|
return Promise.reject(new Error('NOT_OWNER_GROUP_DM_CHANNEL'));
|
||||||
|
if (!force && this.invites.size) return this.invites;
|
||||||
|
const invites = await this.client.api.channels(this.id).invites.get();
|
||||||
|
await Promise.all(invites.map(invite => this.invites.set(invite.code, new Invite(this.client, invite))));
|
||||||
|
return this.invites;
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeInvite(invite) {
|
||||||
|
if (this.ownerId !== this.client.user.id)
|
||||||
|
return Promise.reject(new Error('NOT_OWNER_GROUP_DM_CHANNEL'));
|
||||||
|
if (!invite instanceof Invite)
|
||||||
|
return Promise.reject(new TypeError('Invite is not an instance of Discord.Invite'));
|
||||||
|
await this.client.api.channels(this.id).invites[invite.code].delete();
|
||||||
|
this.invites.delete(invite.code);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are here only for documentation purposes - they are implemented by TextBasedChannel
|
||||||
|
/* eslint-disable no-empty-function */
|
||||||
|
get lastMessage() { }
|
||||||
|
get lastPinAt() { }
|
||||||
|
send() { }
|
||||||
|
sendTyping() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextBasedChannel.applyToClass(PartialGroupDMChannel, false);
|
||||||
|
|
||||||
module.exports = PartialGroupDMChannel;
|
module.exports = PartialGroupDMChannel;
|
||||||
|
@ -14,120 +14,134 @@ const ApplicationCommandManager = require('../managers/ApplicationCommandManager
|
|||||||
* @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.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.connectedAccounts = [];
|
this.connectedAccounts = [];
|
||||||
this.premiumSince = null;
|
this.premiumSince = null;
|
||||||
this.premiumGuildSince = null;
|
this.premiumGuildSince = null;
|
||||||
this.mutualGuilds = new Collection();
|
this.mutualGuilds = new Collection();
|
||||||
this.applications = null;
|
this.applications = null;
|
||||||
this.note = null;
|
this.note = null;
|
||||||
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);
|
||||||
if (this.bot == true) {
|
if (this.bot == true) {
|
||||||
this.applications = new ApplicationCommandManager(this.client, undefined, this);
|
this.applications = new ApplicationCommandManager(
|
||||||
}
|
this.client,
|
||||||
} else if (!this.partial && typeof this.bot !== 'boolean') {
|
undefined,
|
||||||
this.bot = false;
|
this,
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
} else if (!this.partial && typeof this.bot !== 'boolean') {
|
||||||
|
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 {?UserFlags}
|
* @type {?UserFlags}
|
||||||
*/
|
*/
|
||||||
this.flags = new UserFlags(data.public_flags);
|
this.flags = new UserFlags(data.public_flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Friend ?
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get friend() {
|
||||||
|
return this.client.friends.cache.has(this.id);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Blocked ?
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get blocked() {
|
||||||
|
return this.client.blocked.cache.has(this.id);
|
||||||
|
}
|
||||||
|
|
||||||
// Code written by https://github.com/aiko-chan-ai
|
// Code written by https://github.com/aiko-chan-ai
|
||||||
_ProfilePatch(data) {
|
_ProfilePatch(data) {
|
||||||
@ -223,221 +237,233 @@ class User extends Base {
|
|||||||
.relationships[this.id].delete.then((_) => _);
|
.relationships[this.id].delete.then((_) => _);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this User is a partial
|
||||||
|
* @type {boolean}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get partial() {
|
||||||
|
return typeof this.username !== 'string';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether this User is a partial
|
* The timestamp the user was created at
|
||||||
* @type {boolean}
|
* @type {number}
|
||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
get partial() {
|
get createdTimestamp() {
|
||||||
return typeof this.username !== 'string';
|
return SnowflakeUtil.timestampFrom(this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The timestamp the user was created at
|
* The time the user was created at
|
||||||
* @type {number}
|
* @type {Date}
|
||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
get createdTimestamp() {
|
get createdAt() {
|
||||||
return SnowflakeUtil.timestampFrom(this.id);
|
return new Date(this.createdTimestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The time the user was created at
|
* A link to the user's avatar.
|
||||||
* @type {Date}
|
* @param {ImageURLOptions} [options={}] Options for the Image URL
|
||||||
* @readonly
|
* @returns {?string}
|
||||||
*/
|
*/
|
||||||
get createdAt() {
|
avatarURL({ format, size, dynamic } = {}) {
|
||||||
return new Date(this.createdTimestamp);
|
if (!this.avatar) return null;
|
||||||
}
|
return this.client.rest.cdn.Avatar(
|
||||||
|
this.id,
|
||||||
|
this.avatar,
|
||||||
|
format,
|
||||||
|
size,
|
||||||
|
dynamic,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A link to the user's avatar.
|
* A link to the user's default avatar
|
||||||
* @param {ImageURLOptions} [options={}] Options for the Image URL
|
* @type {string}
|
||||||
* @returns {?string}
|
* @readonly
|
||||||
*/
|
*/
|
||||||
avatarURL({ format, size, dynamic } = {}) {
|
get defaultAvatarURL() {
|
||||||
if (!this.avatar) return null;
|
return this.client.rest.cdn.DefaultAvatar(this.discriminator % 5);
|
||||||
return this.client.rest.cdn.Avatar(this.id, this.avatar, format, size, dynamic);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A link to the user's default avatar
|
* A link to the user's avatar if they have one.
|
||||||
* @type {string}
|
* Otherwise a link to their default avatar will be returned.
|
||||||
* @readonly
|
* @param {ImageURLOptions} [options={}] Options for the Image URL
|
||||||
*/
|
* @returns {string}
|
||||||
get defaultAvatarURL() {
|
*/
|
||||||
return this.client.rest.cdn.DefaultAvatar(this.discriminator % 5);
|
displayAvatarURL(options) {
|
||||||
}
|
return this.avatarURL(options) ?? this.defaultAvatarURL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A link to the user's avatar if they have one.
|
* The hexadecimal version of the user accent color, with a leading hash
|
||||||
* Otherwise a link to their default avatar will be returned.
|
* <info>The user must be force fetched for this property to be present</info>
|
||||||
* @param {ImageURLOptions} [options={}] Options for the Image URL
|
* @type {?string}
|
||||||
* @returns {string}
|
* @readonly
|
||||||
*/
|
*/
|
||||||
displayAvatarURL(options) {
|
get hexAccentColor() {
|
||||||
return this.avatarURL(options) ?? this.defaultAvatarURL;
|
if (typeof this.accentColor !== 'number') return this.accentColor;
|
||||||
}
|
return `#${this.accentColor.toString(16).padStart(6, '0')}`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The hexadecimal version of the user accent color, with a leading hash
|
* A link to the user's banner.
|
||||||
* <info>The user must be force fetched for this property to be present</info>
|
* <info>This method will throw an error if called before the user is force fetched.
|
||||||
* @type {?string}
|
* See {@link User#banner} for more info</info>
|
||||||
* @readonly
|
* @param {ImageURLOptions} [options={}] Options for the Image URL
|
||||||
*/
|
* @returns {?string}
|
||||||
get hexAccentColor() {
|
*/
|
||||||
if (typeof this.accentColor !== 'number') return this.accentColor;
|
bannerURL({ format, size, dynamic } = {}) {
|
||||||
return `#${this.accentColor.toString(16).padStart(6, '0')}`;
|
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A link to the user's banner.
|
* The Discord "tag" (e.g. `hydrabolt#0001`) for this user
|
||||||
* <info>This method will throw an error if called before the user is force fetched.
|
* @type {?string}
|
||||||
* See {@link User#banner} for more info</info>
|
* @readonly
|
||||||
* @param {ImageURLOptions} [options={}] Options for the Image URL
|
*/
|
||||||
* @returns {?string}
|
get tag() {
|
||||||
*/
|
return typeof this.username === 'string'
|
||||||
bannerURL({ format, size, dynamic } = {}) {
|
? `${this.username}#${this.discriminator}`
|
||||||
if (typeof this.banner === 'undefined') throw new Error('USER_BANNER_NOT_FETCHED');
|
: null;
|
||||||
if (!this.banner) return null;
|
}
|
||||||
return this.client.rest.cdn.Banner(this.id, this.banner, format, size, dynamic);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Discord "tag" (e.g. `hydrabolt#0001`) for this user
|
* The DM between the client's user and this user
|
||||||
* @type {?string}
|
* @type {?DMChannel}
|
||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
get tag() {
|
get dmChannel() {
|
||||||
return typeof this.username === 'string' ? `${this.username}#${this.discriminator}` : null;
|
return this.client.users.dmChannel(this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The DM between the client's user and this user
|
* Creates a DM channel between the client and the user.
|
||||||
* @type {?DMChannel}
|
* @param {boolean} [force=false] Whether to skip the cache check and request the API
|
||||||
* @readonly
|
* @returns {Promise<DMChannel>}
|
||||||
*/
|
*/
|
||||||
get dmChannel() {
|
createDM(force = false) {
|
||||||
return this.client.users.dmChannel(this.id);
|
return this.client.users.createDM(this.id, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a DM channel between the client and the user.
|
* Deletes a DM channel (if one exists) between the client and the user. Resolves with the channel if successful.
|
||||||
* @param {boolean} [force=false] Whether to skip the cache check and request the API
|
* @returns {Promise<DMChannel>}
|
||||||
* @returns {Promise<DMChannel>}
|
*/
|
||||||
*/
|
deleteDM() {
|
||||||
createDM(force = false) {
|
return this.client.users.deleteDM(this.id);
|
||||||
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.
|
* Checks if the user is equal to another.
|
||||||
* @returns {Promise<DMChannel>}
|
* 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.
|
||||||
deleteDM() {
|
* @param {User} user User to compare with
|
||||||
return this.client.users.deleteDM(this.id);
|
* @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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the user is equal to another.
|
* Compares the user with an API user object
|
||||||
* It compares id, username, discriminator, avatar, banner, accent color, and bot flags.
|
* @param {APIUser} user The API user object to compare
|
||||||
* It is recommended to compare equality by using `user.id === user2.id` unless you want to compare all properties.
|
* @returns {boolean}
|
||||||
* @param {User} user User to compare with
|
* @private
|
||||||
* @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.public_flags &&
|
||||||
this.flags?.bitfield === user.flags?.bitfield &&
|
('banner' in user ? this.banner === user.banner : true) &&
|
||||||
this.banner === user.banner &&
|
('accent_color' in user ? this.accentColor === user.accent_color : true)
|
||||||
this.accentColor === user.accentColor
|
);
|
||||||
);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares the user with an API user object
|
* Fetches this user's flags.
|
||||||
* @param {APIUser} user The API user object to compare
|
* @param {boolean} [force=false] Whether to skip the cache check and request the API
|
||||||
* @returns {boolean}
|
* @returns {Promise<UserFlags>}
|
||||||
* @private
|
*/
|
||||||
*/
|
fetchFlags(force = false) {
|
||||||
_equals(user) {
|
return this.client.users.fetchFlags(this.id, { force });
|
||||||
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.
|
* Fetches this user.
|
||||||
* @param {boolean} [force=false] 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<UserFlags>}
|
* @returns {Promise<User>}
|
||||||
*/
|
*/
|
||||||
fetchFlags(force = false) {
|
fetch(force = true) {
|
||||||
return this.client.users.fetchFlags(this.id, { force });
|
return this.client.users.fetch(this.id, { force });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches this user.
|
* When concatenated with a string, this automatically returns the user's mention instead of the User object.
|
||||||
* @param {boolean} [force=true] Whether to skip the cache check and request the API
|
* @returns {string}
|
||||||
* @returns {Promise<User>}
|
* @example
|
||||||
*/
|
* // Logs: Hello from <@123456789012345678>!
|
||||||
fetch(force = true) {
|
* console.log(`Hello from ${user}!`);
|
||||||
return this.client.users.fetch(this.id, { force });
|
*/
|
||||||
}
|
toString() {
|
||||||
|
return `<@${this.id}>`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
toJSON(...props) {
|
||||||
* When concatenated with a string, this automatically returns the user's mention instead of the User object.
|
const json = super.toJSON(
|
||||||
* @returns {string}
|
{
|
||||||
* @example
|
createdTimestamp: true,
|
||||||
* // Logs: Hello from <@123456789012345678>!
|
defaultAvatarURL: true,
|
||||||
* console.log(`Hello from ${user}!`);
|
hexAccentColor: true,
|
||||||
*/
|
tag: true,
|
||||||
toString() {
|
},
|
||||||
return `<@${this.id}>`;
|
...props,
|
||||||
}
|
);
|
||||||
|
json.avatarURL = this.avatarURL();
|
||||||
|
json.displayAvatarURL = this.displayAvatarURL();
|
||||||
|
json.bannerURL = this.banner ? this.bannerURL() : this.banner;
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
toJSON(...props) {
|
/**
|
||||||
const json = super.toJSON(
|
* Set note to user
|
||||||
{
|
* @param {String<User.note>} note Note to set
|
||||||
createdTimestamp: true,
|
* @returns {Promise<User.note>}
|
||||||
defaultAvatarURL: true,
|
*/
|
||||||
hexAccentColor: true,
|
async setNote(note = null) {
|
||||||
tag: true,
|
await this.client.api.users['@me'].notes(id).put({ data: { note } });
|
||||||
},
|
return (this.note = note);
|
||||||
...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
|
||||||
* Set note to user
|
/* eslint-disable no-empty-function */
|
||||||
* @param {String<User.note>} note Note to set
|
send() {}
|
||||||
* @returns {Promise<User.note>}
|
|
||||||
*/
|
|
||||||
async setNote(note = null) {
|
|
||||||
await this.client.api.users['@me']
|
|
||||||
.notes(id)
|
|
||||||
.put({ data: { note } });
|
|
||||||
return this.note = note;
|
|
||||||
}
|
|
||||||
|
|
||||||
// These are here only for documentation purposes - they are implemented by TextBasedChannel
|
|
||||||
/* eslint-disable no-empty-function */
|
|
||||||
send() {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextBasedChannel.applyToClass(User);
|
TextBasedChannel.applyToClass(User);
|
||||||
|
20
typings/index.d.ts
vendored
20
typings/index.d.ts
vendored
@ -1897,12 +1897,25 @@ export class OAuth2Guild extends BaseGuild {
|
|||||||
public permissions: Readonly<Permissions>;
|
public permissions: Readonly<Permissions>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PartialGroupDMChannel extends Channel {
|
export class PartialGroupDMChannel extends TextBasedChannelMixin(Channel, ['bulkDelete']) {
|
||||||
private constructor(client: Client, data: RawPartialGroupDMChannelData);
|
private constructor(client: Client, data: RawPartialGroupDMChannelData);
|
||||||
public name: string | null;
|
public name: string | null;
|
||||||
public icon: string | null;
|
public icon: string | null;
|
||||||
public recipients: PartialRecipient[];
|
public recipients: Collection<User>;
|
||||||
|
public messages: MessageManager<PartialGroupDMChannel>;
|
||||||
|
public invites: Collection<Invite.code, Invite>;
|
||||||
|
public lastMessageId: Snowflake | null;
|
||||||
|
public lastPinTimestamp: String<number> | null;
|
||||||
|
public owner: User | null;
|
||||||
|
public ownerId: Snowflake | null;
|
||||||
public iconURL(options?: StaticImageURLOptions): string | null;
|
public iconURL(options?: StaticImageURLOptions): string | null;
|
||||||
|
public addMember(user: User): Promise<PartialGroupDMChannel>;
|
||||||
|
public removeMember(user: User): Promise<PartialGroupDMChannel>;
|
||||||
|
public setName(name: string): Promise<PartialGroupDMChannel>;
|
||||||
|
public setIcon(icon: Base64Resolvable | null): Promise<PartialGroupDMChannel>;
|
||||||
|
public getInvite(): Promise<Invite>;
|
||||||
|
public fetchInvite(force: boolean): Promise<PartialGroupDMChannel.invites>;
|
||||||
|
public removeInvite(invite: Invite): Promise<PartialGroupDMChannel>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PermissionOverwrites extends Base {
|
export class PermissionOverwrites extends Base {
|
||||||
@ -2449,6 +2462,8 @@ export class User extends PartialTextBasedChannel(Base) {
|
|||||||
public banner: string | null | undefined;
|
public banner: string | null | undefined;
|
||||||
public bot: boolean;
|
public bot: boolean;
|
||||||
public readonly createdAt: Date;
|
public readonly createdAt: Date;
|
||||||
|
public readonly friend: Boolean;
|
||||||
|
public readonly blocked: Boolean;
|
||||||
public readonly createdTimestamp: number;
|
public readonly createdTimestamp: number;
|
||||||
public discriminator: string;
|
public discriminator: string;
|
||||||
public readonly defaultAvatarURL: string;
|
public readonly defaultAvatarURL: string;
|
||||||
@ -3044,6 +3059,7 @@ export class BaseGuildEmojiManager extends CachedManager<Snowflake, GuildEmoji,
|
|||||||
export class ChannelManager extends CachedManager<Snowflake, AnyChannel, ChannelResolvable> {
|
export class ChannelManager extends CachedManager<Snowflake, AnyChannel, ChannelResolvable> {
|
||||||
private constructor(client: Client, iterable: Iterable<RawChannelData>);
|
private constructor(client: Client, iterable: Iterable<RawChannelData>);
|
||||||
public fetch(id: Snowflake, options?: FetchChannelOptions): Promise<AnyChannel | null>;
|
public fetch(id: Snowflake, options?: FetchChannelOptions): Promise<AnyChannel | null>;
|
||||||
|
public createGroupDM(recipients: Array<User>): Promise<PartialGroupDMChannel>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ClientUserSettingManager extends CachedManager<Snowflake, null> {
|
export class ClientUserSettingManager extends CachedManager<Snowflake, null> {
|
||||||
|
Loading…
Reference in New Issue
Block a user