@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,60 @@
|
|
|
1
|
+
import { GuildCommandActiveLeaseError, GuildCommandRevisionConflictError, GuildCommandSetupConflictError, OptimisticConcurrencyError, } from "@helyx/database";
|
|
2
|
+
import { parseDashboardFieldValidation, } from "@helyx/control-protocol/field-validation";
|
|
3
|
+
import { DashboardActionValidationError, ModuleStateValidationError, } from "@helyx/sdk";
|
|
4
|
+
import { ZodError } from "zod";
|
|
5
|
+
import { BotControlForbiddenError, BotControlNotFoundError, BotControlValidationError, BotControlTransportError, } from "./control-errors.js";
|
|
6
|
+
export function mapBotControlError(error) {
|
|
7
|
+
if (error instanceof ZodError)
|
|
8
|
+
return { status: 400, message: "The control request was invalid" };
|
|
9
|
+
if (error instanceof BotControlValidationError ||
|
|
10
|
+
error instanceof DashboardActionValidationError ||
|
|
11
|
+
error instanceof ModuleStateValidationError) {
|
|
12
|
+
const fieldError = safeBotControlFieldError(error);
|
|
13
|
+
return {
|
|
14
|
+
status: 400,
|
|
15
|
+
message: error.message,
|
|
16
|
+
...(fieldError ? { fieldError } : {}),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
if (error instanceof BotControlNotFoundError)
|
|
20
|
+
return { status: 404, message: error.message };
|
|
21
|
+
if (error instanceof BotControlForbiddenError)
|
|
22
|
+
return { status: 403, message: error.message };
|
|
23
|
+
if (error instanceof OptimisticConcurrencyError ||
|
|
24
|
+
(error instanceof Error &&
|
|
25
|
+
error.name === "DatabaseFailure" &&
|
|
26
|
+
"kind" in error &&
|
|
27
|
+
error.kind === "conflict"))
|
|
28
|
+
return {
|
|
29
|
+
status: 409,
|
|
30
|
+
message: "Configuration changed in another session. Reload and try again.",
|
|
31
|
+
};
|
|
32
|
+
if (error instanceof GuildCommandRevisionConflictError ||
|
|
33
|
+
(error instanceof Error &&
|
|
34
|
+
error.name === "GuildCommandRevisionConflictError"))
|
|
35
|
+
return {
|
|
36
|
+
status: 409,
|
|
37
|
+
message: "Command state changed in another session. Refresh and try again.",
|
|
38
|
+
};
|
|
39
|
+
if (error instanceof GuildCommandActiveLeaseError ||
|
|
40
|
+
(error instanceof Error && error.name === "GuildCommandActiveLeaseError"))
|
|
41
|
+
return {
|
|
42
|
+
status: 409,
|
|
43
|
+
message: "A command build is already running. Wait for it to finish.",
|
|
44
|
+
};
|
|
45
|
+
if (error instanceof GuildCommandSetupConflictError ||
|
|
46
|
+
(error instanceof Error && error.name === "GuildCommandSetupConflictError"))
|
|
47
|
+
return {
|
|
48
|
+
status: 409,
|
|
49
|
+
message: "Command setup changed. Reload the page and try again.",
|
|
50
|
+
};
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
export function safeBotControlFieldError(error) {
|
|
54
|
+
return error instanceof DashboardActionValidationError ||
|
|
55
|
+
(error instanceof BotControlTransportError &&
|
|
56
|
+
[400, 409].includes(error.status))
|
|
57
|
+
? parseDashboardFieldValidation(error.fieldError)
|
|
58
|
+
: undefined;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=control-error-mapping.js.map
|
package/dist/control-errors.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { type PermissionMutationDenialReason } from "@helyx/core";
|
|
2
|
+
import { type DashboardFieldValidation } from "@helyx/control-protocol/field-validation";
|
|
2
3
|
export declare function isUnknownGuildError(error: unknown): boolean;
|
|
3
4
|
export declare function isUnknownMemberError(error: unknown): boolean;
|
|
4
5
|
export declare class BotControlTransportError extends Error {
|
|
5
6
|
readonly status: 400 | 403 | 404 | 409 | 502 | 503 | 504;
|
|
6
|
-
|
|
7
|
+
readonly fieldError: DashboardFieldValidation | undefined;
|
|
8
|
+
constructor(status: 400 | 403 | 404 | 409 | 502 | 503 | 504, message: string, fieldError?: unknown);
|
|
7
9
|
}
|
|
8
10
|
export declare class BotControlValidationError extends Error {
|
|
9
11
|
constructor(message: string);
|
package/dist/control-errors.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { permissionMutationDenialMessage, } from "@helyx/core";
|
|
2
|
+
import { parseDashboardFieldValidation, } from "@helyx/control-protocol/field-validation";
|
|
2
3
|
export function isUnknownGuildError(error) {
|
|
3
4
|
return (typeof error === "object" &&
|
|
4
5
|
error !== null &&
|
|
@@ -13,10 +14,12 @@ export function isUnknownMemberError(error) {
|
|
|
13
14
|
}
|
|
14
15
|
export class BotControlTransportError extends Error {
|
|
15
16
|
status;
|
|
16
|
-
|
|
17
|
+
fieldError;
|
|
18
|
+
constructor(status, message, fieldError) {
|
|
17
19
|
super(message);
|
|
18
20
|
this.status = status;
|
|
19
21
|
this.name = "BotControlTransportError";
|
|
22
|
+
this.fieldError = parseDashboardFieldValidation(fieldError);
|
|
20
23
|
}
|
|
21
24
|
}
|
|
22
25
|
export class BotControlValidationError extends Error {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { FastifyInstance } from "fastify";
|
|
2
|
+
import type { ShardHealthSnapshot } from "./shard-contracts.js";
|
|
3
|
+
export declare function registerControlHealthRoutes(server: FastifyInstance, input: {
|
|
4
|
+
readiness?: () => Promise<boolean>;
|
|
5
|
+
shardHealth?: () => Promise<ShardHealthSnapshot>;
|
|
6
|
+
runtimeConfiguration?: {
|
|
7
|
+
hostedModerationEnabled: boolean;
|
|
8
|
+
};
|
|
9
|
+
}): void;
|
|
10
|
+
//# sourceMappingURL=control-health-routes.d.ts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function registerControlHealthRoutes(server, input) {
|
|
2
|
+
async function readiness(reply) {
|
|
3
|
+
const deploymentReadiness = input.readiness;
|
|
4
|
+
const shardHealth = input.shardHealth;
|
|
5
|
+
if (!deploymentReadiness && !shardHealth)
|
|
6
|
+
return { status: "ok" };
|
|
7
|
+
try {
|
|
8
|
+
if (deploymentReadiness && (await deploymentReadiness()))
|
|
9
|
+
return { status: "ok" };
|
|
10
|
+
if (deploymentReadiness)
|
|
11
|
+
return reply.code(503).send({ status: "unavailable" });
|
|
12
|
+
if (!shardHealth)
|
|
13
|
+
return reply.code(503).send({ status: "unavailable" });
|
|
14
|
+
if ((await shardHealth()).status === "healthy")
|
|
15
|
+
return { status: "ok" };
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// Public readiness deliberately returns no internal detail.
|
|
19
|
+
}
|
|
20
|
+
return reply.code(503).send({ status: "unavailable" });
|
|
21
|
+
}
|
|
22
|
+
server.get("/health", (_request, reply) => readiness(reply));
|
|
23
|
+
server.get("/v1/health", (_request, reply) => readiness(reply));
|
|
24
|
+
server.get("/v1/runtime-configuration", (_request, reply) => reply.header("Cache-Control", "no-store").send({
|
|
25
|
+
hostedModerationEnabled: input.runtimeConfiguration?.hostedModerationEnabled ?? false,
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=control-health-routes.js.map
|
|
@@ -57,6 +57,13 @@ export declare const managedResourceDetailSchema: z.ZodObject<{
|
|
|
57
57
|
updatedAt: z.ZodISODateTime;
|
|
58
58
|
value: z.ZodRecord<z.ZodString, z.ZodCustom<ManagedResourceValue, ManagedResourceValue>>;
|
|
59
59
|
availableActionIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
60
|
+
notices: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
61
|
+
kind: z.ZodEnum<{
|
|
62
|
+
info: "info";
|
|
63
|
+
warning: "warning";
|
|
64
|
+
}>;
|
|
65
|
+
message: z.ZodString;
|
|
66
|
+
}, z.core.$strict>>>;
|
|
60
67
|
}, z.core.$strict>;
|
|
61
68
|
export declare const managedResourcePageSchema: z.ZodObject<{
|
|
62
69
|
items: z.ZodArray<z.ZodObject<{
|
|
@@ -70,6 +70,15 @@ export const managedResourceDetailSchema = managedResourceSummarySchema
|
|
|
70
70
|
.extend({
|
|
71
71
|
value: managedResourceObject,
|
|
72
72
|
availableActionIds: z.array(z.string().min(1).max(100)).max(50).optional(),
|
|
73
|
+
notices: z
|
|
74
|
+
.array(z
|
|
75
|
+
.object({
|
|
76
|
+
kind: z.enum(["info", "warning"]),
|
|
77
|
+
message: z.string().trim().min(1).max(500),
|
|
78
|
+
})
|
|
79
|
+
.strict())
|
|
80
|
+
.max(5)
|
|
81
|
+
.optional(),
|
|
73
82
|
})
|
|
74
83
|
.strict()
|
|
75
84
|
.refine(hasBoundedManagedResourceBytes);
|
package/dist/control-server.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { type DiscordUserIdentityControl, type DiscordUserIdentityRequest } from "@helyx/control-protocol/discord-user-identities";
|
|
1
2
|
import { AuditRepository, GuildAccessPolicyRepository, InstallationRepository, ModuleLoggingPolicyRepository, PermissionPolicyRepository, type DatabasePool, type GuildShardOwnership } from "@helyx/database";
|
|
2
3
|
import type { MysqlPool } from "@helyx/database-mysql";
|
|
3
4
|
import type { CreatorOnboardingRequest, CreatorOnboardingResponse } from "@helyx/sdk";
|
|
5
|
+
export { mapBotControlError, safeBotControlFieldError, } from "./control-error-mapping.js";
|
|
4
6
|
import { type DashboardActionInspection, type DashboardActionResult, type FeatureAccessDecision, type GuildAccessPolicy, type ManagedResourceCreationIntent, type ManagedResourceDetail, type ManagedResourcePage, type ManagedResourceRelationPage, type ManagedResourceValue, type MessageResponse, type ModuleDefinition, type OAuthConsumerCleanupResult, type PermissionActor, type PrivacyCapturePage, type PrivacyErasePage, type PrivacyProviderExecutor, type PublicAchievementProjectionPage, type ServiceAccess } from "@helyx/sdk";
|
|
5
7
|
import { type ActionReceipt, type SignedAssessmentEnvelope } from "@helyx/verification-contracts";
|
|
6
8
|
import { type Client } from "discord.js";
|
|
@@ -10,10 +12,12 @@ import { type FirstTimeCommandSetupHandler } from "./first-time-command-setup.js
|
|
|
10
12
|
import { type ShardHealthSnapshot } from "./shard-contracts.js";
|
|
11
13
|
import { type DashboardPermissionState } from "./control-permissions.js";
|
|
12
14
|
export { BotControlTransportError } from "./control-errors.js";
|
|
15
|
+
import { type ManagedResourceCreateDefaultsInput, type ManagedResourceCreateDefaultsControl } from "./managed-resource-create-defaults.js";
|
|
13
16
|
import type { ChannelAuditControl } from "@helyx/sdk";
|
|
14
17
|
import { type DashboardModuleDefinition } from "./dashboard-module-projection.js";
|
|
15
18
|
import { type AvailabilityModuleCatalogue } from "./module-availability-control.js";
|
|
16
19
|
import { registerGuildCommandControlRoutes } from "./guild-command-control-routes.js";
|
|
20
|
+
import { registerNexusCommandRecoveryControlRoutes } from "./nexus-command-recovery-control-routes.js";
|
|
17
21
|
import { type DashboardActionInput, type DashboardActionPreviewInput } from "./dashboard-action-control.js";
|
|
18
22
|
import { type DashboardActionHandler } from "./dashboard-action-routes.js";
|
|
19
23
|
import { type EckoTelemetrySink } from "./ecko-transport-routes.js";
|
|
@@ -52,7 +56,7 @@ export interface BotGuildPresenceObservation {
|
|
|
52
56
|
state: BotGuildPresenceState;
|
|
53
57
|
observedAt: string;
|
|
54
58
|
}
|
|
55
|
-
export interface BotControlGuildResourceDirectory {
|
|
59
|
+
export interface BotControlGuildResourceDirectory extends DiscordUserIdentityControl {
|
|
56
60
|
listGuildPresence(guildIds: readonly string[]): Promise<BotGuildPresenceObservation[]>;
|
|
57
61
|
getGuildResources(guildId: string): Promise<BotControlGuildResources>;
|
|
58
62
|
getGuildPublicSummary(guildId: string): Promise<{
|
|
@@ -70,8 +74,17 @@ export interface BotControlGuildResourceDirectory {
|
|
|
70
74
|
sendPrivateSupportNotification?(guildId: string, channelId: string, response: MessageResponse, notificationId: string): Promise<void>;
|
|
71
75
|
}
|
|
72
76
|
export declare class DiscordGuildResourceDirectory implements BotControlGuildResourceDirectory {
|
|
77
|
+
#private;
|
|
73
78
|
private readonly client;
|
|
74
79
|
constructor(client: Client);
|
|
80
|
+
resolveDiscordUserIdentities(input: DiscordUserIdentityRequest): Promise<{
|
|
81
|
+
guildId: string;
|
|
82
|
+
users: {
|
|
83
|
+
userId: string;
|
|
84
|
+
username: string | null;
|
|
85
|
+
displayName: string | null;
|
|
86
|
+
}[];
|
|
87
|
+
}>;
|
|
75
88
|
listGuildPresence(guildIds: readonly string[]): Promise<BotGuildPresenceObservation[]>;
|
|
76
89
|
getGuildResources(guildId: string): Promise<BotControlGuildResources>;
|
|
77
90
|
getGuildPublicSummary(guildId: string): Promise<{
|
|
@@ -121,7 +134,7 @@ export interface DashboardGuildAccessState extends GuildAccessPolicy {
|
|
|
121
134
|
actorHighestRolePosition: number | null;
|
|
122
135
|
updatedAt: string | null;
|
|
123
136
|
}
|
|
124
|
-
export interface BotControlHandler extends FirstTimeCommandSetupHandler, DashboardActionHandler, ChannelAuditControl {
|
|
137
|
+
export interface BotControlHandler extends FirstTimeCommandSetupHandler, DashboardActionHandler, ManagedResourceCreateDefaultsControl, ChannelAuditControl, DiscordUserIdentityControl {
|
|
125
138
|
creatorOnboarding?(input: CreatorOnboardingRequest): Promise<CreatorOnboardingResponse>;
|
|
126
139
|
cleanupOAuthConsumer(input: {
|
|
127
140
|
consumerBindingId: string;
|
|
@@ -395,6 +408,14 @@ export declare class BotControlService implements BotControlHandler {
|
|
|
395
408
|
executeThrough: Date;
|
|
396
409
|
cursor?: string;
|
|
397
410
|
}): Promise<PrivacyErasePage>;
|
|
411
|
+
resolveDiscordUserIdentities(input: DiscordUserIdentityRequest): Promise<{
|
|
412
|
+
guildId: string;
|
|
413
|
+
users: {
|
|
414
|
+
userId: string;
|
|
415
|
+
username: string | null;
|
|
416
|
+
displayName: string | null;
|
|
417
|
+
}[];
|
|
418
|
+
}>;
|
|
398
419
|
getGuildState(guildId: string): Promise<BotControlState>;
|
|
399
420
|
canAccessDashboard(input: {
|
|
400
421
|
guildId: string;
|
|
@@ -571,6 +592,9 @@ export declare class BotControlService implements BotControlHandler {
|
|
|
571
592
|
correlationId: string;
|
|
572
593
|
actorAuthenticatedAt?: string;
|
|
573
594
|
}): Promise<ManagedResourceDetail | null>;
|
|
595
|
+
getManagedResourceCreateDefaults(input: ManagedResourceCreateDefaultsInput): Promise<{
|
|
596
|
+
value: Readonly<Record<string, ManagedResourceValue>>;
|
|
597
|
+
}>;
|
|
574
598
|
listAvailabilityModules(): Promise<AvailabilityModuleCatalogue>;
|
|
575
599
|
}
|
|
576
600
|
export declare function createBotControlServer(input: {
|
|
@@ -593,6 +617,7 @@ export declare function createBotControlServer(input: {
|
|
|
593
617
|
observedAt: string;
|
|
594
618
|
}>;
|
|
595
619
|
guildCommands?: Parameters<typeof registerGuildCommandControlRoutes>[1];
|
|
620
|
+
nexusCommandRecovery?: Parameters<typeof registerNexusCommandRecoveryControlRoutes>[1];
|
|
596
621
|
ecko?: {
|
|
597
622
|
authenticator: EckoTransportAuthenticator;
|
|
598
623
|
registry: AutoModerationScannerProviderRegistryService;
|
|
@@ -604,8 +629,4 @@ export declare function createBotControlServer(input: {
|
|
|
604
629
|
telemetry?: EckoTelemetrySink;
|
|
605
630
|
};
|
|
606
631
|
}): Promise<FastifyInstance>;
|
|
607
|
-
export declare function mapBotControlError(error: unknown): {
|
|
608
|
-
status: 400 | 403 | 404 | 409;
|
|
609
|
-
message: string;
|
|
610
|
-
} | undefined;
|
|
611
632
|
//# sourceMappingURL=control-server.d.ts.map
|
package/dist/control-server.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { discordUserIdentityRequestSchema, } from "@helyx/control-protocol/discord-user-identities";
|
|
2
|
+
import { createDiscordUserIdentityLookup, } from "./discord-user-identities.js";
|
|
3
|
+
import { AuditRepository, GuildAccessPolicyRepository, InstallationRepository, ModuleLoggingPolicyRepository, OptimisticConcurrencyError, PermissionPolicyRepository, withTransaction, } from "@helyx/database";
|
|
2
4
|
import { safeError } from "@helyx/core";
|
|
3
|
-
import {
|
|
5
|
+
import { MemberRoleSnapshotError, snapshotMemberRoleIds, } from "./member-role-ids.js";
|
|
6
|
+
import { mapBotControlError, safeBotControlFieldError, } from "./control-error-mapping.js";
|
|
7
|
+
export { mapBotControlError, safeBotControlFieldError, } from "./control-error-mapping.js";
|
|
8
|
+
import { DashboardActionValidationError, DEFAULT_GUILD_ACCESS_POLICY, HELYX_SERVICE_NAMES, nexusCommandLoggingControlOwner, ModuleStateValidationError, } from "@helyx/sdk";
|
|
4
9
|
import { actionReceiptSchema, signedAssessmentEnvelopeSchema, } from "@helyx/verification-contracts";
|
|
5
10
|
import { canAccessGuildDashboard, canMutatePermissionPolicy, canUseCommand, resolveHelyxAuthority, } from "@helyx/core";
|
|
6
11
|
import { ChannelType, PermissionFlagsBits } from "discord.js";
|
|
7
12
|
import Fastify, {} from "fastify";
|
|
8
13
|
import { createHash, timingSafeEqual } from "node:crypto";
|
|
9
|
-
import { z
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
import { nexusModuleLabels } from "./nexus-command-references.js";
|
|
10
16
|
import { addChannelMessageReaction as addDiscordChannelMessageReaction, deleteChannelMessage as deleteDiscordChannelMessage, editChannelMessage as editDiscordChannelMessage, normalizeDiscordPermission, sendToChannel as sendDiscordMessageToChannel, } from "./interactions.js";
|
|
11
17
|
import { mutateDashboardModuleStatus, } from "./module-status-control.js";
|
|
12
18
|
import { canAccessModule, PREMIUM_MODULE_UNAVAILABLE_MESSAGE, } from "./premium-access.js";
|
|
@@ -16,12 +22,18 @@ import { assertVersion, declaredManagedResourcePermission, fromDashboardPolicy,
|
|
|
16
22
|
import { assertPermissionMutationAllowed, BotControlForbiddenError, BotControlNotFoundError, BotControlValidationError, BotControlTransportError, isUnknownGuildError, isUnknownMemberError, } from "./control-errors.js";
|
|
17
23
|
export { BotControlTransportError } from "./control-errors.js";
|
|
18
24
|
import { dashboardCommands } from "./dashboard-commands.js";
|
|
25
|
+
import { dashboardLoggingPolicies } from "./dashboard-logging.js";
|
|
26
|
+
import { authorizeManagedResourceContext } from "./managed-resource-authorization.js";
|
|
27
|
+
import { readManagedResourceCreateDefaults, } from "./managed-resource-create-defaults.js";
|
|
28
|
+
import { validateManagedResourceQuery } from "./managed-resource-query.js";
|
|
19
29
|
import { registerChannelAuditRoutes } from "./channel-audit-routes.js";
|
|
20
30
|
import { createChannelAuditControl } from "./channel-audit-control.js";
|
|
21
31
|
import { executeCreatorOnboarding, registerCreatorOnboardingControlRoute, } from "./creator-onboarding-control.js";
|
|
22
32
|
import { mapDashboardModule, } from "./dashboard-module-projection.js";
|
|
23
33
|
import { availabilityCatalogueDefinitions, availabilityCatalogue, dashboardModuleStatus, disabledModuleSummary, moduleAvailabilityService, moduleAvailabilityStates, statusCatalogueDefinitions, } from "./module-availability-control.js";
|
|
24
34
|
import { registerGuildCommandControlRoutes } from "./guild-command-control-routes.js";
|
|
35
|
+
import { registerControlHealthRoutes } from "./control-health-routes.js";
|
|
36
|
+
import { registerNexusCommandRecoveryControlRoutes } from "./nexus-command-recovery-control-routes.js";
|
|
25
37
|
import { createPrivacyProviderRegistry, describePrivacyProviders, } from "./privacy-provider-registry.js";
|
|
26
38
|
import { changedSettingFeatureIds, configurationPreviewFeatureIds, managedResourceColumnKeys, normalizeManagedResourceDetail, normalizeManagedResourcePage, normalizeManagedResourceRelationPage, projectManagedResourceObject, requireDashboardFeatures, resourceFieldFeatureIds, scalarInputFeatureIds, validateManagedResourceInput, } from "./dashboard-feature-control.js";
|
|
27
39
|
import { registerDashboardPreviewImportRoutes, registerDashboardResourceMutationRoutes, } from "./dashboard-preview-import-routes.js";
|
|
@@ -32,8 +44,13 @@ import { validateDiscordUserSettingSelections, validateDiscordSettingSelections,
|
|
|
32
44
|
import { configurationValue, guildAccessPolicy, managedResourceDeleteResultSchema, managedResourceDetailSchema, managedResourceFilters, managedResourceObject, managedResourcePageSchema, managedResourceRelationPageSchema, moduleId, mutationContext, permissionState, resourceActionId, resourceCursor, resourceId, snowflake, } from "./control-server-schemas.js";
|
|
33
45
|
export class DiscordGuildResourceDirectory {
|
|
34
46
|
client;
|
|
47
|
+
#identities;
|
|
35
48
|
constructor(client) {
|
|
36
49
|
this.client = client;
|
|
50
|
+
this.#identities = createDiscordUserIdentityLookup(client);
|
|
51
|
+
}
|
|
52
|
+
resolveDiscordUserIdentities(input) {
|
|
53
|
+
return this.#identities.resolve(input);
|
|
37
54
|
}
|
|
38
55
|
listGuildPresence(guildIds) {
|
|
39
56
|
const observedAt = new Date().toISOString();
|
|
@@ -133,7 +150,13 @@ export class DiscordGuildResourceDirectory {
|
|
|
133
150
|
});
|
|
134
151
|
return {
|
|
135
152
|
userId: member.id,
|
|
136
|
-
roleIds:
|
|
153
|
+
roleIds: snapshotMemberRoleIds({
|
|
154
|
+
member,
|
|
155
|
+
client: this.client,
|
|
156
|
+
guild,
|
|
157
|
+
guildId,
|
|
158
|
+
actorUserId,
|
|
159
|
+
}),
|
|
137
160
|
permissions: member.permissions
|
|
138
161
|
.toArray()
|
|
139
162
|
.map(normalizeDiscordPermission),
|
|
@@ -143,6 +166,8 @@ export class DiscordGuildResourceDirectory {
|
|
|
143
166
|
};
|
|
144
167
|
}
|
|
145
168
|
catch (error) {
|
|
169
|
+
if (error instanceof MemberRoleSnapshotError)
|
|
170
|
+
throw new BotControlForbiddenError();
|
|
146
171
|
if (isUnknownGuildError(error))
|
|
147
172
|
throw new BotControlNotFoundError();
|
|
148
173
|
if (isUnknownMemberError(error))
|
|
@@ -231,7 +256,7 @@ export class BotControlService {
|
|
|
231
256
|
this.#modules = new Map(modules.map((module) => [module.manifest.id, module]));
|
|
232
257
|
this.#dashboardModules = modules
|
|
233
258
|
.filter((module) => module.manifest.dashboard !== undefined)
|
|
234
|
-
.map(mapDashboardModule);
|
|
259
|
+
.map((module) => mapDashboardModule(module, modules));
|
|
235
260
|
this.#availabilityModules = availabilityCatalogueDefinitions(modules);
|
|
236
261
|
this.#statusModules = statusCatalogueDefinitions(statusModules ?? modules);
|
|
237
262
|
this.#configurations = services.get(HELYX_SERVICE_NAMES.configuration);
|
|
@@ -377,6 +402,11 @@ export class BotControlService {
|
|
|
377
402
|
...(input.cursor ? { cursor: input.cursor } : {}),
|
|
378
403
|
});
|
|
379
404
|
}
|
|
405
|
+
resolveDiscordUserIdentities(input) {
|
|
406
|
+
if (!this.resources.resolveDiscordUserIdentities)
|
|
407
|
+
throw new BotControlTransportError(503, "Discord identity lookup is unavailable");
|
|
408
|
+
return this.resources.resolveDiscordUserIdentities(discordUserIdentityRequestSchema.parse(input));
|
|
409
|
+
}
|
|
380
410
|
async getGuildState(guildId) {
|
|
381
411
|
const dashboardEntries = this.#dashboardModules.map((dashboardModule) => ({
|
|
382
412
|
dashboardModule,
|
|
@@ -404,12 +434,8 @@ export class BotControlService {
|
|
|
404
434
|
const moduleStatuses = {};
|
|
405
435
|
const configurations = {};
|
|
406
436
|
const permissions = {};
|
|
407
|
-
const logging =
|
|
437
|
+
const logging = dashboardLoggingPolicies(this.#dashboardModules, storedLoggingPolicies);
|
|
408
438
|
const featureAccess = {};
|
|
409
|
-
const loggingPolicies = new Map(storedLoggingPolicies.map((policy) => [
|
|
410
|
-
`${policy.sourceModuleId}:${policy.eventId}`,
|
|
411
|
-
policy,
|
|
412
|
-
]));
|
|
413
439
|
for (const { dashboardModule, module } of dashboardEntries) {
|
|
414
440
|
const installation = installed.get(dashboardModule.id);
|
|
415
441
|
moduleStatuses[dashboardModule.id] = dashboardModuleStatus(disabledModules.set, dashboardModule.id, installation);
|
|
@@ -420,14 +446,6 @@ export class BotControlService {
|
|
|
420
446
|
value: configuration.value,
|
|
421
447
|
};
|
|
422
448
|
permissions[dashboardModule.id] = this.#buildPermissionState(module, storedPermissions);
|
|
423
|
-
logging[dashboardModule.id] = dashboardModule.logEvents.map((event) => {
|
|
424
|
-
const stored = loggingPolicies.get(`${dashboardModule.id}:${event.id}`);
|
|
425
|
-
return {
|
|
426
|
-
eventId: event.id,
|
|
427
|
-
enabled: stored?.enabled ?? event.defaultEnabled,
|
|
428
|
-
version: stored ? safeVersion(stored.version) : 0,
|
|
429
|
-
};
|
|
430
|
-
});
|
|
431
449
|
if (module.manifest.manifestVersion === "0.5" &&
|
|
432
450
|
module.manifest.features.length > 0) {
|
|
433
451
|
const access = this.#services.has(HELYX_SERVICE_NAMES.featureAccess)
|
|
@@ -450,7 +468,7 @@ export class BotControlService {
|
|
|
450
468
|
messageContent: runtime.messageContent,
|
|
451
469
|
},
|
|
452
470
|
...resources,
|
|
453
|
-
modules:
|
|
471
|
+
modules: await nexusModuleLabels(this.#dashboardModules, this.#services, guildId),
|
|
454
472
|
featureAccess,
|
|
455
473
|
moduleStatuses,
|
|
456
474
|
globallyDisabledModuleIds: disabledModules.ids,
|
|
@@ -832,7 +850,10 @@ export class BotControlService {
|
|
|
832
850
|
if (!(await this.#persistence.repositories.installations.isModuleEnabled(input.guildId, "helyx.logging"))) {
|
|
833
851
|
throw new BotControlValidationError("Enable Logging before changing activity events");
|
|
834
852
|
}
|
|
835
|
-
const
|
|
853
|
+
const ownerModuleId = nexusCommandLoggingControlOwner(input.moduleId, input.eventId);
|
|
854
|
+
if (ownerModuleId !== input.moduleId)
|
|
855
|
+
await this.#requireModuleAccessible(input.guildId, ownerModuleId);
|
|
856
|
+
const source = this.#requireModule(ownerModuleId);
|
|
836
857
|
const event = source.logging?.events.find(({ id }) => id === input.eventId);
|
|
837
858
|
if (!event) {
|
|
838
859
|
throw new BotControlNotFoundError("Logging event is not declared by this module");
|
|
@@ -840,7 +861,7 @@ export class BotControlService {
|
|
|
840
861
|
const saved = await this.#persistence.withTransaction(async (repositories) => {
|
|
841
862
|
const policy = await repositories.loggingPolicies.save({
|
|
842
863
|
guildId: input.guildId,
|
|
843
|
-
sourceModuleId:
|
|
864
|
+
sourceModuleId: ownerModuleId,
|
|
844
865
|
eventId: input.eventId,
|
|
845
866
|
enabled: input.enabled,
|
|
846
867
|
expectedVersion: input.expectedVersion === 0 ? null : BigInt(input.expectedVersion),
|
|
@@ -852,7 +873,7 @@ export class BotControlService {
|
|
|
852
873
|
action: "module.logging.updated",
|
|
853
874
|
source: "dashboard",
|
|
854
875
|
correlationId: input.correlationId,
|
|
855
|
-
moduleId:
|
|
876
|
+
moduleId: ownerModuleId,
|
|
856
877
|
targetType: "module_log_event",
|
|
857
878
|
targetId: input.eventId,
|
|
858
879
|
afterState: {
|
|
@@ -1029,6 +1050,16 @@ export class BotControlService {
|
|
|
1029
1050
|
? null
|
|
1030
1051
|
: normalizeManagedResourceDetail(managedResourceDetailSchema.parse(result), managedResourceColumnKeys(module, input.resourceType));
|
|
1031
1052
|
}
|
|
1053
|
+
async getManagedResourceCreateDefaults(input) {
|
|
1054
|
+
await this.#requireModuleAccessible(input.guildId, input.moduleId);
|
|
1055
|
+
const { module, resource } = this.#prepareManagedResource(input);
|
|
1056
|
+
this.#requireManagedResourceOperation(module, input.resourceType, "create");
|
|
1057
|
+
return readManagedResourceCreateDefaults({
|
|
1058
|
+
module,
|
|
1059
|
+
resource,
|
|
1060
|
+
context: await this.#managedResourceContext(input, module, "create"),
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1032
1063
|
#prepareManagedResource(input) {
|
|
1033
1064
|
const module = this.#requireDashboardModule(input.moduleId);
|
|
1034
1065
|
const resource = this.#requireManagedResource(module, input.resourceType);
|
|
@@ -1049,17 +1080,7 @@ export class BotControlService {
|
|
|
1049
1080
|
throw new BotControlNotFoundError("Managed resource operation is not declared by this module");
|
|
1050
1081
|
}
|
|
1051
1082
|
#validateManagedResourceQuery(module, resourceType, input) {
|
|
1052
|
-
|
|
1053
|
-
if (input.search !== undefined && !query?.search)
|
|
1054
|
-
throw new BotControlValidationError("Managed resource search is not declared by this module");
|
|
1055
|
-
for (const [key, value] of Object.entries(input.filters ?? {})) {
|
|
1056
|
-
const filter = query?.filters?.find((candidate) => candidate.key === key);
|
|
1057
|
-
if (!filter)
|
|
1058
|
-
throw new BotControlValidationError("Managed resource filter is not declared by this module");
|
|
1059
|
-
if (filter.type === "select" &&
|
|
1060
|
-
!filter.options.some((option) => option.value === value))
|
|
1061
|
-
throw new BotControlValidationError("Managed resource filter value is not declared by this module");
|
|
1062
|
-
}
|
|
1083
|
+
validateManagedResourceQuery(module, resourceType, input);
|
|
1063
1084
|
}
|
|
1064
1085
|
async #requireEnabledManagedResource(guildId, moduleIdValue) {
|
|
1065
1086
|
if (!(await this.#persistence.repositories.installations.isModuleEnabled(guildId, moduleIdValue)))
|
|
@@ -1067,26 +1088,31 @@ export class BotControlService {
|
|
|
1067
1088
|
}
|
|
1068
1089
|
async #managedResourceContext(input, module, operation, permissionId = managedResourcePermissionId(module, input.resourceType, operation, undefined, declaredManagedResourcePermission(module, input.resourceType, operation))) {
|
|
1069
1090
|
const resource = this.#requireManagedResource(module, input.resourceType);
|
|
1070
|
-
|
|
1071
|
-
this.#
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1091
|
+
if (!resource.availableWhileDisabled)
|
|
1092
|
+
await this.#requireEnabledManagedResource(input.guildId, module.manifest.id);
|
|
1093
|
+
return authorizeManagedResourceContext({
|
|
1094
|
+
...input,
|
|
1095
|
+
module,
|
|
1096
|
+
resource,
|
|
1097
|
+
operation,
|
|
1098
|
+
permissionId,
|
|
1099
|
+
authorize: (id) => this.#authorizeAction(input.guildId, module, input.actorUserId, id),
|
|
1100
|
+
createContext: (actor, authorizationScope) => ({
|
|
1101
|
+
guildId: input.guildId,
|
|
1102
|
+
actor,
|
|
1103
|
+
authorizationScope,
|
|
1104
|
+
...("actorAuthenticatedAt" in input &&
|
|
1105
|
+
typeof input.actorAuthenticatedAt === "string"
|
|
1106
|
+
? { actorAuthenticatedAt: input.actorAuthenticatedAt }
|
|
1107
|
+
: {}),
|
|
1108
|
+
correlationId: input.correlationId,
|
|
1109
|
+
services: this.#services,
|
|
1110
|
+
sendToChannel: (channelId, response) => this.resources.sendToChannel(channelId, response),
|
|
1111
|
+
deleteChannelMessage: (channelId, messageId) => this.resources.deleteChannelMessage(channelId, messageId),
|
|
1112
|
+
editChannelMessage: (channelId, messageId, response) => this.resources.editChannelMessage(channelId, messageId, response),
|
|
1113
|
+
addChannelMessageReaction: (channelId, messageId, emojiIdentifier) => this.resources.addChannelMessageReaction(channelId, messageId, emojiIdentifier),
|
|
1114
|
+
}),
|
|
1115
|
+
});
|
|
1090
1116
|
}
|
|
1091
1117
|
async #authorizeAction(guildId, module, actorUserId, permissionId) {
|
|
1092
1118
|
const [actor, policy, guildAccess] = await Promise.all([
|
|
@@ -1246,33 +1272,8 @@ export async function createBotControlServer(input) {
|
|
|
1246
1272
|
if (!timingSafeEqual(expectedSecret, digest(supplied)))
|
|
1247
1273
|
return reply.code(401).send({ message: "Control authentication failed" });
|
|
1248
1274
|
});
|
|
1249
|
-
|
|
1250
|
-
const deploymentReadiness = input.readiness;
|
|
1251
|
-
const shardHealth = input.shardHealth;
|
|
1252
|
-
if (!deploymentReadiness && !shardHealth)
|
|
1253
|
-
return { status: "ok" };
|
|
1254
|
-
try {
|
|
1255
|
-
if (deploymentReadiness && (await deploymentReadiness()))
|
|
1256
|
-
return { status: "ok" };
|
|
1257
|
-
if (deploymentReadiness)
|
|
1258
|
-
return reply.code(503).send({ status: "unavailable" });
|
|
1259
|
-
if (!shardHealth)
|
|
1260
|
-
return reply.code(503).send({ status: "unavailable" });
|
|
1261
|
-
const health = await shardHealth();
|
|
1262
|
-
if (health.status === "healthy")
|
|
1263
|
-
return { status: "ok" };
|
|
1264
|
-
}
|
|
1265
|
-
catch {
|
|
1266
|
-
// The public readiness route deliberately returns no internal detail.
|
|
1267
|
-
}
|
|
1268
|
-
return reply.code(503).send({ status: "unavailable" });
|
|
1269
|
-
}
|
|
1270
|
-
server.get("/health", (_request, reply) => readiness(reply));
|
|
1275
|
+
registerControlHealthRoutes(server, input);
|
|
1271
1276
|
registerCreatorOnboardingControlRoute(server, input.service);
|
|
1272
|
-
server.get("/v1/health", (_request, reply) => readiness(reply));
|
|
1273
|
-
server.get("/v1/runtime-configuration", (_request, reply) => reply.header("Cache-Control", "no-store").send({
|
|
1274
|
-
hostedModerationEnabled: input.runtimeConfiguration?.hostedModerationEnabled ?? false,
|
|
1275
|
-
}));
|
|
1276
1277
|
server.post("/v1/hosted-server-moderation/leave/deliver", async (request) => {
|
|
1277
1278
|
if (!input.deliverHostedServerLeave)
|
|
1278
1279
|
throw new BotControlTransportError(503, "Hosted server moderation delivery is unavailable");
|
|
@@ -1289,6 +1290,7 @@ export async function createBotControlServer(input) {
|
|
|
1289
1290
|
return input.refreshHostedServerOwner(guildId);
|
|
1290
1291
|
});
|
|
1291
1292
|
registerGuildCommandControlRoutes(server, input.guildCommands, input.service);
|
|
1293
|
+
registerNexusCommandRecoveryControlRoutes(server, input.nexusCommandRecovery);
|
|
1292
1294
|
if (input.ecko)
|
|
1293
1295
|
registerEckoTransportRoutes(server, input.ecko);
|
|
1294
1296
|
server.post("/v1/verification/results", async (request) => input.service.acceptVerificationResult(signedAssessmentEnvelopeSchema.parse(request.body)));
|
|
@@ -1423,6 +1425,18 @@ export async function createBotControlServer(input) {
|
|
|
1423
1425
|
...(cursor ? { cursor } : {}),
|
|
1424
1426
|
});
|
|
1425
1427
|
});
|
|
1428
|
+
server.post("/v1/guilds/:guildId/user-identities", async (request) => {
|
|
1429
|
+
const { guildId } = z
|
|
1430
|
+
.object({ guildId: snowflake })
|
|
1431
|
+
.strict()
|
|
1432
|
+
.parse(request.params);
|
|
1433
|
+
const body = discordUserIdentityRequestSchema
|
|
1434
|
+
.pick({ userIds: true })
|
|
1435
|
+
.parse(request.body);
|
|
1436
|
+
if (!input.service.resolveDiscordUserIdentities)
|
|
1437
|
+
throw new BotControlTransportError(503, "Discord identity lookup is unavailable");
|
|
1438
|
+
return input.service.resolveDiscordUserIdentities({ guildId, ...body });
|
|
1439
|
+
});
|
|
1426
1440
|
server.get("/v1/guilds/:guildId/state", async (request) => {
|
|
1427
1441
|
const { guildId } = z.object({ guildId: snowflake }).parse(request.params);
|
|
1428
1442
|
return input.service.getGuildState(guildId);
|
|
@@ -1653,9 +1667,17 @@ export async function createBotControlServer(input) {
|
|
|
1653
1667
|
server.setErrorHandler(async (error, request, reply) => {
|
|
1654
1668
|
const mapped = mapBotControlError(error);
|
|
1655
1669
|
if (mapped)
|
|
1656
|
-
return reply.code(mapped.status).send({
|
|
1670
|
+
return reply.code(mapped.status).send({
|
|
1671
|
+
message: mapped.message,
|
|
1672
|
+
...(mapped.fieldError ? { fieldError: mapped.fieldError } : {}),
|
|
1673
|
+
});
|
|
1657
1674
|
if (error instanceof BotControlTransportError)
|
|
1658
|
-
return reply.code(error.status).send({
|
|
1675
|
+
return reply.code(error.status).send({
|
|
1676
|
+
message: error.message,
|
|
1677
|
+
...(safeBotControlFieldError(error)
|
|
1678
|
+
? { fieldError: safeBotControlFieldError(error) }
|
|
1679
|
+
: {}),
|
|
1680
|
+
});
|
|
1659
1681
|
request.log.error({
|
|
1660
1682
|
error: safeError(error),
|
|
1661
1683
|
errorCode: typeof error.code === "string"
|
|
@@ -1666,47 +1688,6 @@ export async function createBotControlServer(input) {
|
|
|
1666
1688
|
});
|
|
1667
1689
|
return server;
|
|
1668
1690
|
}
|
|
1669
|
-
export function mapBotControlError(error) {
|
|
1670
|
-
if (error instanceof ZodError)
|
|
1671
|
-
return { status: 400, message: "The control request was invalid" };
|
|
1672
|
-
if (error instanceof BotControlValidationError ||
|
|
1673
|
-
error instanceof DashboardActionValidationError ||
|
|
1674
|
-
error instanceof ModuleStateValidationError)
|
|
1675
|
-
return { status: 400, message: error.message };
|
|
1676
|
-
if (error instanceof BotControlNotFoundError)
|
|
1677
|
-
return { status: 404, message: error.message };
|
|
1678
|
-
if (error instanceof BotControlForbiddenError)
|
|
1679
|
-
return { status: 403, message: error.message };
|
|
1680
|
-
if (error instanceof OptimisticConcurrencyError ||
|
|
1681
|
-
(error instanceof Error &&
|
|
1682
|
-
error.name === "DatabaseFailure" &&
|
|
1683
|
-
"kind" in error &&
|
|
1684
|
-
error.kind === "conflict"))
|
|
1685
|
-
return {
|
|
1686
|
-
status: 409,
|
|
1687
|
-
message: "Configuration changed in another session. Reload and try again.",
|
|
1688
|
-
};
|
|
1689
|
-
if (error instanceof GuildCommandRevisionConflictError ||
|
|
1690
|
-
(error instanceof Error &&
|
|
1691
|
-
error.name === "GuildCommandRevisionConflictError"))
|
|
1692
|
-
return {
|
|
1693
|
-
status: 409,
|
|
1694
|
-
message: "Command state changed in another session. Refresh and try again.",
|
|
1695
|
-
};
|
|
1696
|
-
if (error instanceof GuildCommandActiveLeaseError ||
|
|
1697
|
-
(error instanceof Error && error.name === "GuildCommandActiveLeaseError"))
|
|
1698
|
-
return {
|
|
1699
|
-
status: 409,
|
|
1700
|
-
message: "A command build is already running. Wait for it to finish.",
|
|
1701
|
-
};
|
|
1702
|
-
if (error instanceof GuildCommandSetupConflictError ||
|
|
1703
|
-
(error instanceof Error && error.name === "GuildCommandSetupConflictError"))
|
|
1704
|
-
return {
|
|
1705
|
-
status: 409,
|
|
1706
|
-
message: "Command setup changed. Reload the page and try again.",
|
|
1707
|
-
};
|
|
1708
|
-
return undefined;
|
|
1709
|
-
}
|
|
1710
1691
|
function digest(value) {
|
|
1711
1692
|
return createHash("sha256").update(value).digest();
|
|
1712
1693
|
}
|