feat: @discordjs/ws (#8260)

Co-authored-by: Parbez <imranbarbhuiya.fsd@gmail.com>
This commit is contained in:
DD
2022-07-22 20:13:47 +03:00
committed by GitHub
parent 830c670c61
commit 748d7271c4
37 changed files with 3659 additions and 2612 deletions

View File

@@ -0,0 +1,29 @@
import { setTimeout as sleep } from 'node:timers/promises';
import type { WebSocketManager } from '../ws/WebSocketManager';
export class IdentifyThrottler {
private identifyState = {
remaining: 0,
resetsAt: Infinity,
};
public constructor(private readonly manager: WebSocketManager) {}
public async waitForIdentify(): Promise<void> {
if (this.identifyState.remaining <= 0) {
const diff = this.identifyState.resetsAt - Date.now();
if (diff <= 5_000) {
const time = diff + Math.random() * 1_500;
await sleep(time);
}
const info = await this.manager.fetchGatewayInformation();
this.identifyState = {
remaining: info.session_start_limit.max_concurrency,
resetsAt: Date.now() + 5_000,
};
}
this.identifyState.remaining--;
}
}

View File

@@ -0,0 +1,68 @@
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { Collection } from '@discordjs/collection';
import { APIVersion, GatewayOpcodes } from 'discord-api-types/v10';
import { lazy } from './utils';
import type { OptionalWebSocketManagerOptions, SessionInfo } from '../ws/WebSocketManager';
/**
* Valid encoding types
*/
export enum Encoding {
JSON = 'json',
}
/**
* Valid compression methods
*/
export enum CompressionMethod {
ZlibStream = 'zlib-stream',
}
const packageJson = readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf8');
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const Package = JSON.parse(packageJson);
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions, @typescript-eslint/no-unsafe-member-access
export const DefaultDeviceProperty = `@discordjs/ws ${Package.version}`;
const getDefaultSessionStore = lazy(() => new Collection<number, SessionInfo | null>());
/**
* Default options used by the manager
*/
export const DefaultWebSocketManagerOptions: OptionalWebSocketManagerOptions = {
shardCount: null,
shardIds: null,
largeThreshold: null,
initialPresence: null,
identifyProperties: {
browser: DefaultDeviceProperty,
device: DefaultDeviceProperty,
os: process.platform,
},
version: APIVersion,
encoding: Encoding.JSON,
compression: null,
retrieveSessionInfo(shardId) {
const store = getDefaultSessionStore();
return store.get(shardId) ?? null;
},
updateSessionInfo(shardId: number, info: SessionInfo | null) {
const store = getDefaultSessionStore();
if (info) {
store.set(shardId, info);
} else {
store.delete(shardId);
}
},
handshakeTimeout: 30_000,
helloTimeout: 60_000,
readyTimeout: 15_000,
};
export const ImportantGatewayOpcodes = new Set([
GatewayOpcodes.Heartbeat,
GatewayOpcodes.Identify,
GatewayOpcodes.Resume,
]);

View File

@@ -0,0 +1,20 @@
import type { ShardRange } from '../ws/WebSocketManager';
export type Awaitable<T> = T | Promise<T>;
/**
* Yields the numbers in the given range as an array
* @example
* range({ start: 3, end: 5 }); // [3, 4, 5]
*/
export function range({ start, end }: ShardRange): number[] {
return Array.from({ length: end - start + 1 }, (_, i) => i + start);
}
/**
* Lazily evaluate a callback, storing its result
*/
export function lazy<T>(cb: () => T): () => T {
let defaultValue: T;
return () => (defaultValue ??= cb());
}