@helyx/bot 1.3.1 → 1.5.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 +100 -28
- package/README.md +65 -11
- package/dist/channel-audit-configuration.d.ts +7 -0
- package/dist/channel-audit-configuration.js +40 -0
- package/dist/channel-audit-control.d.ts +14 -0
- package/dist/channel-audit-control.js +61 -0
- package/dist/channel-audit-permissions.d.ts +56 -0
- package/dist/channel-audit-permissions.js +417 -0
- package/dist/channel-audit-routes.d.ts +4 -0
- package/dist/channel-audit-routes.js +42 -0
- package/dist/cloud-connector.d.ts +11 -4
- package/dist/cloud-connector.js +54 -15
- package/dist/cloud-control-dispatcher.d.ts +8 -3
- package/dist/cloud-control-dispatcher.js +84 -4
- package/dist/control-errors.d.ts +12 -0
- package/dist/control-errors.js +32 -0
- package/dist/control-server-schemas.d.ts +84 -0
- package/dist/control-server-schemas.js +101 -0
- package/dist/control-server.d.ts +25 -8
- package/dist/control-server.js +101 -152
- 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-channel-audit.d.ts +9 -0
- package/dist/discord-channel-audit.js +195 -0
- package/dist/discord-guild-health.d.ts +2 -2
- package/dist/discord-owned-message-service.d.ts +1 -0
- package/dist/discord-owned-message-service.js +3 -0
- package/dist/discord-role-safety.d.ts +4 -0
- package/dist/discord-role-safety.js +30 -0
- package/dist/events.d.ts +2 -0
- package/dist/events.js +5 -20
- 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.js +10 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +200 -82
- 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 +222 -0
- package/dist/shard-contracts.js +52 -1
- package/dist/shard-router.d.ts +4 -1
- package/dist/shard-router.js +19 -0
- package/dist/shard-supervisor.js +43 -9
- package/dist/shard-worker.d.ts +5 -2
- package/dist/shard-worker.js +41 -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,13 +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
|
-
|
|
375
|
-
|
|
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) {
|
|
376
445
|
platformServices.set(name, service);
|
|
377
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
|
+
});
|
|
378
456
|
await serviceAccess
|
|
379
457
|
.get(HELYX_SERVICE_NAMES.moduleAvailability)
|
|
380
458
|
.refresh();
|
|
@@ -386,6 +464,10 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
386
464
|
internalSecret: environment.HELYX_VERIFICATION_INTERNAL_SECRET,
|
|
387
465
|
installationId: environment.HELYX_VERIFICATION_INSTALLATION_ID,
|
|
388
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 }),
|
|
389
471
|
}));
|
|
390
472
|
platformServices.set(HELYX_SERVICE_NAMES.verificationMembers, new DiscordVerificationMemberService(client));
|
|
391
473
|
}
|
|
@@ -400,8 +482,29 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
400
482
|
platformServices.set(HELYX_SERVICE_NAMES.memberEnforcement, new DiscordModerationMemberEnforcementAdapter(client));
|
|
401
483
|
const memberRoles = new DiscordMemberRoleService(client);
|
|
402
484
|
platformServices.set(HELYX_SERVICE_NAMES.memberRoles, memberRoles);
|
|
403
|
-
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));
|
|
404
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
|
+
});
|
|
405
508
|
const moderationPlatformServices = await installModerationPlatformServices({
|
|
406
509
|
services: platformServices,
|
|
407
510
|
serviceAccess,
|
|
@@ -422,7 +525,7 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
422
525
|
if (serviceOptions.inviteFingerprintKey)
|
|
423
526
|
platformServices.set(HELYX_SERVICE_NAMES.discordInvites, new DiscordInviteInspection(client, serviceOptions.inviteFingerprintKey));
|
|
424
527
|
if (featureModules.some(({ manifest }) => manifest.id === "helyx.logging")) {
|
|
425
|
-
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));
|
|
426
529
|
}
|
|
427
530
|
const runtime = createRuntime({
|
|
428
531
|
modules,
|
|
@@ -438,14 +541,18 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
438
541
|
...(cloudManagementModule ? [cloudManagementModule.manifest.id] : []),
|
|
439
542
|
...(operationsModule ? [operationsModule.manifest.id] : []),
|
|
440
543
|
]);
|
|
441
|
-
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);
|
|
442
|
-
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;
|
|
443
548
|
const commandsReady = installInteractions(client, modules, {
|
|
444
549
|
globalCommandMode: environment.HELYX_GLOBAL_COMMAND_MODE,
|
|
445
550
|
...(environment.DISCORD_INTERNAL_GUILD_ID
|
|
446
551
|
? { internalGuildId: environment.DISCORD_INTERNAL_GUILD_ID }
|
|
447
552
|
: {}),
|
|
448
|
-
|
|
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,
|
|
449
556
|
prepare: prepareGuilds,
|
|
450
557
|
selfAuthorizingGuildModuleIds: new Set([
|
|
451
558
|
supportModule.manifest.id,
|
|
@@ -460,7 +567,7 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
460
567
|
runtimeProfile: environment.HELYX_RUNTIME_PROFILE,
|
|
461
568
|
evidenceStore: moderationPlatformServices.evidenceStore,
|
|
462
569
|
});
|
|
463
|
-
const controlService = new BotControlService(database, [
|
|
570
|
+
const controlService = new BotControlService(mysql?.control ?? database, [
|
|
464
571
|
...publicModules,
|
|
465
572
|
gamingStatisticsRecordOwner,
|
|
466
573
|
achievementContributionRecordOwner,
|
|
@@ -481,14 +588,15 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
481
588
|
channelId: environment.HELYX_SUPPORT_STAFF_CHANNEL_ID,
|
|
482
589
|
}
|
|
483
590
|
: undefined, undefined, undefined, featureModules);
|
|
484
|
-
|
|
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);
|
|
485
593
|
let controlServer;
|
|
486
594
|
let shuttingDown = false;
|
|
487
595
|
async function shutdown(reason = "programmatic") {
|
|
488
596
|
if (shuttingDown)
|
|
489
597
|
return;
|
|
490
598
|
shuttingDown = true;
|
|
491
|
-
prepareGuilds.stop();
|
|
599
|
+
await prepareGuilds.stop();
|
|
492
600
|
await hostedLeaveWorker?.stop();
|
|
493
601
|
process.off("SIGINT", onSigint);
|
|
494
602
|
process.off("SIGTERM", onSigterm);
|
|
@@ -496,6 +604,7 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
496
604
|
clearInterval(retentionCleanupTimer);
|
|
497
605
|
if (premiumCatalogueTimer)
|
|
498
606
|
clearInterval(premiumCatalogueTimer);
|
|
607
|
+
await creatorInvitePlatform?.stop();
|
|
499
608
|
await scheduledTasks?.stop().catch((error) => {
|
|
500
609
|
process.exitCode = 1;
|
|
501
610
|
bootstrapLogger.error({ event: "scheduler.stop_failed", error: safeError(error) }, "Scheduled task runner shutdown failed");
|
|
@@ -567,7 +676,9 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
567
676
|
await commandsReady;
|
|
568
677
|
hostedLeaveWorker?.start();
|
|
569
678
|
scheduledTasks = createScheduledTaskRunner({
|
|
570
|
-
|
|
679
|
+
...(mysql
|
|
680
|
+
? { repository: mysql.scheduledTasks }
|
|
681
|
+
: { database: database }),
|
|
571
682
|
modules: publicModules,
|
|
572
683
|
route: createLocalScheduledTaskRoute({
|
|
573
684
|
modules: publicModules,
|
|
@@ -579,7 +690,9 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
579
690
|
});
|
|
580
691
|
scheduledTasks.start();
|
|
581
692
|
guildCommandWorker = new GuildCommandWorker({
|
|
582
|
-
|
|
693
|
+
...(mysql
|
|
694
|
+
? { repositories: mysql.commandWorker }
|
|
695
|
+
: { database: database }),
|
|
583
696
|
modules,
|
|
584
697
|
logger: bootstrapLogger,
|
|
585
698
|
workerId: `unsharded-command:${unshardedSupervisorId}`,
|
|
@@ -602,7 +715,7 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
602
715
|
if (environment.HELYX_GUILD_COMMAND_WRITES_ENABLED &&
|
|
603
716
|
environment.HELYX_GUILD_COMMAND_WRITE_SCOPE === "all") {
|
|
604
717
|
void scheduleGuildCommandRelease({
|
|
605
|
-
database,
|
|
718
|
+
...(mysql ? mysql.commandRelease : { database: database }),
|
|
606
719
|
applicationId: environment.DISCORD_CLIENT_ID,
|
|
607
720
|
releaseId: environment.HELYX_GUILD_COMMAND_RELEASE_ID,
|
|
608
721
|
})
|
|
@@ -637,18 +750,22 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
637
750
|
controlServer = await createBotControlServer({
|
|
638
751
|
service: controlService,
|
|
639
752
|
secret: environment.HELYX_BOT_CONTROL_SECRET,
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
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
|
+
: {}),
|
|
647
764
|
logger: { level: environment.LOG_LEVEL },
|
|
648
765
|
runtimeConfiguration: {
|
|
649
766
|
hostedModerationEnabled: environment.HELYX_HOSTED_MODERATION_ENABLED,
|
|
650
767
|
},
|
|
651
|
-
guildCommands:
|
|
768
|
+
guildCommands: guildCommandReads,
|
|
652
769
|
...(ecko ? { ecko } : {}),
|
|
653
770
|
});
|
|
654
771
|
await controlServer.listen({
|
|
@@ -665,6 +782,7 @@ async function startUnshardedBot(processEnvironment) {
|
|
|
665
782
|
}
|
|
666
783
|
}
|
|
667
784
|
catch (error) {
|
|
785
|
+
await prepareGuilds.stop();
|
|
668
786
|
await scheduledTasks?.stop().catch(() => undefined);
|
|
669
787
|
await guildCommandWorker?.stop().catch(() => undefined);
|
|
670
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 };
|