@helyx/bot 1.4.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/channel-audit-configuration.d.ts +7 -0
  3. package/dist/channel-audit-configuration.js +40 -0
  4. package/dist/channel-audit-control.d.ts +14 -0
  5. package/dist/channel-audit-control.js +61 -0
  6. package/dist/channel-audit-permissions.d.ts +56 -0
  7. package/dist/channel-audit-permissions.js +417 -0
  8. package/dist/channel-audit-routes.d.ts +4 -0
  9. package/dist/channel-audit-routes.js +42 -0
  10. package/dist/cloud-connector.js +2 -0
  11. package/dist/cloud-control-dispatcher.js +22 -0
  12. package/dist/command-registration-policy.js +1 -0
  13. package/dist/control-errors.d.ts +8 -0
  14. package/dist/control-errors.js +24 -0
  15. package/dist/control-server-schemas.d.ts +2 -2
  16. package/dist/control-server.d.ts +4 -1
  17. package/dist/control-server.js +18 -25
  18. package/dist/diagnostic-command.d.ts +76 -0
  19. package/dist/diagnostic-command.js +1962 -0
  20. package/dist/discord-channel-audit.d.ts +9 -0
  21. package/dist/discord-channel-audit.js +195 -0
  22. package/dist/discord-guild-health.d.ts +2 -2
  23. package/dist/discord-role-safety.d.ts +4 -0
  24. package/dist/discord-role-safety.js +30 -0
  25. package/dist/events.d.ts +2 -0
  26. package/dist/events.js +5 -20
  27. package/dist/hosted-server-moderation.d.ts +2 -2
  28. package/dist/index.js +30 -1
  29. package/dist/shard-contracts.d.ts +87 -0
  30. package/dist/shard-contracts.js +39 -0
  31. package/dist/shard-router.d.ts +2 -0
  32. package/dist/shard-router.js +12 -0
  33. package/dist/shard-supervisor.js +7 -0
  34. package/dist/shard-worker.d.ts +1 -0
  35. package/dist/shard-worker.js +38 -1
  36. package/documentation/reference/environment.md +1 -1
  37. package/package.json +7 -7
@@ -0,0 +1,42 @@
1
+ import { channelAuditModuleIdSchema, channelAuditInputSchema, channelAuditConfigurationSchema, channelAuditReportSchema, } from "@helyx/control-protocol/channel-audit";
2
+ import { z } from "zod";
3
+ import { BotControlTransportError } from "./control-errors.js";
4
+ const parameters = z
5
+ .object({
6
+ guildId: z.string().regex(/^\d{17,20}$/u),
7
+ moduleId: channelAuditModuleIdSchema,
8
+ })
9
+ .strict();
10
+ const actor = z
11
+ .object({
12
+ actorUserId: z.string().regex(/^\d{17,20}$/u),
13
+ correlationId: z.uuid(),
14
+ })
15
+ .strict();
16
+ export function registerChannelAuditRoutes(server, control) {
17
+ server.post("/v1/guilds/:guildId/modules/:moduleId/channel-audit/config", async (request) => {
18
+ const input = {
19
+ ...parameters.parse(request.params),
20
+ ...actor.parse(request.body),
21
+ };
22
+ if (!control.readChannelAuditConfiguration)
23
+ throw unsupported();
24
+ return channelAuditConfigurationSchema.parse(await control.readChannelAuditConfiguration(input));
25
+ });
26
+ server.post("/v1/guilds/:guildId/modules/:moduleId/channel-audit/check", async (request) => {
27
+ const input = {
28
+ ...parameters.parse(request.params),
29
+ ...actor
30
+ .extend({ assumptions: channelAuditInputSchema })
31
+ .strict()
32
+ .parse(request.body),
33
+ };
34
+ if (!control.checkChannelAudit)
35
+ throw unsupported();
36
+ return channelAuditReportSchema.parse(await control.checkChannelAudit(input));
37
+ });
38
+ }
39
+ function unsupported() {
40
+ return new BotControlTransportError(503, "This bot version does not support Channel audit. Update the bot before trying again.");
41
+ }
42
+ //# sourceMappingURL=channel-audit-routes.js.map
@@ -763,6 +763,8 @@ function connectorCapabilities() {
763
763
  "guild.module.status.write.v1",
764
764
  "guild.module.configuration.write.v1",
765
765
  "guild.module.configuration.preview.v1",
766
+ "guild.module.channel-audit.config.read.v1",
767
+ "guild.module.channel-audit.check.v1",
766
768
  "guild.module.permissions.write.v1",
767
769
  "guild.module.logging.write.v1",
768
770
  "guild.module.action.execute.v1",
@@ -121,6 +121,28 @@ export class CloudControlDispatcher {
121
121
  });
