import type { Worker } from 'node:worker_threads'; import { createGatewayManager, createLogger, createRestManager } from '@discordeno/bot'; import { DISCORD_TOKEN, GATEWAY_INTENTS, REST_AUTHORIZATION, REST_URL, SHARDS_PER_WORKER, TOTAL_SHARDS, TOTAL_WORKERS } from '../config.js'; import { promiseWithResolvers } from '../util.js'; import { createWorker } from './worker/createWorker.js'; import type { ManagerMessage, WorkerMessage } from './worker/types.js'; export const workers = new Map(); export const logger = createLogger({ name: 'GATEWAY' }); const restManager = createRestManager({ token: DISCORD_TOKEN, proxy: { baseUrl: REST_URL, authorization: REST_AUTHORIZATION, }, }); const gatewayBotConfig = await restManager.getGatewayBot(); const gatewayManager = createGatewayManager({ token: DISCORD_TOKEN, intents: GATEWAY_INTENTS, connection: gatewayBotConfig, shardsPerWorker: SHARDS_PER_WORKER, totalShards: TOTAL_SHARDS, totalWorkers: TOTAL_WORKERS, resharding: { getSessionInfo: restManager.getGatewayBot, }, }); gatewayManager.resharding.tellWorkerToPrepare = async (workerId, shardId, bucketId) => { logger.info(`Tell worker to prepare, workerId: ${workerId}, shardId: ${shardId}, bucketId: ${bucketId}`); let worker = workers.get(workerId); if (!worker) { worker = createWorker(workerId); workers.set(workerId, worker); } worker.postMessage({ type: 'PrepareShard', shardId, totalShards: gatewayManager.totalShards, } satisfies WorkerMessage); const { promise, resolve } = promiseWithResolvers(); const waitForShardPrepared = (message: ManagerMessage) => { if (message.type === 'ShardPrepared' && message.shardId === shardId) { resolve(); } }; worker.on('message', waitForShardPrepared); await promise; worker.off('message', waitForShardPrepared); }; gatewayManager.resharding.onReshardingSwitch = async () => { logger.info('Resharding switch triggered, telling workers to switch the shards'); for (const worker of workers.values()) { worker.postMessage({ type: 'SwitchShards', } satisfies WorkerMessage); } }; gatewayManager.tellWorkerToIdentify = async (workerId, shardId, bucketId) => { logger.info(`Tell worker to identify, workerId: ${workerId}, shardId: ${shardId}, bucketId: ${bucketId}`); const worker = workers.get(workerId) ?? createWorker(workerId); workers.set(workerId, worker); worker.postMessage({ type: 'IdentifyShard', shardId, } satisfies WorkerMessage); const { promise, resolve } = promiseWithResolvers(); const waitForShardIdentified = (message: ManagerMessage) => { if (message.type === 'ShardIdentified' && message.shardId === shardId) { resolve(); } }; worker.on('message', waitForShardIdentified); await promise; worker.off('message', waitForShardIdentified); }; gatewayManager.sendPayload = async (shardId, payload) => { const workerId = gatewayManager.calculateWorkerId(shardId); const worker = workers.get(workerId); if (!worker) return; worker.postMessage({ type: 'ShardPayload', shardId, payload, } satisfies WorkerMessage); }; gatewayManager.editBotStatus = async (payload) => { const workersArray = Array.from(workers.values()); for (const worker of workersArray) { worker.postMessage({ type: 'EditShardsPresence', payload, } satisfies WorkerMessage); } }; export default gatewayManager;