@helyx/bot 1.5.1 → 1.5.3
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 +25 -0
- package/command-root-catalogue.json +452 -0
- package/dist/cached-message-deletion-authority.d.ts +27 -0
- package/dist/cached-message-deletion-authority.js +288 -0
- package/dist/channel-audit-configuration.d.ts +11 -1
- package/dist/channel-audit-configuration.js +28 -6
- package/dist/channel-audit-control.js +42 -2
- package/dist/channel-audit-permissions.js +76 -3
- package/dist/cloud-command-policy-sync.d.ts +26 -0
- package/dist/cloud-command-policy-sync.js +198 -0
- package/dist/cloud-connector.d.ts +6 -1
- package/dist/cloud-connector.js +224 -53
- package/dist/cloud-control-dispatcher.js +18 -0
- package/dist/command-policy-runtime.d.ts +23 -0
- package/dist/command-policy-runtime.js +53 -0
- package/dist/command-root-catalogue.d.ts +5 -0
- package/dist/command-root-catalogue.js +16 -0
- package/dist/control-error-mapping.d.ts +8 -0
- package/dist/control-error-mapping.js +60 -0
- package/dist/control-errors.d.ts +3 -1
- package/dist/control-errors.js +4 -1
- package/dist/control-health-routes.d.ts +10 -0
- package/dist/control-health-routes.js +28 -0
- package/dist/control-server-schemas.d.ts +7 -0
- package/dist/control-server-schemas.js +9 -0
- package/dist/control-server.d.ts +27 -6
- package/dist/control-server.js +103 -122
- package/dist/creator-invite-key.d.ts +6 -0
- package/dist/creator-invite-key.js +16 -0
- package/dist/creator-invite-service.d.ts +3 -1
- package/dist/creator-invite-service.js +8 -7
- package/dist/creator-onboarding-platform.d.ts +1 -0
- package/dist/creator-onboarding-platform.js +5 -2
- package/dist/dashboard-feature-control.d.ts +2 -1
- package/dist/dashboard-feature-control.js +1 -0
- package/dist/dashboard-logging.d.ts +20 -0
- package/dist/dashboard-logging.js +39 -0
- package/dist/dashboard-module-projection.d.ts +1 -1
- package/dist/dashboard-module-projection.js +5 -2
- package/dist/dashboard-preview-import-routes.js +8 -0
- package/dist/database-bootstrap.d.ts +1 -1
- package/dist/database-bootstrap.js +4 -3
- package/dist/discord-channel-audit.js +1 -0
- package/dist/discord-user-identities.d.ts +14 -0
- package/dist/discord-user-identities.js +64 -0
- package/dist/emergency-deletion-dispatcher.d.ts +68 -0
- package/dist/emergency-deletion-dispatcher.js +502 -0
- package/dist/emergency-deletion-rest-transport.d.ts +24 -0
- package/dist/emergency-deletion-rest-transport.js +253 -0
- package/dist/events.d.ts +5 -0
- package/dist/events.js +53 -10
- package/dist/guarded-discord-dispatcher.d.ts +29 -0
- package/dist/guarded-discord-dispatcher.js +204 -0
- package/dist/guild-command-discord-client.d.ts +2 -0
- package/dist/guild-command-discord-client.js +7 -0
- package/dist/guild-command-projection.d.ts +4 -1
- package/dist/guild-command-projection.js +25 -0
- package/dist/guild-command-read-service.d.ts +10 -1
- package/dist/guild-command-read-service.js +37 -0
- package/dist/guild-command-worker.d.ts +19 -4
- package/dist/guild-command-worker.js +356 -26
- package/dist/guild-message-protection-author-roles.d.ts +7 -0
- package/dist/guild-message-protection-author-roles.js +93 -0
- package/dist/guild-message-protection-deletion.d.ts +21 -0
- package/dist/guild-message-protection-deletion.js +138 -0
- package/dist/guild-message-protection-delivery.d.ts +41 -0
- package/dist/guild-message-protection-delivery.js +223 -0
- package/dist/guild-message-update-metadata.d.ts +32 -0
- package/dist/guild-message-update-metadata.js +255 -0
- package/dist/guilds.d.ts +2 -1
- package/dist/guilds.js +11 -1
- package/dist/hosted-server-moderation.d.ts +2 -2
- package/dist/index.js +68 -4
- package/dist/interactions.d.ts +3 -1
- package/dist/interactions.js +77 -12
- package/dist/invite-rollup-maintenance.d.ts +9 -0
- package/dist/invite-rollup-maintenance.js +12 -0
- package/dist/managed-resource-authorization.d.ts +15 -0
- package/dist/managed-resource-authorization.js +37 -0
- package/dist/managed-resource-create-defaults.d.ts +21 -0
- package/dist/managed-resource-create-defaults.js +83 -0
- package/dist/managed-resource-query.d.ts +6 -0
- package/dist/managed-resource-query.js +15 -0
- package/dist/member-role-ids.d.ts +17 -0
- package/dist/member-role-ids.js +66 -0
- package/dist/message-protection-admission.d.ts +60 -0
- package/dist/message-protection-admission.js +279 -0
- package/dist/message-protection-cold-data.d.ts +42 -0
- package/dist/message-protection-cold-data.js +180 -0
- package/dist/message-protection-coordinator.d.ts +73 -0
- package/dist/message-protection-coordinator.js +622 -0
- package/dist/message-protection-settings.d.ts +40 -0
- package/dist/message-protection-settings.js +275 -0
- package/dist/message-protection-summary.d.ts +8 -0
- package/dist/message-protection-summary.js +98 -0
- package/dist/module-availability-control.js +20 -12
- package/dist/module-logging.d.ts +3 -2
- package/dist/module-logging.js +6 -1
- package/dist/modules.js +56 -1
- package/dist/mysql-runtime-persistence.d.ts +1 -0
- package/dist/mysql-runtime-persistence.js +4 -1
- package/dist/nexus-command-recovery-control-routes.d.ts +5 -0
- package/dist/nexus-command-recovery-control-routes.js +48 -0
- package/dist/nexus-command-references.d.ts +6 -0
- package/dist/nexus-command-references.js +33 -0
- package/dist/scheduled-task-runner.js +23 -4
- package/dist/shard-command-startup.d.ts +13 -0
- package/dist/shard-command-startup.js +27 -0
- package/dist/shard-contracts.d.ts +84 -0
- package/dist/shard-contracts.js +33 -0
- package/dist/shard-core-readiness.d.ts +5 -0
- package/dist/shard-core-readiness.js +19 -0
- package/dist/shard-router.d.ts +3 -0
- package/dist/shard-router.js +10 -1
- package/dist/shard-supervisor.d.ts +1 -1
- package/dist/shard-supervisor.js +131 -43
- package/dist/shard-worker.d.ts +1 -0
- package/dist/shard-worker.js +37 -15
- package/documentation/README.md +1 -0
- package/documentation/guides/self-hosting.md +54 -0
- package/package.json +18 -17
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Internal launch material, never the application-wide data encryption key. */
|
|
2
|
+
export declare const creatorInviteWorkerKeyName = "HELYX_INTERNAL_CREATOR_INVITE_KEY";
|
|
3
|
+
export declare function decodeCreatorInviteKey(encodedKey: string): Buffer;
|
|
4
|
+
/** Keep the existing v1 derivation: old ciphertext and fingerprints stay valid. */
|
|
5
|
+
export declare function deriveCreatorInviteKey(encodedDataEncryptionKey: string): Buffer;
|
|
6
|
+
//# sourceMappingURL=creator-invite-key.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createHmac } from "node:crypto";
|
|
2
|
+
/** Internal launch material, never the application-wide data encryption key. */
|
|
3
|
+
export const creatorInviteWorkerKeyName = "HELYX_INTERNAL_CREATOR_INVITE_KEY";
|
|
4
|
+
export function decodeCreatorInviteKey(encodedKey) {
|
|
5
|
+
const key = Buffer.from(encodedKey, "base64");
|
|
6
|
+
if (key.length !== 32)
|
|
7
|
+
throw new Error("Creator invites require a 32-byte encryption key");
|
|
8
|
+
return key;
|
|
9
|
+
}
|
|
10
|
+
/** Keep the existing v1 derivation: old ciphertext and fingerprints stay valid. */
|
|
11
|
+
export function deriveCreatorInviteKey(encodedDataEncryptionKey) {
|
|
12
|
+
return createHmac("sha256", decodeCreatorInviteKey(encodedDataEncryptionKey))
|
|
13
|
+
.update("helyx.creator-owned-invites.v1")
|
|
14
|
+
.digest();
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=creator-invite-key.js.map
|
|
@@ -8,7 +8,9 @@ export declare class DiscordCreatorInviteService implements CreatorInviteService
|
|
|
8
8
|
private readonly repository;
|
|
9
9
|
private readonly services;
|
|
10
10
|
private readonly module;
|
|
11
|
-
constructor(client: Client, repository: CreatorInviteRepository, services: ServiceAccess, module: ModuleDefinition | undefined,
|
|
11
|
+
constructor(client: Client, repository: CreatorInviteRepository, services: ServiceAccess, module: ModuleDefinition | undefined, encryption: string | {
|
|
12
|
+
inviteEncryptionKey: string;
|
|
13
|
+
});
|
|
12
14
|
selectableRoles(input: Parameters<CreatorInviteService["selectableRoles"]>[0]): ReturnType<CreatorInviteService["selectableRoles"]>;
|
|
13
15
|
create(input: Parameters<CreatorInviteService["create"]>[0]): Promise<CreatorOwnedInvite>;
|
|
14
16
|
inspect(input: Parameters<CreatorInviteService["inspect"]>[0]): Promise<CreatorOwnedInvite>;
|
|
@@ -5,6 +5,7 @@ import { createCipheriv, createDecipheriv, createHmac, createHash, randomBytes,
|
|
|
5
5
|
import { normalizeDiscordPermission } from "./interactions.js";
|
|
6
6
|
import { canAccessModule } from "./premium-access.js";
|
|
7
7
|
import { discordRestOptions } from "./discord-rest.js";
|
|
8
|
+
import { decodeCreatorInviteKey, deriveCreatorInviteKey, } from "./creator-invite-key.js";
|
|
8
9
|
const MODULE_ID = "helyx.creator-connections";
|
|
9
10
|
const snowflake = /^\d{17,20}$/u;
|
|
10
11
|
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
|
|
@@ -18,17 +19,17 @@ export class DiscordCreatorInviteService {
|
|
|
18
19
|
#key;
|
|
19
20
|
#rest = null;
|
|
20
21
|
#cleanupCursor;
|
|
21
|
-
constructor(client, repository, services, module,
|
|
22
|
+
constructor(client, repository, services, module, encryption) {
|
|
22
23
|
this.client = client;
|
|
23
24
|
this.repository = repository;
|
|
24
25
|
this.services = services;
|
|
25
26
|
this.module = module;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
// Unsharded runtimes derive locally; workers receive only this scoped key.
|
|
28
|
+
// Do not derive again in workers: existing v1 records use this exact key.
|
|
29
|
+
this.#key =
|
|
30
|
+
typeof encryption === "string"
|
|
31
|
+
? deriveCreatorInviteKey(encryption)
|
|
32
|
+
: decodeCreatorInviteKey(encryption.inviteEncryptionKey);
|
|
32
33
|
}
|
|
33
34
|
async selectableRoles(input) {
|
|
34
35
|
validateId(input.guildId);
|
|
@@ -8,9 +8,12 @@ export async function installCreatorInvitePlatform(input) {
|
|
|
8
8
|
const repository = new CreatorInviteRepository(input.database);
|
|
9
9
|
if (!module && !(await repository.isInstalled()))
|
|
10
10
|
return undefined;
|
|
11
|
-
|
|
11
|
+
const encryption = input.inviteEncryptionKey
|
|
12
|
+
? { inviteEncryptionKey: input.inviteEncryptionKey }
|
|
13
|
+
: input.encryptionKey;
|
|
14
|
+
if (!encryption)
|
|
12
15
|
throw new Error("Hosted Creator Connections invites require HELYX_DATA_ENCRYPTION_KEY");
|
|
13
|
-
const service = new DiscordCreatorInviteService(input.client, repository, input.services, module,
|
|
16
|
+
const service = new DiscordCreatorInviteService(input.client, repository, input.services, module, encryption);
|
|
14
17
|
input.register(HELYX_SERVICE_NAMES.creatorInvites, service);
|
|
15
18
|
if (input.oauthBroker) {
|
|
16
19
|
const broker = input.oauthBroker;
|
|
@@ -16,10 +16,11 @@ export declare function isBoundedManagedResourceValue(value: unknown, depth?: nu
|
|
|
16
16
|
keys: number;
|
|
17
17
|
}): value is ManagedResourceValue;
|
|
18
18
|
export declare function hasBoundedManagedResourceBytes(value: unknown): boolean;
|
|
19
|
-
export declare function normalizeManagedResourceDetail(value: Omit<ManagedResourceDetail, "description" | "attributes" | "availableActionIds"> & {
|
|
19
|
+
export declare function normalizeManagedResourceDetail(value: Omit<ManagedResourceDetail, "description" | "attributes" | "availableActionIds" | "notices"> & {
|
|
20
20
|
description?: string | undefined;
|
|
21
21
|
attributes?: Readonly<Record<string, ManagedResourceValue>> | undefined;
|
|
22
22
|
availableActionIds?: readonly string[] | undefined;
|
|
23
|
+
notices?: ManagedResourceDetail["notices"] | undefined;
|
|
23
24
|
}, attributeKeys: readonly string[]): ManagedResourceDetail;
|
|
24
25
|
export declare function normalizeManagedResourcePage(value: {
|
|
25
26
|
items: readonly (Omit<ManagedResourcePage["items"][number], "description" | "attributes"> & {
|
|
@@ -154,6 +154,7 @@ export function normalizeManagedResourceDetail(value, attributeKeys) {
|
|
|
154
154
|
...(value.availableActionIds === undefined
|
|
155
155
|
? {}
|
|
156
156
|
: { availableActionIds: value.availableActionIds }),
|
|
157
|
+
...(value.notices === undefined ? {} : { notices: value.notices }),
|
|
157
158
|
};
|
|
158
159
|
}
|
|
159
160
|
export function normalizeManagedResourcePage(value, attributeKeys) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ModuleDefinition } from "@helyx/sdk";
|
|
2
|
+
type Event = NonNullable<ModuleDefinition["logging"]>["events"][number];
|
|
3
|
+
interface LoggingPolicy {
|
|
4
|
+
eventId: string;
|
|
5
|
+
enabled: boolean;
|
|
6
|
+
version: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function dashboardLogEvents(module: ModuleDefinition, modules: readonly ModuleDefinition[]): Event[];
|
|
9
|
+
/** Visible aliases and the hidden owner all read the same canonical stored row. */
|
|
10
|
+
export declare function dashboardLoggingPolicies(modules: readonly {
|
|
11
|
+
id: string;
|
|
12
|
+
logEvents: readonly Event[];
|
|
13
|
+
}[], stored: readonly {
|
|
14
|
+
sourceModuleId: string;
|
|
15
|
+
eventId: string;
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
version: bigint;
|
|
18
|
+
}[]): Record<string, LoggingPolicy[]>;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=dashboard-logging.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { nexusCommandLoggingControlOwner, } from "@helyx/sdk";
|
|
2
|
+
import { safeVersion } from "./control-permissions.js";
|
|
3
|
+
export function dashboardLogEvents(module, modules) {
|
|
4
|
+
const own = module.logging?.events ?? [];
|
|
5
|
+
const shared = modules
|
|
6
|
+
.find(({ manifest }) => manifest.id === "helyx.nexus-core")
|
|
7
|
+
?.logging?.events.filter(({ id }) => nexusCommandLoggingControlOwner(module.manifest.id, id) !==
|
|
8
|
+
module.manifest.id) ?? [];
|
|
9
|
+
return [
|
|
10
|
+
...new Map([...own, ...shared].map((event) => [event.id, { ...event }])).values(),
|
|
11
|
+
];
|
|
12
|
+
}
|
|
13
|
+
/** Visible aliases and the hidden owner all read the same canonical stored row. */
|
|
14
|
+
export function dashboardLoggingPolicies(modules, stored) {
|
|
15
|
+
const source = new Map(stored.map((policy) => [
|
|
16
|
+
`${policy.sourceModuleId}:${policy.eventId}`,
|
|
17
|
+
policy,
|
|
18
|
+
]));
|
|
19
|
+
const result = {};
|
|
20
|
+
for (const module of modules) {
|
|
21
|
+
result[module.id] = module.logEvents.map((event) => {
|
|
22
|
+
const owner = nexusCommandLoggingControlOwner(module.id, event.id);
|
|
23
|
+
const current = source.get(`${owner}:${event.id}`);
|
|
24
|
+
const policy = {
|
|
25
|
+
eventId: event.id,
|
|
26
|
+
enabled: current?.enabled ?? event.defaultEnabled,
|
|
27
|
+
version: current ? safeVersion(current.version) : 0,
|
|
28
|
+
};
|
|
29
|
+
if (owner !== module.id) {
|
|
30
|
+
const canonical = (result[owner] ??= []);
|
|
31
|
+
if (!canonical.some((item) => item.eventId === event.id))
|
|
32
|
+
canonical.push({ ...policy });
|
|
33
|
+
}
|
|
34
|
+
return policy;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=dashboard-logging.js.map
|
|
@@ -43,6 +43,6 @@ export interface DashboardModuleDefinition {
|
|
|
43
43
|
resources: readonly DashboardResourceDefinition[];
|
|
44
44
|
workspace?: NonNullable<ModuleDefinition["manifest"]["dashboard"]>["workspace"];
|
|
45
45
|
}
|
|
46
|
-
export declare function mapDashboardModule(module: ModuleDefinition): DashboardModuleDefinition;
|
|
46
|
+
export declare function mapDashboardModule(module: ModuleDefinition, modules?: readonly ModuleDefinition[]): DashboardModuleDefinition;
|
|
47
47
|
export {};
|
|
48
48
|
//# sourceMappingURL=dashboard-module-projection.d.ts.map
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { getModuleTier } from "@helyx/module-manifest";
|
|
2
2
|
import { dashboardCategoryLabel } from "./dashboard-category.js";
|
|
3
|
+
import { dashboardLogEvents } from "./dashboard-logging.js";
|
|
3
4
|
import { dashboardCommands, } from "./dashboard-commands.js";
|
|
4
|
-
export function mapDashboardModule(module) {
|
|
5
|
+
export function mapDashboardModule(module, modules = []) {
|
|
6
|
+
if (module.manifest.releaseStatus === "alpha")
|
|
7
|
+
throw new Error("Alpha modules have no runtime dashboard projection");
|
|
5
8
|
const dashboard = module.manifest.dashboard;
|
|
6
9
|
return {
|
|
7
10
|
id: module.manifest.id,
|
|
@@ -31,7 +34,7 @@ export function mapDashboardModule(module) {
|
|
|
31
34
|
? module.manifest.permissionOperations.map((operation) => structuredClone(operation))
|
|
32
35
|
: [],
|
|
33
36
|
commands: dashboardCommands(module),
|
|
34
|
-
logEvents: (module
|
|
37
|
+
logEvents: dashboardLogEvents(module, modules),
|
|
35
38
|
actions: (dashboard.actions ?? []).map((action) => ({ ...action })),
|
|
36
39
|
achievementTriggers: (module.manifest.contributions?.achievementTriggers ?? []).map((trigger) => structuredClone(trigger)),
|
|
37
40
|
resources: (dashboard.resources ?? []).map((resource) => structuredClone(resource)),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { BotControlNotFoundError } from "./control-errors.js";
|
|
2
3
|
import { hasBoundedManagedResourceBytes, isBoundedManagedResourceValue, } from "./dashboard-feature-control.js";
|
|
3
4
|
const snowflake = z.string().regex(/^\d{17,20}$/u);
|
|
4
5
|
const moduleId = z.string().regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/u);
|
|
@@ -56,6 +57,13 @@ export function registerDashboardResourceMutationRoutes(server, service) {
|
|
|
56
57
|
const identifiedResourceParams = resourceParams.extend({
|
|
57
58
|
resourceId: z.string().trim().min(1).max(100),
|
|
58
59
|
});
|
|
60
|
+
server.post("/v1/guilds/:guildId/modules/:moduleId/resources/:resourceType/create-defaults", async (request) => {
|
|
61
|
+
const params = resourceParams.parse(request.params);
|
|
62
|
+
const body = mutationContext.strict().parse(request.body);
|
|
63
|
+
if (!service.getManagedResourceCreateDefaults)
|
|
64
|
+
throw new BotControlNotFoundError("Creation defaults are unavailable");
|
|
65
|
+
return service.getManagedResourceCreateDefaults({ ...params, ...body });
|
|
66
|
+
});
|
|
59
67
|
server.post("/v1/guilds/:guildId/modules/:moduleId/resources/:resourceType/preview", async (request) => {
|
|
60
68
|
const params = resourceParams.parse(request.params);
|
|
61
69
|
const body = mutationContext
|
|
@@ -31,6 +31,6 @@ export interface BotDatabaseBootstrapInput {
|
|
|
31
31
|
*/
|
|
32
32
|
export declare function openBotDatabase(input: BotDatabaseBootstrapInput, resolveAdapter?: typeof resolveDatabaseAdapter): Promise<BotDatabaseHandle>;
|
|
33
33
|
/** Connect with an explicit repository-bearing adapter and the same startup checks. */
|
|
34
|
-
export declare function openBotDatabaseWithAdapter<Repositories>(input: BotDatabaseBootstrapInput, adapter: DatabaseAdapter<Repositories>): Promise<BotDatabaseHandle<Repositories>>;
|
|
34
|
+
export declare function openBotDatabaseWithAdapter<Repositories>(input: BotDatabaseBootstrapInput, adapter: DatabaseAdapter<Repositories>, onCoreReady?: (session: DatabaseSession<Repositories>) => Promise<void>): Promise<BotDatabaseHandle<Repositories>>;
|
|
35
35
|
export {};
|
|
36
36
|
//# sourceMappingURL=database-bootstrap.d.ts.map
|
|
@@ -13,9 +13,9 @@ export async function openBotDatabase(input, resolveAdapter = resolveDatabaseAda
|
|
|
13
13
|
return connectPreparedBotDatabase(input, prepared, adapter);
|
|
14
14
|
}
|
|
15
15
|
/** Connect with an explicit repository-bearing adapter and the same startup checks. */
|
|
16
|
-
export async function openBotDatabaseWithAdapter(input, adapter) {
|
|
16
|
+
export async function openBotDatabaseWithAdapter(input, adapter, onCoreReady) {
|
|
17
17
|
const prepared = await prepareBotDatabase(input);
|
|
18
|
-
return connectPreparedBotDatabase(input, prepared, adapter);
|
|
18
|
+
return connectPreparedBotDatabase(input, prepared, adapter, onCoreReady);
|
|
19
19
|
}
|
|
20
20
|
async function prepareBotDatabase(input) {
|
|
21
21
|
const { environment, modulePackages } = input;
|
|
@@ -55,7 +55,7 @@ async function prepareBotDatabase(input) {
|
|
|
55
55
|
}));
|
|
56
56
|
return { engine, migrationSources };
|
|
57
57
|
}
|
|
58
|
-
async function connectPreparedBotDatabase(input, prepared, adapter) {
|
|
58
|
+
async function connectPreparedBotDatabase(input, prepared, adapter, onCoreReady) {
|
|
59
59
|
const { environment } = input;
|
|
60
60
|
const { engine, migrationSources } = prepared;
|
|
61
61
|
if (adapter.engine !== engine) {
|
|
@@ -71,6 +71,7 @@ async function connectPreparedBotDatabase(input, prepared, adapter) {
|
|
|
71
71
|
throw new Error(`Database identity mismatch: requested ${engine}, received ${session.identity.engine}`);
|
|
72
72
|
}
|
|
73
73
|
const core = await session.migrations.runCore();
|
|
74
|
+
await onCoreReady?.(session);
|
|
74
75
|
const modules = [];
|
|
75
76
|
for (const source of migrationSources) {
|
|
76
77
|
const result = await session.migrations.runModule(source);
|
|
@@ -126,6 +126,7 @@ export async function inspectDiscordChannelAudit(client, input) {
|
|
|
126
126
|
const selected = new Set([
|
|
127
127
|
assumptions.entryChannelId,
|
|
128
128
|
...assumptions.allowedChannelIds,
|
|
129
|
+
...(assumptions.requiredAfterChannelIds ?? []),
|
|
129
130
|
...(assumptions.reviewChannelId ? [assumptions.reviewChannelId] : []),
|
|
130
131
|
]);
|
|
131
132
|
snapshot.invalidSelectedChannels = [];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Client } from "discord.js";
|
|
2
|
+
import { type DiscordUserIdentity, type DiscordUserIdentityRequest, type DiscordUserIdentityResponse } from "@helyx/control-protocol/discord-user-identities";
|
|
3
|
+
type FetchIdentity = (guildId: string, userId: string) => Promise<DiscordUserIdentity>;
|
|
4
|
+
/** Bounded singular member reads; never downloads a guild member directory. */
|
|
5
|
+
export declare class DiscordUserIdentityLookup {
|
|
6
|
+
#private;
|
|
7
|
+
private readonly fetchIdentity;
|
|
8
|
+
private readonly now;
|
|
9
|
+
constructor(fetchIdentity: FetchIdentity, now?: () => number);
|
|
10
|
+
resolve(input: DiscordUserIdentityRequest): Promise<DiscordUserIdentityResponse>;
|
|
11
|
+
}
|
|
12
|
+
export declare function createDiscordUserIdentityLookup(client: Client): DiscordUserIdentityLookup;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=discord-user-identities.d.ts.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { discordUserIdentityRequestSchema, } from "@helyx/control-protocol/discord-user-identities";
|
|
2
|
+
/** Bounded singular member reads; never downloads a guild member directory. */
|
|
3
|
+
export class DiscordUserIdentityLookup {
|
|
4
|
+
fetchIdentity;
|
|
5
|
+
now;
|
|
6
|
+
#cache = new Map();
|
|
7
|
+
#waiting = [];
|
|
8
|
+
#active = 0;
|
|
9
|
+
constructor(fetchIdentity, now = Date.now) {
|
|
10
|
+
this.fetchIdentity = fetchIdentity;
|
|
11
|
+
this.now = now;
|
|
12
|
+
}
|
|
13
|
+
async resolve(input) {
|
|
14
|
+
const parsed = discordUserIdentityRequestSchema.parse(input);
|
|
15
|
+
const users = await Promise.all([...new Set(parsed.userIds)].map((id) => this.#read(parsed.guildId, id)));
|
|
16
|
+
return { guildId: parsed.guildId, users };
|
|
17
|
+
}
|
|
18
|
+
#read(guildId, userId) {
|
|
19
|
+
const key = `${guildId}:${userId}`;
|
|
20
|
+
const cached = this.#cache.get(key);
|
|
21
|
+
if (cached && cached.expiresAt > this.now())
|
|
22
|
+
return cached.value;
|
|
23
|
+
const value = this.#load(guildId, userId);
|
|
24
|
+
this.#cache.delete(key);
|
|
25
|
+
this.#cache.set(key, { expiresAt: this.now() + 120_000, value });
|
|
26
|
+
while (this.#cache.size > 4096)
|
|
27
|
+
this.#cache.delete(this.#cache.keys().next().value);
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
async #load(guildId, userId) {
|
|
31
|
+
if (this.#active >= 4)
|
|
32
|
+
await new Promise((resolve) => this.#waiting.push(resolve));
|
|
33
|
+
else
|
|
34
|
+
this.#active++;
|
|
35
|
+
try {
|
|
36
|
+
return await this.fetchIdentity(guildId, userId);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Failed or unavailable names are not invented and must be retried sooner.
|
|
40
|
+
this.#cache.delete(`${guildId}:${userId}`);
|
|
41
|
+
return { userId, username: null, displayName: null };
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
const next = this.#waiting.shift();
|
|
45
|
+
if (next)
|
|
46
|
+
next();
|
|
47
|
+
else
|
|
48
|
+
this.#active--;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function createDiscordUserIdentityLookup(client) {
|
|
53
|
+
return new DiscordUserIdentityLookup(async (guildId, userId) => {
|
|
54
|
+
const guild = client.guilds.cache.get(guildId) ?? (await client.guilds.fetch(guildId));
|
|
55
|
+
const member = guild.members.cache.get(userId) ??
|
|
56
|
+
(await guild.members.fetch({ user: userId, cache: false }));
|
|
57
|
+
return {
|
|
58
|
+
userId,
|
|
59
|
+
username: member.user.username,
|
|
60
|
+
displayName: member.displayName,
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=discord-user-identities.js.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { CachedMessageDeletionAuthority } from "./cached-message-deletion-authority.js";
|
|
2
|
+
import type { GuildMessageProtectionDeletionResult } from "@helyx/sdk";
|
|
3
|
+
export interface EmergencyMessageDeletion {
|
|
4
|
+
readonly guildId: string;
|
|
5
|
+
readonly moduleId: string;
|
|
6
|
+
readonly channelId: string;
|
|
7
|
+
readonly messageId: string;
|
|
8
|
+
readonly admissionToken: string;
|
|
9
|
+
}
|
|
10
|
+
type Terminal = "deleted" | "already-absent" | "missing-permission" | "invalid-request" | "failed";
|
|
11
|
+
export type EmergencyDeletionTransportResult = Readonly<{
|
|
12
|
+
kind: Terminal | "transient";
|
|
13
|
+
}> | Readonly<{
|
|
14
|
+
kind: "retry";
|
|
15
|
+
afterMs: number;
|
|
16
|
+
scope: "route" | "global";
|
|
17
|
+
}>;
|
|
18
|
+
export type EmergencyDeletionResult = Readonly<{
|
|
19
|
+
kind: Terminal | "revoked" | "expired" | "capacity" | "stopped" | "attempts-exhausted";
|
|
20
|
+
issued: boolean;
|
|
21
|
+
attempts: number;
|
|
22
|
+
}>;
|
|
23
|
+
export interface EmergencyDeletionTransport {
|
|
24
|
+
deleteExact(input: EmergencyMessageDeletion, attempt: EmergencyDeletionAttemptContext, authority?: CachedMessageDeletionAuthority): Promise<EmergencyDeletionTransportResult>;
|
|
25
|
+
}
|
|
26
|
+
/** Private exact attempt witness; no module receives it or supplies its clock. */
|
|
27
|
+
export interface EmergencyDeletionAttemptContext {
|
|
28
|
+
/** Also fences original queue age, stop and actual attempt settlement. */
|
|
29
|
+
isCurrent(this: void): boolean;
|
|
30
|
+
}
|
|
31
|
+
interface Options {
|
|
32
|
+
transport: EmergencyDeletionTransport;
|
|
33
|
+
isCurrent(this: void, input: EmergencyMessageDeletion): boolean;
|
|
34
|
+
capacity: number;
|
|
35
|
+
perChannelCapacity: number;
|
|
36
|
+
perGuildCapacity: number;
|
|
37
|
+
concurrency: number;
|
|
38
|
+
maximumAgeMs: number;
|
|
39
|
+
maximumAttempts: number;
|
|
40
|
+
transientBackoffMs: number;
|
|
41
|
+
maximumBackoffMs: number;
|
|
42
|
+
maximumRetryAfterMs: number;
|
|
43
|
+
now?: () => number;
|
|
44
|
+
}
|
|
45
|
+
export interface CapturedEmergencyDeletion {
|
|
46
|
+
readonly result: Promise<GuildMessageProtectionDeletionResult>;
|
|
47
|
+
readonly retainedProvidedAuthority: boolean;
|
|
48
|
+
}
|
|
49
|
+
/** Dormant private queue. No SDK service, provider adapter or runtime registration. */
|
|
50
|
+
export declare class EmergencyDeletionDispatcher {
|
|
51
|
+
#private;
|
|
52
|
+
constructor(options: Options);
|
|
53
|
+
enqueue(input: EmergencyMessageDeletion, authority?: CachedMessageDeletionAuthority): Promise<EmergencyDeletionResult>;
|
|
54
|
+
/** Synchronous Core ownership result; duplicate entries share their SDK promise. */
|
|
55
|
+
enqueueCaptured(input: EmergencyMessageDeletion, authority?: CachedMessageDeletionAuthority): Readonly<CapturedEmergencyDeletion>;
|
|
56
|
+
/** Lifecycle callers can synchronously discard waiting work after revocation. */
|
|
57
|
+
revalidate(): void;
|
|
58
|
+
stop(): void;
|
|
59
|
+
stats(): Readonly<{
|
|
60
|
+
pending: number;
|
|
61
|
+
inFlight: number;
|
|
62
|
+
retainedRoutes: number;
|
|
63
|
+
dropped: number;
|
|
64
|
+
stopped: boolean;
|
|
65
|
+
}>;
|
|
66
|
+
}
|
|
67
|
+
export {};
|
|
68
|
+
//# sourceMappingURL=emergency-deletion-dispatcher.d.ts.map
|