@helyx/bot 0.4.8 → 0.4.11

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.
@@ -37,6 +37,7 @@ export declare function parseShardWorkerEnvironment(processEnvironment: NodeJS.P
37
37
  HELYX_SUPPORT_STAFF_CHANNEL_ID: string | undefined;
38
38
  HELYX_BOT_CONTROL_PORT: number;
39
39
  HELYX_PRIVACY_ENABLED: boolean;
40
+ HELYX_MESSAGE_CONTENT_ENABLED: boolean;
40
41
  HELYX_CLOUD_CONNECT_ENABLED: boolean;
41
42
  HELYX_CLOUD_ENROLMENT_TOKEN: string | undefined;
42
43
  HELYX_CLOUD_ENROLMENT_FILE: string;
@@ -9,7 +9,7 @@ import { monitorEventLoopDelay } from "node:perf_hooks";
9
9
  import pino from "pino";
10
10
  import { createCloudManagementModule } from "./cloud-management-module.js";
11
11
  import { BotControlService, DiscordGuildResourceDirectory, mapBotControlError, } from "./control-server.js";
12
- import { DiscordMemberRoleService, DiscordResourceInspection, installModuleEvents, } from "./events.js";
12
+ import { DiscordMessageDeliveryService, DiscordMemberEnforcementService, DiscordMemberRoleService, DiscordResourceInspection, installModuleEvents, } from "./events.js";
13
13
  import { installGuildTracking } from "./guilds.js";
14
14
  import { installInteractions, } from "./interactions.js";
15
15
  import { createManagementModule } from "./management-module.js";
@@ -25,6 +25,8 @@ import { assertShardMessageSize, controlOperationGuildId, executeShardControlOpe
25
25
  import { gatewayIntentsForModules, readBotVersion, } from "./index.js";
26
26
  import { createSupportModule } from "./support-module.js";
27
27
  import { createLocalScheduledTaskRoute, } from "./scheduled-task-runner.js";
28
+ import { createLocalNumericWriteBehind } from "./numeric-write-behind.js";
29
+ import { WorkerNumericWriteBehindProxy } from "./numeric-write-behind-proxy.js";
28
30
  const HEALTH_INTERVAL_MS = 5_000;
29
31
  export const WORKER_ORPHAN_TERMINATION_BOUND_MS = SHARD_LEASE_HEARTBEAT_TIMEOUT_MS + 5_000;
30
32
  export function createLeaseHeartbeatWatchdog(onExpired, timeoutMs = SHARD_LEASE_HEARTBEAT_TIMEOUT_MS) {
@@ -76,6 +78,7 @@ export async function startShardWorker(processEnvironment = process.env, identit
76
78
  let shardHealthProvider;
77
79
  let leaseHeartbeatWatchdog;
78
80
  let forcedLeaseExit;
81
+ let numericWriteBehind;
79
82
  async function shutdown(reason = "programmatic") {
80
83
  if (shuttingDown)
81
84
  return;
@@ -92,6 +95,7 @@ export async function startShardWorker(processEnvironment = process.env, identit
92
95
  await moduleEvents?.stop().catch(() => undefined);
93
96
  if (runtimeStarted)
94
97
  await runtime?.stop().catch(() => undefined);
98
+ await numericWriteBehind?.stop();
95
99
  await client?.destroy();
96
100
  eventLoopDelay.disable();
97
101
  await database.end();
@@ -104,7 +108,7 @@ export async function startShardWorker(processEnvironment = process.env, identit
104
108
  const modulePackages = await loadConfiguredModulePackages(moduleSpecifiers);
105
109
  const featureModules = modulePackages.map(({ definition }) => definition);
106
110
  client = new Client({
107
- intents: gatewayIntentsForModules(featureModules),
111
+ intents: gatewayIntentsForModules(featureModules, environment.HELYX_MESSAGE_CONTENT_ENABLED),
108
112
  partials: [Partials.Message, Partials.Channel, Partials.Reaction],
109
113
  allowedMentions: { parse: [] },
110
114
  shards: [identity.shardId],
@@ -156,14 +160,27 @@ export async function startShardWorker(processEnvironment = process.env, identit
156
160
  ...publicModules,
157
161
  ...(operationsModule ? [operationsModule] : []),
158
162
  ];
159
- const platformServices = new Map(createPlatformServices(database, publicModules, platformServiceOptions(environment, publicModules)));
163
+ const platformServices = new Map();
164
+ const serviceAccess = new ServiceRegistry(platformServices);
165
+ for (const [name, service] of createPlatformServices(database, publicModules, {
166
+ ...platformServiceOptions(environment, publicModules),
167
+ activationServices: serviceAccess,
168
+ })) {
169
+ platformServices.set(name, service);
170
+ }
171
+ numericWriteBehind =
172
+ identity.scope === "coordinated"
173
+ ? new WorkerNumericWriteBehindProxy(identity)
174
+ : createLocalNumericWriteBehind(platformServices, publicModules);
175
+ platformServices.set(HELYX_SERVICE_NAMES.numericWriteBehind, numericWriteBehind);
160
176
  platformServices.set(HELYX_SERVICE_NAMES.resourceAuthorization, new CoreResourceAuthorizationService(platformServices.get(HELYX_SERVICE_NAMES.permissions)));
177
+ platformServices.set(HELYX_SERVICE_NAMES.discordMessages, new DiscordMessageDeliveryService(client));
161
178
  platformServices.set(HELYX_SERVICE_NAMES.discordOwnedMessages, new DiscordOwnedMessageDeletionService(client));
179
+ platformServices.set(HELYX_SERVICE_NAMES.memberEnforcement, new DiscordMemberEnforcementService(client));
162
180
  platformServices.set(HELYX_SERVICE_NAMES.memberRoles, new DiscordMemberRoleService(client));
163
181
  platformServices.set(HELYX_SERVICE_NAMES.discordResources, new DiscordResourceInspection(client));
164
182
  if (featureModules.some(({ manifest }) => manifest.id === "helyx.logging"))
165
183
  platformServices.set(HELYX_SERVICE_NAMES.moduleLogging, new DiscordModuleLoggingService(client, database, publicModules, logger));
166
- const serviceAccess = new ServiceRegistry(platformServices);
167
184
  const scheduledTaskRoute = createLocalScheduledTaskRoute({
168
185
  modules: publicModules,
169
186
  services: serviceAccess,
@@ -207,7 +224,7 @@ export async function startShardWorker(processEnvironment = process.env, identit
207
224
  if (!parsed.success)
208
225
  return;
209
226
  if (parsed.data.type === "helyx.control.request") {
210
- void handleControl(identity, controlService, parsed.data);
227
+ void handleControl(identity, controlService, parsed.data, logger);
211
228
  }
212
229
  else if (parsed.data.type === "helyx.lease.heartbeat") {
213
230
  if (sameShardIdentity(parsed.data.identity, identity))
@@ -390,7 +407,7 @@ export function shardWorkerCommandRegistration(input) {
390
407
  ]),
391
408
  };
392
409
  }
393
- async function handleControl(identity, service, request) {
410
+ async function handleControl(identity, service, request, logger) {
394
411
  let response;
395
412
  const guildId = controlOperationGuildId(request.operation);
396
413
  if (request.generation !== identity.generation ||
@@ -418,6 +435,13 @@ async function handleControl(identity, service, request) {
418
435
  };
419
436
  }
420
437
  catch (error) {
438
+ logger.error({
439
+ event: "shard.control_failed",
440
+ requestId: request.requestId,
441
+ operation: request.operation.kind,
442
+ guildId,
443
+ error: safeError(error),
444
+ }, "Shard control operation failed");
421
445
  const mapped = mapBotControlError(error);
422
446
  response = {
423
447
  version: 1,
@@ -429,7 +453,27 @@ async function handleControl(identity, service, request) {
429
453
  };
430
454
  }
431
455
  }
432
- assertShardMessageSize(response);
456
+ try {
457
+ assertShardMessageSize(response);
458
+ }
459
+ catch (error) {
460
+ logger.error({
461
+ event: "shard.control_response_oversized",
462
+ requestId: request.requestId,
463
+ operation: request.operation.kind,
464
+ guildId,
465
+ error: safeError(error),
466
+ }, "Shard control response exceeded the transport limit");
467
+ response = {
468
+ version: 1,
469
+ type: "helyx.control.response",
470
+ requestId: request.requestId,
471
+ ok: false,
472
+ status: 502,
473
+ message: "The owning shard returned too much data",
474
+ };
475
+ assertShardMessageSize(response);
476
+ }
433
477
  process.send?.(response);
434
478
  }
435
479
  function health(identity, client, status, eventLoopDelayNanoseconds, queueDepth) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@helyx/bot",
3
- "version": "0.4.8",
3
+ "version": "0.4.11",
4
4
  "description": "Production Discord runtime for the modular Helyx platform.",
5
5
  "keywords": [
6
6
  "helyx",
@@ -48,14 +48,14 @@
48
48
  "start": "node --env-file-if-exists=../../.env dist/index.js"
49
49
  },
50
50
  "dependencies": {
51
- "@helyx/config": "0.4.2",
52
- "@helyx/control-protocol": "0.1.4",
53
- "@helyx/core": "0.1.8",
54
- "@helyx/database": "0.4.2",
55
- "@helyx/framework": "0.1.8",
56
- "@helyx/module-manifest": "0.4.1",
57
- "@helyx/platform": "0.2.6",
58
- "@helyx/sdk": "0.3.3",
51
+ "@helyx/config": "0.4.3",
52
+ "@helyx/control-protocol": "0.2.0",
53
+ "@helyx/core": "0.1.11",
54
+ "@helyx/database": "0.5.0",
55
+ "@helyx/framework": "0.1.11",
56
+ "@helyx/module-manifest": "0.5.0",
57
+ "@helyx/platform": "0.3.0",
58
+ "@helyx/sdk": "0.4.0",
59
59
  "discord.js": "14.27.0",
60
60
  "fastify": "5.10.0",
61
61
  "pino": "10.3.1",