@helyx/bot 0.4.46 → 0.6.1

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 (44) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/README.md +4 -4
  3. package/dist/cloud-management-module.js +1 -5
  4. package/dist/command-path-contract.d.ts +26 -0
  5. package/dist/command-path-contract.js +157 -0
  6. package/dist/command-registration-policy.js +1 -0
  7. package/dist/control-permissions.js +1 -0
  8. package/dist/control-server.d.ts +2 -34
  9. package/dist/control-server.js +31 -43
  10. package/dist/dashboard-commands.d.ts +1 -0
  11. package/dist/dashboard-commands.js +6 -35
  12. package/dist/dashboard-module-projection.d.ts +39 -0
  13. package/dist/dashboard-module-projection.js +28 -0
  14. package/dist/discord-analytics.d.ts +30 -0
  15. package/dist/discord-analytics.js +337 -0
  16. package/dist/discord-invites.d.ts +24 -0
  17. package/dist/discord-invites.js +91 -0
  18. package/dist/events.d.ts +1 -1
  19. package/dist/events.js +78 -22
  20. package/dist/guilds.js +1 -4
  21. package/dist/index.js +49 -8
  22. package/dist/interactions.d.ts +1 -1
  23. package/dist/interactions.js +89 -68
  24. package/dist/management-module.d.ts +1 -1
  25. package/dist/module-logging.js +1 -4
  26. package/dist/module-status-control.js +1 -1
  27. package/dist/modules.d.ts +2 -0
  28. package/dist/modules.js +27 -4
  29. package/dist/permissions-module.js +13 -16
  30. package/dist/premium-access.d.ts +7 -0
  31. package/dist/premium-access.js +38 -0
  32. package/dist/premium-activation-module.d.ts +20 -0
  33. package/dist/premium-activation-module.js +542 -0
  34. package/dist/premium-catalogue-enforcement.d.ts +5 -0
  35. package/dist/premium-catalogue-enforcement.js +29 -0
  36. package/dist/premium-guild-authority.d.ts +12 -0
  37. package/dist/premium-guild-authority.js +25 -0
  38. package/dist/scheduled-task-runner.js +14 -1
  39. package/dist/shard-contracts.js +1 -1
  40. package/dist/shard-supervisor.js +30 -1
  41. package/dist/shard-worker.d.ts +2 -0
  42. package/dist/shard-worker.js +36 -4
  43. package/documentation/examples/.env.example +2 -2
  44. package/package.json +6 -6
@@ -1,5 +1,5 @@
1
1
  import { safeError, ServiceRegistry } from "@helyx/core";
2
- import { checkDatabaseConnection, createDatabasePool, AuditRepository, GuildCommandRepository, InteractionSessionRepository, RateLimitRepository, runConfiguredModuleMigrations, runCoreMigrations, RuntimeStateRepository, } from "@helyx/database";
2
+ import { checkDatabaseConnection, createDatabasePool, AuditRepository, GuildCommandRepository, InteractionSessionRepository, PremiumAllocationRepository, RateLimitRepository, runConfiguredModuleMigrations, runCoreMigrations, RuntimeStateRepository, } from "@helyx/database";
3
3
  import { createPlatformServices, gamingStatisticsRecordOwner, achievementContributionRecordOwner, PostgresPrivacyProviderExecutor, } from "@helyx/platform";
4
4
  import { HELYX_SERVICE_NAMES } from "@helyx/sdk";
5
5
  import { Client, ShardingManager } from "discord.js";
@@ -24,6 +24,9 @@ import { SupervisorCloudDiscordDirectory } from "./supervisor-cloud-directory.js
24
24
  import { createSupportModule } from "./support-module.js";
25
25
  import { createOAuthModule } from "./oauth-module.js";
26
26
  import { createPrivacyModule } from "./privacy-module.js";
