@helyx/bot 1.5.0 → 1.5.2

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/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { parseBotEnvironment } from "@helyx/config";
2
2
  import { StatusHeartbeatReporter } from "@helyx/status-client";
3
3
  import { safeError, ServiceRegistry } from "@helyx/core";
4
- import { checkDatabaseConnection, createDatabasePool, EckoOperationsRepository, inspectConfiguredModuleMigrations, inspectCoreMigrations, inspectCreatorOnboardingMigrations, runCreatorOnboardingMigrations, PremiumAllocationRepository, runCoreMigrations, runConfiguredModuleMigrations, } from "@helyx/database";
4
+ import { checkDatabaseConnection, createDatabasePool, EckoOperationsRepository, InstallationRepository, inspectConfiguredModuleMigrations, inspectCoreMigrations, inspectCreatorOnboardingMigrations, runCreatorOnboardingMigrations, PremiumAllocationRepository, runCoreMigrations, runConfiguredModuleMigrations, } from "@helyx/database";
5
5
  import { createRuntime } from "@helyx/framework";
6
6
  import { assertRepositoryEngineSupported } from "@helyx/database/adapters";
7
7
  import { createPlatformServices, gamingStatisticsRecordOwner, achievementContributionRecordOwner, PostgresPrivacyProviderExecutor, PlatformMemberRoleGrantOwnershipService, PostgresMemberRoleGrantOwnershipStore, } from "@helyx/platform";
@@ -20,6 +20,7 @@ import { assertConfiguredModuleDatabaseCompatibility, loadConfiguredModulePackag
20
20
  import { resolveModulePackageSpecifiers } from "./module-configuration.js";
21
21
  import { createPermissionsModule } from "./permissions-module.js";
22
22
  import { createManagementModule } from "./management-module.js";
23
+ import { createDiagnosticModule, createMysqlDiagnosticLock, createPostgresDiagnosticLock, } from "./diagnostic-command.js";
23
24
  import { DiscordMessageDeliveryService, DiscordMemberRoleService, DiscordVerificationMemberService, DiscordResourceInspection, installModuleEvents, } from "./events.js";
24
25
  import { DiscordModerationMemberEnforcementAdapter } from "./discord-member-enforcement-service.js";
25
26
  import { DiscordModuleLoggingService } from "./module-logging.js";
@@ -303,6 +304,30 @@ async function startUnshardedBot(processEnvironment) {
303
304
  });
304
305
  const permissionsModule = createPermissionsModule(featureModules);
305
306
  const managementModule = createManagementModule(featureModules, guildResources);
307
+ const diagnosticInstallations = mysql
308
+ ? new (await import("@helyx/database-mysql")).InstallationRepository(mysql.pool)
309
+ : new InstallationRepository(database);
310
+ const diagnosticModule = createDiagnosticModule({
311
+ client,
312
+ modules: featureModules,
313
+ runExclusive: mysql
314
+ ? createMysqlDiagnosticLock(mysql.pool)
315
+ : createPostgresDiagnosticLock(database),
316
+ assertGuildAvailable: async (guildId) => {
317
+ if (shuttingDown ||
318
+ !(await diagnosticInstallations.getActiveGuild(guildId)))
319
+ throw new Error("The server installation is not available");
320
+ },
321
+ listEnabledModules: (guildId) => diagnosticInstallations.listEnabledModules(guildId),
322
+ commandStatus: {
323
+ get: (guildId) => {
324
+ return guildCommandReads.getStatus(guildId);
325
+ },
326
+ refresh: (input) => {
327
+ return guildCommandReads.refreshStatus(input);
328
+ },
329
+ },
330
+ });
306
331
  const cloudManagementModule = environment.HELYX_CLOUD_CONNECT_ENABLED
