seyfert 4.5.0 → 4.6.0
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/lib/api/Routes/guilds.d.ts +12 -1
- package/lib/api/Routes/index.d.ts +3 -1
- package/lib/api/Routes/invites.d.ts +1 -1
- package/lib/api/Routes/oauth2.d.ts +11 -0
- package/lib/api/Routes/oauth2.js +2 -0
- package/lib/api/Routes/skus.d.ts +11 -4
- package/lib/api/Routes/users.d.ts +2 -1
- package/lib/api/Routes/voice.d.ts +4 -0
- package/lib/api/Routes/webhooks.d.ts +1 -1
- package/lib/api/api.d.ts +15 -5
- package/lib/api/api.js +243 -114
- package/lib/api/bucket.js +13 -7
- package/lib/api/shared.d.ts +3 -1
- package/lib/api/utils/constants.d.ts +1 -1
- package/lib/api/utils/constants.js +1 -1
- package/lib/builders/Attachment.d.ts +6 -1
- package/lib/builders/Attachment.js +13 -9
- package/lib/builders/CheckboxGroup.js +1 -1
- package/lib/builders/FileUpload.d.ts +8 -1
- package/lib/builders/FileUpload.js +9 -0
- package/lib/builders/RadioGroup.js +1 -1
- package/lib/client/base.d.ts +3 -3
- package/lib/client/collectors.d.ts +10 -8
- package/lib/client/transformers.d.ts +5 -0
- package/lib/client/workerclient.d.ts +8 -1
- package/lib/client/workerclient.js +28 -9
- package/lib/commands/applications/chat.d.ts +6 -4
- package/lib/commands/applications/options.d.ts +6 -4
- package/lib/commands/applications/shared.d.ts +2 -2
- package/lib/commands/handle.d.ts +7 -3
- package/lib/commands/handle.js +36 -11
- package/lib/commands/handler.js +11 -0
- package/lib/commands/optionresolver.d.ts +6 -7
- package/lib/common/it/error.d.ts +13 -0
- package/lib/common/it/error.js +15 -1
- package/lib/common/it/logger.js +12 -9
- package/lib/common/shorters/application.d.ts +1 -1
- package/lib/common/shorters/application.js +1 -1
- package/lib/common/shorters/bans.js +1 -1
- package/lib/common/shorters/guilds.js +5 -1
- package/lib/common/shorters/interaction.js +7 -4
- package/lib/common/shorters/invites.d.ts +5 -0
- package/lib/common/shorters/members.js +2 -1
- package/lib/common/shorters/messages.js +2 -2
- package/lib/common/shorters/threads.js +1 -1
- package/lib/common/shorters/webhook.js +3 -3
- package/lib/common/types/util.d.ts +7 -0
- package/lib/components/componentcommand.js +2 -2
- package/lib/components/customId.d.ts +3 -0
- package/lib/components/customId.js +11 -0
- package/lib/components/handler.js +2 -3
- package/lib/components/modalcommand.js +2 -2
- package/lib/events/hooks/invite.d.ts +1 -0
- package/lib/events/hooks/presence.d.ts +1 -0
- package/lib/events/hooks/thread.d.ts +1 -0
- package/lib/structures/AnonymousGuild.d.ts +1 -1
- package/lib/structures/AnonymousGuild.js +1 -1
- package/lib/structures/Guild.d.ts +3 -0
- package/lib/structures/GuildMember.d.ts +2 -2
- package/lib/structures/GuildMember.js +22 -4
- package/lib/structures/GuildPreview.d.ts +1 -1
- package/lib/structures/GuildPreview.js +1 -1
- package/lib/structures/GuildRole.d.ts +13 -0
- package/lib/structures/GuildRole.js +21 -0
- package/lib/structures/GuildTemplate.js +1 -1
- package/lib/structures/Interaction.d.ts +7 -7
- package/lib/structures/Interaction.js +35 -21
- package/lib/structures/Message.d.ts +2 -1
- package/lib/structures/channels.d.ts +29 -16
- package/lib/structures/channels.js +59 -19
- package/lib/structures/extra/BaseGuild.d.ts +4 -4
- package/lib/structures/extra/BaseGuild.js +4 -4
- package/lib/types/gateway.d.ts +243 -239
- package/lib/types/index.d.ts +2 -2
- package/lib/types/payloads/_interactions/_applicationCommands/_chatInput/attachment.d.ts +5 -2
- package/lib/types/payloads/_interactions/_applicationCommands/_chatInput/shared.d.ts +2 -2
- package/lib/types/payloads/_interactions/_applicationCommands/_chatInput/shared.js +1 -1
- package/lib/types/payloads/_interactions/_applicationCommands/chatInput.d.ts +7 -7
- package/lib/types/payloads/_interactions/_applicationCommands/contextMenu.d.ts +13 -13
- package/lib/types/payloads/_interactions/_applicationCommands/permissions.d.ts +4 -4
- package/lib/types/payloads/_interactions/_applicationCommands/permissions.js +2 -2
- package/lib/types/payloads/_interactions/applicationCommands.d.ts +10 -10
- package/lib/types/payloads/_interactions/applicationCommands.js +4 -4
- package/lib/types/payloads/_interactions/autocomplete.d.ts +2 -2
- package/lib/types/payloads/_interactions/base.d.ts +13 -7
- package/lib/types/payloads/_interactions/modalSubmit.d.ts +4 -4
- package/lib/types/payloads/_interactions/responses.d.ts +10 -10
- package/lib/types/payloads/_interactions/responses.js +2 -2
- package/lib/types/payloads/application.d.ts +21 -15
- package/lib/types/payloads/application.js +7 -7
- package/lib/types/payloads/auditLog.d.ts +22 -17
- package/lib/types/payloads/auditLog.js +7 -2
- package/lib/types/payloads/autoModeration.d.ts +11 -11
- package/lib/types/payloads/autoModeration.js +5 -5
- package/lib/types/payloads/channel.d.ts +87 -78
- package/lib/types/payloads/channel.js +16 -11
- package/lib/types/payloads/components.d.ts +41 -31
- package/lib/types/payloads/components.js +9 -5
- package/lib/types/payloads/emoji.d.ts +2 -2
- package/lib/types/payloads/emoji.js +1 -1
- package/lib/types/payloads/gateway.d.ts +33 -29
- package/lib/types/payloads/gateway.js +5 -5
- package/lib/types/payloads/guild.d.ts +68 -68
- package/lib/types/payloads/guild.js +14 -14
- package/lib/types/payloads/guildScheduledEvent.d.ts +11 -11
- package/lib/types/payloads/guildScheduledEvent.js +3 -3
- package/lib/types/payloads/index.d.ts +2 -2
- package/lib/types/payloads/interactions.d.ts +3 -3
- package/lib/types/payloads/invite.d.ts +11 -11
- package/lib/types/payloads/invite.js +3 -3
- package/lib/types/payloads/monetization.d.ts +4 -4
- package/lib/types/payloads/monetization.js +2 -2
- package/lib/types/payloads/oauth2.d.ts +21 -17
- package/lib/types/payloads/oauth2.js +21 -17
- package/lib/types/payloads/permissions.d.ts +5 -5
- package/lib/types/payloads/permissions.js +2 -2
- package/lib/types/payloads/poll.d.ts +7 -7
- package/lib/types/payloads/poll.js +2 -2
- package/lib/types/payloads/soundboard.d.ts +2 -2
- package/lib/types/payloads/soundboard.js +1 -1
- package/lib/types/payloads/stageInstance.d.ts +5 -5
- package/lib/types/payloads/stageInstance.js +1 -1
- package/lib/types/payloads/sticker.d.ts +8 -8
- package/lib/types/payloads/sticker.js +3 -3
- package/lib/types/payloads/teams.d.ts +8 -8
- package/lib/types/payloads/teams.js +3 -3
- package/lib/types/payloads/template.d.ts +3 -3
- package/lib/types/payloads/template.js +1 -1
- package/lib/types/payloads/user.d.ts +24 -22
- package/lib/types/payloads/user.js +7 -5
- package/lib/types/payloads/voice.d.ts +4 -4
- package/lib/types/payloads/voice.js +1 -1
- package/lib/types/payloads/webhokEvents.d.ts +5 -5
- package/lib/types/payloads/webhokEvents.js +2 -2
- package/lib/types/payloads/webhook.d.ts +4 -4
- package/lib/types/payloads/webhook.js +1 -1
- package/lib/types/rest/application.d.ts +14 -14
- package/lib/types/rest/auditLog.d.ts +1 -1
- package/lib/types/rest/autoModeration.d.ts +7 -7
- package/lib/types/rest/channel.d.ts +79 -74
- package/lib/types/rest/channel.js +1 -1
- package/lib/types/rest/emoji.d.ts +8 -8
- package/lib/types/rest/gateway.d.ts +2 -2
- package/lib/types/rest/guild.d.ts +98 -98
- package/lib/types/rest/guild.js +5 -5
- package/lib/types/rest/guildScheduledEvent.d.ts +11 -11
- package/lib/types/rest/index.d.ts +34 -3
- package/lib/types/rest/index.js +33 -2
- package/lib/types/rest/interactions.d.ts +48 -48
- package/lib/types/rest/invite.d.ts +5 -5
- package/lib/types/rest/monetization.d.ts +9 -9
- package/lib/types/rest/monetization.js +1 -1
- package/lib/types/rest/oauth2.d.ts +22 -18
- package/lib/types/rest/poll.d.ts +4 -4
- package/lib/types/rest/soundboard.d.ts +8 -8
- package/lib/types/rest/stageInstance.d.ts +6 -6
- package/lib/types/rest/sticker.d.ts +10 -10
- package/lib/types/rest/template.d.ts +11 -11
- package/lib/types/rest/user.d.ts +18 -14
- package/lib/types/rest/voice.d.ts +5 -5
- package/lib/types/rest/webhook.d.ts +38 -38
- package/lib/types/utils/index.d.ts +19 -19
- package/lib/types/utils/index.js +19 -19
- package/lib/websocket/SharedTypes.d.ts +2 -2
- package/lib/websocket/constants/index.js +2 -2
- package/lib/websocket/discord/heartbeater.d.ts +1 -0
- package/lib/websocket/discord/heartbeater.js +8 -0
- package/lib/websocket/discord/shard.d.ts +19 -10
- package/lib/websocket/discord/shard.js +254 -121
- package/lib/websocket/discord/sharder.d.ts +2 -2
- package/lib/websocket/discord/sharder.js +1 -1
- package/lib/websocket/discord/shared.d.ts +7 -2
- package/lib/websocket/discord/workermanager.d.ts +3 -1
- package/lib/websocket/discord/workermanager.js +31 -24
- package/package.json +2 -2
|
@@ -22,8 +22,13 @@ class Shard {
|
|
|
22
22
|
interval: 30e3,
|
|
23
23
|
ack: true,
|
|
24
24
|
};
|
|
25
|
-
isConnecting = false;
|
|
26
25
|
reconnectPromise;
|
|
26
|
+
// Invalidates delayed connects and callbacks that belong to a replaced socket.
|
|
27
|
+
lifecycle = 0;
|
|
28
|
+
connectingLifecycle;
|
|
29
|
+
handledWebsocket;
|
|
30
|
+
localClose;
|
|
31
|
+
firstHeartbeatTimeout;
|
|
27
32
|
bucket;
|
|
28
33
|
offlineSendQueue = [];
|
|
29
34
|
pendingGuilds;
|
|
@@ -76,45 +81,99 @@ class Shard {
|
|
|
76
81
|
return this.websocket.ping();
|
|
77
82
|
}
|
|
78
83
|
async connect() {
|
|
79
|
-
if (this.
|
|
84
|
+
if (this.connectingLifecycle === undefined)
|
|
85
|
+
this.reconnectPromise = undefined;
|
|
86
|
+
return this.connectSocket(false);
|
|
87
|
+
}
|
|
88
|
+
async connectSocket(immediate) {
|
|
89
|
+
if (this.connectingLifecycle !== undefined) {
|
|
80
90
|
this.debugger?.debug(`[Shard #${this.id}] Already connecting, skipping`);
|
|
81
91
|
return;
|
|
82
92
|
}
|
|
83
|
-
|
|
84
|
-
|
|
93
|
+
const lifecycle = ++this.lifecycle;
|
|
94
|
+
this.connectingLifecycle = lifecycle;
|
|
95
|
+
if (!immediate)
|
|
96
|
+
await this.connectTimeout.wait();
|
|
97
|
+
if (this.connectingLifecycle !== lifecycle)
|
|
98
|
+
return;
|
|
99
|
+
if (lifecycle !== this.lifecycle) {
|
|
100
|
+
this.connectingLifecycle = undefined;
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
85
103
|
if (this.isOpen) {
|
|
86
104
|
this.debugger?.debug(`[Shard #${this.id}] Attempted to connect while open`);
|
|
87
|
-
this.
|
|
105
|
+
this.connectingLifecycle = undefined;
|
|
88
106
|
return;
|
|
89
107
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
108
|
+
this.clearHeartbeat();
|
|
109
|
+
clearTimeout(this.connectionTimeout);
|
|
110
|
+
const url = this.currentGatewayURL;
|
|
111
|
+
this.debugger?.debug(`[Shard #${this.id}] Connecting to ${url}`);
|
|
93
112
|
// @ts-expect-error Use native websocket when using Bun
|
|
94
|
-
|
|
95
|
-
this.
|
|
96
|
-
|
|
113
|
+
const websocket = new basesocket_1.BaseSocket(typeof Bun === 'undefined' ? 'ws' : 'bun', url);
|
|
114
|
+
this.isReady = false;
|
|
115
|
+
this.handledWebsocket = undefined;
|
|
116
|
+
this.localClose = undefined;
|
|
117
|
+
this.websocket = websocket;
|
|
118
|
+
this.connectionTimeout = setTimeout(() => {
|
|
119
|
+
if (!this.isCurrentConnection(websocket))
|
|
120
|
+
return;
|
|
121
|
+
void this.reconnectWith(shared_1.ShardSocketCloseCodes.Timeout, this.options.reconnectTimeout, 'Gateway connection timed out').catch(error => this.logger.error(error));
|
|
122
|
+
}, this.options.connectionTimeout);
|
|
123
|
+
websocket.onmessage = ({ data }) => {
|
|
124
|
+
if (!this.isCurrentConnection(websocket))
|
|
125
|
+
return;
|
|
126
|
+
try {
|
|
127
|
+
void Promise.resolve(this.handleMessage(data)).catch(error => this.logger.error(error));
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
this.logger.error(error);
|
|
131
|
+
}
|
|
97
132
|
};
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
this.
|
|
133
|
+
websocket.onclose = (event) => {
|
|
134
|
+
if (!this.isCurrentConnection(websocket))
|
|
135
|
+
return;
|
|
136
|
+
const localClose = this.localClose?.websocket === websocket ? this.localClose : undefined;
|
|
137
|
+
void this.handleConnectionClosed({
|
|
138
|
+
code: event.code,
|
|
139
|
+
reason: event.reason,
|
|
140
|
+
locallyInitiated: !!localClose,
|
|
141
|
+
applyPolicy: localClose?.applyPolicy,
|
|
142
|
+
}, websocket).catch(error => this.logger.error(error));
|
|
143
|
+
};
|
|
144
|
+
websocket.onerror = (event) => {
|
|
145
|
+
if (!this.isCurrentConnection(websocket))
|
|
146
|
+
return;
|
|
147
|
+
this.logger.error(`Shard #${this.id}`, event);
|
|
148
|
+
};
|
|
149
|
+
websocket.onopen = () => {
|
|
150
|
+
if (!this.isCurrentConnection(websocket))
|
|
151
|
+
return;
|
|
152
|
+
this.connectingLifecycle = undefined;
|
|
102
153
|
this.heart.ack = true;
|
|
103
154
|
void this.options.onShardReconnect?.({ shardId: this.id });
|
|
104
155
|
};
|
|
105
156
|
}
|
|
106
157
|
async send(force, message) {
|
|
107
|
-
this.debugger
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
158
|
+
if (this.debugger) {
|
|
159
|
+
this.debugger.info(`[Shard #${this.id}] Sending: ${types_1.GatewayOpcodes[message.op]} ${JSON.stringify(message.d, (_, value) => {
|
|
160
|
+
if (typeof value === 'string')
|
|
161
|
+
return value.replaceAll(this.options.token, v => {
|
|
162
|
+
const split = v.split('.');
|
|
163
|
+
return `${split[0]}.${'*'.repeat(split[1].length)}.${'*'.repeat(split[2].length)}`;
|
|
164
|
+
});
|
|
165
|
+
return value;
|
|
166
|
+
}, 1)}`);
|
|
167
|
+
}
|
|
115
168
|
await this.checkOffline(force);
|
|
169
|
+
const websocket = this.websocket;
|
|
116
170
|
await this.bucket.acquire(force);
|
|
117
171
|
await this.checkOffline(force);
|
|
172
|
+
// Authentication is connection-scoped; never deliver an Identify or Resume
|
|
173
|
+
// that finished waiting after its socket was replaced.
|
|
174
|
+
if ((message.op === types_1.GatewayOpcodes.Identify || message.op === types_1.GatewayOpcodes.Resume) &&
|
|
175
|
+
(!websocket || !this.isCurrentConnection(websocket)))
|
|
176
|
+
return;
|
|
118
177
|
this.websocket?.send(JSON.stringify(message));
|
|
119
178
|
}
|
|
120
179
|
async identify() {
|
|
@@ -144,43 +203,110 @@ class Shard {
|
|
|
144
203
|
});
|
|
145
204
|
}
|
|
146
205
|
heartbeat(requested) {
|
|
206
|
+
const websocket = this.websocket;
|
|
207
|
+
if (!websocket)
|
|
208
|
+
return;
|
|
147
209
|
this.debugger?.debug(`[Shard #${this.id}] Sending ${requested ? '' : 'un'}requested heartbeat (Ack=${this.heart.ack})`);
|
|
210
|
+
if (!requested && !this.heart.ack) {
|
|
211
|
+
void this.reconnectWith(shared_1.ShardSocketCloseCodes.ZombiedConnection, 0, 'Heartbeat ACK was not received before the next interval').catch(error => this.logger.error(error));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (websocket.readyState !== 1)
|
|
215
|
+
return;
|
|
148
216
|
if (!requested) {
|
|
149
|
-
if (!this.heart.ack) {
|
|
150
|
-
this.reconnect(shared_1.ShardSocketCloseCodes.ZombiedConnection);
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
217
|
this.heart.ack = false;
|
|
154
218
|
}
|
|
155
219
|
this.heart.lastBeat = Date.now();
|
|
156
|
-
|
|
220
|
+
websocket.send(JSON.stringify({
|
|
157
221
|
op: types_1.GatewayOpcodes.Heartbeat,
|
|
158
222
|
d: this.data.resume_seq ?? null,
|
|
159
223
|
}));
|
|
160
|
-
clearTimeout(this.heart.ackTimeout);
|
|
161
|
-
this.heart.ackTimeout = setTimeout(() => {
|
|
162
|
-
if (!this.heart.ack) {
|
|
163
|
-
this.reconnect(shared_1.ShardSocketCloseCodes.ZombiedConnection);
|
|
164
|
-
}
|
|
165
|
-
}, this.heart.interval * 1.5);
|
|
166
224
|
}
|
|
167
225
|
disconnect(code = shared_1.ShardSocketCloseCodes.Shutdown) {
|
|
168
|
-
|
|
169
|
-
this.
|
|
170
|
-
clearTimeout(this.heart.ackTimeout);
|
|
171
|
-
this.isConnecting = false;
|
|
226
|
+
this.lifecycle++;
|
|
227
|
+
this.reconnectPromise = undefined;
|
|
172
228
|
this.debugger?.info(`[Shard #${this.id}] Disconnecting`);
|
|
173
|
-
this.
|
|
229
|
+
this.closeConnection(code, 'Shard down request', this.shouldApplyLocalClosePolicy(code));
|
|
174
230
|
}
|
|
175
231
|
async reconnect(code = shared_1.ShardSocketCloseCodes.Reconnect) {
|
|
176
|
-
return (this.
|
|
232
|
+
return this.reconnectWith(code, this.options.reconnectTimeout, 'Shard reconnect request');
|
|
233
|
+
}
|
|
234
|
+
reconnectWith(code, wait, reason, immediate = code === shared_1.ShardSocketCloseCodes.Timeout ? false : this.resumable) {
|
|
235
|
+
if (this.reconnectPromise)
|
|
236
|
+
return this.reconnectPromise;
|
|
237
|
+
const reconnect = (async () => {
|
|
238
|
+
const lifecycle = ++this.lifecycle;
|
|
177
239
|
this.debugger?.info(`[Shard #${this.id}] Reconnecting`);
|
|
178
|
-
this.
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
240
|
+
this.closeConnection(code, reason);
|
|
241
|
+
if (code === shared_1.ShardSocketCloseCodes.Timeout)
|
|
242
|
+
this.resetSession();
|
|
243
|
+
if (wait > 0)
|
|
244
|
+
await (0, common_1.delay)(wait);
|
|
245
|
+
if (lifecycle !== this.lifecycle)
|
|
246
|
+
return;
|
|
247
|
+
await this.connectSocket(immediate);
|
|
248
|
+
})();
|
|
249
|
+
const settled = reconnect.finally(() => {
|
|
250
|
+
if (this.reconnectPromise === settled)
|
|
251
|
+
this.reconnectPromise = undefined;
|
|
252
|
+
});
|
|
253
|
+
this.reconnectPromise = settled;
|
|
254
|
+
return settled;
|
|
255
|
+
}
|
|
256
|
+
isCurrentConnection(websocket) {
|
|
257
|
+
return this.websocket === websocket && this.handledWebsocket !== websocket;
|
|
258
|
+
}
|
|
259
|
+
clearHeartbeat() {
|
|
260
|
+
clearTimeout(this.firstHeartbeatTimeout);
|
|
261
|
+
clearInterval(this.heart.nodeInterval);
|
|
262
|
+
this.firstHeartbeatTimeout = undefined;
|
|
263
|
+
this.heart.nodeInterval = undefined;
|
|
264
|
+
}
|
|
265
|
+
resetSession() {
|
|
266
|
+
this.data.resume_seq = null;
|
|
267
|
+
this.data.session_id = undefined;
|
|
268
|
+
this.data.resume_gateway_url = undefined;
|
|
269
|
+
this.pendingGuilds = undefined;
|
|
270
|
+
}
|
|
271
|
+
startHeartbeat(websocket) {
|
|
272
|
+
this.clearHeartbeat();
|
|
273
|
+
this.heart.ack = true;
|
|
274
|
+
this.firstHeartbeatTimeout = setTimeout(() => {
|
|
275
|
+
if (!this.isCurrentConnection(websocket))
|
|
276
|
+
return;
|
|
277
|
+
this.heartbeat(false);
|
|
278
|
+
this.heart.nodeInterval = setInterval(() => {
|
|
279
|
+
if (this.isCurrentConnection(websocket))
|
|
280
|
+
this.heartbeat(false);
|
|
281
|
+
}, this.heart.interval);
|
|
282
|
+
}, this.heart.interval * Math.random());
|
|
283
|
+
}
|
|
284
|
+
closeConnection(code, reason, applyPolicy = false) {
|
|
285
|
+
const websocket = this.websocket;
|
|
286
|
+
if (!websocket) {
|
|
287
|
+
this.connectingLifecycle = undefined;
|
|
288
|
+
clearTimeout(this.connectionTimeout);
|
|
289
|
+
this.connectionTimeout = undefined;
|
|
290
|
+
this.clearHeartbeat();
|
|
291
|
+
this.debugger?.warn(`[Shard #${this.id}] Is not open, reason:`, reason);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
this.debugger?.debug(`[Shard #${this.id}] Called close with reason:`, reason);
|
|
295
|
+
// Native and custom sockets report local Close frames differently. Mark the
|
|
296
|
+
// owner before closing so both paths finalize the same lifecycle exactly once.
|
|
297
|
+
this.localClose = { websocket, applyPolicy };
|
|
298
|
+
try {
|
|
299
|
+
websocket.close(code, reason);
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
this.localClose = undefined;
|
|
303
|
+
throw error;
|
|
304
|
+
}
|
|
305
|
+
this.connectingLifecycle = undefined;
|
|
306
|
+
if (this.handledWebsocket !== websocket) {
|
|
307
|
+
const close = { code, reason, locallyInitiated: true, applyPolicy };
|
|
308
|
+
void this.handleConnectionClosed(close, websocket).catch(error => this.logger.error(error));
|
|
309
|
+
}
|
|
184
310
|
}
|
|
185
311
|
flushOfflineSendQueue() {
|
|
186
312
|
const queue = this.offlineSendQueue.splice(0);
|
|
@@ -194,12 +320,13 @@ class Shard {
|
|
|
194
320
|
this.debugger?.debug(`[Shard #${this.id}]`, packet.t ? packet.t : types_1.GatewayOpcodes[packet.op], this.data.resume_seq);
|
|
195
321
|
switch (packet.op) {
|
|
196
322
|
case types_1.GatewayOpcodes.Hello: {
|
|
323
|
+
const websocket = this.websocket;
|
|
324
|
+
if (!websocket)
|
|
325
|
+
return;
|
|
197
326
|
clearTimeout(this.connectionTimeout);
|
|
198
327
|
this.connectionTimeout = undefined;
|
|
199
|
-
clearInterval(this.heart.nodeInterval);
|
|
200
328
|
this.heart.interval = packet.d.heartbeat_interval;
|
|
201
|
-
this.
|
|
202
|
-
this.heart.nodeInterval = setInterval(() => this.heartbeat(false), this.heart.interval);
|
|
329
|
+
this.startHeartbeat(websocket);
|
|
203
330
|
if (this.resumable) {
|
|
204
331
|
return this.resume();
|
|
205
332
|
}
|
|
@@ -209,24 +336,18 @@ class Shard {
|
|
|
209
336
|
{
|
|
210
337
|
this.heart.ack = true;
|
|
211
338
|
this.heart.lastAck = Date.now();
|
|
212
|
-
clearTimeout(this.heart.ackTimeout);
|
|
213
339
|
}
|
|
214
340
|
break;
|
|
215
341
|
case types_1.GatewayOpcodes.Heartbeat:
|
|
216
342
|
this.heartbeat(true);
|
|
217
343
|
break;
|
|
218
344
|
case types_1.GatewayOpcodes.Reconnect:
|
|
219
|
-
return this.reconnect
|
|
345
|
+
return this.reconnectWith(shared_1.ShardSocketCloseCodes.Reconnect, 0, 'Discord requested a reconnect', true);
|
|
220
346
|
case types_1.GatewayOpcodes.InvalidSession: {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
return this.resume();
|
|
226
|
-
}
|
|
227
|
-
this.data.resume_seq = 0;
|
|
228
|
-
this.data.session_id = undefined;
|
|
229
|
-
return this.identify();
|
|
347
|
+
const canResume = packet.d && this.resumable;
|
|
348
|
+
if (!canResume)
|
|
349
|
+
this.resetSession();
|
|
350
|
+
return this.reconnectWith(shared_1.ShardSocketCloseCodes.Reconnect, 0, canResume ? 'Discord invalidated the resumable session' : 'Discord invalidated the session');
|
|
230
351
|
}
|
|
231
352
|
case types_1.GatewayOpcodes.Dispatch:
|
|
232
353
|
{
|
|
@@ -342,6 +463,7 @@ class Shard {
|
|
|
342
463
|
}
|
|
343
464
|
break;
|
|
344
465
|
}
|
|
466
|
+
return undefined;
|
|
345
467
|
}
|
|
346
468
|
async requestGuildMember(options) {
|
|
347
469
|
const nonce = Date.now().toString() + Math.random().toString(36);
|
|
@@ -379,59 +501,74 @@ class Shard {
|
|
|
379
501
|
});
|
|
380
502
|
return promise;
|
|
381
503
|
}
|
|
382
|
-
|
|
504
|
+
isExpectedLocalClose(code) {
|
|
505
|
+
return (code === shared_1.ShardSocketCloseCodes.Shutdown ||
|
|
506
|
+
code === shared_1.ShardSocketCloseCodes.Reconnect ||
|
|
507
|
+
code === shared_1.ShardSocketCloseCodes.Resharding ||
|
|
508
|
+
code === shared_1.ShardSocketCloseCodes.ShutdownAll);
|
|
509
|
+
}
|
|
510
|
+
shouldApplyLocalClosePolicy(code) {
|
|
511
|
+
return !this.isExpectedLocalClose(code) && code !== shared_1.ShardSocketCloseCodes.ZombiedConnection;
|
|
512
|
+
}
|
|
513
|
+
async handleConnectionClosed(close, websocket) {
|
|
514
|
+
if (!websocket || !this.isCurrentConnection(websocket))
|
|
515
|
+
return;
|
|
516
|
+
this.handledWebsocket = websocket;
|
|
517
|
+
if (this.localClose?.websocket === websocket)
|
|
518
|
+
this.localClose = undefined;
|
|
383
519
|
this.isReady = false;
|
|
384
|
-
|
|
385
|
-
clearTimeout(this.
|
|
386
|
-
this.
|
|
520
|
+
this.connectingLifecycle = undefined;
|
|
521
|
+
clearTimeout(this.connectionTimeout);
|
|
522
|
+
this.connectionTimeout = undefined;
|
|
523
|
+
this.clearHeartbeat();
|
|
524
|
+
const closeMessage = `Shard #${this.id} closed: ${shared_1.ShardSocketCloseCodes[close.code] ?? types_1.GatewayCloseCodes[close.code] ?? close.code} (${close.code})`;
|
|
525
|
+
if (close.locallyInitiated && this.isExpectedLocalClose(close.code)) {
|
|
526
|
+
this.logger.info(closeMessage, close.reason);
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
this.logger.warn(closeMessage, close.reason);
|
|
530
|
+
}
|
|
387
531
|
try {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
break;
|
|
429
|
-
default:
|
|
430
|
-
{
|
|
431
|
-
this.logger.warn('Unknown close code, trying to reconnect anyways');
|
|
432
|
-
await this.reconnect();
|
|
433
|
-
}
|
|
434
|
-
break;
|
|
532
|
+
if (!close.locallyInitiated || close.applyPolicy) {
|
|
533
|
+
switch (close.code) {
|
|
534
|
+
case 1000:
|
|
535
|
+
case types_1.GatewayCloseCodes.UnknownOpcode:
|
|
536
|
+
case types_1.GatewayCloseCodes.InvalidSeq:
|
|
537
|
+
case types_1.GatewayCloseCodes.SessionTimedOut:
|
|
538
|
+
case shared_1.ShardSocketCloseCodes.Timeout:
|
|
539
|
+
{
|
|
540
|
+
this.resetSession();
|
|
541
|
+
await this.reconnect();
|
|
542
|
+
}
|
|
543
|
+
break;
|
|
544
|
+
case 1001:
|
|
545
|
+
case 1006:
|
|
546
|
+
case types_1.GatewayCloseCodes.UnknownError:
|
|
547
|
+
case types_1.GatewayCloseCodes.DecodeError:
|
|
548
|
+
case types_1.GatewayCloseCodes.NotAuthenticated:
|
|
549
|
+
case types_1.GatewayCloseCodes.AlreadyAuthenticated:
|
|
550
|
+
case types_1.GatewayCloseCodes.RateLimited:
|
|
551
|
+
{
|
|
552
|
+
this.logger.info('Trying to reconnect');
|
|
553
|
+
await this.reconnect();
|
|
554
|
+
}
|
|
555
|
+
break;
|
|
556
|
+
case types_1.GatewayCloseCodes.AuthenticationFailed:
|
|
557
|
+
case types_1.GatewayCloseCodes.DisallowedIntents:
|
|
558
|
+
case types_1.GatewayCloseCodes.InvalidAPIVersion:
|
|
559
|
+
case types_1.GatewayCloseCodes.InvalidIntents:
|
|
560
|
+
case types_1.GatewayCloseCodes.InvalidShard:
|
|
561
|
+
case types_1.GatewayCloseCodes.ShardingRequired:
|
|
562
|
+
this.lifecycle++;
|
|
563
|
+
this.logger.fatal(`Shard #${this.id} cannot reconnect`);
|
|
564
|
+
break;
|
|
565
|
+
default:
|
|
566
|
+
{
|
|
567
|
+
this.logger.warn(`Shard #${this.id} unknown close code (${close.code}), trying to reconnect anyways`);
|
|
568
|
+
await this.reconnect();
|
|
569
|
+
}
|
|
570
|
+
break;
|
|
571
|
+
}
|
|
435
572
|
}
|
|
436
573
|
}
|
|
437
574
|
finally {
|
|
@@ -443,23 +580,19 @@ class Shard {
|
|
|
443
580
|
}
|
|
444
581
|
}
|
|
445
582
|
close(code, reason) {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
}
|
|
450
|
-
this.debugger?.debug(`[Shard #${this.id}] Called close with reason:`, reason);
|
|
451
|
-
this.websocket?.close(code, reason);
|
|
583
|
+
this.lifecycle++;
|
|
584
|
+
this.reconnectPromise = undefined;
|
|
585
|
+
this.closeConnection(code, reason, this.shouldApplyLocalClosePolicy(code));
|
|
452
586
|
}
|
|
453
587
|
handleMessage(data) {
|
|
454
588
|
let packet;
|
|
455
589
|
try {
|
|
456
|
-
if (data instanceof Buffer)
|
|
590
|
+
if (data instanceof Buffer)
|
|
457
591
|
data = (0, node_zlib_1.inflateSync)(data);
|
|
458
|
-
}
|
|
459
592
|
packet = JSON.parse(data);
|
|
460
593
|
}
|
|
461
|
-
catch (
|
|
462
|
-
this.logger.error(
|
|
594
|
+
catch (error) {
|
|
595
|
+
this.logger.error(error);
|
|
463
596
|
return;
|
|
464
597
|
}
|
|
465
598
|
return this.onpacket(packet);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Logger, type MakeRequired } from '../../common';
|
|
1
|
+
import { Logger, type MakePresent, type MakeRequired } from '../../common';
|
|
2
2
|
import type { MakeDeepPartial } from '../../common/types/util';
|
|
3
3
|
import { type GatewaySendPayload, type GatewayUpdatePresence, type GatewayVoiceStateUpdate } from '../../types';
|
|
4
4
|
import { ShardManagerDefaults } from '../constants';
|
|
@@ -7,7 +7,7 @@ import { Shard } from './shard';
|
|
|
7
7
|
import { type ShardData, type ShardManagerOptions, ShardSocketCloseCodes } from './shared';
|
|
8
8
|
export declare class ShardManager extends Map<number, Shard> {
|
|
9
9
|
connectQueue: ConnectQueue;
|
|
10
|
-
options:
|
|
10
|
+
options: MakePresent<ShardManagerOptions, keyof typeof ShardManagerDefaults>;
|
|
11
11
|
debugger?: Logger;
|
|
12
12
|
constructor(options: MakeDeepPartial<ShardManagerOptions, 'resharding'>);
|
|
13
13
|
get totalShards(): number;
|
|
@@ -156,7 +156,7 @@ class ShardManager extends Map {
|
|
|
156
156
|
}
|
|
157
157
|
/*
|
|
158
158
|
* spawns buckets in order
|
|
159
|
-
* https://discord.com/developers/
|
|
159
|
+
* https://docs.discord.com/developers/topics/gateway#sharding-max-concurrency
|
|
160
160
|
*/
|
|
161
161
|
spawnBuckets() {
|
|
162
162
|
this.debugger?.info('#0 Preparing buckets');
|
|
@@ -61,7 +61,12 @@ export interface WorkerManagerOptions extends Omit<ShardManagerOptions, 'handleP
|
|
|
61
61
|
adapter?: CustomManagerAdapter;
|
|
62
62
|
workers?: number;
|
|
63
63
|
/**
|
|
64
|
-
*
|
|
64
|
+
* Environment variables to add to each worker.
|
|
65
|
+
* Variables managed by Seyfert take precedence over matching keys.
|
|
66
|
+
*/
|
|
67
|
+
workerEnv?: Readonly<Record<string, string>>;
|
|
68
|
+
/**
|
|
69
|
+
* @default 8
|
|
65
70
|
*/
|
|
66
71
|
shardsPerWorker?: number;
|
|
67
72
|
workerProxy?: boolean;
|
|
@@ -78,7 +83,7 @@ export interface ShardData {
|
|
|
78
83
|
resume_seq: number | null;
|
|
79
84
|
/**
|
|
80
85
|
* resume_gateway_url is the url to resume the connection
|
|
81
|
-
* @link https://discord.com/developers/
|
|
86
|
+
* @link https://docs.discord.com/developers/topics/gateway#ready-event
|
|
82
87
|
*/
|
|
83
88
|
resume_gateway_url?: string;
|
|
84
89
|
/**
|
|
@@ -20,7 +20,7 @@ export declare class WorkerManager extends Map<number, (ClusterWorker | WorkerTh
|
|
|
20
20
|
shardEnd: number;
|
|
21
21
|
shardsPerWorker: number;
|
|
22
22
|
}, logger?: Logger): number[][];
|
|
23
|
-
options: PickPartial<Required<WorkerManagerOptions>, 'adapter' | 'handleWorkerMessage' | 'handlePayload' | 'getRC'>;
|
|
23
|
+
options: PickPartial<Required<WorkerManagerOptions>, 'adapter' | 'handleWorkerMessage' | 'handlePayload' | 'getRC' | 'workerEnv'>;
|
|
24
24
|
debugger?: Logger;
|
|
25
25
|
connectQueue: ConnectQueue;
|
|
26
26
|
workerQueue: (() => void)[];
|
|
@@ -53,6 +53,7 @@ export declare class WorkerManager extends Map<number, (ClusterWorker | WorkerTh
|
|
|
53
53
|
workerId: number;
|
|
54
54
|
}): Promise<number>;
|
|
55
55
|
calculateShardId(guildId: string): number;
|
|
56
|
+
private createWorkerNotFoundError;
|
|
56
57
|
calculateWorkerId(shardId: number): number;
|
|
57
58
|
postMessage(id: number, body: ManagerMessages | WorkerHeartbeaterMessages): void;
|
|
58
59
|
prepareWorkers(shards: number[][], rawResharding?: boolean): void;
|
|
@@ -111,6 +112,7 @@ export type ManagerSendCacheResult = CreateManagerMessage<'CACHE_RESULT', {
|
|
|
111
112
|
export type ManagerSendBotReady = CreateManagerMessage<'BOT_READY'>;
|
|
112
113
|
export type ManagerSendApiResponse = CreateManagerMessage<'API_RESPONSE', {
|
|
113
114
|
response: any;
|
|
115
|
+
responseType?: 'arrayBuffer';
|
|
114
116
|
error?: any;
|
|
115
117
|
nonce: string;
|
|
116
118
|
}>;
|