seyfert 2.0.0 → 2.1.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.
Files changed (81) hide show
  1. package/lib/api/Routes/interactions.d.ts +8 -2
  2. package/lib/api/Routes/skus.d.ts +11 -0
  3. package/lib/api/Routes/skus.js +2 -0
  4. package/lib/api/api.d.ts +1 -1
  5. package/lib/api/api.js +14 -12
  6. package/lib/api/shared.d.ts +1 -1
  7. package/lib/api/utils/constants.d.ts +1 -1
  8. package/lib/api/utils/constants.js +1 -1
  9. package/lib/builders/Attachment.d.ts +1 -1
  10. package/lib/builders/Attachment.js +12 -12
  11. package/lib/cache/index.d.ts +4 -1
  12. package/lib/cache/index.js +11 -3
  13. package/lib/client/base.d.ts +3 -3
  14. package/lib/client/base.js +8 -3
  15. package/lib/client/client.d.ts +4 -0
  16. package/lib/client/client.js +30 -14
  17. package/lib/client/httpclient.js +2 -2
  18. package/lib/client/workerclient.d.ts +3 -2
  19. package/lib/client/workerclient.js +23 -11
  20. package/lib/commands/applications/entryPoint.d.ts +46 -0
  21. package/lib/commands/applications/entryPoint.js +56 -0
  22. package/lib/commands/applications/entrycontext.d.ts +40 -0
  23. package/lib/commands/applications/entrycontext.js +85 -0
  24. package/lib/commands/applications/options.d.ts +3 -2
  25. package/lib/commands/decorators.d.ts +11 -17
  26. package/lib/commands/decorators.js +3 -0
  27. package/lib/commands/handle.d.ts +6 -5
  28. package/lib/commands/handle.js +42 -0
  29. package/lib/commands/handler.d.ts +6 -4
  30. package/lib/commands/handler.js +6 -1
  31. package/lib/commands/index.d.ts +2 -0
  32. package/lib/commands/index.js +2 -0
  33. package/lib/commands/optionresolver.js +1 -1
  34. package/lib/common/bot/watcher.d.ts +1 -1
  35. package/lib/common/it/utils.d.ts +10 -3
  36. package/lib/common/it/utils.js +10 -0
  37. package/lib/common/shorters/interaction.d.ts +1 -1
  38. package/lib/common/types/util.d.ts +1 -1
  39. package/lib/common/types/write.d.ts +2 -2
  40. package/lib/deps/mixer.js +6 -1
  41. package/lib/events/handler.js +3 -3
  42. package/lib/events/hooks/custom.d.ts +1 -0
  43. package/lib/events/hooks/custom.js +5 -1
  44. package/lib/events/hooks/subscriptions.d.ts +35 -0
  45. package/lib/events/hooks/subscriptions.js +16 -0
  46. package/lib/structures/Interaction.d.ts +24 -4
  47. package/lib/structures/Interaction.js +54 -5
  48. package/lib/structures/Message.d.ts +0 -3
  49. package/lib/structures/Message.js +0 -3
  50. package/lib/structures/channels.js +2 -2
  51. package/lib/structures/extra/BaseGuild.d.ts +11 -1
  52. package/lib/structures/extra/BaseGuild.js +27 -0
  53. package/lib/types/gateway.d.ts +19 -2
  54. package/lib/types/payloads/_interactions/_applicationCommands/chatInput.d.ts +6 -1
  55. package/lib/types/payloads/_interactions/applicationCommands.d.ts +41 -6
  56. package/lib/types/payloads/_interactions/applicationCommands.js +28 -1
  57. package/lib/types/payloads/_interactions/responses.d.ts +74 -2
  58. package/lib/types/payloads/_interactions/responses.js +4 -0
  59. package/lib/types/payloads/channel.d.ts +44 -4
  60. package/lib/types/payloads/channel.js +5 -1
  61. package/lib/types/payloads/monetization.d.ts +29 -1
  62. package/lib/types/payloads/monetization.js +10 -1
  63. package/lib/types/rest/channel.d.ts +4 -17
  64. package/lib/types/rest/interactions.d.ts +30 -8
  65. package/lib/types/rest/monetization.d.ts +22 -1
  66. package/lib/types/rest/webhook.d.ts +2 -2
  67. package/lib/types/utils/index.d.ts +16 -5
  68. package/lib/types/utils/index.js +15 -4
  69. package/lib/websocket/SharedTypes.d.ts +9 -3
  70. package/lib/websocket/constants/index.d.ts +3 -2
  71. package/lib/websocket/constants/index.js +11 -1
  72. package/lib/websocket/discord/shard.d.ts +3 -2
  73. package/lib/websocket/discord/shard.js +25 -11
  74. package/lib/websocket/discord/sharder.js +59 -1
  75. package/lib/websocket/discord/shared.d.ts +21 -1
  76. package/lib/websocket/discord/socket/custom.d.ts +1 -1
  77. package/lib/websocket/discord/socket/custom.js +51 -34
  78. package/lib/websocket/discord/worker.d.ts +2 -1
  79. package/lib/websocket/discord/workermanager.d.ts +2 -1
  80. package/lib/websocket/discord/workermanager.js +30 -13
  81. package/package.json +8 -3
