@discordjs/ws 0.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.
- package/CHANGELOG.md +13 -0
- package/LICENSE +191 -0
- package/README.md +124 -0
- package/dist/index.cjs +39 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +11 -0
- package/dist/index.mjs.map +1 -0
- package/dist/strategies/context/IContextFetchingStrategy.cjs +15 -0
- package/dist/strategies/context/IContextFetchingStrategy.cjs.map +1 -0
- package/dist/strategies/context/IContextFetchingStrategy.d.ts +17 -0
- package/dist/strategies/context/IContextFetchingStrategy.d.ts.map +1 -0
- package/dist/strategies/context/IContextFetchingStrategy.mjs +11 -0
- package/dist/strategies/context/IContextFetchingStrategy.mjs.map +1 -0
- package/dist/strategies/context/SimpleContextFetchingStrategy.cjs +19 -0
- package/dist/strategies/context/SimpleContextFetchingStrategy.cjs.map +1 -0
- package/dist/strategies/context/SimpleContextFetchingStrategy.d.ts +10 -0
- package/dist/strategies/context/SimpleContextFetchingStrategy.d.ts.map +1 -0
- package/dist/strategies/context/SimpleContextFetchingStrategy.mjs +15 -0
- package/dist/strategies/context/SimpleContextFetchingStrategy.mjs.map +1 -0
- package/dist/strategies/context/WorkerContextFetchingStrategy.cjs +46 -0
- package/dist/strategies/context/WorkerContextFetchingStrategy.cjs.map +1 -0
- package/dist/strategies/context/WorkerContextFetchingStrategy.d.ts +10 -0
- package/dist/strategies/context/WorkerContextFetchingStrategy.d.ts.map +1 -0
- package/dist/strategies/context/WorkerContextFetchingStrategy.mjs +42 -0
- package/dist/strategies/context/WorkerContextFetchingStrategy.mjs.map +1 -0
- package/dist/strategies/sharding/IShardingStrategy.d.ts +25 -0
- package/dist/strategies/sharding/IShardingStrategy.d.ts.map +1 -0
- package/dist/strategies/sharding/SimpleShardingStrategy.cjs +53 -0
- package/dist/strategies/sharding/SimpleShardingStrategy.cjs.map +1 -0
- package/dist/strategies/sharding/SimpleShardingStrategy.d.ts +18 -0
- package/dist/strategies/sharding/SimpleShardingStrategy.d.ts.map +1 -0
- package/dist/strategies/sharding/SimpleShardingStrategy.mjs +49 -0
- package/dist/strategies/sharding/SimpleShardingStrategy.mjs.map +1 -0
- package/dist/strategies/sharding/WorkerShardingStrategy.cjs +162 -0
- package/dist/strategies/sharding/WorkerShardingStrategy.cjs.map +1 -0
- package/dist/strategies/sharding/WorkerShardingStrategy.d.ts +84 -0
- package/dist/strategies/sharding/WorkerShardingStrategy.d.ts.map +1 -0
- package/dist/strategies/sharding/WorkerShardingStrategy.mjs +165 -0
- package/dist/strategies/sharding/WorkerShardingStrategy.mjs.map +1 -0
- package/dist/strategies/sharding/worker.cjs +78 -0
- package/dist/strategies/sharding/worker.cjs.map +1 -0
- package/dist/strategies/sharding/worker.d.ts +2 -0
- package/dist/strategies/sharding/worker.d.ts.map +1 -0
- package/dist/strategies/sharding/worker.mjs +76 -0
- package/dist/strategies/sharding/worker.mjs.map +1 -0
- package/dist/utils/IdentifyThrottler.cjs +33 -0
- package/dist/utils/IdentifyThrottler.cjs.map +1 -0
- package/dist/utils/IdentifyThrottler.d.ts +8 -0
- package/dist/utils/IdentifyThrottler.d.ts.map +1 -0
- package/dist/utils/IdentifyThrottler.mjs +29 -0
- package/dist/utils/IdentifyThrottler.mjs.map +1 -0
- package/dist/utils/constants.cjs +63 -0
- package/dist/utils/constants.cjs.map +1 -0
- package/dist/utils/constants.d.ts +21 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.mjs +64 -0
- package/dist/utils/constants.mjs.map +1 -0
- package/dist/utils/utils.cjs +15 -0
- package/dist/utils/utils.cjs.map +1 -0
- package/dist/utils/utils.d.ts +13 -0
- package/dist/utils/utils.d.ts.map +1 -0
- package/dist/utils/utils.mjs +10 -0
- package/dist/utils/utils.mjs.map +1 -0
- package/dist/ws/WebSocketManager.cjs +81 -0
- package/dist/ws/WebSocketManager.cjs.map +1 -0
- package/dist/ws/WebSocketManager.d.ts +186 -0
- package/dist/ws/WebSocketManager.d.ts.map +1 -0
- package/dist/ws/WebSocketManager.mjs +77 -0
- package/dist/ws/WebSocketManager.mjs.map +1 -0
- package/dist/ws/WebSocketShard.cjs +414 -0
- package/dist/ws/WebSocketShard.cjs.map +1 -0
- package/dist/ws/WebSocketShard.d.ts +71 -0
- package/dist/ws/WebSocketShard.d.ts.map +1 -0
- package/dist/ws/WebSocketShard.mjs +406 -0
- package/dist/ws/WebSocketShard.mjs.map +1 -0
- package/package.json +87 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.cjs","sources":["../../src/utils/utils.ts"],"sourcesContent":["import type { ShardRange } from '../ws/WebSocketManager';\n\nexport type Awaitable<T> = T | Promise<T>;\n\n/**\n * Yields the numbers in the given range as an array\n * @example\n * range({ start: 3, end: 5 }); // [3, 4, 5]\n */\nexport function range({ start, end }: ShardRange): number[] {\n\treturn Array.from({ length: end - start + 1 }, (_, i) => i + start);\n}\n\n/**\n * Lazily evaluate a callback, storing its result\n */\nexport function lazy<T>(cb: () => T): () => T {\n\tlet defaultValue: T;\n\treturn () => (defaultValue ??= cb());\n}\n"],"names":[],"mappings":";;;;AAAO,SAAS,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;AACtC,EAAE,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,GAAG,KAAK,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC;AACtE,CAAC;AACM,SAAS,IAAI,CAAC,EAAE,EAAE;AACzB,EAAE,IAAI,YAAY,CAAC;AACnB,EAAE,OAAO,MAAM,YAAY,KAAK,YAAY,GAAG,EAAE,EAAE,CAAC,CAAC;AACrD;;;;;"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ShardRange } from '../ws/WebSocketManager';
|
|
2
|
+
export declare type Awaitable<T> = T | Promise<T>;
|
|
3
|
+
/**
|
|
4
|
+
* Yields the numbers in the given range as an array
|
|
5
|
+
* @example
|
|
6
|
+
* range({ start: 3, end: 5 }); // [3, 4, 5]
|
|
7
|
+
*/
|
|
8
|
+
export declare function range({ start, end }: ShardRange): number[];
|
|
9
|
+
/**
|
|
10
|
+
* Lazily evaluate a callback, storing its result
|
|
11
|
+
*/
|
|
12
|
+
export declare function lazy<T>(cb: () => T): () => T;
|
|
13
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD,oBAAY,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAE1C;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,UAAU,GAAG,MAAM,EAAE,CAE1D;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,CAG5C"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
function range({ start, end }) {
|
|
2
|
+
return Array.from({ length: end - start + 1 }, (_, i) => i + start);
|
|
3
|
+
}
|
|
4
|
+
function lazy(cb) {
|
|
5
|
+
let defaultValue;
|
|
6
|
+
return () => defaultValue ?? (defaultValue = cb());
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export { lazy, range };
|
|
10
|
+
//# sourceMappingURL=utils.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.mjs","sources":["../../src/utils/utils.ts"],"sourcesContent":["import type { ShardRange } from '../ws/WebSocketManager';\n\nexport type Awaitable<T> = T | Promise<T>;\n\n/**\n * Yields the numbers in the given range as an array\n * @example\n * range({ start: 3, end: 5 }); // [3, 4, 5]\n */\nexport function range({ start, end }: ShardRange): number[] {\n\treturn Array.from({ length: end - start + 1 }, (_, i) => i + start);\n}\n\n/**\n * Lazily evaluate a callback, storing its result\n */\nexport function lazy<T>(cb: () => T): () => T {\n\tlet defaultValue: T;\n\treturn () => (defaultValue ??= cb());\n}\n"],"names":[],"mappings":"AAAO,SAAS,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;AACtC,EAAE,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,GAAG,KAAK,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC;AACtE,CAAC;AACM,SAAS,IAAI,CAAC,EAAE,EAAE;AACzB,EAAE,IAAI,YAAY,CAAC;AACnB,EAAE,OAAO,MAAM,YAAY,KAAK,YAAY,GAAG,EAAE,EAAE,CAAC,CAAC;AACrD;;;;"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
const async_event_emitter = require('@vladfrangu/async_event_emitter');
|
|
6
|
+
const v10 = require('discord-api-types/v10');
|
|
7
|
+
const SimpleShardingStrategy = require('../strategies/sharding/SimpleShardingStrategy.cjs');
|
|
8
|
+
const constants = require('../utils/constants.cjs');
|
|
9
|
+
const utils = require('../utils/utils.cjs');
|
|
10
|
+
|
|
11
|
+
class WebSocketManager extends async_event_emitter.AsyncEventEmitter {
|
|
12
|
+
constructor(options) {
|
|
13
|
+
super();
|
|
14
|
+
this.gatewayInformation = null;
|
|
15
|
+
this.shardIds = null;
|
|
16
|
+
this.strategy = new SimpleShardingStrategy.SimpleShardingStrategy(this);
|
|
17
|
+
this.options = { ...constants.DefaultWebSocketManagerOptions, ...options };
|
|
18
|
+
}
|
|
19
|
+
setStrategy(strategy) {
|
|
20
|
+
this.strategy = strategy;
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
async fetchGatewayInformation(force = false) {
|
|
24
|
+
if (this.gatewayInformation) {
|
|
25
|
+
if (this.gatewayInformation.expiresAt <= Date.now()) {
|
|
26
|
+
this.gatewayInformation = null;
|
|
27
|
+
} else if (!force) {
|
|
28
|
+
return this.gatewayInformation.data;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const data = await this.options.rest.get(v10.Routes.gatewayBot());
|
|
32
|
+
this.gatewayInformation = { data, expiresAt: Date.now() + data.session_start_limit.reset_after };
|
|
33
|
+
return this.gatewayInformation.data;
|
|
34
|
+
}
|
|
35
|
+
async updateShardCount(shardCount) {
|
|
36
|
+
await this.strategy.destroy({ reason: "User is adjusting their shards" });
|
|
37
|
+
this.options.shardCount = shardCount;
|
|
38
|
+
const shardIds = await this.getShardIds(true);
|
|
39
|
+
await this.strategy.spawn(shardIds);
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
async getShardCount() {
|
|
43
|
+
if (this.options.shardCount) {
|
|
44
|
+
return this.options.shardCount;
|
|
45
|
+
}
|
|
46
|
+
const shardIds = await this.getShardIds();
|
|
47
|
+
return Math.max(...shardIds) + 1;
|
|
48
|
+
}
|
|
49
|
+
async getShardIds(force = false) {
|
|
50
|
+
if (this.shardIds && !force) {
|
|
51
|
+
return this.shardIds;
|
|
52
|
+
}
|
|
53
|
+
let shardIds;
|
|
54
|
+
if (this.options.shardIds) {
|
|
55
|
+
if (Array.isArray(this.options.shardIds)) {
|
|
56
|
+
shardIds = this.options.shardIds;
|
|
57
|
+
} else {
|
|
58
|
+
shardIds = utils.range(this.options.shardIds);
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
const data = await this.fetchGatewayInformation();
|
|
62
|
+
shardIds = utils.range({ start: 0, end: (this.options.shardCount ?? data.shards) - 1 });
|
|
63
|
+
}
|
|
64
|
+
this.shardIds = shardIds;
|
|
65
|
+
return shardIds;
|
|
66
|
+
}
|
|
67
|
+
async connect() {
|
|
68
|
+
const shardCount = await this.getShardCount();
|
|
69
|
+
await this.updateShardCount(shardCount);
|
|
70
|
+
await this.strategy.connect();
|
|
71
|
+
}
|
|
72
|
+
destroy(options) {
|
|
73
|
+
return this.strategy.destroy(options);
|
|
74
|
+
}
|
|
75
|
+
send(shardId, payload) {
|
|
76
|
+
return this.strategy.send(shardId, payload);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
exports.WebSocketManager = WebSocketManager;
|
|
81
|
+
//# sourceMappingURL=WebSocketManager.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebSocketManager.cjs","sources":["../../src/ws/WebSocketManager.ts"],"sourcesContent":["import type { REST } from '@discordjs/rest';\nimport { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';\nimport {\n\tAPIGatewayBotInfo,\n\tGatewayIdentifyProperties,\n\tGatewayPresenceUpdateData,\n\tRESTGetAPIGatewayBotResult,\n\tGatewayIntentBits,\n\tRoutes,\n\tGatewaySendPayload,\n} from 'discord-api-types/v10';\nimport type { WebSocketShardDestroyOptions, WebSocketShardEventsMap } from './WebSocketShard';\nimport type { IShardingStrategy } from '../strategies/sharding/IShardingStrategy';\nimport { SimpleShardingStrategy } from '../strategies/sharding/SimpleShardingStrategy';\nimport { CompressionMethod, DefaultWebSocketManagerOptions, Encoding } from '../utils/constants';\nimport { Awaitable, range } from '../utils/utils';\n\n/**\n * Represents a range of shard ids\n */\nexport interface ShardRange {\n\tstart: number;\n\tend: number;\n}\n\n/**\n * Session information for a given shard, used to resume a session\n */\nexport interface SessionInfo {\n\t/**\n\t * Session id for this shard\n\t */\n\tsessionId: string;\n\t/**\n\t * The sequence number of the last message sent by the shard\n\t */\n\tsequence: number;\n\t/**\n\t * The id of the shard\n\t */\n\tshardId: number;\n\t/**\n\t * The total number of shards at the time of this shard identifying\n\t */\n\tshardCount: number;\n}\n\n/**\n * Required options for the WebSocketManager\n */\nexport interface RequiredWebSocketManagerOptions {\n\t/**\n\t * The token to use for identifying with the gateway\n\t */\n\ttoken: string;\n\t/**\n\t * The intents to request\n\t */\n\tintents: GatewayIntentBits;\n\t/**\n\t * The REST instance to use for fetching gateway information\n\t */\n\trest: REST;\n}\n\n/**\n * Optional additional configuration for the WebSocketManager\n */\nexport interface OptionalWebSocketManagerOptions {\n\t/**\n\t * The total number of shards across all WebsocketManagers you intend to instantiate.\n\t * Use `null` to use Discord's recommended shard count\n\t */\n\tshardCount: number | null;\n\t/**\n\t * The ids of the shards this WebSocketManager should manage.\n\t * Use `null` to simply spawn 0 through `shardCount - 1`\n\t * @example\n\t * const manager = new WebSocketManager({\n\t * shardIds: [1, 3, 7], // spawns shard 1, 3, and 7, nothing else\n\t * });\n\t * @example\n\t * const manager = new WebSocketManager({\n\t * shardIds: {\n\t * start: 3,\n\t * end: 6,\n\t * }, // spawns shards 3, 4, 5, and 6\n\t * });\n\t */\n\tshardIds: number[] | ShardRange | null;\n\t/**\n\t * Value between 50 and 250, total number of members where the gateway will stop sending offline members in the guild member list\n\t */\n\tlargeThreshold: number | null;\n\t/**\n\t * Initial presence data to send to the gateway when identifying\n\t */\n\tinitialPresence: GatewayPresenceUpdateData | null;\n\t/**\n\t * Properties to send to the gateway when identifying\n\t */\n\tidentifyProperties: GatewayIdentifyProperties;\n\t/**\n\t * The gateway version to use\n\t * @default '10'\n\t */\n\tversion: string;\n\t/**\n\t * The encoding to use\n\t * @default 'json'\n\t */\n\tencoding: Encoding;\n\t/**\n\t * The compression method to use\n\t * @default null (no compression)\n\t */\n\tcompression: CompressionMethod | null;\n\t/**\n\t * Function used to retrieve session information (and attempt to resume) for a given shard\n\t * @example\n\t * const manager = new WebSocketManager({\n\t * async retrieveSessionInfo(shardId): Awaitable<SessionInfo | null> {\n\t * // Fetch this info from redis or similar\n\t * return { sessionId: string, sequence: number };\n\t * // Return null if no information is found\n\t * },\n\t * });\n\t */\n\tretrieveSessionInfo: (shardId: number) => Awaitable<SessionInfo | null>;\n\t/**\n\t * Function used to store session information for a given shard\n\t */\n\tupdateSessionInfo: (shardId: number, sessionInfo: SessionInfo | null) => Awaitable<void>;\n\t/**\n\t * How long to wait for a shard to connect before giving up\n\t */\n\thandshakeTimeout: number | null;\n\t/**\n\t * How long to wait for a shard's HELLO packet before giving up\n\t */\n\thelloTimeout: number | null;\n\t/**\n\t * How long to wait for a shard's READY packet before giving up\n\t */\n\treadyTimeout: number | null;\n}\n\nexport type WebSocketManagerOptions = RequiredWebSocketManagerOptions & OptionalWebSocketManagerOptions;\n\nexport type ManagerShardEventsMap = {\n\t[K in keyof WebSocketShardEventsMap]: [\n\t\tWebSocketShardEventsMap[K] extends [] ? { shardId: number } : WebSocketShardEventsMap[K][0] & { shardId: number },\n\t];\n};\n\nexport class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> {\n\t/**\n\t * The options being used by this manager\n\t */\n\tpublic readonly options: WebSocketManagerOptions;\n\n\t/**\n\t * Internal cache for a GET /gateway/bot result\n\t */\n\tprivate gatewayInformation: {\n\t\tdata: APIGatewayBotInfo;\n\t\texpiresAt: number;\n\t} | null = null;\n\n\t/**\n\t * Internal cache for the shard ids\n\t */\n\tprivate shardIds: number[] | null = null;\n\n\t/**\n\t * Strategy used to manage shards\n\t * @default SimpleManagerToShardStrategy\n\t */\n\tprivate strategy: IShardingStrategy = new SimpleShardingStrategy(this);\n\n\tpublic constructor(options: RequiredWebSocketManagerOptions & Partial<OptionalWebSocketManagerOptions>) {\n\t\tsuper();\n\t\tthis.options = { ...DefaultWebSocketManagerOptions, ...options };\n\t}\n\n\tpublic setStrategy(strategy: IShardingStrategy) {\n\t\tthis.strategy = strategy;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Fetches the gateway information from Discord - or returns it from cache if available\n\t * @param force Whether to ignore the cache and force a fresh fetch\n\t */\n\tpublic async fetchGatewayInformation(force = false) {\n\t\tif (this.gatewayInformation) {\n\t\t\tif (this.gatewayInformation.expiresAt <= Date.now()) {\n\t\t\t\tthis.gatewayInformation = null;\n\t\t\t} else if (!force) {\n\t\t\t\treturn this.gatewayInformation.data;\n\t\t\t}\n\t\t}\n\n\t\tconst data = (await this.options.rest.get(Routes.gatewayBot())) as RESTGetAPIGatewayBotResult;\n\n\t\tthis.gatewayInformation = { data, expiresAt: Date.now() + data.session_start_limit.reset_after };\n\t\treturn this.gatewayInformation.data;\n\t}\n\n\t/**\n\t * Updates your total shard count on-the-fly, spawning shards as needed\n\t * @param shardCount The new shard count to use\n\t */\n\tpublic async updateShardCount(shardCount: number | null) {\n\t\tawait this.strategy.destroy({ reason: 'User is adjusting their shards' });\n\t\tthis.options.shardCount = shardCount;\n\n\t\tconst shardIds = await this.getShardIds(true);\n\t\tawait this.strategy.spawn(shardIds);\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Yields the total number of shards across for your bot, accounting for Discord recommendations\n\t */\n\tpublic async getShardCount(): Promise<number> {\n\t\tif (this.options.shardCount) {\n\t\t\treturn this.options.shardCount;\n\t\t}\n\n\t\tconst shardIds = await this.getShardIds();\n\t\treturn Math.max(...shardIds) + 1;\n\t}\n\n\t/**\n\t * Yields the ids of the shards this manager should manage\n\t */\n\tpublic async getShardIds(force = false): Promise<number[]> {\n\t\tif (this.shardIds && !force) {\n\t\t\treturn this.shardIds;\n\t\t}\n\n\t\tlet shardIds: number[];\n\t\tif (this.options.shardIds) {\n\t\t\tif (Array.isArray(this.options.shardIds)) {\n\t\t\t\tshardIds = this.options.shardIds;\n\t\t\t} else {\n\t\t\t\tshardIds = range(this.options.shardIds);\n\t\t\t}\n\t\t} else {\n\t\t\tconst data = await this.fetchGatewayInformation();\n\t\t\tshardIds = range({ start: 0, end: (this.options.shardCount ?? data.shards) - 1 });\n\t\t}\n\n\t\tthis.shardIds = shardIds;\n\t\treturn shardIds;\n\t}\n\n\tpublic async connect() {\n\t\tconst shardCount = await this.getShardCount();\n\t\t// First, make sure all our shards are spawned\n\t\tawait this.updateShardCount(shardCount);\n\t\tawait this.strategy.connect();\n\t}\n\n\tpublic destroy(options?: Omit<WebSocketShardDestroyOptions, 'recover'>) {\n\t\treturn this.strategy.destroy(options);\n\t}\n\n\tpublic send(shardId: number, payload: GatewaySendPayload) {\n\t\treturn this.strategy.send(shardId, payload);\n\t}\n}\n"],"names":["AsyncEventEmitter","SimpleShardingStrategy","DefaultWebSocketManagerOptions","Routes","range"],"mappings":"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"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import type { REST } from '@discordjs/rest';
|
|
2
|
+
import { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';
|
|
3
|
+
import { APIGatewayBotInfo, GatewayIdentifyProperties, GatewayPresenceUpdateData, GatewayIntentBits, GatewaySendPayload } from 'discord-api-types/v10';
|
|
4
|
+
import type { WebSocketShardDestroyOptions, WebSocketShardEventsMap } from './WebSocketShard';
|
|
5
|
+
import type { IShardingStrategy } from '../strategies/sharding/IShardingStrategy';
|
|
6
|
+
import { CompressionMethod, Encoding } from '../utils/constants';
|
|
7
|
+
import { Awaitable } from '../utils/utils';
|
|
8
|
+
/**
|
|
9
|
+
* Represents a range of shard ids
|
|
10
|
+
*/
|
|
11
|
+
export interface ShardRange {
|
|
12
|
+
start: number;
|
|
13
|
+
end: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Session information for a given shard, used to resume a session
|
|
17
|
+
*/
|
|
18
|
+
export interface SessionInfo {
|
|
19
|
+
/**
|
|
20
|
+
* Session id for this shard
|
|
21
|
+
*/
|
|
22
|
+
sessionId: string;
|
|
23
|
+
/**
|
|
24
|
+
* The sequence number of the last message sent by the shard
|
|
25
|
+
*/
|
|
26
|
+
sequence: number;
|
|
27
|
+
/**
|
|
28
|
+
* The id of the shard
|
|
29
|
+
*/
|
|
30
|
+
shardId: number;
|
|
31
|
+
/**
|
|
32
|
+
* The total number of shards at the time of this shard identifying
|
|
33
|
+
*/
|
|
34
|
+
shardCount: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Required options for the WebSocketManager
|
|
38
|
+
*/
|
|
39
|
+
export interface RequiredWebSocketManagerOptions {
|
|
40
|
+
/**
|
|
41
|
+
* The token to use for identifying with the gateway
|
|
42
|
+
*/
|
|
43
|
+
token: string;
|
|
44
|
+
/**
|
|
45
|
+
* The intents to request
|
|
46
|
+
*/
|
|
47
|
+
intents: GatewayIntentBits;
|
|
48
|
+
/**
|
|
49
|
+
* The REST instance to use for fetching gateway information
|
|
50
|
+
*/
|
|
51
|
+
rest: REST;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Optional additional configuration for the WebSocketManager
|
|
55
|
+
*/
|
|
56
|
+
export interface OptionalWebSocketManagerOptions {
|
|
57
|
+
/**
|
|
58
|
+
* The total number of shards across all WebsocketManagers you intend to instantiate.
|
|
59
|
+
* Use `null` to use Discord's recommended shard count
|
|
60
|
+
*/
|
|
61
|
+
shardCount: number | null;
|
|
62
|
+
/**
|
|
63
|
+
* The ids of the shards this WebSocketManager should manage.
|
|
64
|
+
* Use `null` to simply spawn 0 through `shardCount - 1`
|
|
65
|
+
* @example
|
|
66
|
+
* const manager = new WebSocketManager({
|
|
67
|
+
* shardIds: [1, 3, 7], // spawns shard 1, 3, and 7, nothing else
|
|
68
|
+
* });
|
|
69
|
+
* @example
|
|
70
|
+
* const manager = new WebSocketManager({
|
|
71
|
+
* shardIds: {
|
|
72
|
+
* start: 3,
|
|
73
|
+
* end: 6,
|
|
74
|
+
* }, // spawns shards 3, 4, 5, and 6
|
|
75
|
+
* });
|
|
76
|
+
*/
|
|
77
|
+
shardIds: number[] | ShardRange | null;
|
|
78
|
+
/**
|
|
79
|
+
* Value between 50 and 250, total number of members where the gateway will stop sending offline members in the guild member list
|
|
80
|
+
*/
|
|
81
|
+
largeThreshold: number | null;
|
|
82
|
+
/**
|
|
83
|
+
* Initial presence data to send to the gateway when identifying
|
|
84
|
+
*/
|
|
85
|
+
initialPresence: GatewayPresenceUpdateData | null;
|
|
86
|
+
/**
|
|
87
|
+
* Properties to send to the gateway when identifying
|
|
88
|
+
*/
|
|
89
|
+
identifyProperties: GatewayIdentifyProperties;
|
|
90
|
+
/**
|
|
91
|
+
* The gateway version to use
|
|
92
|
+
* @default '10'
|
|
93
|
+
*/
|
|
94
|
+
version: string;
|
|
95
|
+
/**
|
|
96
|
+
* The encoding to use
|
|
97
|
+
* @default 'json'
|
|
98
|
+
*/
|
|
99
|
+
encoding: Encoding;
|
|
100
|
+
/**
|
|
101
|
+
* The compression method to use
|
|
102
|
+
* @default null (no compression)
|
|
103
|
+
*/
|
|
104
|
+
compression: CompressionMethod | null;
|
|
105
|
+
/**
|
|
106
|
+
* Function used to retrieve session information (and attempt to resume) for a given shard
|
|
107
|
+
* @example
|
|
108
|
+
* const manager = new WebSocketManager({
|
|
109
|
+
* async retrieveSessionInfo(shardId): Awaitable<SessionInfo | null> {
|
|
110
|
+
* // Fetch this info from redis or similar
|
|
111
|
+
* return { sessionId: string, sequence: number };
|
|
112
|
+
* // Return null if no information is found
|
|
113
|
+
* },
|
|
114
|
+
* });
|
|
115
|
+
*/
|
|
116
|
+
retrieveSessionInfo: (shardId: number) => Awaitable<SessionInfo | null>;
|
|
117
|
+
/**
|
|
118
|
+
* Function used to store session information for a given shard
|
|
119
|
+
*/
|
|
120
|
+
updateSessionInfo: (shardId: number, sessionInfo: SessionInfo | null) => Awaitable<void>;
|
|
121
|
+
/**
|
|
122
|
+
* How long to wait for a shard to connect before giving up
|
|
123
|
+
*/
|
|
124
|
+
handshakeTimeout: number | null;
|
|
125
|
+
/**
|
|
126
|
+
* How long to wait for a shard's HELLO packet before giving up
|
|
127
|
+
*/
|
|
128
|
+
helloTimeout: number | null;
|
|
129
|
+
/**
|
|
130
|
+
* How long to wait for a shard's READY packet before giving up
|
|
131
|
+
*/
|
|
132
|
+
readyTimeout: number | null;
|
|
133
|
+
}
|
|
134
|
+
export declare type WebSocketManagerOptions = RequiredWebSocketManagerOptions & OptionalWebSocketManagerOptions;
|
|
135
|
+
export declare type ManagerShardEventsMap = {
|
|
136
|
+
[K in keyof WebSocketShardEventsMap]: [
|
|
137
|
+
WebSocketShardEventsMap[K] extends [] ? {
|
|
138
|
+
shardId: number;
|
|
139
|
+
} : WebSocketShardEventsMap[K][0] & {
|
|
140
|
+
shardId: number;
|
|
141
|
+
}
|
|
142
|
+
];
|
|
143
|
+
};
|
|
144
|
+
export declare class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> {
|
|
145
|
+
/**
|
|
146
|
+
* The options being used by this manager
|
|
147
|
+
*/
|
|
148
|
+
readonly options: WebSocketManagerOptions;
|
|
149
|
+
/**
|
|
150
|
+
* Internal cache for a GET /gateway/bot result
|
|
151
|
+
*/
|
|
152
|
+
private gatewayInformation;
|
|
153
|
+
/**
|
|
154
|
+
* Internal cache for the shard ids
|
|
155
|
+
*/
|
|
156
|
+
private shardIds;
|
|
157
|
+
/**
|
|
158
|
+
* Strategy used to manage shards
|
|
159
|
+
* @default SimpleManagerToShardStrategy
|
|
160
|
+
*/
|
|
161
|
+
private strategy;
|
|
162
|
+
constructor(options: RequiredWebSocketManagerOptions & Partial<OptionalWebSocketManagerOptions>);
|
|
163
|
+
setStrategy(strategy: IShardingStrategy): this;
|
|
164
|
+
/**
|
|
165
|
+
* Fetches the gateway information from Discord - or returns it from cache if available
|
|
166
|
+
* @param force Whether to ignore the cache and force a fresh fetch
|
|
167
|
+
*/
|
|
168
|
+
fetchGatewayInformation(force?: boolean): Promise<APIGatewayBotInfo>;
|
|
169
|
+
/**
|
|
170
|
+
* Updates your total shard count on-the-fly, spawning shards as needed
|
|
171
|
+
* @param shardCount The new shard count to use
|
|
172
|
+
*/
|
|
173
|
+
updateShardCount(shardCount: number | null): Promise<this>;
|
|
174
|
+
/**
|
|
175
|
+
* Yields the total number of shards across for your bot, accounting for Discord recommendations
|
|
176
|
+
*/
|
|
177
|
+
getShardCount(): Promise<number>;
|
|
178
|
+
/**
|
|
179
|
+
* Yields the ids of the shards this manager should manage
|
|
180
|
+
*/
|
|
181
|
+
getShardIds(force?: boolean): Promise<number[]>;
|
|
182
|
+
connect(): Promise<void>;
|
|
183
|
+
destroy(options?: Omit<WebSocketShardDestroyOptions, 'recover'>): Awaitable<void>;
|
|
184
|
+
send(shardId: number, payload: GatewaySendPayload): Awaitable<void>;
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=WebSocketManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebSocketManager.d.ts","sourceRoot":"","sources":["../../src/ws/WebSocketManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';
|
|
2
|
+
import { Routes } from 'discord-api-types/v10';
|
|
3
|
+
import { SimpleShardingStrategy } from '../strategies/sharding/SimpleShardingStrategy.mjs';
|
|
4
|
+
import { DefaultWebSocketManagerOptions } from '../utils/constants.mjs';
|
|
5
|
+
import { range } from '../utils/utils.mjs';
|
|
6
|
+
|
|
7
|
+
class WebSocketManager extends AsyncEventEmitter {
|
|
8
|
+
constructor(options) {
|
|
9
|
+
super();
|
|
10
|
+
this.gatewayInformation = null;
|
|
11
|
+
this.shardIds = null;
|
|
12
|
+
this.strategy = new SimpleShardingStrategy(this);
|
|
13
|
+
this.options = { ...DefaultWebSocketManagerOptions, ...options };
|
|
14
|
+
}
|
|
15
|
+
setStrategy(strategy) {
|
|
16
|
+
this.strategy = strategy;
|
|
17
|
+
return this;
|
|
18
|
+
}
|
|
19
|
+
async fetchGatewayInformation(force = false) {
|
|
20
|
+
if (this.gatewayInformation) {
|
|
21
|
+
if (this.gatewayInformation.expiresAt <= Date.now()) {
|
|
22
|
+
this.gatewayInformation = null;
|
|
23
|
+
} else if (!force) {
|
|
24
|
+
return this.gatewayInformation.data;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const data = await this.options.rest.get(Routes.gatewayBot());
|
|
28
|
+
this.gatewayInformation = { data, expiresAt: Date.now() + data.session_start_limit.reset_after };
|
|
29
|
+
return this.gatewayInformation.data;
|
|
30
|
+
}
|
|
31
|
+
async updateShardCount(shardCount) {
|
|
32
|
+
await this.strategy.destroy({ reason: "User is adjusting their shards" });
|
|
33
|
+
this.options.shardCount = shardCount;
|
|
34
|
+
const shardIds = await this.getShardIds(true);
|
|
35
|
+
await this.strategy.spawn(shardIds);
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
async getShardCount() {
|
|
39
|
+
if (this.options.shardCount) {
|
|
40
|
+
return this.options.shardCount;
|
|
41
|
+
}
|
|
42
|
+
const shardIds = await this.getShardIds();
|
|
43
|
+
return Math.max(...shardIds) + 1;
|
|
44
|
+
}
|
|
45
|
+
async getShardIds(force = false) {
|
|
46
|
+
if (this.shardIds && !force) {
|
|
47
|
+
return this.shardIds;
|
|
48
|
+
}
|
|
49
|
+
let shardIds;
|
|
50
|
+
if (this.options.shardIds) {
|
|
51
|
+
if (Array.isArray(this.options.shardIds)) {
|
|
52
|
+
shardIds = this.options.shardIds;
|
|
53
|
+
} else {
|
|
54
|
+
shardIds = range(this.options.shardIds);
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
const data = await this.fetchGatewayInformation();
|
|
58
|
+
shardIds = range({ start: 0, end: (this.options.shardCount ?? data.shards) - 1 });
|
|
59
|
+
}
|
|
60
|
+
this.shardIds = shardIds;
|
|
61
|
+
return shardIds;
|
|
62
|
+
}
|
|
63
|
+
async connect() {
|
|
64
|
+
const shardCount = await this.getShardCount();
|
|
65
|
+
await this.updateShardCount(shardCount);
|
|
66
|
+
await this.strategy.connect();
|
|
67
|
+
}
|
|
68
|
+
destroy(options) {
|
|
69
|
+
return this.strategy.destroy(options);
|
|
70
|
+
}
|
|
71
|
+
send(shardId, payload) {
|
|
72
|
+
return this.strategy.send(shardId, payload);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export { WebSocketManager };
|
|
77
|
+
//# sourceMappingURL=WebSocketManager.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebSocketManager.mjs","sources":["../../src/ws/WebSocketManager.ts"],"sourcesContent":["import type { REST } from '@discordjs/rest';\nimport { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';\nimport {\n\tAPIGatewayBotInfo,\n\tGatewayIdentifyProperties,\n\tGatewayPresenceUpdateData,\n\tRESTGetAPIGatewayBotResult,\n\tGatewayIntentBits,\n\tRoutes,\n\tGatewaySendPayload,\n} from 'discord-api-types/v10';\nimport type { WebSocketShardDestroyOptions, WebSocketShardEventsMap } from './WebSocketShard';\nimport type { IShardingStrategy } from '../strategies/sharding/IShardingStrategy';\nimport { SimpleShardingStrategy } from '../strategies/sharding/SimpleShardingStrategy';\nimport { CompressionMethod, DefaultWebSocketManagerOptions, Encoding } from '../utils/constants';\nimport { Awaitable, range } from '../utils/utils';\n\n/**\n * Represents a range of shard ids\n */\nexport interface ShardRange {\n\tstart: number;\n\tend: number;\n}\n\n/**\n * Session information for a given shard, used to resume a session\n */\nexport interface SessionInfo {\n\t/**\n\t * Session id for this shard\n\t */\n\tsessionId: string;\n\t/**\n\t * The sequence number of the last message sent by the shard\n\t */\n\tsequence: number;\n\t/**\n\t * The id of the shard\n\t */\n\tshardId: number;\n\t/**\n\t * The total number of shards at the time of this shard identifying\n\t */\n\tshardCount: number;\n}\n\n/**\n * Required options for the WebSocketManager\n */\nexport interface RequiredWebSocketManagerOptions {\n\t/**\n\t * The token to use for identifying with the gateway\n\t */\n\ttoken: string;\n\t/**\n\t * The intents to request\n\t */\n\tintents: GatewayIntentBits;\n\t/**\n\t * The REST instance to use for fetching gateway information\n\t */\n\trest: REST;\n}\n\n/**\n * Optional additional configuration for the WebSocketManager\n */\nexport interface OptionalWebSocketManagerOptions {\n\t/**\n\t * The total number of shards across all WebsocketManagers you intend to instantiate.\n\t * Use `null` to use Discord's recommended shard count\n\t */\n\tshardCount: number | null;\n\t/**\n\t * The ids of the shards this WebSocketManager should manage.\n\t * Use `null` to simply spawn 0 through `shardCount - 1`\n\t * @example\n\t * const manager = new WebSocketManager({\n\t * shardIds: [1, 3, 7], // spawns shard 1, 3, and 7, nothing else\n\t * });\n\t * @example\n\t * const manager = new WebSocketManager({\n\t * shardIds: {\n\t * start: 3,\n\t * end: 6,\n\t * }, // spawns shards 3, 4, 5, and 6\n\t * });\n\t */\n\tshardIds: number[] | ShardRange | null;\n\t/**\n\t * Value between 50 and 250, total number of members where the gateway will stop sending offline members in the guild member list\n\t */\n\tlargeThreshold: number | null;\n\t/**\n\t * Initial presence data to send to the gateway when identifying\n\t */\n\tinitialPresence: GatewayPresenceUpdateData | null;\n\t/**\n\t * Properties to send to the gateway when identifying\n\t */\n\tidentifyProperties: GatewayIdentifyProperties;\n\t/**\n\t * The gateway version to use\n\t * @default '10'\n\t */\n\tversion: string;\n\t/**\n\t * The encoding to use\n\t * @default 'json'\n\t */\n\tencoding: Encoding;\n\t/**\n\t * The compression method to use\n\t * @default null (no compression)\n\t */\n\tcompression: CompressionMethod | null;\n\t/**\n\t * Function used to retrieve session information (and attempt to resume) for a given shard\n\t * @example\n\t * const manager = new WebSocketManager({\n\t * async retrieveSessionInfo(shardId): Awaitable<SessionInfo | null> {\n\t * // Fetch this info from redis or similar\n\t * return { sessionId: string, sequence: number };\n\t * // Return null if no information is found\n\t * },\n\t * });\n\t */\n\tretrieveSessionInfo: (shardId: number) => Awaitable<SessionInfo | null>;\n\t/**\n\t * Function used to store session information for a given shard\n\t */\n\tupdateSessionInfo: (shardId: number, sessionInfo: SessionInfo | null) => Awaitable<void>;\n\t/**\n\t * How long to wait for a shard to connect before giving up\n\t */\n\thandshakeTimeout: number | null;\n\t/**\n\t * How long to wait for a shard's HELLO packet before giving up\n\t */\n\thelloTimeout: number | null;\n\t/**\n\t * How long to wait for a shard's READY packet before giving up\n\t */\n\treadyTimeout: number | null;\n}\n\nexport type WebSocketManagerOptions = RequiredWebSocketManagerOptions & OptionalWebSocketManagerOptions;\n\nexport type ManagerShardEventsMap = {\n\t[K in keyof WebSocketShardEventsMap]: [\n\t\tWebSocketShardEventsMap[K] extends [] ? { shardId: number } : WebSocketShardEventsMap[K][0] & { shardId: number },\n\t];\n};\n\nexport class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> {\n\t/**\n\t * The options being used by this manager\n\t */\n\tpublic readonly options: WebSocketManagerOptions;\n\n\t/**\n\t * Internal cache for a GET /gateway/bot result\n\t */\n\tprivate gatewayInformation: {\n\t\tdata: APIGatewayBotInfo;\n\t\texpiresAt: number;\n\t} | null = null;\n\n\t/**\n\t * Internal cache for the shard ids\n\t */\n\tprivate shardIds: number[] | null = null;\n\n\t/**\n\t * Strategy used to manage shards\n\t * @default SimpleManagerToShardStrategy\n\t */\n\tprivate strategy: IShardingStrategy = new SimpleShardingStrategy(this);\n\n\tpublic constructor(options: RequiredWebSocketManagerOptions & Partial<OptionalWebSocketManagerOptions>) {\n\t\tsuper();\n\t\tthis.options = { ...DefaultWebSocketManagerOptions, ...options };\n\t}\n\n\tpublic setStrategy(strategy: IShardingStrategy) {\n\t\tthis.strategy = strategy;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Fetches the gateway information from Discord - or returns it from cache if available\n\t * @param force Whether to ignore the cache and force a fresh fetch\n\t */\n\tpublic async fetchGatewayInformation(force = false) {\n\t\tif (this.gatewayInformation) {\n\t\t\tif (this.gatewayInformation.expiresAt <= Date.now()) {\n\t\t\t\tthis.gatewayInformation = null;\n\t\t\t} else if (!force) {\n\t\t\t\treturn this.gatewayInformation.data;\n\t\t\t}\n\t\t}\n\n\t\tconst data = (await this.options.rest.get(Routes.gatewayBot())) as RESTGetAPIGatewayBotResult;\n\n\t\tthis.gatewayInformation = { data, expiresAt: Date.now() + data.session_start_limit.reset_after };\n\t\treturn this.gatewayInformation.data;\n\t}\n\n\t/**\n\t * Updates your total shard count on-the-fly, spawning shards as needed\n\t * @param shardCount The new shard count to use\n\t */\n\tpublic async updateShardCount(shardCount: number | null) {\n\t\tawait this.strategy.destroy({ reason: 'User is adjusting their shards' });\n\t\tthis.options.shardCount = shardCount;\n\n\t\tconst shardIds = await this.getShardIds(true);\n\t\tawait this.strategy.spawn(shardIds);\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Yields the total number of shards across for your bot, accounting for Discord recommendations\n\t */\n\tpublic async getShardCount(): Promise<number> {\n\t\tif (this.options.shardCount) {\n\t\t\treturn this.options.shardCount;\n\t\t}\n\n\t\tconst shardIds = await this.getShardIds();\n\t\treturn Math.max(...shardIds) + 1;\n\t}\n\n\t/**\n\t * Yields the ids of the shards this manager should manage\n\t */\n\tpublic async getShardIds(force = false): Promise<number[]> {\n\t\tif (this.shardIds && !force) {\n\t\t\treturn this.shardIds;\n\t\t}\n\n\t\tlet shardIds: number[];\n\t\tif (this.options.shardIds) {\n\t\t\tif (Array.isArray(this.options.shardIds)) {\n\t\t\t\tshardIds = this.options.shardIds;\n\t\t\t} else {\n\t\t\t\tshardIds = range(this.options.shardIds);\n\t\t\t}\n\t\t} else {\n\t\t\tconst data = await this.fetchGatewayInformation();\n\t\t\tshardIds = range({ start: 0, end: (this.options.shardCount ?? data.shards) - 1 });\n\t\t}\n\n\t\tthis.shardIds = shardIds;\n\t\treturn shardIds;\n\t}\n\n\tpublic async connect() {\n\t\tconst shardCount = await this.getShardCount();\n\t\t// First, make sure all our shards are spawned\n\t\tawait this.updateShardCount(shardCount);\n\t\tawait this.strategy.connect();\n\t}\n\n\tpublic destroy(options?: Omit<WebSocketShardDestroyOptions, 'recover'>) {\n\t\treturn this.strategy.destroy(options);\n\t}\n\n\tpublic send(shardId: number, payload: GatewaySendPayload) {\n\t\treturn this.strategy.send(shardId, payload);\n\t}\n}\n"],"names":[],"mappings":"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"}
|