@helyx/bot 1.3.0 → 1.4.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 +92 -28
- package/README.md +65 -11
- package/dist/cloud-connector.d.ts +11 -4
- package/dist/cloud-connector.js +52 -15
- package/dist/cloud-control-dispatcher.d.ts +8 -3
- package/dist/cloud-control-dispatcher.js +62 -4
- package/dist/control-errors.d.ts +4 -0
- package/dist/control-errors.js +8 -0
- package/dist/control-server-schemas.d.ts +84 -0
- package/dist/control-server-schemas.js +101 -0
- package/dist/control-server.d.ts +21 -7
- package/dist/control-server.js +88 -132
- package/dist/core-retention.d.ts +26 -0
- package/dist/core-retention.js +49 -0
- package/dist/creator-guild-join-service.d.ts +25 -0
- package/dist/creator-guild-join-service.js +218 -0
- package/dist/creator-invite-service.d.ts +37 -0
- package/dist/creator-invite-service.js +686 -0
- package/dist/creator-onboarding-control.d.ts +12 -0
- package/dist/creator-onboarding-control.js +65 -0
- package/dist/creator-onboarding-platform.d.ts +20 -0
- package/dist/creator-onboarding-platform.js +58 -0
- package/dist/dashboard-action-control.d.ts +2 -2
- package/dist/dashboard-action-control.js +2 -3
- package/dist/database-bootstrap.d.ts +36 -0
- package/dist/database-bootstrap.js +101 -0
- package/dist/developer-operations-module.d.ts +1 -0
- package/dist/developer-operations-module.js +34 -16
- package/dist/discord-owned-message-service.d.ts +1 -0
- package/dist/discord-owned-message-service.js +3 -0
- package/dist/guild-command-read-service.d.ts +8 -3
- package/dist/guild-command-read-service.js +14 -7
- package/dist/guild-command-release.d.ts +15 -8
- package/dist/guild-command-release.js +10 -3
- package/dist/guild-command-worker.d.ts +17 -4
- package/dist/guild-command-worker.js +17 -6
- package/dist/guilds.d.ts +24 -3
- package/dist/guilds.js +185 -64
- package/dist/hosted-server-moderation.d.ts +2 -2
- package/dist/hosted-server-moderation.js +10 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +200 -81
- package/dist/interactions.js +16 -3
- package/dist/module-configuration.d.ts +1 -1
- package/dist/module-configuration.js +5 -2
- package/dist/module-logging.d.ts +8 -3
- package/dist/module-logging.js +13 -7
- package/dist/modules.d.ts +1 -0
- package/dist/modules.js +8 -1
- package/dist/mysql-runtime-persistence.d.ts +46 -0
- package/dist/mysql-runtime-persistence.js +91 -0
- package/dist/oauth-module.js +2 -2
- package/dist/oauth-service.d.ts +7 -0
- package/dist/oauth-service.js +3 -0
- package/dist/scheduled-task-runner.d.ts +9 -85
- package/dist/scheduled-task-runner.js +1 -1
- package/dist/shard-contracts.d.ts +135 -0
- package/dist/shard-contracts.js +13 -1
- package/dist/shard-router.d.ts +2 -1
- package/dist/shard-router.js +7 -0
- package/dist/shard-supervisor.js +43 -9
- package/dist/shard-worker.d.ts +5 -2
- package/dist/shard-worker.js +46 -5
- package/dist/supervisor-cloud-directory.d.ts +9 -3
- package/dist/supervisor-cloud-directory.js +8 -12
- package/dist/trivia-data-provider.d.ts +11 -0
- package/dist/trivia-data-provider.js +46 -26
- package/dist/verification-service.d.ts +2 -2
- package/dist/verification-service.js +6 -3
- package/documentation/README.md +5 -3
- package/documentation/examples/.env.example +15 -11
- package/documentation/guides/getting-started.md +29 -12
- package/documentation/guides/privacy-and-support.md +6 -5
- package/documentation/guides/self-hosting.md +16 -11
- package/documentation/modules/auto-moderation.md +7 -5
- package/documentation/modules/building-a-module.md +48 -2
- package/documentation/modules/moderation.md +5 -5
- package/documentation/modules/publishing-a-module.md +6 -4
- package/documentation/modules/testing-a-module.md +39 -4
- package/documentation/reference/environment.md +43 -9
- package/documentation/runbooks/backup-and-recovery.md +26 -7
- package/documentation/runbooks/installation-and-upgrades.md +97 -27
- package/package.json +20 -16
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
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,
|
|
4
|
+
import { checkDatabaseConnection, createDatabasePool, EckoOperationsRepository, inspectConfiguredModuleMigrations, inspectCoreMigrations, inspectCreatorOnboardingMigrations, runCreatorOnboardingMigrations, PremiumAllocationRepository, runCoreMigrations, runConfiguredModuleMigrations, } from "@helyx/database";
|
|
5
5
|
import { createRuntime } from "@helyx/framework";
|
|
6
|
+
import { assertRepositoryEngineSupported } from "@helyx/database/adapters";
|
|
6
7
|
import { createPlatformServices, gamingStatisticsRecordOwner, achievementContributionRecordOwner, PostgresPrivacyProviderExecutor, PlatformMemberRoleGrantOwnershipService, PostgresMemberRoleGrantOwnershipStore, } from "@helyx/platform";
|
|
7
8
|
import { HELYX_SERVICE_NAMES, } from "@helyx/sdk";
|
|
8
9
|
import { Client, Events, GatewayIntentBits, Partials } from "discord.js";
|
|
@@ -15,7 +16,7 @@ import { ClientCloudDiscordDirectory, CloudConnectorController, } from "./cloud-
|
|
|
15
16
|
import { createCloudManagementModule } from "./cloud-management-module.js";
|
|
16
17
|
import { installInteractions } from "./interactions.js";
|
|
17
18
|
import { installGuildTracking } from "./guilds.js";
|
|
18
|
-
import { loadConfiguredModulePackages, platformServiceOptions, } from "./modules.js";
|
|
19
|
+
import { assertConfiguredModuleDatabaseCompatibility, loadConfiguredModulePackages, platformServiceOptions, } from "./modules.js";
|
|
19
20
|
import { resolveModulePackageSpecifiers } from "./module-configuration.js";
|
|
20
21
|
import { createPermissionsModule } from "./permissions-module.js";
|
|
21
22
|
import { createManagementModule } from "./management-module.js";
|
|
@@ -42,12 +43,16 @@ import { DiscordOwnedMessageDeletionService } from "./discord-owned-message-serv
|
|
|
42
43
|
import { DiscordGuildHealthInspection } from "./discord-guild-health.js";
|
|
43
44
|
import { DiscordAnalyticsInspection } from "./discord-analytics.js";
|
|
44
45
|
import { DiscordInviteInspection } from "./discord-invites.js";
|
|
46
|
+
import { installCreatorInvitePlatform } from "./creator-onboarding-platform.js";
|
|
45
47
|
import { GuildCommandWorker } from "./guild-command-worker.js";
|
|
46
48
|
import { GuildCommandDiscordClient } from "./guild-command-discord-client.js";
|
|
47
49
|
import { GuildCommandReadService } from "./guild-command-read-service.js";
|
|
48
50
|
import { scheduleGuildCommandRelease } from "./guild-command-release.js";
|
|
49
51
|
import { CoreResourceAuthorizationService } from "./resource-authorization.js";
|
|
50
52
|
import { PostgresTriviaDataProvider } from "./trivia-data-provider.js";
|
|
53
|
+
import { MysqlTriviaDataProvider } from "./trivia-data-provider.js";
|
|
54
|
+
import { cleanupExpiredCoreRecords, createPostgresCoreRetentionRepositories, } from "./core-retention.js";
|
|
55
|
+
import { openMysqlBotRuntimePersistence } from "./mysql-runtime-persistence.js";
|
|
51
56
|
import { createLocalScheduledTaskRoute, createScheduledTaskRunner, schedulerOperationalHealth, } from "./scheduled-task-runner.js";
|
|
52
57
|
import { createLocalNumericWriteBehind } from "./numeric-write-behind.js";
|
|
53
58
|
import { discordRestOptions } from "./discord-rest.js";
|
|
@@ -56,6 +61,7 @@ import { assertReviewedPremiumCatalogue, requirePremiumProviderEnvironment, } fr
|
|
|
56
61
|
import { enforcePremiumCatalogue, PREMIUM_CATALOGUE_ENFORCEMENT_INTERVAL_MS, } from "./premium-catalogue-enforcement.js";
|
|
57
62
|
import { HostedBotInviteGateClient, HostedServerLeaveWorker, HostedServerModerationClient, PostgresHostedGuildSafety, } from "./hosted-server-moderation.js";
|
|
58
63
|
export { loadConfiguredModulePackages } from "./modules.js";
|
|
64
|
+
export { assertConfiguredModuleDatabaseCompatibility } from "./modules.js";
|
|
59
65
|
export { resolveModulePackageSpecifiers } from "./module-configuration.js";
|
|
60
66
|
export * from "./control-server.js";
|
|
61
67
|
const bootstrapLogger = pino({ base: { component: "bot" } });
|
|
@@ -65,6 +71,11 @@ export async function startBot(processEnvironment = process.env) {
|
|
|
65
71
|
if (processEnvironment.HELYX_SHARD_WORKER_CONTRACT !== undefined)
|
|
66
72
|
return startShardWorker(processEnvironment, parseShardWorkerLaunchContract(processEnvironment.HELYX_SHARD_WORKER_CONTRACT));
|
|
67
73
|
const environment = parseBotEnvironment(processEnvironment);
|
|
74
|
+
assertRepositoryEngineSupported(environment.DATABASE_ENGINE, ["postgresql", "mysql"], "Bot platform");
|
|
75
|
+
if (environment.DATABASE_ENGINE === "mysql" &&
|
|
76
|
+
environment.HELYX_SHARD_MODE !== "off") {
|
|
77
|
+
throw new Error("MySQL bot runtime does not support shard mode");
|
|
78
|
+
}
|
|
68
79
|
bootstrapLogger.level = environment.LOG_LEVEL;
|
|
69
80
|
const handle = environment.HELYX_SHARD_MODE !== "off"
|
|
70
81
|
? await startShardSupervisor(environment, processEnvironment, resolveBotWorkerEntryPoint())
|
|
@@ -117,15 +128,49 @@ export async function startBot(processEnvironment = process.env) {
|
|
|
117
128
|
export function resolveBotWorkerEntryPoint(entryUrl = import.meta.url) {
|
|
118
129
|
return fileURLToPath(entryUrl);
|
|
119
130
|
}
|
|
131
|
+
async function inspectRequiredCoreMigrations(database) {
|
|
132
|
+
const inspection = await inspectCoreMigrations(database);
|
|
133
|
+
assertNoPendingMigrations("core", inspection.pending);
|
|
134
|
+
return { applied: [], verified: inspection.verified };
|
|
135
|
+
}
|
|
136
|
+
async function inspectRequiredModuleMigrations(database, sources) {
|
|
137
|
+
const inspections = await inspectConfiguredModuleMigrations(database, sources);
|
|
138
|
+
for (const inspection of inspections)
|
|
139
|
+
assertNoPendingMigrations(`module ${inspection.moduleId}`, inspection.pending);
|
|
140
|
+
return inspections.map((inspection) => ({
|
|
141
|
+
moduleId: inspection.moduleId,
|
|
142
|
+
applied: [],
|
|
143
|
+
verified: inspection.verified,
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
146
|
+
function assertNoPendingMigrations(scope, pending) {
|
|
147
|
+
if (pending.length > 0)
|
|
148
|
+
throw new Error(`Hosted ${scope} migrations must be applied by the dedicated primary migration identity before bot startup: ${pending.join(", ")}`);
|
|
149
|
+
}
|
|
120
150
|
async function startUnshardedBot(processEnvironment) {
|
|
121
151
|
const environment = parseBotEnvironment(processEnvironment);
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
152
|
+
const mysqlModulePackages = environment.DATABASE_ENGINE === "mysql"
|
|
153
|
+
? await loadConfiguredModulePackages(await resolveModulePackageSpecifiers(environment.HELYX_MODULE_PACKAGES, undefined, undefined, environment.HELYX_MODULE_CATALOGUE_PATH, environment.HELYX_RUNTIME_PROFILE))
|
|
154
|
+
: undefined;
|
|
155
|
+
const mysql = mysqlModulePackages
|
|
156
|
+
? await openMysqlBotRuntimePersistence({
|
|
157
|
+
environment,
|
|
158
|
+
modulePackages: mysqlModulePackages,
|
|
159
|
+
})
|
|
160
|
+
: undefined;
|
|
161
|
+
const database = mysql
|
|
162
|
+
? undefined
|
|
163
|
+
: createDatabasePool({
|
|
164
|
+
connectionString: environment.DATABASE_URL,
|
|
165
|
+
sslMode: environment.DATABASE_SSL,
|
|
166
|
+
applicationName: "helyx-bot",
|
|
167
|
+
});
|
|
168
|
+
const hostedOwnerObserver = database
|
|
169
|
+
? new PostgresHostedGuildSafety(database)
|
|
170
|
+
: undefined;
|
|
171
|
+
const eckoOperations = database
|
|
172
|
+
? new EckoOperationsRepository(database)
|
|
173
|
+
: undefined;
|
|
129
174
|
const hostedSafety = environment.HELYX_HOSTED_MODERATION_ENABLED
|
|
130
175
|
? hostedOwnerObserver
|
|
131
176
|
: undefined;
|
|
@@ -148,70 +193,77 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
148
193
|
let databaseClosed = false;
|
|
149
194
|
let retentionCleanupTimer;
|
|
150
195
|
let premiumCatalogueTimer;
|
|
196
|
+
let creatorInvitePlatform;
|
|
151
197
|
async function closeDatabase() {
|
|
152
198
|
if (databaseClosed)
|
|
153
199
|
return;
|
|
154
200
|
databaseClosed = true;
|
|
155
201
|
if (privacyDatabase)
|
|
156
202
|
await privacyDatabase.end();
|
|
157
|
-
|
|
203
|
+
if (database)
|
|
204
|
+
await database.end();
|
|
205
|
+
else
|
|
206
|
+
await mysql?.database.close();
|
|
158
207
|
}
|
|
159
208
|
try {
|
|
160
|
-
|
|
209
|
+
if (database)
|
|
210
|
+
await checkDatabaseConnection(database);
|
|
161
211
|
if (privacyDatabase) {
|
|
162
212
|
await checkDatabaseConnection(privacyDatabase);
|
|
213
|
+
if (!database)
|
|
214
|
+
throw new Error("Privacy executor requires PostgreSQL");
|
|
163
215
|
await assertDistinctDatabaseRoles(database, privacyDatabase);
|
|
164
216
|
}
|
|
165
|
-
const migrationResult =
|
|
217
|
+
const migrationResult = database
|
|
218
|
+
? environment.HELYX_DATABASE_STARTUP_MODE === "inspect"
|
|
219
|
+
? await inspectRequiredCoreMigrations(database)
|
|
220
|
+
: await runCoreMigrations(database)
|
|
221
|
+
: mysql.database.migrations.core;
|
|
166
222
|
bootstrapLogger.info({
|
|
167
223
|
event: "database.ready",
|
|
168
224
|
appliedMigrations: migrationResult.applied,
|
|
169
225
|
verifiedMigrationCount: migrationResult.verified.length,
|
|
170
226
|
}, "Core database ready");
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
verificationAudit > 0 ||
|
|
186
|
-
commandRecordCount > 0 ||
|
|
187
|
-
hostedLeaveDeliveries > 0) {
|
|
188
|
-
bootstrapLogger.info({
|
|
189
|
-
event: "database.retention_cleaned",
|
|
190
|
-
interactionSessions: sessions,
|
|
191
|
-
rateLimitWindows: rateLimits,
|
|
192
|
-
verificationAuditEvents: verificationAudit,
|
|
193
|
-
commandRecords: commandRecordCount,
|
|
194
|
-
hostedLeaveDeliveries,
|
|
195
|
-
}, "Expired core records removed");
|
|
227
|
+
const modulePackages = mysqlModulePackages ??
|
|
228
|
+
(await loadConfiguredModulePackages(await resolveModulePackageSpecifiers(environment.HELYX_MODULE_PACKAGES, undefined, undefined, environment.HELYX_MODULE_CATALOGUE_PATH, environment.HELYX_RUNTIME_PROFILE)));
|
|
229
|
+
assertConfiguredModuleDatabaseCompatibility(modulePackages, environment.DATABASE_ENGINE);
|
|
230
|
+
const moduleMigrationSources = modulePackages.map(({ definition, packageRoot }) => ({
|
|
231
|
+
moduleId: definition.manifest.id,
|
|
232
|
+
moduleVersion: definition.manifest.version,
|
|
233
|
+
migrationsDirectory: join(packageRoot, "migrations"),
|
|
234
|
+
}));
|
|
235
|
+
if (database &&
|
|
236
|
+
environment.HELYX_RUNTIME_PROFILE === "hosted" &&
|
|
237
|
+
modulePackages.some(({ definition }) => definition.manifest.id === "helyx.creator-connections")) {
|
|
238
|
+
if (environment.HELYX_DATABASE_STARTUP_MODE === "inspect") {
|
|
239
|
+
const inspection = await inspectCreatorOnboardingMigrations(database);
|
|
240
|
+
assertNoPendingMigrations("hosted creator onboarding", inspection.pending);
|
|
196
241
|
}
|
|
242
|
+
else
|
|
243
|
+
await runCreatorOnboardingMigrations(database);
|
|
244
|
+
}
|
|
245
|
+
const inspectedModuleMigrationResults = database && environment.HELYX_DATABASE_STARTUP_MODE === "inspect"
|
|
246
|
+
? await inspectRequiredModuleMigrations(database, moduleMigrationSources)
|
|
247
|
+
: undefined;
|
|
248
|
+
const retentionRepositories = mysql?.coreRetention ??
|
|
249
|
+
createPostgresCoreRetentionRepositories(database);
|
|
250
|
+
const runRetentionCleanup = async () => {
|
|
251
|
+
await cleanupExpiredCoreRecords(retentionRepositories, bootstrapLogger);
|
|
197
252
|
};
|
|
198
|
-
await
|
|
253
|
+
await runRetentionCleanup().catch((error) => {
|
|
199
254
|
bootstrapLogger.warn({ event: "database.retention_cleanup_failed", error: safeError(error) }, "Expired core record cleanup failed");
|
|
200
255
|
});
|
|
201
|
-
retentionCleanupTimer = setInterval(() => void
|
|
256
|
+
retentionCleanupTimer = setInterval(() => void runRetentionCleanup().catch((error) => {
|
|
202
257
|
bootstrapLogger.warn({
|
|
203
258
|
event: "database.retention_cleanup_failed",
|
|
204
259
|
error: safeError(error),
|
|
205
260
|
}, "Expired core record cleanup failed");
|
|
206
261
|
}), RETENTION_CLEANUP_INTERVAL_MS);
|
|
207
262
|
retentionCleanupTimer.unref();
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
moduleVersion: definition.manifest.version,
|
|
213
|
-
migrationsDirectory: join(packageRoot, "migrations"),
|
|
214
|
-
})));
|
|
263
|
+
const moduleMigrationResults = inspectedModuleMigrationResults ??
|
|
264
|
+
(database
|
|
265
|
+
? await runConfiguredModuleMigrations(database, moduleMigrationSources)
|
|
266
|
+
: mysql.database.migrations.modules);
|
|
215
267
|
bootstrapLogger.info({
|
|
216
268
|
event: "database.modules_ready",
|
|
217
269
|
modules: moduleMigrationResults.map((result) => ({
|
|
@@ -222,14 +274,16 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
222
274
|
}, "Module databases ready");
|
|
223
275
|
const featureModules = modulePackages.map(({ definition }) => definition);
|
|
224
276
|
assertReviewedPremiumCatalogue(featureModules);
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
bootstrapLogger.
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
277
|
+
if (database) {
|
|
278
|
+
await enforcePremiumCatalogue(database, bootstrapLogger);
|
|
279
|
+
premiumCatalogueTimer = setInterval(() => void enforcePremiumCatalogue(database, bootstrapLogger).catch((error) => {
|
|
280
|
+
bootstrapLogger.warn({
|
|
281
|
+
event: "premium.catalogue_enforcement_failed",
|
|
282
|
+
error: safeError(error),
|
|
283
|
+
}, "Premium module catalogue enforcement failed");
|
|
284
|
+
}), PREMIUM_CATALOGUE_ENFORCEMENT_INTERVAL_MS);
|
|
285
|
+
premiumCatalogueTimer.unref();
|
|
286
|
+
}
|
|
233
287
|
const client = new Client({
|
|
234
288
|
rest: discordRestOptions,
|
|
235
289
|
intents: gatewayIntentsForModules(featureModules, environment.HELYX_MESSAGE_CONTENT_ENABLED, environment.HELYX_GUILD_MEMBERS_ENABLED, environment.HELYX_RUNTIME_PROFILE),
|
|
@@ -239,7 +293,9 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
239
293
|
const guildResources = new DiscordGuildResourceDirectory(client);
|
|
240
294
|
const cloudConnector = new CloudConnectorController({
|
|
241
295
|
environment,
|
|
242
|
-
|
|
296
|
+
...(mysql
|
|
297
|
+
? { repository: mysql.cloudConnector }
|
|
298
|
+
: { database: database }),
|
|
243
299
|
discord: new ClientCloudDiscordDirectory(client),
|
|
244
300
|
modules: modulePackages,
|
|
245
301
|
coreVersion: readBotVersion(),
|
|
@@ -257,7 +313,7 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
257
313
|
const supportModule = createSupportModule(cloudConnector);
|
|
258
314
|
const privacyModule = createPrivacyModule();
|
|
259
315
|
const premiumEnvironment = requirePremiumProviderEnvironment(featureModules, environment.STRIPE_ENVIRONMENT);
|
|
260
|
-
const premiumActivationModule = !premiumEnvironment
|
|
316
|
+
const premiumActivationModule = !premiumEnvironment || !database
|
|
261
317
|
? undefined
|
|
262
318
|
: createPremiumActivationModule({
|
|
263
319
|
allocations: new PremiumAllocationRepository(database),
|
|
@@ -286,7 +342,10 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
286
342
|
const buildHealth = async () => {
|
|
287
343
|
let databaseHealthy = true;
|
|
288
344
|
try {
|
|
289
|
-
|
|
345
|
+
if (database)
|
|
346
|
+
await checkDatabaseConnection(database);
|
|
347
|
+
else
|
|
348
|
+
await mysql.database.healthCheck();
|
|
290
349
|
}
|
|
291
350
|
catch {
|
|
292
351
|
databaseHealthy = false;
|
|
@@ -342,6 +401,7 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
342
401
|
internalGuildId: environment.DISCORD_INTERNAL_GUILD_ID,
|
|
343
402
|
approvedRoleIds: environment.APPROVED_ROLES,
|
|
344
403
|
botVersion: readBotVersion(),
|
|
404
|
+
databaseEngine: environment.DATABASE_ENGINE,
|
|
345
405
|
...(deployedCommit ? { deploymentReference: deployedCommit } : {}),
|
|
346
406
|
coreFunctions: [
|
|
347
407
|
managementModule,
|
|
@@ -354,7 +414,9 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
354
414
|
].map(({ manifest }) => manifest),
|
|
355
415
|
modules: featureModules.map(({ manifest }) => manifest),
|
|
356
416
|
processLabel: "unsharded bot process",
|
|
357
|
-
triviaData:
|
|
417
|
+
triviaData: mysql
|
|
418
|
+
? new MysqlTriviaDataProvider((await import("@helyx/database-mysql")).createMysqlQueryExecutor(mysql.pool))
|
|
419
|
+
: new PostgresTriviaDataProvider(database),
|
|
358
420
|
health: {
|
|
359
421
|
getHealth: buildHealth,
|
|
360
422
|
},
|
|
@@ -368,12 +430,29 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
368
430
|
const platformServices = new Map();
|
|
369
431
|
const serviceAccess = new ServiceRegistry(platformServices);
|
|
370
432
|
const serviceOptions = platformServiceOptions(environment, publicModules);
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
433
|
+
const platformFactory = mysql
|
|
434
|
+
? mysql.createPlatformServices(publicModules, {
|
|
435
|
+
...serviceOptions,
|
|
436
|
+
recordRecoveryOwnership: { mode: "single-worker" },
|
|
437
|
+
activationServices: serviceAccess,
|
|
438
|
+
})
|
|
439
|
+
: createPlatformServices(database, publicModules, {
|
|
440
|
+
...serviceOptions,
|
|
441
|
+
recordRecoveryOwnership: { mode: "single-worker" },
|
|
442
|
+
activationServices: serviceAccess,
|
|
443
|
+
});
|
|
444
|
+
for (const [name, service] of platformFactory) {
|
|
375
445
|
platformServices.set(name, service);
|
|
376
446
|
}
|
|
447
|
+
const applicationUrls = serviceAccess.get(HELYX_SERVICE_NAMES.applicationUrls);
|
|
448
|
+
platformServices.set(HELYX_SERVICE_NAMES.applicationUrls, {
|
|
449
|
+
dashboardBaseUrl: applicationUrls.dashboardBaseUrl,
|
|
450
|
+
get dashboardInstallationId() {
|
|
451
|
+
return environment.HELYX_RUNTIME_PROFILE === "self-hosted"
|
|
452
|
+
? cloudConnector.status().installationId
|
|
453
|
+
: undefined;
|
|
454
|
+
},
|
|
455
|
+
});
|
|
377
456
|
await serviceAccess
|
|
378
457
|
.get(HELYX_SERVICE_NAMES.moduleAvailability)
|
|
379
458
|
.refresh();
|
|
@@ -385,6 +464,10 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
385
464
|
internalSecret: environment.HELYX_VERIFICATION_INTERNAL_SECRET,
|
|
386
465
|
installationId: environment.HELYX_VERIFICATION_INSTALLATION_ID,
|
|
387
466
|
publicSigningKeys: environment.HELYX_VERIFICATION_SIGNING_PUBLIC_KEYS,
|
|
467
|
+
runtimeEnvironment: environment.NODE_ENV,
|
|
468
|
+
...(environment.HELYX_VERIFICATION_PUBLIC_URL === undefined
|
|
469
|
+
? {}
|
|
470
|
+
: { publicOrigin: environment.HELYX_VERIFICATION_PUBLIC_URL }),
|
|
388
471
|
}));
|
|
389
472
|
platformServices.set(HELYX_SERVICE_NAMES.verificationMembers, new DiscordVerificationMemberService(client));
|
|
390
473
|
}
|
|
@@ -399,8 +482,29 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
399
482
|
platformServices.set(HELYX_SERVICE_NAMES.memberEnforcement, new DiscordModerationMemberEnforcementAdapter(client));
|
|
400
483
|
const memberRoles = new DiscordMemberRoleService(client);
|
|
401
484
|
platformServices.set(HELYX_SERVICE_NAMES.memberRoles, memberRoles);
|
|
402
|
-
platformServices.set(HELYX_SERVICE_NAMES.memberRoleGrants, new PlatformMemberRoleGrantOwnershipService(
|
|
485
|
+
platformServices.set(HELYX_SERVICE_NAMES.memberRoleGrants, new PlatformMemberRoleGrantOwnershipService(mysql
|
|
486
|
+
? mysql.memberRoleOwnership
|
|
487
|
+
: new PostgresMemberRoleGrantOwnershipStore(database), memberRoles));
|
|
403
488
|
platformServices.set(HELYX_SERVICE_NAMES.discordResources, new DiscordResourceInspection(client));
|
|
489
|
+
if (database && environment.HELYX_RUNTIME_PROFILE === "hosted")
|
|
490
|
+
creatorInvitePlatform = await installCreatorInvitePlatform({
|
|
491
|
+
client,
|
|
492
|
+
database,
|
|
493
|
+
services: serviceAccess,
|
|
494
|
+
modules: publicModules,
|
|
495
|
+
encryptionKey: environment.HELYX_DATA_ENCRYPTION_KEY,
|
|
496
|
+
...(environment.HELYX_CLOUD_API_URL &&
|
|
497
|
+
environment.HELYX_OAUTH_INTERNAL_SECRET
|
|
498
|
+
? {
|
|
499
|
+
oauthBroker: {
|
|
500
|
+
baseUrl: environment.HELYX_CLOUD_API_URL,
|
|
501
|
+
secret: environment.HELYX_OAUTH_INTERNAL_SECRET,
|
|
502
|
+
},
|
|
503
|
+
}
|
|
504
|
+
: {}),
|
|
505
|
+
register: (name, service) => platformServices.set(name, service),
|
|
506
|
+
warn: (event) => bootstrapLogger.warn({ event }, "Creator invite cleanup will retry"),
|
|
507
|
+
});
|
|
404
508
|
const moderationPlatformServices = await installModerationPlatformServices({
|
|
405
509
|
services: platformServices,
|
|
406
510
|
serviceAccess,
|
|
@@ -421,7 +525,7 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
421
525
|
if (serviceOptions.inviteFingerprintKey)
|
|
422
526
|
platformServices.set(HELYX_SERVICE_NAMES.discordInvites, new DiscordInviteInspection(client, serviceOptions.inviteFingerprintKey));
|
|
423
527
|
if (featureModules.some(({ manifest }) => manifest.id === "helyx.logging")) {
|
|
424
|
-
platformServices.set(HELYX_SERVICE_NAMES.moduleLogging, new DiscordModuleLoggingService(client, database, publicModules, bootstrapLogger));
|
|
528
|
+
platformServices.set(HELYX_SERVICE_NAMES.moduleLogging, new DiscordModuleLoggingService(client, mysql?.logging ?? database, publicModules, bootstrapLogger));
|
|
425
529
|
}
|
|
426
530
|
const runtime = createRuntime({
|
|
427
531
|
modules,
|
|
@@ -437,14 +541,18 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
437
541
|
...(cloudManagementModule ? [cloudManagementModule.manifest.id] : []),
|
|
438
542
|
...(operationsModule ? [operationsModule.manifest.id] : []),
|
|
439
543
|
]);
|
|
440
|
-
const prepareGuilds = installGuildTracking(client, database, publicModules, bootstrapLogger, enabledCoreModuleIds, undefined, environment.HELYX_GUILD_RECONCILIATION_INTERVAL_SECONDS * 1_000, undefined, environment.DISCORD_CLIENT_ID, hostedSafety, hostedInviteGate, hostedOwnerObserver);
|
|
441
|
-
const hostedLeaveWorker =
|
|
544
|
+
const prepareGuilds = installGuildTracking(client, mysql?.guildTracking ?? database, publicModules, bootstrapLogger, enabledCoreModuleIds, undefined, environment.HELYX_GUILD_RECONCILIATION_INTERVAL_SECONDS * 1_000, undefined, environment.DISCORD_CLIENT_ID, hostedSafety, hostedInviteGate, hostedOwnerObserver);
|
|
545
|
+
const hostedLeaveWorker = database && environment.HELYX_HOSTED_MODERATION_ENABLED
|
|
546
|
+
? new HostedServerLeaveWorker(database, client, bootstrapLogger)
|
|
547
|
+
: undefined;
|
|
442
548
|
const commandsReady = installInteractions(client, modules, {
|
|
443
549
|
globalCommandMode: environment.HELYX_GLOBAL_COMMAND_MODE,
|
|
444
550
|
...(environment.DISCORD_INTERNAL_GUILD_ID
|
|
445
551
|
? { internalGuildId: environment.DISCORD_INTERNAL_GUILD_ID }
|
|
446
552
|
: {}),
|
|
447
|
-
|
|
553
|
+
// The guild-command worker owns the complete collection. A write-disabled
|
|
554
|
+
// startup must not replace existing module commands with internal roots.
|
|
555
|
+
synchroniseInternalGuild: false,
|
|
448
556
|
prepare: prepareGuilds,
|
|
449
557
|
selfAuthorizingGuildModuleIds: new Set([
|
|
450
558
|
supportModule.manifest.id,
|
|
@@ -459,7 +567,7 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
459
567
|
runtimeProfile: environment.HELYX_RUNTIME_PROFILE,
|
|
460
568
|
evidenceStore: moderationPlatformServices.evidenceStore,
|
|
461
569
|
});
|
|
462
|
-
const controlService = new BotControlService(database, [
|
|
570
|
+
const controlService = new BotControlService(mysql?.control ?? database, [
|
|
463
571
|
...publicModules,
|
|
464
572
|
gamingStatisticsRecordOwner,
|
|
465
573
|
achievementContributionRecordOwner,
|
|
@@ -480,14 +588,15 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
480
588
|
channelId: environment.HELYX_SUPPORT_STAFF_CHANNEL_ID,
|
|
481
589
|
}
|
|
482
590
|
: undefined, undefined, undefined, featureModules);
|
|
483
|
-
|
|
591
|
+
const guildCommandReads = new GuildCommandReadService(mysql?.commandRead ?? database, environment.DISCORD_CLIENT_ID, () => guildCommandWorker?.getHealth(), environment.DISCORD_TOKEN, environment.HELYX_GUILD_COMMAND_WRITES_ENABLED, environment.HELYX_GUILD_COMMAND_WRITE_GUILD_IDS, environment.HELYX_GUILD_COMMAND_WRITE_SCOPE === "all", modules);
|
|
592
|
+
cloudConnector.setControlHandler(controlService, guildCommandReads);
|
|
484
593
|
let controlServer;
|
|
485
594
|
let shuttingDown = false;
|
|
486
595
|
async function shutdown(reason = "programmatic") {
|
|
487
596
|
if (shuttingDown)
|
|
488
597
|
return;
|
|
489
598
|
shuttingDown = true;
|
|
490
|
-
prepareGuilds.stop();
|
|
599
|
+
await prepareGuilds.stop();
|
|
491
600
|
await hostedLeaveWorker?.stop();
|
|
492
601
|
process.off("SIGINT", onSigint);
|
|
493
602
|
process.off("SIGTERM", onSigterm);
|
|
@@ -495,6 +604,7 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
495
604
|
clearInterval(retentionCleanupTimer);
|
|
496
605
|
if (premiumCatalogueTimer)
|
|
497
606
|
clearInterval(premiumCatalogueTimer);
|
|
607
|
+
await creatorInvitePlatform?.stop();
|
|
498
608
|
await scheduledTasks?.stop().catch((error) => {
|
|
499
609
|
process.exitCode = 1;
|
|
500
610
|
bootstrapLogger.error({ event: "scheduler.stop_failed", error: safeError(error) }, "Scheduled task runner shutdown failed");
|
|
@@ -566,7 +676,9 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
566
676
|
await commandsReady;
|
|
567
677
|
hostedLeaveWorker?.start();
|
|
568
678
|
scheduledTasks = createScheduledTaskRunner({
|
|
569
|
-
|
|
679
|
+
...(mysql
|
|
680
|
+
? { repository: mysql.scheduledTasks }
|
|
681
|
+
: { database: database }),
|
|
570
682
|
modules: publicModules,
|
|
571
683
|
route: createLocalScheduledTaskRoute({
|
|
572
684
|
modules: publicModules,
|
|
@@ -578,7 +690,9 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
578
690
|
});
|
|
579
691
|
scheduledTasks.start();
|
|
580
692
|
guildCommandWorker = new GuildCommandWorker({
|
|
581
|
-
|
|
693
|
+
...(mysql
|
|
694
|
+
? { repositories: mysql.commandWorker }
|
|
695
|
+
: { database: database }),
|
|
582
696
|
modules,
|
|
583
697
|
logger: bootstrapLogger,
|
|
584
698
|
workerId: `unsharded-command:${unshardedSupervisorId}`,
|
|
@@ -601,7 +715,7 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
601
715
|
if (environment.HELYX_GUILD_COMMAND_WRITES_ENABLED &&
|
|
602
716
|
environment.HELYX_GUILD_COMMAND_WRITE_SCOPE === "all") {
|
|
603
717
|
void scheduleGuildCommandRelease({
|
|
604
|
-
database,
|
|
718
|
+
...(mysql ? mysql.commandRelease : { database: database }),
|
|
605
719
|
applicationId: environment.DISCORD_CLIENT_ID,
|
|
606
720
|
releaseId: environment.HELYX_GUILD_COMMAND_RELEASE_ID,
|
|
607
721
|
})
|
|
@@ -636,18 +750,22 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
636
750
|
controlServer = await createBotControlServer({
|
|
637
751
|
service: controlService,
|
|
638
752
|
secret: environment.HELYX_BOT_CONTROL_SECRET,
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
753
|
+
...(hostedLeaveWorker && hostedOwnerObserver
|
|
754
|
+
? {
|
|
755
|
+
deliverHostedServerLeave: (input) => hostedLeaveWorker.deliverOperation(input),
|
|
756
|
+
refreshHostedServerOwner: async (guildId) => {
|
|
757
|
+
const guild = client.guilds.cache.get(guildId);
|
|
758
|
+
if (!guild)
|
|
759
|
+
throw new BotControlTransportError(404, "The hosted server is not installed");
|
|
760
|
+
return hostedOwnerObserver.refreshOwner(guild);
|
|
761
|
+
},
|
|
762
|
+
}
|
|
763
|
+
: {}),
|
|
646
764
|
logger: { level: environment.LOG_LEVEL },
|
|
647
765
|
runtimeConfiguration: {
|
|
648
766
|
hostedModerationEnabled: environment.HELYX_HOSTED_MODERATION_ENABLED,
|
|
649
767
|
},
|
|
650
|
-
guildCommands:
|
|
768
|
+
guildCommands: guildCommandReads,
|
|
651
769
|
...(ecko ? { ecko } : {}),
|
|
652
770
|
});
|
|
653
771
|
await controlServer.listen({
|
|
@@ -664,6 +782,7 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
664
782
|
}
|
|
665
783
|
}
|
|
666
784
|
catch (error) {
|
|
785
|
+
await prepareGuilds.stop();
|
|
667
786
|
await scheduledTasks?.stop().catch(() => undefined);
|
|
668
787
|
await guildCommandWorker?.stop().catch(() => undefined);
|
|
669
788
|
await cloudConnector.stop().catch(() => undefined);
|
package/dist/interactions.js
CHANGED
|
@@ -229,10 +229,16 @@ export function installInteractions(client, modules, registration, logger, servi
|
|
|
229
229
|
await interaction.reply(staleComponentResponse());
|
|
230
230
|
return;
|
|
231
231
|
}
|
|
232
|
-
if (modal
|
|
232
|
+
if (modal.contribution.acknowledgement !== undefined &&
|
|
233
233
|
!interaction.deferred &&
|
|
234
234
|
!interaction.replied) {
|
|
235
|
-
|
|
235
|
+
if (modal.contribution.acknowledgement === "deferred-update" &&
|
|
236
|
+
interaction.isFromMessage()) {
|
|
237
|
+
await interaction.deferUpdate();
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
|
241
|
+
}
|
|
236
242
|
}
|
|
237
243
|
if (modal &&
|
|
238
244
|
(await authorizeComponent(interaction, modal.moduleId, services, undefined, isInstallationIndependentModule(modulesById, modal.moduleId), modulesById.get(modal.moduleId)))) {
|
|
@@ -727,7 +733,14 @@ function normalizeAutocompleteChoices(choices) {
|
|
|
727
733
|
}
|
|
728
734
|
function createModalContext(interaction, services) {
|
|
729
735
|
const updateResponse = interaction.isFromMessage()
|
|
730
|
-
? async (response) =>
|
|
736
|
+
? async (response) => {
|
|
737
|
+
if (interaction.deferred || interaction.replied) {
|
|
738
|
+
await interaction.editReply(toUpdateOptions(response));
|
|
739
|
+
}
|
|
740
|
+
else {
|
|
741
|
+
await interaction.update(toUpdateOptions(response));
|
|
742
|
+
}
|
|
743
|
+
}
|
|
731
744
|
: undefined;
|
|
732
745
|
return {
|
|
733
746
|
interactionId: interaction.id,
|
|
@@ -5,6 +5,6 @@ type CatalogueReader = (path: CatalogueLocation) => Promise<string>;
|
|
|
5
5
|
* optional runtime allowlist. A package being installed is never sufficient
|
|
6
6
|
* to make it executable: it must be named by one of these explicit sources.
|
|
7
7
|
*/
|
|
8
|
-
export declare function resolveModulePackageSpecifiers(configuredPackages: readonly string[], bundledCatalogueUrl?: URL, reader?: CatalogueReader, selectedCataloguePath?: string): Promise<string[]>;
|
|
8
|
+
export declare function resolveModulePackageSpecifiers(configuredPackages: readonly string[], bundledCatalogueUrl?: URL, reader?: CatalogueReader, selectedCataloguePath?: string, runtimeProfile?: "hosted" | "self-hosted"): Promise<string[]>;
|
|
9
9
|
export {};
|
|
10
10
|
//# sourceMappingURL=module-configuration.d.ts.map
|
|
@@ -5,6 +5,7 @@ const officialModulePackage = /^@helyx\/module-[a-z0-9][a-z0-9-]*$/u;
|
|
|
5
5
|
const modulePackageCatalogueSchema = z
|
|
6
6
|
.object({
|
|
7
7
|
schemaVersion: z.literal(1),
|
|
8
|
+
databaseEngine: z.enum(["postgresql", "mysql"]).optional(),
|
|
8
9
|
packages: z
|
|
9
10
|
.array(z.string().regex(officialModulePackage))
|
|
10
11
|
.superRefine((packages, context) => {
|
|
@@ -23,8 +24,10 @@ const defaultCatalogueUrl = new URL("../hosted-modules.json", import.meta.url);
|
|
|
23
24
|
* optional runtime allowlist. A package being installed is never sufficient
|
|
24
25
|
* to make it executable: it must be named by one of these explicit sources.
|
|
25
26
|
*/
|
|
26
|
-
export async function resolveModulePackageSpecifiers(configuredPackages, bundledCatalogueUrl = defaultCatalogueUrl, reader = (path) => readFile(path, "utf8"), selectedCataloguePath) {
|
|
27
|
-
const bundledPackages =
|
|
27
|
+
export async function resolveModulePackageSpecifiers(configuredPackages, bundledCatalogueUrl = defaultCatalogueUrl, reader = (path) => readFile(path, "utf8"), selectedCataloguePath, runtimeProfile = "hosted") {
|
|
28
|
+
const bundledPackages = runtimeProfile === "hosted"
|
|
29
|
+
? await readModuleCatalogue(bundledCatalogueUrl, reader, true, "hosted")
|
|
30
|
+
: [];
|
|
28
31
|
const selectedPackages = selectedCataloguePath
|
|
29
32
|
? await readModuleCatalogue(resolve(selectedCataloguePath), reader, false, "selected")
|
|
30
33
|
: [];
|
package/dist/module-logging.d.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
import { type DatabasePool } from "@helyx/database";
|
|
1
|
+
import { InstallationRepository, ModuleConfigurationRepository, ModuleLoggingPolicyRepository, type DatabasePool } from "@helyx/database";
|
|
2
|
+
import type { InstallationRepository as MysqlInstallationRepository, ModuleConfigurationRepository as MysqlModuleConfigurationRepository, ModuleLoggingPolicyRepository as MysqlModuleLoggingPolicyRepository } from "@helyx/database-mysql";
|
|
2
3
|
import type { ModuleDefinition, ModuleLogDetail, ModuleLoggingService } from "@helyx/sdk";
|
|
3
4
|
import type { Client } from "discord.js";
|
|
4
5
|
import { type InteractionLogger } from "./interactions.js";
|
|
6
|
+
export interface ModuleLoggingRepositories {
|
|
7
|
+
installations: Pick<InstallationRepository, "isModuleEnabled"> & Pick<MysqlInstallationRepository, "isModuleEnabled">;
|
|
8
|
+
configuration: Pick<ModuleConfigurationRepository, "get"> & Pick<MysqlModuleConfigurationRepository, "get">;
|
|
9
|
+
policies: Pick<ModuleLoggingPolicyRepository, "get"> & Pick<MysqlModuleLoggingPolicyRepository, "get">;
|
|
10
|
+
}
|
|
5
11
|
export declare class DiscordModuleLoggingService implements ModuleLoggingService {
|
|
6
12
|
#private;
|
|
7
13
|
private readonly client;
|
|
8
|
-
private readonly database;
|
|
9
14
|
private readonly logger;
|
|
10
|
-
constructor(client: Client,
|
|
15
|
+
constructor(client: Client, databaseOrRepositories: DatabasePool | ModuleLoggingRepositories, modules: readonly ModuleDefinition[], logger: InteractionLogger);
|
|
11
16
|
emit(input: {
|
|
12
17
|
guildId: string;
|
|
13
18
|
moduleId: string;
|
package/dist/module-logging.js
CHANGED
|
@@ -6,14 +6,21 @@ const LOGGING_MODULE_ID = "helyx.logging";
|
|
|
6
6
|
const VIOLET = 0x7c3aed;
|
|
7
7
|
export class DiscordModuleLoggingService {
|
|
8
8
|
client;
|
|
9
|
-
database;
|
|
10
9
|
logger;
|
|
11
10
|
#modules;
|
|
12
11
|
#loggingInstalled;
|
|
13
|
-
|
|
12
|
+
#repositories;
|
|
13
|
+
constructor(client, databaseOrRepositories, modules, logger) {
|
|
14
14
|
this.client = client;
|
|
15
|
-
this.database = database;
|
|
16
15
|
this.logger = logger;
|
|
16
|
+
this.#repositories =
|
|
17
|
+
"installations" in databaseOrRepositories
|
|
18
|
+
? databaseOrRepositories
|
|
19
|
+
: {
|
|
20
|
+
installations: new InstallationRepository(databaseOrRepositories),
|
|
21
|
+
configuration: new ModuleConfigurationRepository(databaseOrRepositories),
|
|
22
|
+
policies: new ModuleLoggingPolicyRepository(databaseOrRepositories),
|
|
23
|
+
};
|
|
17
24
|
this.#modules = new Map(modules.map((module) => [module.manifest.id, module]));
|
|
18
25
|
this.#loggingInstalled = this.#modules.has(LOGGING_MODULE_ID);
|
|
19
26
|
}
|
|
@@ -26,13 +33,12 @@ export class DiscordModuleLoggingService {
|
|
|
26
33
|
if (!source || !event) {
|
|
27
34
|
throw new Error("Module emitted an undeclared logging event");
|
|
28
35
|
}
|
|
29
|
-
|
|
30
|
-
if (!(await installations.isModuleEnabled(input.guildId, LOGGING_MODULE_ID))) {
|
|
36
|
+
if (!(await this.#repositories.installations.isModuleEnabled(input.guildId, LOGGING_MODULE_ID))) {
|
|
31
37
|
return { delivered: false, retryable: false };
|
|
32
38
|
}
|
|
33
39
|
const [configuration, policy] = await Promise.all([
|
|
34
|
-
|
|
35
|
-
|
|
40
|
+
this.#repositories.configuration.get(input.guildId, LOGGING_MODULE_ID),
|
|
41
|
+
this.#repositories.policies.get(input.guildId, input.moduleId, input.eventId),
|
|
36
42
|
]);
|
|
37
43
|
if (!(policy?.enabled ?? event.defaultEnabled)) {
|
|
38
44
|
return { delivered: false, retryable: false };
|