122
122
  break;
123
123
  }
124
+ case "guild.module.channel-audit.config.read.v1": {
125
+ const input = parseOperationInput(request.operation, request.input);
126
+ if (!this.control.readChannelAuditConfiguration)
127
+ throw new Error("Channel audit is not supported by this bot version");
128
+ result = await this.control.readChannelAuditConfiguration({
129
+ ...input,
130
+ actorUserId: requiredActor(actorUserId),
131
+ correlationId,
132
+ });
133
+ break;
134
+ }
135
+ case "guild.module.channel-audit.check.v1": {
136
+ const input = parseOperationInput(request.operation, request.input);
137
+ if (!this.control.checkChannelAudit)
138
+ throw new Error("Channel audit is not supported by this bot version");
139
+ result = await this.control.checkChannelAudit({
140
+ ...input,
141
+ actorUserId: requiredActor(actorUserId),
142
+ correlationId,
143
+ });
144
+ break;
145
+ }
124
146
  case "guild.module.configuration.write.v1": {
125
147
  const input = parseOperationInput(request.operation, request.input);
126
148
  result = await this.control.updateConfiguration({
@@ -1,6 +1,7 @@
1
1
  import { isCustomerSelectableModule } from "@helyx/module-manifest";
2
2
  const platformGlobalModuleIds = new Set([
3
3
  "helyx.management",
4
+ "helyx.diagnostics",
4
5
  "helyx.support",
5
6
  "helyx.oauth",
6
7
  "helyx.permissions",
@@ -1,8 +1,16 @@
1
1
  import { type PermissionMutationDenialReason } from "@helyx/core";
2
+ export declare function isUnknownGuildError(error: unknown): boolean;
3
+ export declare function isUnknownMemberError(error: unknown): boolean;
2
4
  export declare class BotControlTransportError extends Error {
3
5
  readonly status: 400 | 403 | 404 | 409 | 502 | 503 | 504;
4
6
  constructor(status: 400 | 403 | 404 | 409 | 502 | 503 | 504, message: string);
5
7
  }
8
+ export declare class BotControlValidationError extends Error {
9
+ constructor(message: string);
10
+ }
11
+ export declare class BotControlNotFoundError extends Error {
12
+ constructor(message?: string);
13
+ }
6
14
  export declare class BotControlForbiddenError extends Error {
7
15
  constructor(message?: string);
8
16
  }
@@ -1,4 +1,16 @@
1
1
  import { permissionMutationDenialMessage, } from "@helyx/core";
2
+ export function isUnknownGuildError(error) {
3
+ return (typeof error === "object" &&
4
+ error !== null &&
5
+ "code" in error &&
6
+ error.code === 10_004);
7
+ }
8
+ export function isUnknownMemberError(error) {
9
+ return (typeof error === "object" &&
10
+ error !== null &&
11
+ "code" in error &&
12
+ error.code === 10_007);
13
+ }
2
14
  export class BotControlTransportError extends Error {
3
15
  status;
4
16
  constructor(status, message) {
@@ -7,6 +19,18 @@ export class BotControlTransportError extends Error {
7
19
  this.name = "BotControlTransportError";
8
20
  }
9
21
  }
22
+ export class BotControlValidationError extends Error {
23
+ constructor(message) {
24
+ super(message);
25
+ this.name = "BotControlValidationError";
26
+ }
27
+ }
28
+ export class BotControlNotFoundError extends Error {
29
+ constructor(message = "Discord server is not available to Helyx") {
30
+ super(message);
31
+ this.name = "BotControlNotFoundError";
32
+ }
33
+ }
10
34
  export class BotControlForbiddenError extends Error {
11
35
  constructor(message = "You no longer have permission to perform this action") {
12
36
  super(message);
@@ -7,10 +7,10 @@ export declare const permissionState: z.ZodObject<{
7
7
  version: z.ZodNumber;
8
8
  module: z.ZodObject<{
9
9
  preset: z.ZodEnum<{
10
- custom: "custom";
11
10
  everyone: "everyone";
12
11
  moderators: "moderators";
13
12
  administrators: "administrators";
13
+ custom: "custom";
14
14
  }>;
15
15
  allowedRoleIds: z.ZodArray<z.ZodString>;
16
16
  deniedRoleIds: z.ZodArray<z.ZodString>;
@@ -21,10 +21,10 @@ export declare const permissionState: z.ZodObject<{
21
21
  enabled: z.ZodBoolean;
22
22
  policy: z.ZodObject<{
23
23
  preset: z.ZodEnum<{
24
- custom: "custom";
25
24
  everyone: "everyone";
26
25
  moderators: "moderators";
27
26
  administrators: "administrators";
27
+ custom: "custom";
28
28
  }>;
29
29
  allowedRoleIds: z.ZodArray<z.ZodString>;
30
30
  deniedRoleIds: z.ZodArray<z.ZodString>;
@@ -10,6 +10,7 @@ import { type FirstTimeCommandSetupHandler } from "./first-time-command-setup.js
10
10
  import { type ShardHealthSnapshot } from "./shard-contracts.js";
11
11
  import { type DashboardPermissionState } from "./control-permissions.js";
12
12
  export { BotControlTransportError } from "./control-errors.js";
13
+ import type { ChannelAuditControl } from "@helyx/sdk";
13
14
  import { type DashboardModuleDefinition } from "./dashboard-module-projection.js";
14
15
  import { type AvailabilityModuleCatalogue } from "./module-availability-control.js";
15
16
  import { registerGuildCommandControlRoutes } from "./guild-command-control-routes.js";
@@ -120,7 +121,7 @@ export interface DashboardGuildAccessState extends GuildAccessPolicy {
120
121
  actorHighestRolePosition: number | null;
121
122
  updatedAt: string | null;
122
123
  }
123
- export interface BotControlHandler extends FirstTimeCommandSetupHandler, DashboardActionHandler {
124
+ export interface BotControlHandler extends FirstTimeCommandSetupHandler, DashboardActionHandler, ChannelAuditControl {
124
125
  creatorOnboarding?(input: CreatorOnboardingRequest): Promise<CreatorOnboardingResponse>;
125
126
  cleanupOAuthConsumer(input: {
126
127
  consumerBindingId: string;
@@ -349,6 +350,8 @@ export declare class BotControlService implements BotControlHandler {
349
350
  private readonly supportNotifications?;
350
351
  private readonly presenceOwnership?;
351
352
  private readonly presenceShardCount?;
353
+ readonly readChannelAuditConfiguration: Required<ChannelAuditControl>["readChannelAuditConfiguration"];
354
+ readonly checkChannelAudit: Required<ChannelAuditControl>["checkChannelAudit"];
352
355
  constructor(databaseOrPersistence: DatabasePool | BotControlPersistence, modules: readonly ModuleDefinition[], resources: BotControlGuildResourceDirectory, services: ServiceAccess, privacyExecutor?: PrivacyProviderExecutor, supportNotifications?: {
353
356
  guildId: string;
354
357
  channelId: string;
@@ -13,9 +13,11 @@ import { canAccessModule, PREMIUM_MODULE_UNAVAILABLE_MESSAGE, } from "./premium-
13
13
  import { completeFirstTimeCommandSetup, } from "./first-time-command-setup.js";
14
14
  import { SHARD_HEALTH_STALE_MS, } from "./shard-contracts.js";
15
15
  import { assertVersion, declaredManagedResourcePermission, fromDashboardPolicy, managedResourcePermissionId, permissionPoliciesEqual, permissionPolicyKey, safeOptionalVersion, safeVersion, toDashboardPolicy, } from "./control-permissions.js";
16
- import { assertPermissionMutationAllowed, BotControlForbiddenError, BotControlTransportError, } from "./control-errors.js";
16
+ import { assertPermissionMutationAllowed, BotControlForbiddenError, BotControlNotFoundError, BotControlValidationError, BotControlTransportError, isUnknownGuildError, isUnknownMemberError, } from "./control-errors.js";
17
17
  export { BotControlTransportError } from "./control-errors.js";
18
18
  import { dashboardCommands } from "./dashboard-commands.js";
19
+ import { registerChannelAuditRoutes } from "./channel-audit-routes.js";
20
+ import { createChannelAuditControl } from "./channel-audit-control.js";
19
21
  import { executeCreatorOnboarding, registerCreatorOnboardingControlRoute, } from "./creator-onboarding-control.js";
20
22
  import { mapDashboardModule, } from "./dashboard-module-projection.js";
21
23
  import { availabilityCatalogueDefinitions, availabilityCatalogue, dashboardModuleStatus, disabledModuleSummary, moduleAvailabilityService, moduleAvailabilityStates, statusCatalogueDefinitions, } from "./module-availability-control.js";
@@ -215,6 +217,8 @@ export class BotControlService {
215
217
  #services;
216
218
  #privacyProviders;
217
219
  #persistence;
220
+ readChannelAuditConfiguration;
221
+ checkChannelAudit;
218
222
  constructor(databaseOrPersistence, modules, resources, services, privacyExecutor, supportNotifications, presenceOwnership, presenceShardCount, statusModules) {
219
223
  this.resources = resources;
220
224
  this.supportNotifications = supportNotifications;
@@ -235,6 +239,18 @@ export class BotControlService {
235
239
  this.#permissions = services.get(HELYX_SERVICE_NAMES.permissions);
236
240
  this.#services = services;
237
241
  this.#privacyProviders = createPrivacyProviderRegistry(modules, privacyExecutor, this.#services);
242
+ const audit = createChannelAuditControl({
243
+ modules: this.#modules,
244
+ resources,
245
+ services,
246
+ configurations: this.#configurations,
247
+ permissions: this.#permissions,
248
+ listModules: (guildId) => this.#persistence.repositories.installations.listModules(guildId),
249
+ readGuildPolicy: (guildId) => this.#readGuildAccessPolicy(guildId),
250
+ requireAccessible: (guildId, moduleId) => this.#requireModuleAccessible(guildId, moduleId),
251
+ });
252
+ this.readChannelAuditConfiguration = audit.readChannelAuditConfiguration;
253
+ this.checkChannelAudit = audit.checkChannelAudit;
238
254
  }
239
255
  async creatorOnboarding(input) {
240
256
  return executeCreatorOnboarding(input, {
@@ -1493,6 +1509,7 @@ export async function createBotControlServer(input) {
1493
1509
  return input.service.setModuleStatus({ ...params, ...body });
1494
1510
  });
1495
1511
  registerDashboardActionRoutes(server, input.service);
1512
+ registerChannelAuditRoutes(server, input.service);
1496
1513
  server.get("/v1/guilds/:guildId/modules/:moduleId/resources/:resourceType", async (request) => {
1497
1514
  const params = z
1498
1515
  .object({
@@ -1690,31 +1707,7 @@ export function mapBotControlError(error) {
1690
1707
  };
1691
1708
  return undefined;
1692
1709
  }
1693
- class BotControlValidationError extends Error {
1694
- constructor(message) {
1695
- super(message);
1696
- this.name = "BotControlValidationError";
1697
- }
1698
- }
1699
- class BotControlNotFoundError extends Error {
1700
- constructor(message = "Discord server is not available to Helyx") {
1701
- super(message);
1702
- this.name = "BotControlNotFoundError";
1703
- }
1704
- }
1705
1710
  function digest(value) {
1706
1711
  return createHash("sha256").update(value).digest();
1707
1712
  }
1708
- function isUnknownGuildError(error) {
1709
- return (typeof error === "object" &&
1710
- error !== null &&
1711
- "code" in error &&
1712
- error.code === 10_004);
1713
- }
1714
- function isUnknownMemberError(error) {
1715
- return (typeof error === "object" &&
1716
- error !== null &&
1717
- "code" in error &&
1718
- error.code === 10_007);
1719
- }
1720
1713
  //# sourceMappingURL=control-server.js.map
@@ -0,0 +1,76 @@
1
+ import type { DatabasePool } from "@helyx/database";
2
+ import type { MysqlPool } from "@helyx/database-mysql";
3
+ import { type ModuleDefinition, type ModuleRecordService, type PermissionActor, type ServiceAccess } from "@helyx/sdk";
4
+ import { type Client } from "discord.js";
5
+ export interface DiagnosticIssue {
6
+ kind: "repairable" | "human" | "technical" | "incomplete";
7
+ message: string;
8
+ evidence?: Readonly<Record<string, unknown>>;
9
+ afterRepair?: string;
10
+ }
11
+ export interface DiagnosticProbe {
12
+ id: string;
13
+ moduleId?: string;
14
+ label: string;
15
+ inspect(this: void): Promise<DiagnosticIssue | null>;
16
+ repair?(round: 1 | 2, issue: DiagnosticIssue): Promise<"attempted" | "unchanged" | "stale">;
17
+ verify?(issue: DiagnosticIssue): Promise<boolean>;
18
+ }
19
+ export interface DiagnosticLease {
20
+ signal: AbortSignal;
21
+ assertOwned(): Promise<void>;
22
+ }
23
+ export type DiagnosticRunExclusive = (guildId: string, operation: (lease: DiagnosticLease) => Promise<void>) => Promise<boolean>;
24
+ export interface DiagnosticResources {
25
+ actor(userId: string): Promise<PermissionActor>;
26
+ message(channelId: string, messageId: string): Promise<"present" | "absent" | "unknown">;
27
+ channel(channelId: string, types: readonly string[], permissions: readonly string[]): Promise<DiagnosticIssue | null>;
28
+ role(roleId: string, managed: boolean): Promise<DiagnosticIssue | null>;
29
+ capabilities(): {
30
+ guildMembers: boolean;
31
+ messageContent: boolean;
32
+ };
33
+ guildCommands?(): Promise<{
34
+ definitionHash: string;
35
+ rootCount: number;
36
+ }>;
37
+ }
38
+ export interface DiagnosticModuleOptions {
39
+ modules: readonly ModuleDefinition[];
40
+ client?: Client;
41
+ resources?: (guildId: string) => DiagnosticResources;
42
+ runExclusive?: DiagnosticRunExclusive;
43
+ assertGuildAvailable?: (guildId: string) => Promise<void>;
44
+ listEnabledModules?: (guildId: string) => Promise<readonly {
45
+ moduleId: string;
46
+ }[]>;
47
+ commandStatus?: {
48
+ get(guildId: string): Promise<unknown>;
49
+ refresh(input: {
50
+ guildId: string;
51
+ actorUserId: string;
52
+ correlationId: string;
53
+ }): Promise<unknown>;
54
+ };
55
+ stageMilliseconds?: number;
56
+ maximumRunMilliseconds?: number;
57
+ }
58
+ export interface DiagnosticRecordEnvironment {
59
+ guildId: string;
60
+ actorUserId: string;
61
+ correlationId: string;
62
+ modules: readonly ModuleDefinition[];
63
+ records: ModuleRecordService;
64
+ services: ServiceAccess;
65
+ assertCanInspect(): Promise<void>;
66
+ assertCanMutate(moduleId: string): Promise<void>;
67
+ inspectMessage(channelId: string, messageId: string): Promise<"present" | "absent" | "unknown">;
68
+ now(): Date;
69
+ }
70
+ /** A core command, never a selectable module or a module-repair hook. */
71
+ export declare function createDiagnosticModule(options: DiagnosticModuleOptions): ModuleDefinition;
72
+ /** Reserve at most four sessions, leaving at least two pool slots for repairs. */
73
+ export declare function createPostgresDiagnosticLock(pool: DatabasePool): DiagnosticRunExclusive;
74
+ /** Reserve at most four sessions, leaving at least two pool slots for repairs. */
75
+ export declare function createMysqlDiagnosticLock(pool: MysqlPool): DiagnosticRunExclusive;
76
+ //# sourceMappingURL=diagnostic-command.d.ts.map