307
332
  ? createCloudManagementModule({
308
333
  connector: cloudConnector,
@@ -327,6 +352,7 @@ async function startUnshardedBot(processEnvironment) {
327
352
  : undefined;
328
353
  const publicModules = [
329
354
  managementModule,
355
+ diagnosticModule,
330
356
  permissionsModule,
331
357
  supportModule,
332
358
  privacyModule,
@@ -405,6 +431,7 @@ async function startUnshardedBot(processEnvironment) {
405
431
  ...(deployedCommit ? { deploymentReference: deployedCommit } : {}),
406
432
  coreFunctions: [
407
433
  managementModule,
434
+ diagnosticModule,
408
435
  permissionsModule,
409
436
  supportModule,
410
437
  privacyModule,
@@ -534,6 +561,7 @@ async function startUnshardedBot(processEnvironment) {
534
561
  });
535
562
  const enabledCoreModuleIds = new Set([
536
563
  managementModule.manifest.id,
564
+ diagnosticModule.manifest.id,
537
565
  permissionsModule.manifest.id,
538
566
  supportModule.manifest.id,
539
567
  ...(premiumActivationModule ? [premiumActivationModule.manifest.id] : []),
@@ -555,6 +583,7 @@ async function startUnshardedBot(processEnvironment) {
555
583
  synchroniseInternalGuild: false,
556
584
  prepare: prepareGuilds,
557
585
  selfAuthorizingGuildModuleIds: new Set([
586
+ diagnosticModule.manifest.id,
558
587
  supportModule.manifest.id,
559
588
  ...(premiumActivationModule
560
589
  ? [premiumActivationModule.manifest.id]
@@ -13,6 +13,8 @@ import { CloudConnectorController, } from "./cloud-connector.js";
13
13
  import { createCloudManagementModule } from "./cloud-management-module.js";
14
14
  import { BotControlService, createBotControlServer, mapBotControlError, } from "./control-server.js";
15
15
  import { createManagementModule } from "./management-module.js";
16
+ import { creatorInviteWorkerKeyName, deriveCreatorInviteKey, } from "./creator-invite-key.js";
17
+ import { createDiagnosticModule } from "./diagnostic-command.js";
16
18
  import { buildApplicationCommandDefinitions } from "./interactions.js";
17
19
  import { resolveModulePackageSpecifiers } from "./module-configuration.js";
18
20
  import { deriveSecureRandomSelectionKey, loadConfiguredModulePackages, platformServiceOptions, secureRandomSelectionWorkerKeyName, } from "./modules.js";
@@ -217,6 +219,10 @@ export async function startShardSupervisor(environment, processEnvironment, work
217
219
  };
218
220
  const permissionsModule = createPermissionsModule(featureModules);
219
221
  const managementModule = createManagementModule(featureModules, unavailableResources);
222
+ // The supervisor registers metadata; guild checks and repairs run in workers.
223
+ const diagnosticModule = createDiagnosticModule({
224
+ modules: featureModules,
225
+ });
220
226
  const cloudManagementModule = environment.HELYX_CLOUD_CONNECT_ENABLED
221
227
  ? createCloudManagementModule({
222
228
  connector: cloudConnector,
@@ -242,6 +248,7 @@ export async function startShardSupervisor(environment, processEnvironment, work
242
248
  : undefined;
243
249
  const publicModules = [
244
250
  managementModule,
251
+ diagnosticModule,
245
252
  permissionsModule,
246
253
  supportModule,
247
254
  privacyModule,
@@ -265,6 +272,7 @@ export async function startShardSupervisor(environment, processEnvironment, work
265
272
  : {}),
266
273
  coreFunctions: [
267
274
  managementModule,
275
+ diagnosticModule,
268
276
  permissionsModule,
269
277
  supportModule,
270
278
  privacyModule,
@@ -1709,6 +1717,9 @@ export function buildShardWorkerEnvironment(source, identity, restartCount) {
1709
1717
  const selectionKey = source.HELYX_DATA_ENCRYPTION_KEY
1710
1718
  ? Buffer.from(deriveSecureRandomSelectionKey(source.HELYX_DATA_ENCRYPTION_KEY)).toString("base64")
1711
1719
  : undefined;
1720
+ const creatorInviteKey = source.HELYX_DATA_ENCRYPTION_KEY
1721
+ ? deriveCreatorInviteKey(source.HELYX_DATA_ENCRYPTION_KEY).toString("base64")
1722
+ : undefined;
1712
1723
  const hostedModerationCredential = source.HELYX_HOSTED_MODERATION_ENABLED === "true" &&
1713
1724
  source.HELYX_BOT_CONTROL_SECRET
1714
1725
  ? { HELYX_BOT_CONTROL_SECRET: source.HELYX_BOT_CONTROL_SECRET }
@@ -1719,6 +1730,9 @@ export function buildShardWorkerEnvironment(source, identity, restartCount) {
1719
1730
  ...(selectionKey
1720
1731
  ? { [secureRandomSelectionWorkerKeyName]: selectionKey }
1721
1732
  : {}),
1733
+ ...(creatorInviteKey
1734
+ ? { [creatorInviteWorkerKeyName]: creatorInviteKey }
1735
+ : {}),
1722
1736
  SHARDING_MANAGER: "true",
1723
1737
  SHARDING_MANAGER_MODE: "process",
1724
1738
  SHARDS: String(identity.shardId),
@@ -23,6 +23,7 @@ export declare function startShardWorker(processEnvironment?: NodeJS.ProcessEnv,
23
23
  export declare function executeShardScheduledTaskRequest(identity: ShardIdentity, request: ShardScheduledTaskRequest, route: ScheduledTaskRoute, now?: number): Promise<ShardScheduledTaskResponse>;
24
24
  export declare function parseShardWorkerLaunchContract(value: string | undefined): ShardIdentity;
25
25
  export declare function parseShardWorkerEnvironment(processEnvironment: NodeJS.ProcessEnv, identity: ShardIdentity): {
26
+ HELYX_INTERNAL_CREATOR_INVITE_KEY?: string;
26
27
  HELYX_INTERNAL_SECURE_RANDOM_SELECTION_KEY?: string;
27
28
  HELYX_DATA_ENCRYPTION_KEY: undefined;
28
29
  HELYX_SHARD_WORKERS_PER_REPLICA: number;
@@ -102,6 +103,7 @@ export declare function shardWorkerCommandRegistration(input: {
102
103
  prepare: NonNullable<CommandRegistration["prepare"]>;
103
104
  operationsModuleId?: string;
104
105
  supportModuleId: string;
106
+ diagnosticModuleId?: string;
105
107
  premiumActivationModuleId?: string;
106
108
  oauthModuleId?: string;
107
109
  }): CommandRegistration;
@@ -1,6 +1,6 @@
1
1
  import { parseBotEnvironment } from "@helyx/config";
2
2
  import { safeError, ServiceRegistry } from "@helyx/core";
3
- import { checkDatabaseConnection, createDatabasePool, PremiumAllocationRepository, } from "@helyx/database";
3
+ import { checkDatabaseConnection, createDatabasePool, PremiumAllocationRepository, InstallationRepository, } from "@helyx/database";
4
4
  import { assertRepositoryEngineSupported } from "@helyx/database/adapters";
5
5
  import { REVIEWED_PREMIUM_MODULE_IDS } from "@helyx/module-manifest";
6
6
  import { createRuntime } from "@helyx/framework";
@@ -15,9 +15,12 @@ import { HttpVerificationService } from "./verification-service.js";
15
15
  import { DiscordMessageDeliveryService, DiscordMemberRoleService, DiscordVerificationMemberService, DiscordResourceInspection, installModuleEvents, } from "./events.js";
16
16
  import { DiscordModerationMemberEnforcementAdapter } from "./discord-member-enforcement-service.js";
17
17
  import { installCreatorInvitePlatform } from "./creator-onboarding-platform.js";
18
+ import { creatorInviteWorkerKeyName, decodeCreatorInviteKey, } from "./creator-invite-key.js";
18
19
  import { installGuildTracking, } from "./guilds.js";
19
20
  import { installInteractions, } from "./interactions.js";
20
21
  import { createManagementModule } from "./management-module.js";
22
+ import { createDiagnosticModule, createPostgresDiagnosticLock, } from "./diagnostic-command.js";
23
+ import { GuildCommandReadService } from "./guild-command-read-service.js";
21
24
  import { DiscordModuleLoggingService } from "./module-logging.js";
22
25
  import { resolveModulePackageSpecifiers } from "./module-configuration.js";
23
26
  import { loadConfiguredModulePackages, platformServiceOptions, secureRandomSelectionWorkerKeyName, } from "./modules.js";
@@ -158,6 +161,35 @@ export async function startShardWorker(processEnvironment = process.env, identit
158
161
  const guildResources = new DiscordGuildResourceDirectory(client);
159
162
  const permissionsModule = createPermissionsModule(featureModules);
160
163
  const managementModule = createManagementModule(featureModules, guildResources);
164
+ const diagnosticInstallations = new InstallationRepository(database);
165
+ const diagnosticModule = createDiagnosticModule({
166
+ client,
167
+ modules: featureModules,
168
+ runExclusive: createPostgresDiagnosticLock(database),
169
+ assertGuildAvailable: async (guildId) => {
170
+ if (shuttingDown ||
171
+ guildIdToShardId(guildId, identity.shardCount) !== identity.shardId)
172
+ throw new Error("The server is no longer owned by this shard");
173
+ const installation = await diagnosticInstallations.getActiveGuild(guildId);
174
+ if (!installation ||
175
+ installation.shardGeneration !== identity.generation ||
176
+ installation.shardDeploymentOrder !==
177
+ BigInt(identity.deploymentOrder ?? 1) ||
178
+ installation.shardSupervisorId !== identity.supervisorId ||
179
+ installation.shardId !== identity.shardId ||
180
+ installation.shardFencingToken !== BigInt(identity.fencingToken))
181
+ throw new Error("The server installation or shard ownership changed");
182
+ },
183
+ listEnabledModules: (guildId) => diagnosticInstallations.listEnabledModules(guildId),
184
+ commandStatus: {
185
+ get: (guildId) => {
186
+ return guildCommandReads.getStatus(guildId);
187
+ },
188
+ refresh: (input) => {
189
+ return guildCommandReads.refreshStatus(input);
190
+ },
191
+ },
192
+ });
161
193
  cloudProxy = new WorkerCloudConnectorProxy();
162
194
  const cloudManagementModule = environment.HELYX_CLOUD_CONNECT_ENABLED
163
195
  ? createCloudManagementModule({
@@ -184,6 +216,7 @@ export async function startShardWorker(processEnvironment = process.env, identit
184
216
  : undefined;
185
217
  const publicModules = [
186
218
  managementModule,
219
+ diagnosticModule,
187
220
  permissionsModule,
188
221
  supportModule,
189
222
  privacyModule,
@@ -206,6 +239,7 @@ export async function startShardWorker(processEnvironment = process.env, identit
206
239
  : {}),
207
240
  coreFunctions: [
208
241
  managementModule,
242
+ diagnosticModule,
209
243
  permissionsModule,
210
244
  supportModule,
211
245
  privacyModule,
@@ -224,6 +258,7 @@ export async function startShardWorker(processEnvironment = process.env, identit
224
258
  ...publicModules,
225
259
  ...(operationsModule ? [operationsModule] : []),
226
260
  ];
261
+ const guildCommandReads = new GuildCommandReadService(database, environment.DISCORD_CLIENT_ID, undefined, environment.DISCORD_TOKEN, environment.HELYX_GUILD_COMMAND_WRITES_ENABLED, environment.HELYX_GUILD_COMMAND_WRITE_GUILD_IDS, environment.HELYX_GUILD_COMMAND_WRITE_SCOPE === "all", modules);
227
262
  const platformServices = new Map();
228
263
  const serviceAccess = new ServiceRegistry(platformServices);
229
264
  const serviceOptions = platformServiceOptions(environment, publicModules);
@@ -284,6 +319,11 @@ export async function startShardWorker(processEnvironment = process.env, identit
284
319
  services: serviceAccess,
285
320
  modules: publicModules,
286
321
  encryptionKey: environment.HELYX_DATA_ENCRYPTION_KEY,
322
+ ...(environment.HELYX_INTERNAL_CREATOR_INVITE_KEY
323
+ ? {
324
+ inviteEncryptionKey: environment.HELYX_INTERNAL_CREATOR_INVITE_KEY,
325
+ }
326
+ : {}),
287
327
  ...(environment.HELYX_CLOUD_API_URL &&
288
328
  environment.HELYX_OAUTH_INTERNAL_SECRET
289
329
  ? {
@@ -328,6 +368,7 @@ export async function startShardWorker(processEnvironment = process.env, identit
328
368
  runtime = createRuntime({ modules, logger, services: platformServices });
329
369
  const enabledCoreModuleIds = new Set([
330
370
  managementModule.manifest.id,
371
+ diagnosticModule.manifest.id,
331
372
  permissionsModule.manifest.id,
332
373
  supportModule.manifest.id,
333
374
  ...(premiumActivationModule ? [premiumActivationModule.manifest.id] : []),
@@ -359,6 +400,7 @@ export async function startShardWorker(processEnvironment = process.env, identit
359
400
  ? { operationsModuleId: operationsModule.manifest.id }
360
401
  : {}),
361
402
  supportModuleId: supportModule.manifest.id,
403
+ diagnosticModuleId: diagnosticModule.manifest.id,
362
404
  ...(premiumActivationModule
363
405
  ? { premiumActivationModuleId: premiumActivationModule.manifest.id }
364
406
  : {}),
@@ -546,12 +588,18 @@ export function parseShardWorkerEnvironment(processEnvironment, identity) {
546
588
  environment.HELYX_SHARD_SCOPE !== identity.scope)
547
589
  throw new Error("Shard worker launch contract conflicts with configuration");
548
590
  const internalSelectionKey = processEnvironment[secureRandomSelectionWorkerKeyName];
591
+ const internalCreatorInviteKey = processEnvironment[creatorInviteWorkerKeyName];
592
+ if (internalCreatorInviteKey !== undefined)
593
+ decodeCreatorInviteKey(internalCreatorInviteKey);
549
594
  return {
550
595
  ...environment,
551
596
  HELYX_DATA_ENCRYPTION_KEY: undefined,
552
597
  ...(internalSelectionKey
553
598
  ? { HELYX_INTERNAL_SECURE_RANDOM_SELECTION_KEY: internalSelectionKey }
554
599
  : {}),
600
+ ...(internalCreatorInviteKey
601
+ ? { HELYX_INTERNAL_CREATOR_INVITE_KEY: internalCreatorInviteKey }
602
+ : {}),
555
603
  };
556
604
  }
557
605
  export function shardWorkerCommandRegistration(input) {
@@ -563,6 +611,7 @@ export function shardWorkerCommandRegistration(input) {
563
611
  synchronise: false,
564
612
  selfAuthorizingGuildModuleIds: new Set([
565
613
  input.supportModuleId,
614
+ ...(input.diagnosticModuleId ? [input.diagnosticModuleId] : []),
566
615
  ...(input.premiumActivationModuleId
567
616
  ? [input.premiumActivationModuleId]
568
617
  : []),
@@ -37,7 +37,7 @@ commented.
37
37
 
38
38
  Enable privileged intents in the Discord Developer Portal as well as the bot
39
39
  environment when required: Counting needs Message Content, Welcome and Access
40
- Control need Guild Members, and Levels needs both. Access Control's Password
40
+ Control need Guild Members. Levels needs Guild Members, and Message Content only when using **Custom message types** rather than **All messages**. Access Control's Password
41
41
  Entry also needs Message Content. The corresponding variables are
42
42
  `HELYX_MESSAGE_CONTENT_ENABLED` and `HELYX_GUILD_MEMBERS_ENABLED`.
43
43
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@helyx/bot",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "description": "Production Discord runtime for the modular Helyx platform.",
5
5
  "keywords": [
6
6
  "helyx",
@@ -53,7 +53,7 @@
53
53
  "@helyx/config": ">=1.4.0 <2.0.0",
54
54
  "@helyx/control-protocol": ">=0.8.0 <0.9.0",
55
55
  "@helyx/core": ">=0.2.0 <0.3.0",
56
- "@helyx/database": ">=2.4.0 <3.0.0",
56
+ "@helyx/database": ">=2.4.1 <3.0.0",
57
57
  "@helyx/database-contracts": ">=0.2.0 <0.3.0",
58
58
  "@helyx/framework": ">=0.2.0 <0.3.0",
59
59
  "@helyx/module-manifest": ">=0.13.0 <0.14.0",
@@ -70,7 +70,7 @@
70
70
  "zod": "4.4.3"
71
71
  },
72
72
  "optionalDependencies": {
73
- "@helyx/database-mysql": ">=0.2.0 <0.3.0"
73
+ "@helyx/database-mysql": ">=0.2.1 <0.3.0"
74
74
  },
75
75
  "peerDependencies": {
76
76
  "@helyx/module-automod": ">=1.1.1 <2.0.0",