27
+ import { REVIEWED_PREMIUM_MODULE_IDS } from "@helyx/module-manifest";
28
+ import { createPremiumActivationModule } from "./premium-activation-module.js";
29
+ import { DiscordPremiumGuildAuthority } from "./premium-guild-authority.js";
27
30
  import { discordRestOptions } from "./discord-rest.js";
28
31
  import { availabilityCatalogue, availabilityCatalogueDefinitions, statusCatalogueDefinitions, } from "./module-availability-control.js";
29
32
  import { createScheduledTaskRunner, schedulerOperationalHealth, } from "./scheduled-task-runner.js";
@@ -35,6 +38,8 @@ import { GuildCommandWorker } from "./guild-command-worker.js";
35
38
  import { GuildCommandDiscordClient } from "./guild-command-discord-client.js";
36
39
  import { GuildCommandReadService } from "./guild-command-read-service.js";
37
40
  import { scheduleGuildCommandRelease } from "./guild-command-release.js";
41
+ import { assertReviewedPremiumCatalogue, requirePremiumProviderEnvironment, } from "./premium-access.js";
42
+ import { enforcePremiumCatalogue, PREMIUM_CATALOGUE_ENFORCEMENT_INTERVAL_MS, } from "./premium-catalogue-enforcement.js";
38
43
  const LEASE_RENEW_INTERVAL_MS = 10_000;
39
44
  const STARTUP_LEASE_RENEW_INTERVAL_MS = 5_000;
40
45
  const LEASE_VALIDATION_TIMEOUT_MS = 1_000;
