mirror of
https://github.com/tiramisulabs/seyfert.git
synced 2025-07-04 14:06:07 +00:00
sharding replaces
This commit is contained in:
parent
f7c293aaf2
commit
025d4176c2
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@biscuitland/api-types",
|
"name": "@biscuitland/api-types",
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.mjs",
|
"module": "./dist/index.mjs",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
|
2
packages/cache/package.json
vendored
2
packages/cache/package.json
vendored
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@biscuitland/cache",
|
"name": "@biscuitland/cache",
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.mjs",
|
"module": "./dist/index.mjs",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@biscuitland/core",
|
"name": "@biscuitland/core",
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.mjs",
|
"module": "./dist/index.mjs",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
|
@ -2,7 +2,7 @@ import type {
|
|||||||
AtLeastOne,
|
AtLeastOne,
|
||||||
ApplicationCommandPermissionTypes,
|
ApplicationCommandPermissionTypes,
|
||||||
DiscordApplicationCommand,
|
DiscordApplicationCommand,
|
||||||
DiscordGatewayPayload,
|
// DiscordGatewayPayload,
|
||||||
DiscordGuildApplicationCommandPermissions,
|
DiscordGuildApplicationCommandPermissions,
|
||||||
DiscordUser,
|
DiscordUser,
|
||||||
DiscordApplicationCommandOption,
|
DiscordApplicationCommandOption,
|
||||||
@ -210,8 +210,8 @@ export class Session {
|
|||||||
// makeWs
|
// makeWs
|
||||||
|
|
||||||
const defHandler: DiscordRawEventHandler = (shard, event) => {
|
const defHandler: DiscordRawEventHandler = (shard, event) => {
|
||||||
let message = event.data;
|
let data = event as any;
|
||||||
let data = JSON.parse(message) as DiscordGatewayPayload;
|
// let data = JSON.parse(message) as DiscordGatewayPayload;
|
||||||
|
|
||||||
Actions.raw(this, shard.id, data);
|
Actions.raw(this, shard.id, data);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@biscuitland/rest",
|
"name": "@biscuitland/rest",
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.mjs",
|
"module": "./dist/index.mjs",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@biscuitland/ws",
|
"name": "@biscuitland/ws",
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.mjs",
|
"module": "./dist/index.mjs",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import type {
|
import type {
|
||||||
DiscordGatewayPayload,
|
DiscordGatewayPayload,
|
||||||
|
DiscordHello,
|
||||||
|
DiscordReady,
|
||||||
PickPartial,
|
PickPartial,
|
||||||
} from '@biscuitland/api-types';
|
} from '@biscuitland/api-types';
|
||||||
import type { LeakyBucket } from '../utils/bucket-util';
|
import type { LeakyBucket } from '../utils/bucket-util';
|
||||||
@ -13,7 +15,7 @@ import {
|
|||||||
} from '@biscuitland/api-types';
|
} from '@biscuitland/api-types';
|
||||||
|
|
||||||
import WebSocket from 'ws';
|
import WebSocket from 'ws';
|
||||||
import { checkOffline } from '../utils/shard-util';
|
import { inflateSync } from 'node:zlib';
|
||||||
|
|
||||||
export const DEFAULT_HEARTBEAT_INTERVAL = 45000;
|
export const DEFAULT_HEARTBEAT_INTERVAL = 45000;
|
||||||
|
|
||||||
@ -26,6 +28,9 @@ export type PickOptions = Pick<
|
|||||||
> &
|
> &
|
||||||
Partial<ShardOptions>;
|
Partial<ShardOptions>;
|
||||||
|
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
|
||||||
|
|
||||||
export interface ShardOptions {
|
export interface ShardOptions {
|
||||||
/** Id of the shard which should be created. */
|
/** Id of the shard which should be created. */
|
||||||
id: number;
|
id: number;
|
||||||
@ -363,6 +368,129 @@ export class Shard {
|
|||||||
clearTimeout(this.heart.timeoutId);
|
clearTimeout(this.heart.timeoutId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
|
||||||
|
async handleMessage (message: MessageEvent): Promise<void> {
|
||||||
|
let data = message.data;
|
||||||
|
|
||||||
|
if (this.options.gatewayConfig.compress && data instanceof Blob) {
|
||||||
|
// @ts-ignore
|
||||||
|
data = decoder.decode(inflateSync(new Uint8Array(await message.arrayBuffer())));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof data !== 'string') return;
|
||||||
|
|
||||||
|
const messageData = JSON.parse(data) as DiscordGatewayPayload;
|
||||||
|
|
||||||
|
|
||||||
|
switch (messageData.op) {
|
||||||
|
case GatewayOpcodes.Heartbeat: {
|
||||||
|
if (!this.isOpen()) return;
|
||||||
|
|
||||||
|
this.heart.lastBeat = Date.now();
|
||||||
|
|
||||||
|
this.socket?.send(
|
||||||
|
JSON.stringify({
|
||||||
|
op: GatewayOpcodes.Heartbeat,
|
||||||
|
d: this.options.previousSequenceNumber,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
this.events.heartbeat?.(this);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GatewayOpcodes.Hello: {
|
||||||
|
const interval = (messageData.d as DiscordHello).heartbeat_interval;
|
||||||
|
|
||||||
|
this.startHeartbeating(interval);
|
||||||
|
|
||||||
|
if (this.state !== ShardState.Resuming) {
|
||||||
|
|
||||||
|
this.bucket = createLeakyBucket({
|
||||||
|
max: this.safe(),
|
||||||
|
refillInterval: GATEWAY_RATE_LIMIT_RESET_INTERVAL,
|
||||||
|
refillAmount: this.safe(),
|
||||||
|
waiting: this.bucket.waiting,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.events.hello?.(this);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GatewayOpcodes.HeartbeatACK: {
|
||||||
|
this.heart.acknowledged = true;
|
||||||
|
this.heart.lastAck = Date.now();
|
||||||
|
|
||||||
|
if (this.heart.lastBeat) {
|
||||||
|
this.heart.rtt = this.heart.lastAck - this.heart.lastBeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.events.heartbeatAck?.(this);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GatewayOpcodes.Reconnect: {
|
||||||
|
this.events.requestedReconnect?.(this);
|
||||||
|
|
||||||
|
await this.resume();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GatewayOpcodes.InvalidSession: {
|
||||||
|
const resumable = messageData.d as boolean;
|
||||||
|
|
||||||
|
this.events.invalidSession?.(this, resumable);
|
||||||
|
|
||||||
|
await this.delay(Math.floor((Math.random() * 4 + 1) * 1000));
|
||||||
|
|
||||||
|
this.resolves.get('INVALID_SESSION')?.(messageData);
|
||||||
|
this.resolves.delete('INVALID_SESSION');
|
||||||
|
|
||||||
|
if (!resumable) {
|
||||||
|
await this.identify();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.resume();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messageData.t === 'RESUMED') {
|
||||||
|
|
||||||
|
this.state = ShardState.Connected;
|
||||||
|
this.events.resumed?.(this);
|
||||||
|
|
||||||
|
this.offlineSendQueue.map((resolve) => resolve());
|
||||||
|
|
||||||
|
this.resolves.get('RESUMED')?.(messageData);
|
||||||
|
this.resolves.delete('RESUMED');
|
||||||
|
}
|
||||||
|
else if (messageData.t === 'READY') {
|
||||||
|
const payload = messageData.d as DiscordReady;
|
||||||
|
|
||||||
|
this.sessionId = payload.session_id;
|
||||||
|
this.state = ShardState.Connected;
|
||||||
|
|
||||||
|
this.offlineSendQueue.map((resolve) => resolve());
|
||||||
|
|
||||||
|
this.resolves.get('READY')?.(messageData);
|
||||||
|
this.resolves.delete('READY');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messageData.s !== null) {
|
||||||
|
this.options.previousSequenceNumber = messageData.s;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.events.message?.(this, messageData);
|
||||||
|
this.options.handleMessage(this, messageData as any);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
@ -395,11 +523,11 @@ export class Shard {
|
|||||||
this.state = ShardState.Identifying;
|
this.state = ShardState.Identifying;
|
||||||
this.events.identifying?.(this);
|
this.events.identifying?.(this);
|
||||||
|
|
||||||
if (!this.ready()) {
|
if (!this.isOpen()) {
|
||||||
await this.connect();
|
await this.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
// await this.operator();
|
await this.handleIdentify();
|
||||||
|
|
||||||
this.send(
|
this.send(
|
||||||
{
|
{
|
||||||
@ -409,8 +537,7 @@ export class Shard {
|
|||||||
compress: this.options.gatewayConfig.compress,
|
compress: this.options.gatewayConfig.compress,
|
||||||
properties: this.options.gatewayConfig.properties,
|
properties: this.options.gatewayConfig.properties,
|
||||||
intents: this.options.gatewayConfig.intents,
|
intents: this.options.gatewayConfig.intents,
|
||||||
shard: [this.id, this.options.totalShards],
|
shard: [this.id, this.options.totalShards]
|
||||||
// presence: await this.makePresence?.(this.id),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
true
|
true
|
||||||
@ -455,15 +582,14 @@ export class Shard {
|
|||||||
socket.onmessage = (message: any) => {
|
socket.onmessage = (message: any) => {
|
||||||
hi = true;
|
hi = true;
|
||||||
|
|
||||||
this.options.handleMessage(this, message);
|
this.handleMessage(message);
|
||||||
// this.handle(message);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
socket.onopen = () => {
|
socket.onopen = () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!hi) {
|
if (!hi) {
|
||||||
this.options.handleMessage(this, {
|
this.handleMessage({
|
||||||
data: JSON.stringify({
|
data: JSON.stringify({
|
||||||
t: null,
|
t: null,
|
||||||
s: null,
|
s: null,
|
||||||
@ -494,7 +620,7 @@ export class Shard {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
async resume(): Promise<void> {
|
async resume(): Promise<void> {
|
||||||
if (this.ready()) {
|
if (this.isOpen()) {
|
||||||
this.close(
|
this.close(
|
||||||
ShardSocketCloseCodes.ResumeClosingOldConnection,
|
ShardSocketCloseCodes.ResumeClosingOldConnection,
|
||||||
'Reconnecting the shard, closing old connection.'
|
'Reconnecting the shard, closing old connection.'
|
||||||
@ -536,11 +662,11 @@ export class Shard {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
async send(message: ShardSocketRequest, highPriority: boolean) {
|
async send(message: ShardSocketRequest, highPriority: boolean) {
|
||||||
await checkOffline(this, highPriority);
|
await this.checkOffline(highPriority);
|
||||||
|
|
||||||
await this.bucket.acquire(1, highPriority);
|
await this.bucket.acquire(1, highPriority);
|
||||||
|
|
||||||
await checkOffline(this, highPriority);
|
await this.checkOffline(highPriority);
|
||||||
|
|
||||||
this.socket?.send(JSON.stringify(message));
|
this.socket?.send(JSON.stringify(message));
|
||||||
}
|
}
|
||||||
@ -613,10 +739,26 @@ export class Shard {
|
|||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
|
|
||||||
close(code: number, reason: string): void {
|
async checkOffline(highPriority: boolean): Promise<void> {
|
||||||
if (!this.ready()) {
|
if (!this.isOpen()) {
|
||||||
return;
|
await new Promise(resolve => {
|
||||||
|
if (highPriority) {
|
||||||
|
this.offlineSendQueue.unshift(resolve);
|
||||||
|
} else {
|
||||||
|
this.offlineSendQueue.push(resolve);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
|
||||||
|
close(code: number, reason: string): void {
|
||||||
|
if (this.socket?.readyState !== WebSocket.OPEN) {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
return this.socket?.close(code, reason);
|
return this.socket?.close(code, reason);
|
||||||
}
|
}
|
||||||
@ -625,10 +767,22 @@ export class Shard {
|
|||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ready(): boolean {
|
isOpen(): boolean {
|
||||||
return this.socket?.readyState === WebSocket.OPEN;
|
return this.socket?.readyState === WebSocket.OPEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
|
||||||
|
async delay(ms: number): Promise<void> {
|
||||||
|
return new Promise((res): any =>
|
||||||
|
setTimeout((): void => {
|
||||||
|
res();
|
||||||
|
}, ms)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user