guilds.js 0.0.3 → 0.0.4
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/README.md +13 -10
- package/dist/index.cjs +381 -105
- package/dist/index.d.cts +292 -51
- package/dist/index.d.mts +292 -51
- package/dist/index.mjs +373 -101
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
<
|
|
1
|
+
<div align="center">
|
|
2
2
|
<img
|
|
3
3
|
alt="guilds.js Logo"
|
|
4
4
|
width="85"
|
|
5
|
-
src="https://cdn.discordapp.com/attachments/1025091872198250597/
|
|
5
|
+
src="https://cdn.discordapp.com/attachments/1025091872198250597/1475938039095103669/guilds.js.png?ex=699f4dea&is=699dfc6a&hm=a52d790f5cfa4820e4171126ed454f47ad32f94f5182d781ae08304bb2cdb77e&"
|
|
6
6
|
/>
|
|
7
|
-
</
|
|
7
|
+
</div>
|
|
8
8
|
|
|
9
9
|
<div align="center">
|
|
10
10
|
<a href="https://github.com/andrewdku/guilds.js">GitHub</a> |
|
|
@@ -29,15 +29,18 @@ bun add guilds.js
|
|
|
29
29
|
## Example
|
|
30
30
|
|
|
31
31
|
```js
|
|
32
|
-
import { Client } from "guilds.js";
|
|
32
|
+
import { Client, GatewayIntents } from "guilds.js";
|
|
33
33
|
|
|
34
34
|
const client = new Client({
|
|
35
35
|
// https://discord.com/developers/applications
|
|
36
|
-
token: "
|
|
37
|
-
|
|
38
|
-
// client intents
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
token: "TOKEN",
|
|
37
|
+
|
|
38
|
+
// client intents
|
|
39
|
+
intents: [
|
|
40
|
+
GatewayIntents.Guilds,
|
|
41
|
+
GatewayIntents.GuildMembers,
|
|
42
|
+
GatewayIntents.GuildMessages,
|
|
43
|
+
],
|
|
41
44
|
});
|
|
42
45
|
|
|
43
46
|
client.once("ready", () => {
|
|
@@ -54,4 +57,4 @@ client.connect();
|
|
|
54
57
|
|
|
55
58
|
# License
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
Licensed under the [Apache License 2.0](LICENSE).
|
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
|
+
//#region src/utils/color-convert.ts
|
|
4
|
+
/**
|
|
5
|
+
* Converts an integer color to a hex color string
|
|
6
|
+
* @returns Hex string or null
|
|
7
|
+
*/
|
|
8
|
+
function colorIntToHex(color, addHashSymbol = false) {
|
|
9
|
+
if (color == null || !Number.isFinite(color)) return null;
|
|
10
|
+
const hex = (color >>> 0 & 16777215).toString(16).padStart(6, "0");
|
|
11
|
+
return addHashSymbol ? `#${hex}` : hex;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Converts a hex color string to an integer color
|
|
15
|
+
* @returns Color integer or null
|
|
16
|
+
*/
|
|
17
|
+
function hexToColorInt(hex) {
|
|
18
|
+
if (!hex) return null;
|
|
19
|
+
hex = hex.trim().replace(/^#/, "");
|
|
20
|
+
hex = hex.length === 3 ? hex.split("").map((c) => c + c).join("") : hex;
|
|
21
|
+
if (!/^[0-9a-fA-F]{6}$/.test(hex)) return null;
|
|
22
|
+
return parseInt(hex, 16);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
//#endregion
|
|
3
26
|
//#region src/utils/constants.ts
|
|
4
|
-
const
|
|
27
|
+
const ActivityTypes = {
|
|
5
28
|
Competing: 5,
|
|
6
29
|
Custom: 4,
|
|
7
30
|
Listening: 2,
|
|
@@ -10,15 +33,30 @@ const activityTypes = {
|
|
|
10
33
|
Watching: 3
|
|
11
34
|
};
|
|
12
35
|
const baseApiUrl = "https://discord.com/api/v10";
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
36
|
+
const GatewayIntents = {
|
|
37
|
+
AutoModerationConfiguration: 1 << 20,
|
|
38
|
+
AutoModerationExecution: 1 << 21,
|
|
39
|
+
DirectMessagePolls: 1 << 25,
|
|
40
|
+
DirectMessageReactions: 8192,
|
|
41
|
+
DirectMessages: 4096,
|
|
42
|
+
DirectMessageTyping: 16384,
|
|
43
|
+
GuildExpressions: 8,
|
|
44
|
+
GuildIntegrations: 16,
|
|
45
|
+
GuildInvites: 64,
|
|
46
|
+
GuildMembers: 2,
|
|
47
|
+
GuildMessagePolls: 1 << 24,
|
|
48
|
+
GuildMessageReactions: 1024,
|
|
49
|
+
GuildMessages: 512,
|
|
50
|
+
GuildMessageTyping: 2048,
|
|
51
|
+
GuildModeration: 4,
|
|
52
|
+
GuildPresences: 256,
|
|
53
|
+
Guilds: 1,
|
|
54
|
+
GuildScheduledEvents: 65536,
|
|
55
|
+
GuildVoiceStates: 128,
|
|
56
|
+
GuildWebhooks: 32,
|
|
57
|
+
MessageContent: 32768
|
|
58
|
+
};
|
|
59
|
+
const GatewayOpcodes = {
|
|
22
60
|
Dispatch: 0,
|
|
23
61
|
Heartbeat: 1,
|
|
24
62
|
HeartbeatACK: 11,
|
|
@@ -33,15 +71,41 @@ const opCodes = {
|
|
|
33
71
|
VoiceStateUpdate: 3
|
|
34
72
|
};
|
|
35
73
|
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/utils/endpoints.ts
|
|
76
|
+
/** Discord's API v10 endpoints */
|
|
77
|
+
const Endpoints = {
|
|
78
|
+
gateway() {
|
|
79
|
+
return `${baseApiUrl}/gateway`;
|
|
80
|
+
},
|
|
81
|
+
gatewayBot() {
|
|
82
|
+
return `${Endpoints.gateway()}/bot`;
|
|
83
|
+
},
|
|
84
|
+
user(userId = "@me") {
|
|
85
|
+
return `${baseApiUrl}/users/${userId}`;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
36
89
|
//#endregion
|
|
37
90
|
//#region src/classes/EventHandler.ts
|
|
91
|
+
/** Class representing a type-safe EventEmitter */
|
|
38
92
|
var EventHandler = class {
|
|
39
93
|
#listeners = {};
|
|
94
|
+
/**
|
|
95
|
+
* Add a new event listener
|
|
96
|
+
* @param event Event name
|
|
97
|
+
* @param listener Listener callback
|
|
98
|
+
*/
|
|
40
99
|
on(event, listener) {
|
|
41
100
|
if (!this.#listeners[event]) this.#listeners[event] = [];
|
|
42
101
|
this.#listeners[event].push(listener);
|
|
43
102
|
return this;
|
|
44
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Add a new event listener which only runs once
|
|
106
|
+
* @param event Event name
|
|
107
|
+
* @param listener Listener callback
|
|
108
|
+
*/
|
|
45
109
|
once(event, listener) {
|
|
46
110
|
const wrapped = (...args) => {
|
|
47
111
|
listener(...args);
|
|
@@ -50,11 +114,21 @@ var EventHandler = class {
|
|
|
50
114
|
this.on(event, wrapped);
|
|
51
115
|
return this;
|
|
52
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Remove an event name
|
|
119
|
+
* @param event Event name
|
|
120
|
+
* @param listener Listener callback
|
|
121
|
+
*/
|
|
53
122
|
off(event, listener) {
|
|
54
123
|
if (!this.#listeners[event]) return this;
|
|
55
124
|
this.#listeners[event] = this.#listeners[event].filter((l) => l !== listener);
|
|
56
125
|
return this;
|
|
57
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Emit an event
|
|
129
|
+
* @param event Event name
|
|
130
|
+
* @param args Event arguments
|
|
131
|
+
*/
|
|
58
132
|
async emit(event, ...args) {
|
|
59
133
|
if (!this.#listeners[event]) return false;
|
|
60
134
|
for (const listener of this.#listeners[event]) await listener(...args);
|
|
@@ -64,6 +138,7 @@ var EventHandler = class {
|
|
|
64
138
|
|
|
65
139
|
//#endregion
|
|
66
140
|
//#region src/classes/GuildsError.ts
|
|
141
|
+
/** Class representing a library-related error */
|
|
67
142
|
var GuildsError = class extends Error {
|
|
68
143
|
static name = "GuildsError";
|
|
69
144
|
scope;
|
|
@@ -78,161 +153,362 @@ var GuildsError = class extends Error {
|
|
|
78
153
|
};
|
|
79
154
|
|
|
80
155
|
//#endregion
|
|
81
|
-
//#region src/classes/
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
156
|
+
//#region src/classes/RESTManager.ts
|
|
157
|
+
/** Class representing a Discord REST API manager */
|
|
158
|
+
var RESTManager = class {
|
|
159
|
+
#token;
|
|
160
|
+
/**
|
|
161
|
+
* Instantiate a new Discord API manager
|
|
162
|
+
* @param token Client token
|
|
163
|
+
*/
|
|
164
|
+
constructor(token) {
|
|
165
|
+
this.#token = token;
|
|
85
166
|
}
|
|
86
|
-
|
|
87
|
-
|
|
167
|
+
/**
|
|
168
|
+
* Internal request method
|
|
169
|
+
* @param method HTTP request method
|
|
170
|
+
* @param endpoint Endpoint URI
|
|
171
|
+
* @param init Request data
|
|
172
|
+
*/
|
|
173
|
+
async #request(method, endpoint, init) {
|
|
174
|
+
const res = await fetch(endpoint, {
|
|
175
|
+
method,
|
|
176
|
+
headers: {
|
|
177
|
+
Authorization: this.#token,
|
|
178
|
+
"Content-Type": "application/json",
|
|
179
|
+
...init?.headers ?? {}
|
|
180
|
+
},
|
|
181
|
+
...init
|
|
182
|
+
});
|
|
183
|
+
if (!res.ok) throw new GuildsError(`${await res.text().catch(() => null) ?? res.statusText} (${res.status})`, "DiscordAPIError");
|
|
184
|
+
const json = await res.json().catch(() => null);
|
|
185
|
+
return Object.assign(res, { data: json });
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Create a HTTP DELETE request
|
|
189
|
+
* @param endpoint Endpoint URI
|
|
190
|
+
* @param init Request data
|
|
191
|
+
*/
|
|
192
|
+
delete(endpoint, init) {
|
|
193
|
+
return this.#request("DELETE", endpoint, init);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Create a HTTP GET request
|
|
197
|
+
* @param endpoint Endpoint URI
|
|
198
|
+
* @param init Request data
|
|
199
|
+
*/
|
|
200
|
+
get(endpoint, init) {
|
|
201
|
+
return this.#request("GET", endpoint, init);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Create a HTTP PATCH request
|
|
205
|
+
* @param endpoint Endpoint URI
|
|
206
|
+
* @param init Request data
|
|
207
|
+
*/
|
|
208
|
+
patch(endpoint, init) {
|
|
209
|
+
return this.#request("PATCH", endpoint, init);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Create a HTTP POST request
|
|
213
|
+
* @param endpoint Endpoint URI
|
|
214
|
+
* @param init Request data
|
|
215
|
+
*/
|
|
216
|
+
post(endpoint, init) {
|
|
217
|
+
return this.#request("POST", endpoint, init);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Create a HTTP PUT request
|
|
221
|
+
* @param endpoint Endpoint URI
|
|
222
|
+
* @param init Request data
|
|
223
|
+
*/
|
|
224
|
+
put(endpoint, init) {
|
|
225
|
+
return this.#request("PUT", endpoint, init);
|
|
88
226
|
}
|
|
89
227
|
};
|
|
90
228
|
|
|
91
229
|
//#endregion
|
|
92
|
-
//#region src/classes/
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
230
|
+
//#region src/classes/User.ts
|
|
231
|
+
/**
|
|
232
|
+
* Class representing a Discord user
|
|
233
|
+
* @see https://docs.discord.com/developers/resources/user
|
|
234
|
+
*/
|
|
235
|
+
var User = class {
|
|
236
|
+
#client;
|
|
237
|
+
/** The user's banner color as a color integer */
|
|
238
|
+
accentColor;
|
|
239
|
+
/** The user's banner color as a hex color*/
|
|
240
|
+
accentColorHex;
|
|
241
|
+
/** The user's about me (client only) */
|
|
242
|
+
bio;
|
|
243
|
+
/** Whether the user is an application (bot) */
|
|
244
|
+
bot = false;
|
|
245
|
+
/** The user's discriminator, or "0" if they have none */
|
|
246
|
+
discriminator = "0";
|
|
247
|
+
/** The user's display name or bot's application name */
|
|
248
|
+
displayName;
|
|
249
|
+
/** The user's email (user clients only) */
|
|
250
|
+
email;
|
|
251
|
+
/** The user's ID snowflake */
|
|
252
|
+
id;
|
|
253
|
+
/** Whether the user has two factor authentication enabled (user clients only) */
|
|
254
|
+
mfaEnabled;
|
|
255
|
+
/** The data from Discord's API provided as-is */
|
|
256
|
+
rawData;
|
|
257
|
+
/** Whether the user is a Discord system account */
|
|
258
|
+
system = false;
|
|
259
|
+
/** The user's username (not to be confused with display name or tag) */
|
|
260
|
+
username;
|
|
261
|
+
/** Whether the user's email is verified (user clients only) */
|
|
262
|
+
verified;
|
|
263
|
+
constructor(client, data) {
|
|
264
|
+
if (!client || !(client instanceof Client)) throw new GuildsError("Invalid client provided", "DiscordAPIError");
|
|
265
|
+
this.#client = client;
|
|
266
|
+
this.accentColor = data.accent_color;
|
|
267
|
+
this.accentColorHex = colorIntToHex(data.accent_color || null) || null;
|
|
268
|
+
this.bio = data.bio ?? null;
|
|
269
|
+
this.bot = data.bot || false;
|
|
270
|
+
this.discriminator = data.discriminator || "0";
|
|
271
|
+
this.email = data.email;
|
|
272
|
+
this.displayName = data.global_name;
|
|
273
|
+
this.id = data.id;
|
|
274
|
+
this.mfaEnabled = data.mfa_enabled;
|
|
275
|
+
this.rawData = data;
|
|
276
|
+
this.system = data.system || false;
|
|
277
|
+
this.username = data.username;
|
|
278
|
+
this.verified = data.verified;
|
|
279
|
+
return this;
|
|
98
280
|
}
|
|
99
|
-
|
|
100
|
-
|
|
281
|
+
/** The client associated with this user */
|
|
282
|
+
get client() {
|
|
283
|
+
return this.#client;
|
|
101
284
|
}
|
|
102
|
-
|
|
103
|
-
|
|
285
|
+
/** Get the user's avatar as an image URL */
|
|
286
|
+
avatarURL(props) {
|
|
287
|
+
if (!props || !props.size || props.format && typeof props.format !== "string") throw new GuildsError("Invalid user avatar URL props provided", "DiscordAPIError");
|
|
288
|
+
const avatarHash = this.rawData.avatar;
|
|
289
|
+
if (!avatarHash) return null;
|
|
290
|
+
return `https://cdn.discordapp.com/avatars/${this.id}/${avatarHash}.${props.format || (avatarHash.startsWith("a_") ? "gif" : "png")}?size=${props.size}`;
|
|
291
|
+
}
|
|
292
|
+
/** `username#0000` or just `username` (if no discriminator) */
|
|
293
|
+
get tag() {
|
|
294
|
+
return this.discriminator == "0" ? this.username : `${this.username}#${this.discriminator}`;
|
|
295
|
+
}
|
|
296
|
+
/** User mention as a string, e.g. <@123456789> */
|
|
297
|
+
toString() {
|
|
298
|
+
return `<@${this.id}>`;
|
|
104
299
|
}
|
|
105
300
|
};
|
|
106
301
|
|
|
107
302
|
//#endregion
|
|
108
303
|
//#region src/classes/Client.ts
|
|
304
|
+
/** Class representing a Discord client */
|
|
109
305
|
var Client = class extends EventHandler {
|
|
110
|
-
#api;
|
|
111
306
|
#token;
|
|
112
|
-
|
|
113
|
-
|
|
307
|
+
/** Gateway heartbeat inteval */
|
|
308
|
+
heartbeatInterval;
|
|
309
|
+
/** Last received sequence number */
|
|
310
|
+
sequenceNumber = null;
|
|
311
|
+
/** Gateway session ID */
|
|
312
|
+
sessionId;
|
|
313
|
+
/** REST manager instance to handle API calls */
|
|
314
|
+
rest;
|
|
315
|
+
/** Client intents bitfield */
|
|
316
|
+
intents;
|
|
317
|
+
/** Whether the client is ready */
|
|
318
|
+
ready = false;
|
|
319
|
+
/** WebSocket connection */
|
|
320
|
+
ws;
|
|
321
|
+
/** Client user, or null if not ready */
|
|
322
|
+
user = null;
|
|
323
|
+
/** Current presence information */
|
|
324
|
+
presence = {
|
|
114
325
|
platform: "desktop",
|
|
115
326
|
status: "online",
|
|
116
327
|
activities: []
|
|
117
328
|
};
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
#ws;
|
|
123
|
-
get token() {
|
|
124
|
-
return this.#token;
|
|
125
|
-
}
|
|
126
|
-
get intents() {
|
|
127
|
-
return this.#intents;
|
|
128
|
-
}
|
|
129
|
-
get heartbeatInterval() {
|
|
130
|
-
return this.#heartbeatInterval;
|
|
131
|
-
}
|
|
132
|
-
get presence() {
|
|
133
|
-
return this.#presence;
|
|
134
|
-
}
|
|
135
|
-
isReady() {
|
|
136
|
-
return this.#ready;
|
|
137
|
-
}
|
|
329
|
+
/**
|
|
330
|
+
* Instantiate a new Client
|
|
331
|
+
* @param props Client options
|
|
332
|
+
*/
|
|
138
333
|
constructor(props) {
|
|
139
334
|
super();
|
|
140
|
-
if (!props || typeof props !== "object") throw new GuildsError("Invalid client props
|
|
141
|
-
if (!props.token || typeof props.token !== "string") throw new GuildsError("Invalid token
|
|
142
|
-
if (
|
|
335
|
+
if (!props || typeof props !== "object") throw new GuildsError("Invalid client props provided", "ClientPropsError");
|
|
336
|
+
if (!props.token || typeof props.token !== "string") throw new GuildsError("Invalid token provided", "ClientTokenError");
|
|
337
|
+
if (props.intents === null || props.intents === void 0 || Array.isArray(props.intents) == false && typeof props.intents !== "number") throw new GuildsError("Invalid intents provided", "ClientIntentsError");
|
|
143
338
|
if (props.presence) {
|
|
144
339
|
if (typeof props.presence !== "object") throw new GuildsError("Invalid client presence was provided", "ClientPropsError");
|
|
145
340
|
this.setPresence(props.presence);
|
|
146
341
|
}
|
|
147
342
|
this.#token = props.token.trim().toLowerCase().startsWith("bot ") ? props.token : `Bot ${props.token}`;
|
|
148
|
-
this
|
|
149
|
-
this
|
|
343
|
+
this.rest = new RESTManager(this.#token);
|
|
344
|
+
this.intents = parseIntents(props.intents);
|
|
150
345
|
return this;
|
|
151
346
|
}
|
|
347
|
+
/** The client's token */
|
|
348
|
+
get token() {
|
|
349
|
+
return this.#token;
|
|
350
|
+
}
|
|
351
|
+
/** Start the connection to Discord's gateway */
|
|
152
352
|
async connect() {
|
|
153
|
-
const res = await this
|
|
154
|
-
const userRes = await this
|
|
155
|
-
if (!res.ok || !userRes.ok
|
|
156
|
-
|
|
157
|
-
this
|
|
158
|
-
this
|
|
353
|
+
const res = await this.rest.get(Endpoints.gatewayBot());
|
|
354
|
+
const userRes = await this.rest.get(Endpoints.user());
|
|
355
|
+
if (!res.ok || !userRes.ok) throw new GuildsError("Failed to connect to Discord", "DiscordAPIError");
|
|
356
|
+
this.user = new User(this, userRes.data);
|
|
357
|
+
this.lastHeartbeatAck = true;
|
|
358
|
+
this.destroyed = false;
|
|
359
|
+
this.#connectWebSocket(res.data.url);
|
|
360
|
+
return this;
|
|
361
|
+
}
|
|
362
|
+
/** Initialize the WebSocket */
|
|
363
|
+
#connectWebSocket(url) {
|
|
364
|
+
if (this.destroyed) return;
|
|
365
|
+
this.ws = new WebSocket(`${url}?v=10&encoding=json`);
|
|
366
|
+
this.ws.onopen = () => this.emit("debug", "WebSocket connected");
|
|
367
|
+
this.ws.onerror = (error) => this.emit("error", `WebSocket error: ${error}`);
|
|
368
|
+
this.ws.onmessage = (event) => {
|
|
159
369
|
this.#handleGatewayEvent(JSON.parse(event.data.toString()));
|
|
160
370
|
};
|
|
161
|
-
|
|
371
|
+
this.ws.onclose = (event) => {
|
|
372
|
+
this.emit("debug", `WebSocket closed: ${event.reason} (${event.code})`);
|
|
373
|
+
if (!this.destroyed) setTimeout(() => this.#connectWebSocket(url), 3e3);
|
|
374
|
+
};
|
|
162
375
|
}
|
|
376
|
+
destroyed = false;
|
|
377
|
+
lastHeartbeatAck = true;
|
|
378
|
+
/** Handle incoming gateway events */
|
|
163
379
|
#handleGatewayEvent(payload) {
|
|
164
|
-
if (payload.s !== void 0 && payload.s !== null) this
|
|
380
|
+
if (payload.s !== void 0 && payload.s !== null) this.sequenceNumber = payload.s;
|
|
165
381
|
switch (payload.op) {
|
|
166
|
-
case
|
|
382
|
+
case GatewayOpcodes.Hello:
|
|
167
383
|
this.emit("debug", "Received Hello event");
|
|
168
|
-
this
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
384
|
+
if (this.heartbeatInterval) clearInterval(this.heartbeatInterval);
|
|
385
|
+
this.lastHeartbeatAck = true;
|
|
386
|
+
this.heartbeatInterval = setInterval(() => {
|
|
387
|
+
if (!this.lastHeartbeatAck) {
|
|
388
|
+
this.emit("debug", "Heartbeat ACK failed, reconnecting...");
|
|
389
|
+
this.ws?.close(4e3, "Heartbeat failed");
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
this.lastHeartbeatAck = false;
|
|
393
|
+
this.ws?.send(JSON.stringify({
|
|
394
|
+
op: GatewayOpcodes.Heartbeat,
|
|
395
|
+
d: this.sequenceNumber
|
|
172
396
|
}));
|
|
173
397
|
}, payload.d.heartbeat_interval);
|
|
174
|
-
this.
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
properties: this.#presence.platform === "desktop" ? {
|
|
182
|
-
$os: "linux",
|
|
183
|
-
$browser: "guilds.js",
|
|
184
|
-
$device: "guilds.js"
|
|
185
|
-
} : {
|
|
186
|
-
$os: "Discord Android",
|
|
187
|
-
$browser: "Discord Android",
|
|
188
|
-
$device: "Discord Android"
|
|
398
|
+
if (this.sessionId) {
|
|
399
|
+
this.ws?.send(JSON.stringify({
|
|
400
|
+
op: GatewayOpcodes.Resume,
|
|
401
|
+
d: {
|
|
402
|
+
token: this.#token,
|
|
403
|
+
session_id: this.sessionId,
|
|
404
|
+
seq: this.sequenceNumber
|
|
189
405
|
}
|
|
190
|
-
}
|
|
191
|
-
|
|
406
|
+
}));
|
|
407
|
+
this.emit("debug", "Resuming session...");
|
|
408
|
+
} else {
|
|
409
|
+
this.ws?.send(JSON.stringify({
|
|
410
|
+
op: GatewayOpcodes.Identify,
|
|
411
|
+
d: {
|
|
412
|
+
token: this.#token,
|
|
413
|
+
intents: this.intents,
|
|
414
|
+
presence: this.presence,
|
|
415
|
+
properties: this.presence.platform === "desktop" ? {
|
|
416
|
+
$os: "linux",
|
|
417
|
+
$browser: "guilds.js",
|
|
418
|
+
$device: "guilds.js"
|
|
419
|
+
} : {
|
|
420
|
+
$os: "Discord Android",
|
|
421
|
+
$browser: "Discord Android",
|
|
422
|
+
$device: "Discord Android"
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}));
|
|
426
|
+
this.emit("debug", "Identifying...");
|
|
427
|
+
}
|
|
428
|
+
break;
|
|
429
|
+
case GatewayOpcodes.HeartbeatACK:
|
|
430
|
+
this.lastHeartbeatAck = true;
|
|
192
431
|
break;
|
|
193
|
-
case
|
|
432
|
+
case GatewayOpcodes.Dispatch:
|
|
194
433
|
if (payload.t !== "READY") break;
|
|
195
|
-
this
|
|
196
|
-
this
|
|
434
|
+
this.sessionId = payload.d.session_id;
|
|
435
|
+
this.ready = true;
|
|
436
|
+
this.emit("debug", "Received Dispatch (Ready) event");
|
|
197
437
|
this.emit("ready", this);
|
|
198
438
|
break;
|
|
199
439
|
}
|
|
200
440
|
}
|
|
441
|
+
/** Update the client's user presence */
|
|
201
442
|
setPresence(presence) {
|
|
202
|
-
this
|
|
203
|
-
...this
|
|
443
|
+
this.presence = {
|
|
444
|
+
...this.presence,
|
|
204
445
|
...presence
|
|
205
446
|
};
|
|
206
|
-
if (this
|
|
207
|
-
op:
|
|
447
|
+
if (this.ws) this.ws.send(JSON.stringify({
|
|
448
|
+
op: GatewayOpcodes.PresenceUpdate,
|
|
208
449
|
d: {
|
|
209
|
-
status: this
|
|
450
|
+
status: this.presence.status,
|
|
210
451
|
since: null,
|
|
211
452
|
afk: false,
|
|
212
|
-
activities: (this
|
|
453
|
+
activities: (this.presence.activities ?? []).map((a) => ({
|
|
213
454
|
...a,
|
|
214
|
-
type: typeof a.type === "string" ?
|
|
455
|
+
type: typeof a.type === "string" ? ActivityTypes[a.type] : a.type
|
|
215
456
|
}))
|
|
216
457
|
}
|
|
217
458
|
}));
|
|
218
459
|
return this;
|
|
219
460
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
if (this
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
461
|
+
/** Destroy the connection to Discord's gateway */
|
|
462
|
+
disconnect() {
|
|
463
|
+
if (this.heartbeatInterval) clearInterval(this.heartbeatInterval);
|
|
464
|
+
this.ws?.close(1e3, "Client disconnected");
|
|
465
|
+
this.ws = void 0;
|
|
226
466
|
}
|
|
227
467
|
};
|
|
228
468
|
|
|
229
469
|
//#endregion
|
|
470
|
+
//#region src/utils/parse-intents.ts
|
|
471
|
+
/**
|
|
472
|
+
* Parse intents into bitfield
|
|
473
|
+
* @param intents Intents resolvable
|
|
474
|
+
* @returns Parsed intents as a bitfield
|
|
475
|
+
*/
|
|
476
|
+
function parseIntents(intents) {
|
|
477
|
+
if (typeof intents === "number") return intents;
|
|
478
|
+
else if (typeof intents === "string") {
|
|
479
|
+
const value = GatewayIntents[intents];
|
|
480
|
+
if (typeof value !== "number") throw new GuildsError("Invalid intents provided", "ClientIntentsError");
|
|
481
|
+
return value;
|
|
482
|
+
} else if (Array.isArray(intents)) {
|
|
483
|
+
let bitfield = 0;
|
|
484
|
+
for (const intent of intents) {
|
|
485
|
+
if (typeof intent === "number") {
|
|
486
|
+
bitfield |= intent;
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
if (typeof intent === "string") {
|
|
490
|
+
const value = GatewayIntents[intent];
|
|
491
|
+
if (typeof value !== "number") throw new GuildsError("Invalid intents provided", "ClientIntentsError");
|
|
492
|
+
bitfield |= value;
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
throw new GuildsError("Invalid intents provided", "ClientIntentsError");
|
|
496
|
+
}
|
|
497
|
+
return bitfield;
|
|
498
|
+
} else throw new GuildsError("Invalid intents provided", "ClientIntentsError");
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
//#endregion
|
|
502
|
+
exports.ActivityTypes = ActivityTypes;
|
|
230
503
|
exports.Client = Client;
|
|
231
|
-
exports.
|
|
504
|
+
exports.Endpoints = Endpoints;
|
|
232
505
|
exports.EventHandler = EventHandler;
|
|
506
|
+
exports.GatewayIntents = GatewayIntents;
|
|
507
|
+
exports.GatewayOpcodes = GatewayOpcodes;
|
|
233
508
|
exports.GuildsError = GuildsError;
|
|
234
|
-
exports.
|
|
235
|
-
exports.
|
|
509
|
+
exports.RESTManager = RESTManager;
|
|
510
|
+
exports.User = User;
|
|
236
511
|
exports.baseApiUrl = baseApiUrl;
|
|
237
|
-
exports.
|
|
238
|
-
exports.
|
|
512
|
+
exports.colorIntToHex = colorIntToHex;
|
|
513
|
+
exports.hexToColorInt = hexToColorInt;
|
|
514
|
+
exports.parseIntents = parseIntents;
|