fix: retry connection when failed

This commit is contained in:
MARCROCK22 2025-02-22 20:47:25 -04:00
parent 61c3c283b9
commit 272ddac08b
3 changed files with 32 additions and 4 deletions

View File

@ -1,5 +1,5 @@
import { inflateSync } from 'node:zlib'; import { inflateSync } from 'node:zlib';
import { LogLevels, Logger, type MakeRequired, MergeOptions, hasIntent } from '../../common'; import { LogLevels, Logger, type MakeRequired, MergeOptions, delay, hasIntent } from '../../common';
import { import {
type APIGuildMember, type APIGuildMember,
GatewayCloseCodes, GatewayCloseCodes,
@ -46,8 +46,11 @@ export class Shard {
bucket: DynamicBucket; bucket: DynamicBucket;
offlineSendQueue: ((_?: unknown) => void)[] = []; offlineSendQueue: ((_?: unknown) => void)[] = [];
pendingGuilds = new Set<string>(); pendingGuilds = new Set<string>();
options: MakeRequired<ShardOptions, 'properties' | 'ratelimitOptions'>; options: MakeRequired<ShardOptions, 'properties' | 'ratelimitOptions' | 'reconnectTimeout' | 'connectionTimeout'>;
isReady = false; isReady = false;
connectionTimeout?: NodeJS.Timeout;
private requestGuildMembersChunk = new Map< private requestGuildMembersChunk = new Map<
string, string,
{ {
@ -72,6 +75,8 @@ export class Shard {
rateLimitResetInterval: 60_000, rateLimitResetInterval: 60_000,
maxRequestsPerRateLimitTick: 120, maxRequestsPerRateLimitTick: 120,
}, },
reconnectTimeout: 10e3,
connectionTimeout: 30e3,
} as ShardOptions, } as ShardOptions,
options, options,
); );
@ -127,6 +132,11 @@ export class Shard {
this.debugger?.debug(`[Shard #${this.id}] Connecting to ${this.currentGatewayURL}`); this.debugger?.debug(`[Shard #${this.id}] Connecting to ${this.currentGatewayURL}`);
this.connectionTimeout = setTimeout(
() => this.reconnect(ShardSocketCloseCodes.Timeout),
this.options.connectionTimeout,
);
// @ts-expect-error Use native websocket when using Bun // @ts-expect-error Use native websocket when using Bun
// biome-ignore lint/correctness/noUndeclaredVariables: /\ // biome-ignore lint/correctness/noUndeclaredVariables: /\
this.websocket = new BaseSocket(typeof Bun === 'undefined' ? 'ws' : 'bun', this.currentGatewayURL); this.websocket = new BaseSocket(typeof Bun === 'undefined' ? 'ws' : 'bun', this.currentGatewayURL);
@ -217,13 +227,16 @@ export class Shard {
} }
disconnect(code = ShardSocketCloseCodes.Shutdown) { disconnect(code = ShardSocketCloseCodes.Shutdown) {
clearTimeout(this.connectionTimeout);
this.connectionTimeout = undefined;
this.debugger?.info(`[Shard #${this.id}] Disconnecting`); this.debugger?.info(`[Shard #${this.id}] Disconnecting`);
this.close(code, 'Shard down request'); this.close(code, 'Shard down request');
} }
async reconnect() { async reconnect(code = ShardSocketCloseCodes.Reconnect) {
this.debugger?.info(`[Shard #${this.id}] Reconnecting`); this.debugger?.info(`[Shard #${this.id}] Reconnecting`);
this.disconnect(ShardSocketCloseCodes.Reconnect); this.disconnect(code);
await delay(this.options.reconnectTimeout);
await this.connect(); await this.connect();
} }
@ -236,6 +249,8 @@ export class Shard {
switch (packet.op) { switch (packet.op) {
case GatewayOpcodes.Hello: { case GatewayOpcodes.Hello: {
clearTimeout(this.connectionTimeout);
this.connectionTimeout = undefined;
clearInterval(this.heart.nodeInterval); clearInterval(this.heart.nodeInterval);
this.heart.interval = packet.d.heartbeat_interval; this.heart.interval = packet.d.heartbeat_interval;
@ -275,12 +290,16 @@ export class Shard {
switch (packet.t) { switch (packet.t) {
case GatewayDispatchEvents.Resumed: case GatewayDispatchEvents.Resumed:
{ {
clearTimeout(this.connectionTimeout);
this.connectionTimeout = undefined;
this.isReady = true; this.isReady = true;
this.offlineSendQueue.map(resolve => resolve()); this.offlineSendQueue.map(resolve => resolve());
this.options.handlePayload(this.id, packet); this.options.handlePayload(this.id, packet);
} }
break; break;
case GatewayDispatchEvents.Ready: { case GatewayDispatchEvents.Ready: {
clearTimeout(this.connectionTimeout);
this.connectionTimeout = undefined;
if (hasIntent(this.options.intents, 'Guilds')) { if (hasIntent(this.options.intents, 'Guilds')) {
for (let i = 0; i < packet.d.guilds.length; i++) { for (let i = 0; i < packet.d.guilds.length; i++) {
this.pendingGuilds.add(packet.d.guilds.at(i)!.id); this.pendingGuilds.add(packet.d.guilds.at(i)!.id);
@ -411,6 +430,8 @@ export class Shard {
case GatewayCloseCodes.UnknownOpcode: case GatewayCloseCodes.UnknownOpcode:
case GatewayCloseCodes.InvalidSeq: case GatewayCloseCodes.InvalidSeq:
case GatewayCloseCodes.SessionTimedOut: case GatewayCloseCodes.SessionTimedOut:
// shard failed to connect, try connecting from scratch
case ShardSocketCloseCodes.Timeout:
{ {
this.data.resume_seq = 0; this.data.resume_seq = 0;
this.data.session_id = undefined; this.data.session_id = undefined;

View File

@ -100,6 +100,8 @@ export class ShardManager extends Map<number, Shard> {
debugger: this.debugger, debugger: this.debugger,
compress: this.options.compress ?? false, compress: this.options.compress ?? false,
presence: this.options.presence?.(shardId, -1), presence: this.options.presence?.(shardId, -1),
connectionTimeout: this.options.connectionTimeout,
reconnectTimeout: this.options.reconnectTimeout,
}); });
this.set(shardId, shard); this.set(shardId, shard);

View File

@ -46,6 +46,8 @@ export interface ShardManagerOptions extends ShardDetails {
interval: number; interval: number;
percentage: number; percentage: number;
}; };
reconnectTimeout?: number;
connectionTimeout?: number;
} }
export interface CustomManagerAdapter { export interface CustomManagerAdapter {
@ -128,6 +130,8 @@ export interface ShardOptions extends ShardDetails {
debugger?: Logger; debugger?: Logger;
compress: boolean; compress: boolean;
presence?: GatewayPresenceUpdateData; presence?: GatewayPresenceUpdateData;
reconnectTimeout?: number;
connectionTimeout?: number;
} }
export enum ShardSocketCloseCodes { export enum ShardSocketCloseCodes {
@ -136,6 +140,7 @@ export enum ShardSocketCloseCodes {
Reconnect = 3020, Reconnect = 3020,
Resharding = 3030, Resharding = 3030,
ShutdownAll = 3040, ShutdownAll = 3040,
Timeout = 3050,
} }
export interface WorkerData { export interface WorkerData {