@@ -121,6 +126,7 @@ export async function startShardSupervisor(environment, processEnvironment, work
121
126
  let maintainingLeases = false;
122
127
  let rebalancingShards = false;
123
128
  let retentionTimer;
129
+ let premiumCatalogueTimer;
124
130
  let leaseTimer;
125
131
  let startupLeaseTimer;
126
132
  let startupWorkerHeartbeats;
@@ -156,6 +162,7 @@ export async function startShardSupervisor(environment, processEnvironment, work
156
162
  const moduleSpecifiers = await resolveModulePackageSpecifiers(environment.HELYX_MODULE_PACKAGES, undefined, undefined, environment.HELYX_MODULE_CATALOGUE_PATH);
157
163
  const modulePackages = await loadConfiguredModulePackages(moduleSpecifiers);
158
164
  const featureModules = modulePackages.map(({ definition }) => definition);
165
+ assertReviewedPremiumCatalogue(featureModules);
159
166
  const gateway = await fetchGatewayBotInformation(environment.DISCORD_TOKEN);
160
167
  shardCount =
161
168
  environment.HELYX_SHARD_MODE === "fixed"
@@ -211,6 +218,16 @@ export async function startShardSupervisor(environment, processEnvironment, work
211
218
  : undefined;
212
219
  const supportModule = createSupportModule(cloudConnector);
213
220
  const privacyModule = createPrivacyModule();
221
+ const premiumEnvironment = requirePremiumProviderEnvironment(featureModules, environment.STRIPE_ENVIRONMENT);
222
+ const premiumActivationModule = !premiumEnvironment
223
+ ? undefined
224
+ : createPremiumActivationModule({
225
+ allocations: new PremiumAllocationRepository(database),
226
+ authority: new DiscordPremiumGuildAuthority(commandClient),
227
+ provider: "stripe",
228
+ providerEnvironment: premiumEnvironment,
229
+ reviewedPremiumModuleIds: REVIEWED_PREMIUM_MODULE_IDS,
230
+ });
214
231
  const oauthModule = environment.HELYX_OAUTH_ENABLED
215
232
  ? createOAuthModule()
216
233
  : undefined;
@@ -219,6 +236,7 @@ export async function startShardSupervisor(environment, processEnvironment, work
219
236
  permissionsModule,
220
237
  supportModule,
221
238
  privacyModule,
239
+ ...(premiumActivationModule ? [premiumActivationModule] : []),
222
240
  ...(oauthModule ? [oauthModule] : []),
223
241
  ...(cloudManagementModule ? [cloudManagementModule] : []),
224
242
  ...featureModules,
@@ -240,6 +258,7 @@ export async function startShardSupervisor(environment, processEnvironment, work
240
258
  permissionsModule,
241
259
  supportModule,
242
260
  privacyModule,
261
+ ...(premiumActivationModule ? [premiumActivationModule] : []),
243
262
  ...(oauthModule ? [oauthModule] : []),
244
263
  ...(cloudManagementModule ? [cloudManagementModule] : []),
245
264
  ].map(({ manifest }) => manifest),
@@ -1102,6 +1121,13 @@ export async function startShardSupervisor(environment, processEnvironment, work
1102
1121
  await cleanupExpiredCoreRecords(database, logger);
1103
1122
  retentionTimer = setInterval(() => void cleanupExpiredCoreRecords(database, logger), RETENTION_CLEANUP_INTERVAL_MS);
1104
1123
  retentionTimer.unref();
1124
+ premiumCatalogueTimer = setInterval(() => void enforcePremiumCatalogue(database, logger).catch((error) => {
1125
+ logger.warn({
1126
+ event: "premium.catalogue_enforcement_failed",
1127
+ error: safeError(error),
1128
+ }, "Premium module catalogue enforcement failed");
1129
+ }), PREMIUM_CATALOGUE_ENFORCEMENT_INTERVAL_MS);
1130
+ premiumCatalogueTimer.unref();
1105
1131
  await cloudConnector?.start();
1106
1132
  cloudStarted = true;
1107
1133
  if (workersRoutable)
@@ -1373,6 +1399,8 @@ export async function startShardSupervisor(environment, processEnvironment, work
1373
1399
  startupWorkerHeartbeats = undefined;
1374
1400
  if (retentionTimer)
1375
1401
  clearInterval(retentionTimer);
1402
+ if (premiumCatalogueTimer)
1403
+ clearInterval(premiumCatalogueTimer);
1376
1404
  workersRoutable = false;
1377
1405
  await scheduledTasks?.stop().catch(() => undefined);
1378
1406
  scheduledTasks = undefined;
@@ -1443,6 +1471,7 @@ async function runLeaderStartup(database, environment, modulePackages, modules,
1443
1471
  moduleVersion: definition.manifest.version,
1444
1472
  migrationsDirectory: join(packageRoot, "migrations"),
1445
1473
  })));
1474
+ await enforcePremiumCatalogue(database, logger);
1446
1475
  const repository = new RuntimeStateRepository(database);
1447
1476
  await synchroniseApplicationCommands({
1448
1477
  applicationId: environment.DISCORD_CLIENT_ID,
@@ -63,6 +63,7 @@ export declare function parseShardWorkerEnvironment(processEnvironment: NodeJS.P
63
63
  HELYX_STATUS_ENABLED: boolean;
64
64
  HELYX_STATUS_URL: string;
65
65
  HELYX_STATUS_HEARTBEAT_INTERVAL_SECONDS: number;
66
+ STRIPE_ENVIRONMENT?: "sandbox" | "live" | undefined;
66
67
  HELYX_BOT_CONTROL_SECRET?: string | undefined;
67
68
  HELYX_SUPPORT_SERVICE_SECRET?: string | undefined;
68
69
  HELYX_VERIFICATION_INSTALLATION_ID?: string | undefined;
@@ -86,6 +87,7 @@ export declare function shardWorkerCommandRegistration(input: {
86
87
  prepare: NonNullable<CommandRegistration["prepare"]>;
87
88
  operationsModuleId?: string;
88
89
  supportModuleId: string;
90
+ premiumActivationModuleId?: string;
89
91
  oauthModuleId?: string;
90
92
  }): CommandRegistration;
91
93
  //# sourceMappingURL=shard-worker.d.ts.map
@@ -1,8 +1,9 @@
1
1
  import { parseBotEnvironment } from "@helyx/config";
2
2
  import { safeError, ServiceRegistry } from "@helyx/core";
3
- import { checkDatabaseConnection, createDatabasePool } from "@helyx/database";
3
+ import { checkDatabaseConnection, createDatabasePool, PremiumAllocationRepository, } from "@helyx/database";
4
+ import { REVIEWED_PREMIUM_MODULE_IDS } from "@helyx/module-manifest";
4
5
  import { createRuntime } from "@helyx/framework";
5
- import { createPlatformServices } from "@helyx/platform";
6
+ import { createPlatformServices, PlatformMemberRoleGrantOwnershipService, PostgresMemberRoleGrantOwnershipStore, } from "@helyx/platform";
6
7
  import { HELYX_SERVICE_NAMES, } from "@helyx/sdk";
7
8
  import { Client, Events, Partials } from "discord.js";
8
9
  import { monitorEventLoopDelay } from "node:perf_hooks";
@@ -18,9 +19,14 @@ import { DiscordModuleLoggingService } from "./module-logging.js";
18
19
  import { resolveModulePackageSpecifiers } from "./module-configuration.js";
19
20
  import { loadConfiguredModulePackages, platformServiceOptions, secureRandomSelectionWorkerKeyName, } from "./modules.js";
20
21
  import { createPermissionsModule } from "./permissions-module.js";
22
+ import { createPremiumActivationModule } from "./premium-activation-module.js";
23
+ import { DiscordPremiumGuildAuthority } from "./premium-guild-authority.js";
24
+ import { assertReviewedPremiumCatalogue, requirePremiumProviderEnvironment, } from "./premium-access.js";
21
25
  import { WorkerCloudConnectorProxy } from "./shard-cloud-proxy.js";
22
26
  import { DiscordOwnedMessageDeletionService } from "./discord-owned-message-service.js";
23
27
  import { DiscordGuildHealthInspection } from "./discord-guild-health.js";
28
+ import { DiscordAnalyticsInspection } from "./discord-analytics.js";
29
+ import { DiscordInviteInspection } from "./discord-invites.js";
24
30
  import { CoreResourceAuthorizationService } from "./resource-authorization.js";
25
31
  import { PostgresTriviaDataProvider } from "./trivia-data-provider.js";
26
32
  import { createDeveloperOperationsModule, WorkerShardHealthProvider, } from "./developer-operations-module.js";
@@ -116,6 +122,7 @@ export async function startShardWorker(processEnvironment = process.env, identit
116
122
  const moduleSpecifiers = await resolveModulePackageSpecifiers(environment.HELYX_MODULE_PACKAGES, undefined, undefined, environment.HELYX_MODULE_CATALOGUE_PATH);
117
123
  const modulePackages = await loadConfiguredModulePackages(moduleSpecifiers);
118
124
  const featureModules = modulePackages.map(({ definition }) => definition);
125
+ assertReviewedPremiumCatalogue(featureModules);
119
126
  client = new Client({
120
127
  rest: discordRestOptions,
121
128
  intents: gatewayIntentsForModules(featureModules, environment.HELYX_MESSAGE_CONTENT_ENABLED, environment.HELYX_GUILD_MEMBERS_ENABLED),
@@ -138,6 +145,16 @@ export async function startShardWorker(processEnvironment = process.env, identit
138
145
  : undefined;
139
146
  const supportModule = createSupportModule(cloudProxy);
140
147
  const privacyModule = createPrivacyModule();
148
+ const premiumEnvironment = requirePremiumProviderEnvironment(featureModules, environment.STRIPE_ENVIRONMENT);
149
+ const premiumActivationModule = !premiumEnvironment
150
+ ? undefined
151
+ : createPremiumActivationModule({
152
+ allocations: new PremiumAllocationRepository(database),
153
+ authority: new DiscordPremiumGuildAuthority(client),
154
+ provider: "stripe",
155
+ providerEnvironment: premiumEnvironment,
156
+ reviewedPremiumModuleIds: REVIEWED_PREMIUM_MODULE_IDS,
157
+ });
141
158
  const oauthModule = environment.HELYX_OAUTH_ENABLED
142
159
  ? createOAuthModule()
143
160
  : undefined;
@@ -146,6 +163,7 @@ export async function startShardWorker(processEnvironment = process.env, identit
146
163
  permissionsModule,
147
164
  supportModule,
148
165
  privacyModule,
166
+ ...(premiumActivationModule ? [premiumActivationModule] : []),
149
167
  ...(oauthModule ? [oauthModule] : []),
150
168
  ...(cloudManagementModule ? [cloudManagementModule] : []),
151
169
  ...featureModules,
@@ -166,6 +184,7 @@ export async function startShardWorker(processEnvironment = process.env, identit
166
184
  permissionsModule,
167
185
  supportModule,
168
186
  privacyModule,
187
+ ...(premiumActivationModule ? [premiumActivationModule] : []),
169
188
  ...(oauthModule ? [oauthModule] : []),
170
189
  ...(cloudManagementModule ? [cloudManagementModule] : []),
171
190
  ].map(({ manifest }) => manifest),
@@ -181,8 +200,9 @@ export async function startShardWorker(processEnvironment = process.env, identit
181
200
  ];
182
201
  const platformServices = new Map();
183
202
  const serviceAccess = new ServiceRegistry(platformServices);
203
+ const serviceOptions = platformServiceOptions(environment, publicModules);
184
204
  for (const [name, service] of createPlatformServices(database, publicModules, {
185
- ...platformServiceOptions(environment, publicModules),
205
+ ...serviceOptions,
186
206
  activationServices: serviceAccess,
187
207
  })) {
188
208
  platformServices.set(name, service);
@@ -213,9 +233,14 @@ export async function startShardWorker(processEnvironment = process.env, identit
213
233
  platformServices.set(HELYX_SERVICE_NAMES.discordMessages, new DiscordMessageDeliveryService(client));
214
234
  platformServices.set(HELYX_SERVICE_NAMES.discordOwnedMessages, new DiscordOwnedMessageDeletionService(client));
215
235
  platformServices.set(HELYX_SERVICE_NAMES.memberEnforcement, new DiscordMemberEnforcementService(client));
216
- platformServices.set(HELYX_SERVICE_NAMES.memberRoles, new DiscordMemberRoleService(client));
236
+ const memberRoles = new DiscordMemberRoleService(client);
237
+ platformServices.set(HELYX_SERVICE_NAMES.memberRoles, memberRoles);
238
+ platformServices.set(HELYX_SERVICE_NAMES.memberRoleGrants, new PlatformMemberRoleGrantOwnershipService(new PostgresMemberRoleGrantOwnershipStore(database), memberRoles));
217
239
  platformServices.set(HELYX_SERVICE_NAMES.discordResources, new DiscordResourceInspection(client));
218
240
  platformServices.set(HELYX_SERVICE_NAMES.discordGuildHealth, new DiscordGuildHealthInspection(client, publicModules, serviceAccess));
241
+ platformServices.set(HELYX_SERVICE_NAMES.discordAnalytics, new DiscordAnalyticsInspection(client));
242
+ if (serviceOptions.inviteFingerprintKey)
243
+ platformServices.set(HELYX_SERVICE_NAMES.discordInvites, new DiscordInviteInspection(client, serviceOptions.inviteFingerprintKey));
219
244
  if (featureModules.some(({ manifest }) => manifest.id === "helyx.logging"))
220
245
  platformServices.set(HELYX_SERVICE_NAMES.moduleLogging, new DiscordModuleLoggingService(client, database, publicModules, logger));
221
246
  const scheduledTaskRoute = createLocalScheduledTaskRoute({
@@ -228,6 +253,7 @@ export async function startShardWorker(processEnvironment = process.env, identit
228
253
  managementModule.manifest.id,
229
254
  permissionsModule.manifest.id,
230
255
  supportModule.manifest.id,
256
+ ...(premiumActivationModule ? [premiumActivationModule.manifest.id] : []),
231
257
  ...(oauthModule ? [oauthModule.manifest.id] : []),
232
258
  ...(cloudManagementModule ? [cloudManagementModule.manifest.id] : []),
233
259
  ...(operationsModule ? [operationsModule.manifest.id] : []),
@@ -250,6 +276,9 @@ export async function startShardWorker(processEnvironment = process.env, identit
250
276
  ? { operationsModuleId: operationsModule.manifest.id }
251
277
  : {}),
252
278
  supportModuleId: supportModule.manifest.id,
279
+ ...(premiumActivationModule
280
+ ? { premiumActivationModuleId: premiumActivationModule.manifest.id }
281
+ : {}),
253
282
  ...(oauthModule ? { oauthModuleId: oauthModule.manifest.id } : {}),
254
283
  }), logger, serviceAccess);
255
284
  moduleEvents = installModuleEvents(client, publicModules, logger, serviceAccess);
@@ -444,6 +473,9 @@ export function shardWorkerCommandRegistration(input) {
444
473
  synchronise: false,
445
474
  selfAuthorizingGuildModuleIds: new Set([
446
475
  input.supportModuleId,
476
+ ...(input.premiumActivationModuleId
477
+ ? [input.premiumActivationModuleId]
478
+ : []),
447
479
  ...(input.oauthModuleId ? [input.oauthModuleId] : []),
448
480
  ...(input.operationsModuleId ? [input.operationsModuleId] : []),
449
481
  ]),
@@ -92,8 +92,8 @@ HELYX_OAUTH_ENABLED=false
92
92
  # Generated module selection
93
93
  HELYX_MODULE_CATALOGUE_PATH=./helyx.modules.json
94
94
 
95
- # Generated privately by npm run setup. Never change it while Cloud Connect is
96
- # enrolled or a checkpointed Giveaways draw is in progress.
95
+ # Generated privately by npm run setup. Required by Cloud Connect, Giveaways,
96
+ # and Invite Tracking; retain it unchanged across restarts and deployments.
97
97
  # HELYX_DATA_ENCRYPTION_KEY=replace-with-a-base64-encoded-32-byte-key
98
98
 
99
99
  # Official Cloud Connect endpoints
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@helyx/bot",
3
- "version": "0.4.46",
3
+ "version": "0.6.1",
4
4
  "description": "Production Discord runtime for the modular Helyx platform.",
5
5
  "keywords": [
6
6
  "helyx",
@@ -50,14 +50,14 @@
50
50
  "start": "node --env-file-if-exists=../../.env dist/index.js"
51
51
  },
52
52
  "dependencies": {
53
- "@helyx/config": ">=0.5.0 <0.6.0",
53
+ "@helyx/config": ">=1.0.0 <2.0.0",
54
54
  "@helyx/control-protocol": ">=0.4.0 <0.5.0",
55
55
  "@helyx/core": ">=0.1.0 <0.2.0",
56
- "@helyx/database": ">=0.9.0 <0.10.0",
56
+ "@helyx/database": ">=1.0.0 <2.0.0",
57
57
  "@helyx/framework": ">=0.1.0 <0.2.0",
58
- "@helyx/module-manifest": ">=0.8.0 <0.9.0",
59
- "@helyx/platform": ">=0.6.2 <0.7.0",
60
- "@helyx/sdk": ">=0.9.0 <0.10.0",
58
+ "@helyx/module-manifest": ">=0.9.0 <0.10.0",
59
+ "@helyx/platform": ">=0.8.0 <0.9.0",
60
+ "@helyx/sdk": ">=0.11.0 <0.12.0",
61
61
  "@helyx/status-client": ">=0.1.0 <0.2.0",
62
62
  "@helyx/verification-contracts": ">=0.3.0 <0.4.0",
63
63
  "discord.js": "14.27.0",