discord-self-lite 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/classes/BitField.js +110 -0
- package/src/classes/Channel.js +50 -0
- package/src/classes/Client.js +16 -0
- package/src/classes/Guild.js +71 -0
- package/src/classes/GuildMember.js +108 -0
- package/src/classes/Permissions.js +212 -0
- package/src/connection/rest/RestManager.js +23 -0
- package/src/connection/rest/methods/fetchGuildMember.js +11 -0
- package/src/connection/rest/methods/fetchGuildMembers.js +17 -0
- package/src/connection/ws/WebSocket.js +43 -3
- package/src/index.js +16 -0
package/package.json
CHANGED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A BitField is a data structure that makes it easy to interact with a bitfield.
|
|
3
|
+
*/
|
|
4
|
+
class BitField {
|
|
5
|
+
/**
|
|
6
|
+
* @param {BitFieldResolvable} [bits=BitField.defaultBit] Bit(s) to read from
|
|
7
|
+
*/
|
|
8
|
+
constructor(bits = BitField.defaultBit) {
|
|
9
|
+
/**
|
|
10
|
+
* Bitfield of the packed bits
|
|
11
|
+
* @type {bigint}
|
|
12
|
+
*/
|
|
13
|
+
this.bitfield = BitField.resolve(bits);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Checks whether the bitfield has a bit, or any of multiple bits.
|
|
18
|
+
* @param {BitFieldResolvable} bit Bit(s) to check for
|
|
19
|
+
* @returns {boolean}
|
|
20
|
+
*/
|
|
21
|
+
any(bit) {
|
|
22
|
+
return (this.bitfield & BitField.resolve(bit)) !== BitField.defaultBit;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Checks whether the bitfield has a bit, or multiple bits.
|
|
27
|
+
* @param {BitFieldResolvable} bit Bit(s) to check for
|
|
28
|
+
* @returns {boolean}
|
|
29
|
+
*/
|
|
30
|
+
has(bit) {
|
|
31
|
+
bit = BitField.resolve(bit);
|
|
32
|
+
return (this.bitfield & bit) === bit;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Gets all given bits that are missing from the bitfield.
|
|
37
|
+
* @param {BitFieldResolvable} bits Bit(s) to check for
|
|
38
|
+
* @returns {string[]}
|
|
39
|
+
*/
|
|
40
|
+
missing(bits) {
|
|
41
|
+
const res = [];
|
|
42
|
+
const bit = BitField.resolve(bits);
|
|
43
|
+
|
|
44
|
+
for (const [flag, value] of Object.entries(this.constructor.FLAGS)) {
|
|
45
|
+
if ((bit & value) !== value) continue;
|
|
46
|
+
if ((this.bitfield & value) === value) continue;
|
|
47
|
+
res.push(flag);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return res;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Gets an {@link Array} of bitfield names based on the bits available.
|
|
55
|
+
* @param {boolean} [has=true] Whether to show bits that are set
|
|
56
|
+
* @returns {string[]}
|
|
57
|
+
*/
|
|
58
|
+
toArray(has = true) {
|
|
59
|
+
const arr = [];
|
|
60
|
+
|
|
61
|
+
for (const [flag, value] of Object.entries(this.constructor.FLAGS)) {
|
|
62
|
+
if (has) {
|
|
63
|
+
if ((this.bitfield & value) === value) arr.push(flag);
|
|
64
|
+
} else {
|
|
65
|
+
if ((this.bitfield & value) !== value) arr.push(flag);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return arr;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Data that can be resolved to give a bitfield. This can be:
|
|
74
|
+
* * A string (see {@link BitField.FLAGS})
|
|
75
|
+
* * A bit number
|
|
76
|
+
* * An instance of BitField
|
|
77
|
+
* * An Array of BitFieldResolvable
|
|
78
|
+
* @typedef {string|bigint|BitField|BitFieldResolvable[]} BitFieldResolvable
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Resolves bitfields to their numeric form.
|
|
83
|
+
* @param {BitFieldResolvable} [bit=BitField.defaultBit] bit(s) to resolve
|
|
84
|
+
* @returns {bigint}
|
|
85
|
+
*/
|
|
86
|
+
static resolve(bit = BitField.defaultBit) {
|
|
87
|
+
if (typeof bit === "bigint" && bit >= BitField.defaultBit) return bit;
|
|
88
|
+
if (bit instanceof BitField) return bit.bitfield;
|
|
89
|
+
if (Array.isArray(bit))
|
|
90
|
+
return bit
|
|
91
|
+
.map((p) => this.resolve(p))
|
|
92
|
+
.reduce((prev, p) => prev | p, BitField.defaultBit);
|
|
93
|
+
if (typeof bit === "string") return this.FLAGS[bit];
|
|
94
|
+
throw new TypeError("BitField.resolve: Invalid bitfield");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Numeric bitfield flags.
|
|
100
|
+
* @type {Object<string, bigint>}
|
|
101
|
+
*/
|
|
102
|
+
BitField.FLAGS = {};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The default bit to use if none are provided.
|
|
106
|
+
* @type {bigint}
|
|
107
|
+
*/
|
|
108
|
+
BitField.defaultBit = 0n;
|
|
109
|
+
|
|
110
|
+
module.exports = BitField;
|
package/src/classes/Channel.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Represents a Discord channel
|
|
3
3
|
*/
|
|
4
|
+
const Permissions = require("./Permissions");
|
|
5
|
+
|
|
4
6
|
class Channel {
|
|
5
7
|
/**
|
|
6
8
|
* Create a new Channel instance
|
|
@@ -161,6 +163,54 @@ class Channel {
|
|
|
161
163
|
const guildId = this.data.guild_id;
|
|
162
164
|
return `https://discord.com/channels/${guildId}/${this.id}`;
|
|
163
165
|
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Check permissions for a member in this channel
|
|
169
|
+
* @param {GuildMember} member - The guild member to check permissions for
|
|
170
|
+
* @returns {Permissions} Permissions bitfield for this member in this channel
|
|
171
|
+
*/
|
|
172
|
+
permissionsFor(member) {
|
|
173
|
+
if (!member) return new Permissions(BigInt(0));
|
|
174
|
+
|
|
175
|
+
// Start with the member's base guild permissions
|
|
176
|
+
let permissions = member.permissions.bitfield;
|
|
177
|
+
|
|
178
|
+
// If member has administrator, they have all permissions
|
|
179
|
+
if (member.permissions.has(Permissions.FLAGS.ADMINISTRATOR)) {
|
|
180
|
+
return new Permissions(Permissions.ALL);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Apply channel permission overwrites
|
|
184
|
+
if (this.data.permission_overwrites) {
|
|
185
|
+
const overwrites = this.data.permission_overwrites;
|
|
186
|
+
|
|
187
|
+
// Role overwrites (deny takes precedence over allow)
|
|
188
|
+
const memberRoles = member.roles || [];
|
|
189
|
+
for (const overwrite of overwrites) {
|
|
190
|
+
if (overwrite.type === 0) {
|
|
191
|
+
// Role overwrite
|
|
192
|
+
if (
|
|
193
|
+
memberRoles.includes(overwrite.id) ||
|
|
194
|
+
overwrite.id === this.data.guild_id
|
|
195
|
+
) {
|
|
196
|
+
permissions &= ~BigInt(overwrite.deny || 0);
|
|
197
|
+
permissions |= BigInt(overwrite.allow || 0);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Member-specific overwrites (takes precedence over role overwrites)
|
|
203
|
+
const memberOverwrite = overwrites.find(
|
|
204
|
+
(ow) => ow.type === 1 && ow.id === member.id,
|
|
205
|
+
);
|
|
206
|
+
if (memberOverwrite) {
|
|
207
|
+
permissions &= ~BigInt(memberOverwrite.deny || 0);
|
|
208
|
+
permissions |= BigInt(memberOverwrite.allow || 0);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return new Permissions(permissions);
|
|
213
|
+
}
|
|
164
214
|
}
|
|
165
215
|
|
|
166
216
|
module.exports = Channel;
|
package/src/classes/Client.js
CHANGED
|
@@ -128,6 +128,22 @@ class Client extends EventEmitter {
|
|
|
128
128
|
const data = await this.rest.fetchGuild(id);
|
|
129
129
|
const guild = new Guild(this, this.rest, data);
|
|
130
130
|
this.guilds.set(id, guild); // Update cache with fresh data
|
|
131
|
+
|
|
132
|
+
// Also fetch and cache the client's member information for this guild
|
|
133
|
+
try {
|
|
134
|
+
const memberData = await this.rest.fetchGuildMember(id);
|
|
135
|
+
const GuildMember = require("./GuildMember");
|
|
136
|
+
const member = new GuildMember(this, memberData, guild);
|
|
137
|
+
guild._members.set(this.user.id, member);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
// If fetching member fails, continue without it
|
|
140
|
+
// This might happen if the bot doesn't have permission or other issues
|
|
141
|
+
console.warn(
|
|
142
|
+
`Failed to fetch member data for guild ${id}:`,
|
|
143
|
+
error.message,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
131
147
|
return guild;
|
|
132
148
|
}
|
|
133
149
|
|
package/src/classes/Guild.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
const Channel = require("./Channel");
|
|
2
|
+
const GuildMember = require("./GuildMember");
|
|
3
|
+
const DiscordAPIError = require("./DiscordAPIError");
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Represents a Discord guild (server)
|
|
@@ -31,6 +33,12 @@ class Guild {
|
|
|
31
33
|
this.id = this.id || data.id;
|
|
32
34
|
this.name = this.name || data.name;
|
|
33
35
|
}
|
|
36
|
+
|
|
37
|
+
// Cache for guild members
|
|
38
|
+
this._members = new Map();
|
|
39
|
+
|
|
40
|
+
// Initialize roles from guild data
|
|
41
|
+
this.roles = this.data.roles || [];
|
|
34
42
|
}
|
|
35
43
|
|
|
36
44
|
/**
|
|
@@ -116,6 +124,69 @@ class Guild {
|
|
|
116
124
|
const format = options.format || "png";
|
|
117
125
|
return `https://cdn.discordapp.com/banners/${this.id}/${this.data.banner}.${format}?size=${size}`;
|
|
118
126
|
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Fetch members for this guild
|
|
130
|
+
* @param {object} [options={}] - Fetch options
|
|
131
|
+
* @param {number} [options.limit=1000] - Number of members to fetch
|
|
132
|
+
* @param {string} [options.after] - Member ID to fetch after
|
|
133
|
+
* @returns {Promise<Array<GuildMember>>} Array of guild member instances
|
|
134
|
+
*/
|
|
135
|
+
async fetchMembers(options = {}) {
|
|
136
|
+
const { limit = 1000 } = options;
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
const membersData = await this.client.rest.fetchGuildMembers(this.id, {
|
|
140
|
+
limit,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const members = [];
|
|
144
|
+
for (const memberData of membersData) {
|
|
145
|
+
const member = new GuildMember(this.client, memberData, this);
|
|
146
|
+
this._members.set(member.user.id, member);
|
|
147
|
+
members.push(member);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return members;
|
|
151
|
+
} catch (error) {
|
|
152
|
+
throw new DiscordAPIError(
|
|
153
|
+
`Failed to fetch guild members: ${error.message}`,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get the members collection with a 'me' property
|
|
160
|
+
* @returns {object} Members collection with Map methods and 'me' property
|
|
161
|
+
*/
|
|
162
|
+
get members() {
|
|
163
|
+
const membersMap = this._members;
|
|
164
|
+
const client = this.client;
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
// Map methods
|
|
168
|
+
get: (key) => membersMap.get(key),
|
|
169
|
+
set: (key, value) => membersMap.set(key, value),
|
|
170
|
+
has: (key) => membersMap.has(key),
|
|
171
|
+
delete: (key) => membersMap.delete(key),
|
|
172
|
+
clear: () => membersMap.clear(),
|
|
173
|
+
size: membersMap.size,
|
|
174
|
+
[Symbol.iterator]: () => membersMap[Symbol.iterator](),
|
|
175
|
+
|
|
176
|
+
// Special 'me' property
|
|
177
|
+
get me() {
|
|
178
|
+
return membersMap.get(client.user?.id);
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Set the members cache
|
|
185
|
+
* @param {Map} value - The members map
|
|
186
|
+
*/
|
|
187
|
+
set members(value) {
|
|
188
|
+
this._members = value;
|
|
189
|
+
}
|
|
119
190
|
}
|
|
120
191
|
|
|
121
192
|
module.exports = Guild;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a Discord guild member
|
|
3
|
+
*/
|
|
4
|
+
const Permissions = require("./Permissions");
|
|
5
|
+
|
|
6
|
+
class GuildMember {
|
|
7
|
+
/**
|
|
8
|
+
* Create a new GuildMember instance
|
|
9
|
+
* @param {Client} client - The Discord client
|
|
10
|
+
* @param {object} data - Raw member data from Discord API
|
|
11
|
+
* @param {Guild} guild - The guild this member belongs to
|
|
12
|
+
*/
|
|
13
|
+
constructor(client, data, guild) {
|
|
14
|
+
this.client = client;
|
|
15
|
+
this.guild = guild;
|
|
16
|
+
this.data = data;
|
|
17
|
+
this._permissions = null;
|
|
18
|
+
|
|
19
|
+
// Copy all member properties
|
|
20
|
+
for (const [key, value] of Object.entries(data)) {
|
|
21
|
+
const camelKey = key.replace(/_([a-z])/g, (_, letter) =>
|
|
22
|
+
letter.toUpperCase(),
|
|
23
|
+
);
|
|
24
|
+
this[camelKey] = value;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Handle user data separately
|
|
28
|
+
if (data.user) {
|
|
29
|
+
this.user = data.user;
|
|
30
|
+
this.id = data.user.id;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
this.roles = this.roles || [];
|
|
34
|
+
this.joinedAt = this.joinedAt || data.joined_at;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Check if this member is the client user
|
|
39
|
+
* @returns {boolean} True if this member represents the client
|
|
40
|
+
*/
|
|
41
|
+
get isClient() {
|
|
42
|
+
return this.id === this.client.user?.id;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get the member's display name (nickname or username)
|
|
47
|
+
* @returns {string} The display name
|
|
48
|
+
*/
|
|
49
|
+
get displayName() {
|
|
50
|
+
return this.nick || this.user?.username || "Unknown";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get the member's permissions in this guild
|
|
55
|
+
* @returns {Permissions} Permissions bitfield
|
|
56
|
+
*/
|
|
57
|
+
get permissions() {
|
|
58
|
+
if (this._permissions) return this._permissions;
|
|
59
|
+
|
|
60
|
+
// Calculate permissions based on roles
|
|
61
|
+
let permissions = BigInt(0);
|
|
62
|
+
|
|
63
|
+
// If member is the guild owner, they have all permissions
|
|
64
|
+
if (this.id === this.guild.ownerId) {
|
|
65
|
+
permissions = Permissions.ALL;
|
|
66
|
+
} else {
|
|
67
|
+
// Start with @everyone role permissions
|
|
68
|
+
const everyoneRole = this.guild.roles?.find(
|
|
69
|
+
(role) => role.id === this.guild.id,
|
|
70
|
+
);
|
|
71
|
+
if (everyoneRole) {
|
|
72
|
+
permissions |= BigInt(everyoneRole.permissions || 0);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Add permissions from member's roles
|
|
76
|
+
for (const roleId of this.roles || []) {
|
|
77
|
+
const role = this.guild.roles?.find((r) => r.id === roleId);
|
|
78
|
+
if (role) {
|
|
79
|
+
permissions |= BigInt(role.permissions || 0);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this._permissions = new Permissions(permissions);
|
|
85
|
+
return this._permissions;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
set permissions(value) {
|
|
89
|
+
if (value instanceof Permissions) {
|
|
90
|
+
this._permissions = value;
|
|
91
|
+
} else {
|
|
92
|
+
this._permissions = new Permissions(value);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Check if the member has specific permissions
|
|
98
|
+
* @param {Array<string>} permissions - Permission flags to check
|
|
99
|
+
* @returns {boolean} True if member has all permissions
|
|
100
|
+
*/
|
|
101
|
+
hasPermission() {
|
|
102
|
+
// TODO: Implement proper permission checking
|
|
103
|
+
// Simplified implementation - always return true for now
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = GuildMember;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const BitField = require("./BitField");
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Data structure that makes it easy to interact with a permission bitfield. All {@link GuildMember}s have a set of
|
|
7
|
+
* permissions in their guild, and each channel in the guild may also have {@link PermissionOverwrites} for the member
|
|
8
|
+
* that override their default permissions.
|
|
9
|
+
* @extends {BitField}
|
|
10
|
+
*/
|
|
11
|
+
class Permissions extends BitField {
|
|
12
|
+
/**
|
|
13
|
+
* Bitfield of the packed bits
|
|
14
|
+
* @type {bigint}
|
|
15
|
+
* @name Permissions#bitfield
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Data that can be resolved to give a permission number. This can be:
|
|
20
|
+
* * A string (see {@link Permissions.FLAGS})
|
|
21
|
+
* * A permission number
|
|
22
|
+
* * An instance of Permissions
|
|
23
|
+
* * An Array of PermissionResolvable
|
|
24
|
+
* @typedef {string|bigint|Permissions|PermissionResolvable[]} PermissionResolvable
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Gets all given bits that are missing from the bitfield.
|
|
29
|
+
* @param {BitFieldResolvable} bits Bit(s) to check for
|
|
30
|
+
* @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
|
|
31
|
+
* @returns {string[]}
|
|
32
|
+
*/
|
|
33
|
+
missing(bits, checkAdmin = true) {
|
|
34
|
+
return checkAdmin && this.has(this.constructor.FLAGS.ADMINISTRATOR)
|
|
35
|
+
? []
|
|
36
|
+
: super.missing(bits);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Checks whether the bitfield has a permission, or any of multiple permissions.
|
|
41
|
+
* @param {PermissionResolvable} permission Permission(s) to check for
|
|
42
|
+
* @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
|
|
43
|
+
* @returns {boolean}
|
|
44
|
+
*/
|
|
45
|
+
any(permission, checkAdmin = true) {
|
|
46
|
+
return (
|
|
47
|
+
(checkAdmin && super.has(this.constructor.FLAGS.ADMINISTRATOR)) ||
|
|
48
|
+
super.any(permission)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Checks whether the bitfield has a permission, or multiple permissions.
|
|
54
|
+
* @param {PermissionResolvable} permission Permission(s) to check for
|
|
55
|
+
* @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
|
|
56
|
+
* @returns {boolean}
|
|
57
|
+
*/
|
|
58
|
+
has(permission, checkAdmin = true) {
|
|
59
|
+
return (
|
|
60
|
+
(checkAdmin && super.has(this.constructor.FLAGS.ADMINISTRATOR)) ||
|
|
61
|
+
super.has(permission)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Gets an {@link Array} of bitfield names based on the permissions available.
|
|
67
|
+
* @returns {string[]}
|
|
68
|
+
*/
|
|
69
|
+
toArray() {
|
|
70
|
+
return super.toArray(false);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Numeric permission flags. All available properties:
|
|
76
|
+
* * `CREATE_INSTANT_INVITE` (create invitations to the guild)
|
|
77
|
+
* * `KICK_MEMBERS`
|
|
78
|
+
* * `BAN_MEMBERS`
|
|
79
|
+
* * `ADMINISTRATOR` (implicitly has *all* permissions, and bypasses all channel overwrites)
|
|
80
|
+
* * `MANAGE_CHANNELS` (edit and reorder channels)
|
|
81
|
+
* * `MANAGE_GUILD` (edit the guild information, region, etc.)
|
|
82
|
+
* * `ADD_REACTIONS` (add new reactions to messages)
|
|
83
|
+
* * `VIEW_AUDIT_LOG`
|
|
84
|
+
* * `PRIORITY_SPEAKER`
|
|
85
|
+
* * `STREAM`
|
|
86
|
+
* * `VIEW_CHANNEL`
|
|
87
|
+
* * `SEND_MESSAGES`
|
|
88
|
+
* * `SEND_TTS_MESSAGES`
|
|
89
|
+
* * `MANAGE_MESSAGES` (delete messages and reactions)
|
|
90
|
+
* * `EMBED_LINKS` (links posted will have a preview embedded)
|
|
91
|
+
* * `ATTACH_FILES`
|
|
92
|
+
* * `READ_MESSAGE_HISTORY` (view messages that were posted prior to opening Discord)
|
|
93
|
+
* * `MENTION_EVERYONE`
|
|
94
|
+
* * `USE_EXTERNAL_EMOJIS` (use emojis from different guilds)
|
|
95
|
+
* * `VIEW_GUILD_INSIGHTS`
|
|
96
|
+
* * `CONNECT` (connect to a voice channel)
|
|
97
|
+
* * `SPEAK` (speak in a voice channel)
|
|
98
|
+
* * `MUTE_MEMBERS` (mute members across all voice channels)
|
|
99
|
+
* * `DEAFEN_MEMBERS` (deafen members across all voice channels)
|
|
100
|
+
* * `MOVE_MEMBERS` (move members between voice channels)
|
|
101
|
+
* * `USE_VAD` (use voice activity detection)
|
|
102
|
+
* * `CHANGE_NICKNAME`
|
|
103
|
+
* * `MANAGE_NICKNAMES` (change other members' nicknames)
|
|
104
|
+
* * `MANAGE_ROLES`
|
|
105
|
+
* * `MANAGE_WEBHOOKS`
|
|
106
|
+
* * `MANAGE_EMOJIS_AND_STICKERS`
|
|
107
|
+
* * `USE_APPLICATION_COMMANDS`
|
|
108
|
+
* * `REQUEST_TO_SPEAK`
|
|
109
|
+
* * `MANAGE_EVENTS`
|
|
110
|
+
* * `MANAGE_THREADS`
|
|
111
|
+
* * `USE_PUBLIC_THREADS` (deprecated)
|
|
112
|
+
* * `CREATE_PUBLIC_THREADS`
|
|
113
|
+
* * `USE_PRIVATE_THREADS` (deprecated)
|
|
114
|
+
* * `CREATE_PRIVATE_THREADS`
|
|
115
|
+
* * `USE_EXTERNAL_STICKERS` (use stickers from different guilds)
|
|
116
|
+
* * `SEND_MESSAGES_IN_THREADS`
|
|
117
|
+
* * `START_EMBEDDED_ACTIVITIES`
|
|
118
|
+
* * `MODERATE_MEMBERS`
|
|
119
|
+
* * `VIEW_CREATOR_MONETIZATION_ANALYTICS`
|
|
120
|
+
* * `USE_SOUNDBOARD`
|
|
121
|
+
* * `CREATE_GUILD_EXPRESSIONS`
|
|
122
|
+
* * `CREATE_EVENTS`
|
|
123
|
+
* * `USE_EXTERNAL_SOUNDS`
|
|
124
|
+
* * `SEND_VOICE_MESSAGES`
|
|
125
|
+
* * `USE_CLYDE_AI`
|
|
126
|
+
* * `SET_VOICE_CHANNEL_STATUS`
|
|
127
|
+
* * `SEND_POLLS`
|
|
128
|
+
* * `USE_EXTERNAL_APPS`
|
|
129
|
+
* @type {Object<string, bigint>}
|
|
130
|
+
* @see {@link https://discord.com/developers/docs/topics/permissions#permissions-bitwise-permission-flags}
|
|
131
|
+
*/
|
|
132
|
+
Permissions.FLAGS = {
|
|
133
|
+
CREATE_INSTANT_INVITE: 1n << 0n,
|
|
134
|
+
KICK_MEMBERS: 1n << 1n,
|
|
135
|
+
BAN_MEMBERS: 1n << 2n,
|
|
136
|
+
ADMINISTRATOR: 1n << 3n,
|
|
137
|
+
MANAGE_CHANNELS: 1n << 4n,
|
|
138
|
+
MANAGE_GUILD: 1n << 5n,
|
|
139
|
+
ADD_REACTIONS: 1n << 6n,
|
|
140
|
+
VIEW_AUDIT_LOG: 1n << 7n,
|
|
141
|
+
PRIORITY_SPEAKER: 1n << 8n,
|
|
142
|
+
STREAM: 1n << 9n,
|
|
143
|
+
VIEW_CHANNEL: 1n << 10n,
|
|
144
|
+
SEND_MESSAGES: 1n << 11n,
|
|
145
|
+
SEND_TTS_MESSAGES: 1n << 12n,
|
|
146
|
+
MANAGE_MESSAGES: 1n << 13n,
|
|
147
|
+
EMBED_LINKS: 1n << 14n,
|
|
148
|
+
ATTACH_FILES: 1n << 15n,
|
|
149
|
+
READ_MESSAGE_HISTORY: 1n << 16n,
|
|
150
|
+
MENTION_EVERYONE: 1n << 17n,
|
|
151
|
+
USE_EXTERNAL_EMOJIS: 1n << 18n,
|
|
152
|
+
VIEW_GUILD_INSIGHTS: 1n << 19n,
|
|
153
|
+
CONNECT: 1n << 20n,
|
|
154
|
+
SPEAK: 1n << 21n,
|
|
155
|
+
MUTE_MEMBERS: 1n << 22n,
|
|
156
|
+
DEAFEN_MEMBERS: 1n << 23n,
|
|
157
|
+
MOVE_MEMBERS: 1n << 24n,
|
|
158
|
+
USE_VAD: 1n << 25n,
|
|
159
|
+
CHANGE_NICKNAME: 1n << 26n,
|
|
160
|
+
MANAGE_NICKNAMES: 1n << 27n,
|
|
161
|
+
MANAGE_ROLES: 1n << 28n,
|
|
162
|
+
MANAGE_WEBHOOKS: 1n << 29n,
|
|
163
|
+
MANAGE_EMOJIS_AND_STICKERS: 1n << 30n,
|
|
164
|
+
USE_APPLICATION_COMMANDS: 1n << 31n,
|
|
165
|
+
REQUEST_TO_SPEAK: 1n << 32n,
|
|
166
|
+
MANAGE_EVENTS: 1n << 33n,
|
|
167
|
+
MANAGE_THREADS: 1n << 34n,
|
|
168
|
+
CREATE_PUBLIC_THREADS: 1n << 35n,
|
|
169
|
+
CREATE_PRIVATE_THREADS: 1n << 36n,
|
|
170
|
+
USE_EXTERNAL_STICKERS: 1n << 37n,
|
|
171
|
+
SEND_MESSAGES_IN_THREADS: 1n << 38n,
|
|
172
|
+
START_EMBEDDED_ACTIVITIES: 1n << 39n,
|
|
173
|
+
MODERATE_MEMBERS: 1n << 40n,
|
|
174
|
+
VIEW_CREATOR_MONETIZATION_ANALYTICS: 1n << 41n,
|
|
175
|
+
USE_SOUNDBOARD: 1n << 42n,
|
|
176
|
+
CREATE_GUILD_EXPRESSIONS: 1n << 43n,
|
|
177
|
+
CREATE_EVENTS: 1n << 44n,
|
|
178
|
+
USE_EXTERNAL_SOUNDS: 1n << 45n,
|
|
179
|
+
SEND_VOICE_MESSAGES: 1n << 46n,
|
|
180
|
+
USE_CLYDE_AI: 1n << 47n,
|
|
181
|
+
SET_VOICE_CHANNEL_STATUS: 1n << 48n,
|
|
182
|
+
SEND_POLLS: 1n << 49n,
|
|
183
|
+
USE_EXTERNAL_APPS: 1n << 50n,
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Bitfield representing every permission combined
|
|
188
|
+
* @type {bigint}
|
|
189
|
+
*/
|
|
190
|
+
Permissions.ALL = Object.values(Permissions.FLAGS).reduce(
|
|
191
|
+
(all, p) => all | p,
|
|
192
|
+
0n,
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Bitfield representing the default permissions for users
|
|
197
|
+
* @type {bigint}
|
|
198
|
+
*/
|
|
199
|
+
Permissions.DEFAULT = BigInt(104324673);
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Bitfield representing the permissions required for moderators of stage channels
|
|
203
|
+
* @type {bigint}
|
|
204
|
+
*/
|
|
205
|
+
Permissions.STAGE_MODERATOR =
|
|
206
|
+
Permissions.FLAGS.MANAGE_CHANNELS |
|
|
207
|
+
Permissions.FLAGS.MUTE_MEMBERS |
|
|
208
|
+
Permissions.FLAGS.MOVE_MEMBERS;
|
|
209
|
+
|
|
210
|
+
Permissions.defaultBit = BigInt(0);
|
|
211
|
+
|
|
212
|
+
module.exports = Permissions;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const sendMessage = require("./methods/sendMessage");
|
|
2
2
|
const react = require("./methods/react");
|
|
3
3
|
const clickButton = require("./methods/clickButton");
|
|
4
|
+
const fetchGuildMembers = require("./methods/fetchGuildMembers");
|
|
5
|
+
const fetchGuildMember = require("./methods/fetchGuildMember");
|
|
4
6
|
const DiscordAPIError = require("../../classes/DiscordAPIError");
|
|
5
7
|
|
|
6
8
|
/**
|
|
@@ -287,6 +289,27 @@ class RestManager {
|
|
|
287
289
|
return await this.request(`/guilds/${guildId}/channels`);
|
|
288
290
|
}
|
|
289
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Fetch guild members
|
|
294
|
+
* @param {string} guildId - The guild ID
|
|
295
|
+
* @param {object} [options={}] - Fetch options
|
|
296
|
+
* @param {number} [options.limit=1000] - Number of members to fetch
|
|
297
|
+
* @param {string} [options.after] - Member ID to fetch after
|
|
298
|
+
* @returns {Promise<Array>} Array of member data
|
|
299
|
+
*/
|
|
300
|
+
async fetchGuildMembers(guildId, options = {}) {
|
|
301
|
+
return await fetchGuildMembers(this, guildId, options);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Fetch the current user's member information for a guild
|
|
306
|
+
* @param {string} guildId - The guild ID
|
|
307
|
+
* @returns {Promise<object>} Member data
|
|
308
|
+
*/
|
|
309
|
+
async fetchGuildMember(guildId) {
|
|
310
|
+
return await fetchGuildMember(this, guildId);
|
|
311
|
+
}
|
|
312
|
+
|
|
290
313
|
/**
|
|
291
314
|
* Fetch a specific message from a channel
|
|
292
315
|
* @param {string} channelId - The channel ID
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch the current user's member information for a guild
|
|
3
|
+
* @param {RestManager} rest - The REST manager instance
|
|
4
|
+
* @param {string} guildId - The guild ID
|
|
5
|
+
* @returns {Promise<object>} Member data
|
|
6
|
+
*/
|
|
7
|
+
async function fetchGuildMember(rest, guildId) {
|
|
8
|
+
return await rest.request(`/users/@me/guilds/${guildId}/member`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
module.exports = fetchGuildMember;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch guild members
|
|
3
|
+
* @param {RestManager} rest - The REST manager instance
|
|
4
|
+
* @param {string} guildId - The guild ID
|
|
5
|
+
* @param {object} [options={}] - Fetch options
|
|
6
|
+
* @param {number} [options.limit=1000] - Number of members to fetch
|
|
7
|
+
* @param {string} [options.after] - Member ID to fetch after
|
|
8
|
+
* @returns {Promise<Array>} Array of member data
|
|
9
|
+
*/
|
|
10
|
+
async function fetchGuildMembers(rest, guildId, options = {}) {
|
|
11
|
+
const query = new URLSearchParams(options).toString();
|
|
12
|
+
return await rest.request(`/guilds/${guildId}/members?${query}`, {
|
|
13
|
+
method: "GET",
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = fetchGuildMembers;
|
|
@@ -131,6 +131,14 @@ class DiscordWebSocket extends EventEmitter {
|
|
|
131
131
|
this.sessionId = message.d.session_id;
|
|
132
132
|
this.client.sessionId = message.d.session_id;
|
|
133
133
|
this.client.user = new User(this.client, message.d.user);
|
|
134
|
+
|
|
135
|
+
// Process guilds from READY payload
|
|
136
|
+
if (message.d.guilds) {
|
|
137
|
+
for (const guildData of message.d.guilds) {
|
|
138
|
+
this.handleGuildCreate(guildData);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
134
142
|
this.ready = true;
|
|
135
143
|
this.client.emit("ready", message.d);
|
|
136
144
|
break;
|
|
@@ -140,9 +148,7 @@ class DiscordWebSocket extends EventEmitter {
|
|
|
140
148
|
break;
|
|
141
149
|
}
|
|
142
150
|
case "GUILD_CREATE": {
|
|
143
|
-
|
|
144
|
-
this.client.guilds.set(guild.id, guild);
|
|
145
|
-
this.client.emit("guildCreate", guild);
|
|
151
|
+
this.handleGuildCreate(message.d);
|
|
146
152
|
break;
|
|
147
153
|
}
|
|
148
154
|
case "CHANNEL_CREATE": {
|
|
@@ -156,6 +162,40 @@ class DiscordWebSocket extends EventEmitter {
|
|
|
156
162
|
}
|
|
157
163
|
}
|
|
158
164
|
|
|
165
|
+
async handleGuildCreate(data) {
|
|
166
|
+
const guild = new Guild(this.client, this.client.rest, data);
|
|
167
|
+
this.client.guilds.set(guild.id, guild);
|
|
168
|
+
|
|
169
|
+
// Populate the client's member information for this guild
|
|
170
|
+
if (data.members) {
|
|
171
|
+
const clientMember = data.members.find(
|
|
172
|
+
(member) => member.user.id === this.client.user.id,
|
|
173
|
+
);
|
|
174
|
+
if (clientMember) {
|
|
175
|
+
const GuildMember = require("../../classes/GuildMember");
|
|
176
|
+
const member = new GuildMember(this.client, clientMember, guild);
|
|
177
|
+
guild._members.set(this.client.user.id, member);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// If no member data in GUILD_CREATE, fetch it from API
|
|
182
|
+
if (!guild._members.has(this.client.user.id)) {
|
|
183
|
+
try {
|
|
184
|
+
const memberData = await this.client.rest.fetchGuildMember(guild.id);
|
|
185
|
+
const GuildMember = require("../../classes/GuildMember");
|
|
186
|
+
const member = new GuildMember(this.client, memberData, guild);
|
|
187
|
+
guild._members.set(this.client.user.id, member);
|
|
188
|
+
} catch (err) {
|
|
189
|
+
console.warn(
|
|
190
|
+
`Failed to fetch member for guild ${guild.id}:`,
|
|
191
|
+
err.message,
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
this.client.emit("guildCreate", guild);
|
|
197
|
+
}
|
|
198
|
+
|
|
159
199
|
reconnect() {
|
|
160
200
|
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
161
201
|
const err = new WebSocketError("Max reconnect attempts reached");
|
package/src/index.js
CHANGED
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
|
|
20
20
|
const Client = require("./classes/Client");
|
|
21
21
|
const WebhookClient = require("./connection/webhook/WebhookClient");
|
|
22
|
+
const Permissions = require("./classes/Permissions");
|
|
23
|
+
const BitField = require("./classes/BitField");
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
26
|
* Main package exports
|
|
@@ -38,4 +40,18 @@ module.exports = {
|
|
|
38
40
|
* @see {@link WebhookClient}
|
|
39
41
|
*/
|
|
40
42
|
WebhookClient,
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Discord permission flags and utilities
|
|
46
|
+
* @type {Permissions}
|
|
47
|
+
* @see {@link Permissions}
|
|
48
|
+
*/
|
|
49
|
+
Permissions,
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Base class for handling bitfields
|
|
53
|
+
* @type {BitField}
|
|
54
|
+
* @see {@link BitField}
|
|
55
|
+
*/
|
|
56
|
+
BitField,
|
|
41
57
|
};
|