@@ -31,6 +31,64 @@ class ShardManager extends Map {
31
31
  if (worker_threads.parentPort)
32
32
  parentPort = worker_threads.parentPort;
33
33
  }
34
+ if (this.options.resharding.interval <= 0)
35
+ return;
36
+ setInterval(async () => {
37
+ this.debugger?.debug('Checking if reshard is needed');
38
+ const info = await this.options.resharding.getInfo();
39
+ if (info.shards <= this.totalShards)
40
+ return this.debugger?.debug('Resharding not needed');
41
+ //https://github.com/discordeno/discordeno/blob/6a5f446c0651b9fad9f1550ff1857fe7a026426b/packages/gateway/src/manager.ts#L106C8-L106C94
42
+ const percentage = (info.shards / ((this.totalShards * 2500) / 1000)) * 100;
43
+ if (percentage < this.options.resharding.percentage)
44
+ return this.debugger?.debug(`Percentage is not enough to reshard ${percentage}/${this.options.resharding.percentage}`);
45
+ this.debugger?.info('Starting resharding process');
46
+ this.connectQueue.concurrency = info.session_start_limit.max_concurrency;
47
+ this.options.totalShards = info.shards;
48
+ this.options.info.session_start_limit.max_concurrency = info.session_start_limit.max_concurrency;
49
+ let shardsConnected = 0;
50
+ let handlePayload = async (sharder, _, packet) => {
51
+ if ((packet.t === 'GUILD_CREATE' || packet.t === 'GUILD_DELETE') &&
52
+ this.options.resharding.onGuild(packet.d.id)) {
53
+ return;
54
+ }
55
+ if (packet.t !== 'READY')
56
+ return;
57
+ this.options.resharding.reloadGuilds(packet.d.guilds.map(x => x.id));
58
+ if (++shardsConnected < info.shards)
59
+ return; //waiting for last shard to connect
60
+ // dont listen more events when all shards are ready
61
+ handlePayload = async () => { };
62
+ await this.disconnectAll();
63
+ this.clear();
64
+ for (const [id, shard] of sharder) {
65
+ shard.options.handlePayload = (shardId, packet) => {
66
+ return this.options.handlePayload(shardId, packet);
67
+ };
68
+ this.set(id, shard);
69
+ }
70
+ sharder.clear();
71
+ };
72
+ const resharder = new ShardManager({
73
+ ...this.options,
74
+ resharding: {
75
+ // getInfo mock, we don't need it
76
+ getInfo: () => ({}),
77
+ interval: 0,
78
+ percentage: 0,
79
+ reloadGuilds() { },
80
+ onGuild() {
81
+ return true;
82
+ },
83
+ },
84
+ handlePayload: (shardId, packet) => {
85
+ return handlePayload(resharder, shardId, packet);
86
+ },
87
+ });
88
+ // share ratelimit
89
+ resharder.connectQueue = this.connectQueue;
90
+ await resharder.spawnShards();
91
+ }, this.options.resharding.interval);
34
92
  }
35
93
  get totalShards() {
36
94
  return this.options.totalShards ?? this.options.info.shards;
@@ -53,7 +111,7 @@ class ShardManager extends Map {
53
111
  return acc / this.size;
54
112
  }
55
113
  calculateShardId(guildId) {
56
- return Number((BigInt(guildId) >> 22n) % BigInt(this.options.info.shards ?? 1));
114
+ return (0, common_1.calculateShardId)(guildId, this.totalShards);
57
115
  }
58
116
  spawn(shardId) {
59
117
  this.debugger?.info(`Spawn shard ${shardId}`);
@@ -29,6 +29,26 @@ export interface ShardManagerOptions extends ShardDetails {
29
29
  */
30
30
  presence?: (shardId: number, workerId: number) => GatewayPresenceUpdateData;
31
31
  compress?: boolean;
32
+ resharding?: {
33
+ /**
34
+ * @returns the gateway connection info
35
+ */
36
+ getInfo(): Promise<APIGatewayBotInfo>;
37
+ interval: number;
38
+ percentage: number;
39
+ /**
40
+ *
41
+ * @param ids
42
+ * @returns
43
+ */
44
+ reloadGuilds: (ids: string[]) => unknown;
45
+ /**
46
+ *
47
+ * @param id
48
+ * @returns true if deleted
49
+ */
50
+ onGuild: (id: string) => boolean;
51
+ };
32
52
  }
33
53
  export interface CustomManagerAdapter {
34
54
  postMessage(workerId: number, body: unknown): Awaitable<unknown>;
@@ -39,7 +59,7 @@ export interface WorkerManagerOptions extends Omit<ShardManagerOptions, 'handleP
39
59
  adapter?: CustomManagerAdapter;
40
60
  workers?: number;
41
61
  /**
42
- * @default 32
62
+ * @default 16
43
63
  */
44
64
  shardsPerWorker?: number;
45
65
  workerProxy?: boolean;
@@ -19,7 +19,7 @@ export declare class SeyfertWebSocket {
19
19
  private connect;
20
20
  handleReadable(): void;
21
21
  handleEvent(body: Buffer, opcode: number): void;
22
- handleClose(): void;
22
+ handleClose(): Promise<void>;
23
23
  send(data: string): void;
24
24
  private _write;
25
25
  onping(_data: string): void;
@@ -18,38 +18,51 @@ class SeyfertWebSocket {
18
18
  this.path = `${urlParts.pathname}${urlParts.search || ''}`;
19
19
  this.connect();
20
20
  }
21
- connect() {
22
- const key = (0, node_crypto_1.randomBytes)(16).toString('base64');
23
- const req = (0, node_https_1.request)({
24
- //discord gateway hostname
25
- hostname: this.hostname,
26
- path: this.path,
27
- headers: {
28
- Connection: 'Upgrade',
29
- Upgrade: 'websocket',
30
- 'Sec-WebSocket-Key': key,
31
- 'Sec-WebSocket-Version': '13',
32
- },
33
- });
34
- req.on('upgrade', (res, socket) => {
35
- const hash = (0, node_crypto_1.createHash)('sha1').update(`${key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11`).digest('base64');
36
- const accept = res.headers['sec-websocket-accept'];
37
- if (accept !== hash) {
38
- socket.end(() => {
39
- this.onerror(new Error('Invalid sec-websocket-accept header'));
40
- });
41
- return;
42
- }
43
- this.socket = socket;
44
- socket.on('readable', this.handleReadable.bind(this));
45
- socket.on('close', this.handleClose.bind(this));
46
- socket.on('error', err => this.onerror(err));
47
- this.onopen();
48
- });
49
- req.on('close', () => {
50
- req.removeAllListeners();
21
+ connect(retries = 0) {
22
+ return new Promise((resolve, rej) => {
23
+ const key = (0, node_crypto_1.randomBytes)(16).toString('base64');
24
+ const req = (0, node_https_1.request)({
25
+ //discord gateway hostname
26
+ hostname: this.hostname,
27
+ path: this.path,
28
+ headers: {
29
+ Connection: 'Upgrade',
30
+ Upgrade: 'websocket',
31
+ 'Sec-WebSocket-Key': key,
32
+ 'Sec-WebSocket-Version': '13',
33
+ },
34
+ });
35
+ req.on('upgrade', (res, socket) => {
36
+ const hash = (0, node_crypto_1.createHash)('sha1').update(`${key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11`).digest('base64');
37
+ const accept = res.headers['sec-websocket-accept'];
38
+ if (accept !== hash) {
39
+ socket.end(() => {
40
+ rej(new Error('Invalid sec-websocket-accept header'));
41
+ });
42
+ return;
43
+ }
44
+ this.socket = socket;
45
+ socket.on('readable', this.handleReadable.bind(this));
46
+ socket.on('close', this.handleClose.bind(this));
47
+ socket.on('error', err => this.onerror(err));
48
+ resolve();
49
+ this.onopen();
50
+ });
51
+ req.on('close', () => {
52
+ req.removeAllListeners();
53
+ });
54
+ req.on('error', e => {
55
+ if (retries < 5) {
56
+ setTimeout(() => {
57
+ resolve(this.connect(retries + 1));
58
+ }, 500);
59
+ }
60
+ else {
61
+ rej(e);
62
+ }
63
+ });
64
+ req.end();
51
65
  });
52
- req.end();
53
66
  }
54
67
  handleReadable() {
55
68
  // Keep reading until no data, this is useful when two payloads merges.
@@ -66,9 +79,11 @@ class SeyfertWebSocket {
66
79
  // Equivalent to readUint32BE
67
80
  length = this.readBytes(2, slice - 2);
68
81
  }
82
+ const payloadLength = slice + length;
69
83
  // Read the frame, ignore data next to it, leave it to next `while` cycle
70
- const frame = this.socket.read(slice + length);
71
- if (!frame)
84
+ const frame = this.socket.read(payloadLength);
85
+ // unfinished object when socket closes while reading the data
86
+ if (!frame || frame.length !== payloadLength)
72
87
  return;
73
88
  // Get fin (0 | 1)
74
89
  const fin = frame[0] >> 7;
@@ -131,7 +146,7 @@ class SeyfertWebSocket {
131
146
  break;
132
147
  }
133
148
  }
134
- handleClose() {
149
+ async handleClose() {
135
150
  this.socket?.removeAllListeners();
136
151
  this.socket?.destroy();
137
152
  this.socket = undefined;
@@ -146,6 +161,8 @@ class SeyfertWebSocket {
146
161
  this._write(Buffer.from(data), 1);
147
162
  }
148
163
  _write(buffer, opcode) {
164
+ if (!this.socket?.writable)
165
+ return;
149
166
  const length = buffer.length;
150
167
  let frame;
151
168
  // Kinda same logic as above, but client-side
@@ -35,6 +35,7 @@ export type WorkerSendInfo = CreateWorkerMessage<'WORKER_INFO', WorkerInfo & {
35
35
  nonce: string;
36
36
  }>;
37
37
  export type WorkerReady = CreateWorkerMessage<'WORKER_READY'>;
38
+ export type WorkerShardsConnected = CreateWorkerMessage<'WORKER_SHARDS_CONNECTED'>;
38
39
  export type WorkerStart = CreateWorkerMessage<'WORKER_START'>;
39
40
  export type WorkerSendApiRequest = CreateWorkerMessage<'WORKER_API_REQUEST', {
40
41
  method: HttpMethods;
@@ -51,5 +52,5 @@ export type WorkerSendEval = CreateWorkerMessage<'EVAL', {
51
52
  nonce: string;
52
53
  toWorkerId: number;
53
54
  }>;
54
- export type WorkerMessage = WorkerRequestConnect | WorkerReceivePayload | WorkerSendResultPayload | WorkerSendCacheRequest | WorkerSendShardInfo | WorkerSendInfo | WorkerReady | WorkerSendApiRequest | WorkerSendEvalResponse | WorkerSendEval | WorkerStart;
55
+ export type WorkerMessage = WorkerRequestConnect | WorkerReceivePayload | WorkerSendResultPayload | WorkerSendCacheRequest | WorkerSendShardInfo | WorkerSendInfo | WorkerReady | WorkerShardsConnected | WorkerSendApiRequest | WorkerSendEvalResponse | WorkerSendEval | WorkerStart;
55
56
  export {};
@@ -14,6 +14,7 @@ export declare class WorkerManager extends Map<number, (ClusterWorker | import('
14
14
  options: MakePartial<Required<WorkerManagerOptions>, 'adapter'>;
15
15
  debugger?: Logger;
16
16
  connectQueue: ConnectQueue;
17
+ workerQueue: (() => void)[];
17
18
  cacheAdapter: Adapter;
18
19
  promises: Map<string, {
19
20
  resolve: (value: any) => void;
@@ -50,7 +51,7 @@ export declare class WorkerManager extends Map<number, (ClusterWorker | import('
50
51
  send(data: GatewaySendPayload, shardId: number): Promise<true>;
51
52
  getShardInfo(shardId: number): Promise<WorkerShardInfo>;
52
53
  getWorkerInfo(workerId: number): Promise<WorkerInfo>;
53
- start(): Promise<void>;
54
+ start(): Promise<void | undefined>;
54
55
  }
55
56
  type CreateManagerMessage<T extends string, D extends object = {}> = {
56
57
  type: T;
@@ -17,6 +17,7 @@ class WorkerManager extends Map {
17
17
  options;
18
18
  debugger;
19
19
  connectQueue;
20
+ workerQueue = [];
20
21
  cacheAdapter;
21
22
  promises = new Map();
22
23
  rest;
@@ -109,20 +110,22 @@ class WorkerManager extends Map {
109
110
  if (!worker_threads)
110
111
  throw new Error('Cannot prepare workers without worker_threads.');
111
112
  for (let i = 0; i < shards.length; i++) {
112
- let worker = this.get(i);
113
- if (!worker) {
114
- worker = this.createWorker({
115
- path: this.options.path,
116
- debug: this.options.debug,
117
- token: this.options.token,
118
- shards: shards[i],
119
- intents: this.options.intents,
120
- workerId: i,
121
- workerProxy: this.options.workerProxy,
122
- totalShards: this.totalShards,
123
- mode: this.options.mode,
113
+ const workerExists = this.has(i);
114
+ if (!workerExists) {
115
+ this.workerQueue.push(() => {
116
+ const worker = this.createWorker({
117
+ path: this.options.path,
118
+ debug: this.options.debug,
119
+ token: this.options.token,
120
+ shards: shards[i],
121
+ intents: this.options.intents,
122
+ workerId: i,
123
+ workerProxy: this.options.workerProxy,
124
+ totalShards: this.totalShards,
125
+ mode: this.options.mode,
126
+ });
127
+ this.set(i, worker);
124
128
  });
125
- this.set(i, worker);
126
129
  }
127
130
  }
128
131
  }
@@ -260,6 +263,18 @@ class WorkerManager extends Map {
260
263
  }
261
264
  }
262
265
  break;
266
+ case 'WORKER_SHARDS_CONNECTED':
267
+ {
268
+ const nextWorker = this.workerQueue.shift();
269
+ if (nextWorker) {
270
+ this.debugger?.info('Spawning next worker');
271
+ nextWorker();
272
+ }
273
+ else {
274
+ this.debugger?.info('No more workers to spawn left');
275
+ }
276
+ }
277
+ break;
263
278
  case 'WORKER_API_REQUEST':
264
279
  {
265
280
  const response = await this.rest.request(message.method, message.url, message.requestOptions);
@@ -377,6 +392,8 @@ class WorkerManager extends Map {
377
392
  }
378
393
  const spaces = this.prepareSpaces();
379
394
  await this.prepareWorkers(spaces);
395
+ // Start workers queue
396
+ return this.workerQueue.shift()?.();
380
397
  }
381
398
  }
382
399
  exports.WorkerManager = WorkerManager;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "seyfert",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "The most advanced framework for discord bots",
5
5
  "main": "./lib/index.js",
6
6
  "module": "./lib/index.js",
@@ -18,7 +18,7 @@
18
18
  "check": "biome check --write --no-errors-on-unmatched ./src"
19
19
  },
20
20
  "author": "MARCROCK22",
21
- "license": "Apache-2.0",
21
+ "license": "MIT",
22
22
  "devDependencies": {
23
23
  "@biomejs/biome": "1.8.3",
24
24
  "@commitlint/cli": "^19.4.0",
@@ -61,5 +61,10 @@
61
61
  "name": "David",
62
62
  "url": "https://github.com/Drylozu"
63
63
  }
64
- ]
64
+ ],
65
+ "lint-staged": {
66
+ "*.ts": [
67
+ "biome check --write"
68
+ ]
69
+ }
65
70
  }