@helyx/bot 1.6.0 → 1.7.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/command-root-catalogue.json +13 -5
  3. package/dist/cloud-connector.js +2 -0
  4. package/dist/cloud-control-dispatcher.js +20 -0
  5. package/dist/command-registration-policy.js +1 -0
  6. package/dist/control-directory-contracts.d.ts +50 -0
  7. package/dist/control-directory-contracts.js +2 -0
  8. package/dist/control-server-schemas.d.ts +3 -3
  9. package/dist/control-server.d.ts +10 -46
  10. package/dist/control-server.js +50 -3
  11. package/dist/creator-invite-service.d.ts +7 -0
  12. package/dist/creator-invite-service.js +17 -0
  13. package/dist/discord-guild-health.d.ts +2 -2
  14. package/dist/events.d.ts +1 -0
  15. package/dist/events.js +2 -0
  16. package/dist/help-credentials.d.ts +25 -0
  17. package/dist/help-credentials.js +149 -0
  18. package/dist/help-module.d.ts +27 -0
  19. package/dist/help-module.js +288 -0
  20. package/dist/help-runtime.d.ts +3 -0
  21. package/dist/help-runtime.js +42 -0
  22. package/dist/help-transport.d.ts +53 -0
  23. package/dist/help-transport.js +267 -0
  24. package/dist/index.d.ts +2 -0
  25. package/dist/index.js +6 -0
  26. package/dist/interactions.js +10 -6
  27. package/dist/server-overview-control.d.ts +35 -0
  28. package/dist/server-overview-control.js +362 -0
  29. package/dist/server-overview-discord.d.ts +33 -0
  30. package/dist/server-overview-discord.js +212 -0
  31. package/dist/server-overview-profile.d.ts +8 -0
  32. package/dist/server-overview-profile.js +62 -0
  33. package/dist/server-overview-routes.d.ts +4 -0
  34. package/dist/server-overview-routes.js +41 -0
  35. package/dist/shard-contracts.d.ts +66 -0
  36. package/dist/shard-contracts.js +23 -0
  37. package/dist/shard-coordinator.d.ts +1 -1
  38. package/dist/shard-router.d.ts +9 -0
  39. package/dist/shard-router.js +6 -0
  40. package/dist/shard-supervisor.d.ts +1 -1
  41. package/dist/shard-supervisor.js +4 -0
  42. package/dist/shard-worker.d.ts +1 -1
  43. package/dist/shard-worker.js +4 -0
  44. package/dist/verification-service.d.ts +4 -0
  45. package/dist/verification-service.js +10 -0
  46. package/documentation/README.md +1 -0
  47. package/documentation/guides/help-command.md +62 -0
  48. package/package.json +6 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add the built-in private /help command with module selection, reviewed answer choices and local self-host fallback. Keep question selection and its answer in the same private message, show the original question above choices, and use a compact FAQ ID footer. Add opt-in encrypted online Help enrolment and fail closed for unavailable hosted publication state. Integrate authenticated Server Overview reads, bounded Discord profiles and protected invite provenance across direct, Cloud Connect and sharded runtimes.
8
+
3
9
  ## 1.6.0
4
10
 
5
11
  ### Minor Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "revision": 3,
3
+ "revision": 6,
4
4
  "roots": [
5
5
  { "name": "access", "type": 1, "ownerModuleId": "helyx.account-gate" },
6
6
  { "name": "access", "type": 1, "ownerModuleId": "helyx.verification" },
@@ -17,6 +17,7 @@
17
17
  { "name": "games", "type": 1, "ownerModuleId": "helyx.counting" },
18
18
  { "name": "games", "type": 1, "ownerModuleId": "helyx.trivia" },
19
19
  { "name": "giveaway", "type": 1, "ownerModuleId": "helyx.giveaways" },
20
+ { "name": "help", "type": 1, "ownerModuleId": "helyx.help" },
20
21
  { "name": "helyx", "type": 1, "ownerModuleId": "helyx.cloud-connect" },
21
22
  { "name": "helyx", "type": 1, "ownerModuleId": "helyx.management" },
22
23
  { "name": "helyx", "type": 1, "ownerModuleId": "helyx.oauth" },
@@ -62,6 +63,13 @@
62
63
  "sourcePath": "apps/bot/src/diagnostic-command.ts",
63
64
  "version": "0.1.0"
64
65
  },
66
+ {
67
+ "commands": ["help"],
68
+ "moduleId": "helyx.help",
69
+ "releaseStatus": "live",
70
+ "sourcePath": "apps/bot/src/help-module.ts",
71
+ "version": "0.1.0"
72
+ },
65
73
  {
66
74
  "commands": ["helyx.modules"],
67
75
  "moduleId": "helyx.management",
@@ -124,7 +132,7 @@
124
132
  "moduleId": "helyx.account-gate",
125
133
  "packageName": "@helyx/module-account-gate",
126
134
  "releaseStatus": "live",
127
- "version": "2.1.2"
135
+ "version": "2.1.3"
128
136
  },
129
137
  {
130
138
  "commands": [
@@ -135,7 +143,7 @@
135
143
  "moduleId": "helyx.achievements",
136
144
  "packageName": "@helyx/module-achievements",
137
145
  "releaseStatus": "live",
138
- "version": "2.1.3"
146
+ "version": "2.1.4"
139
147
  },
140
148
  {
141
149
  "commands": ["afk"],
@@ -284,7 +292,7 @@
284
292
  "moduleId": "helyx.reactionroles",
285
293
  "packageName": "@helyx/module-reactionroles",
286
294
  "releaseStatus": "live",
287
- "version": "1.1.2"
295
+ "version": "1.1.3"
288
296
  },
289
297
  {
290
298
  "commands": [],
@@ -456,5 +464,5 @@
456
464
  }
457
465
  ]
458
466
  },
459
- "fingerprint": "70fb5cb9554c8fdb723ff8139a9e1b0ae2128d256b12a6146d5674c63774c42a"
467
+ "fingerprint": "9bd09abbfbaaac0ddb932b6e25911b9a2eb8a3dd5febe92f5862ef903534b2e5"
460
468
  }
@@ -904,6 +904,8 @@ function connectorCapabilities() {
904
904
  "installation.health.read.v1",
905
905
  "guild.state.read.v1",
906
906
  "guild.user-identities.read.v1",
907
+ "guild.overview.read.v1",
908
+ "guild.overview.invite.revoke.v1",
907
909
  "guild.dashboard.access.read.v1",
908
910
  "guild.access-policy.read.v1",
909
911
  "guild.access-policy.write.v1",
@@ -18,6 +18,26 @@ export class CloudControlDispatcher {
18
18
  : undefined;
19
19
  let result;
20
20
  switch (request.operation) {
21
+ case "guild.overview.read.v1": {
22
+ const input = parseOperationInput(request.operation, request.input);
23
+ if (!this.control.readServerOverview)
24
+ throw new Error("Overview is unavailable");
25
+ result = await this.control.readServerOverview({
26
+ ...input,
27
+ actorUserId: requiredActor(actorUserId),
28
+ });
29
+ break;
30
+ }
31
+ case "guild.overview.invite.revoke.v1": {
32
+ const input = parseOperationInput(request.operation, request.input);
33
+ if (!this.control.revokeServerOverviewInvite)
34
+ throw new Error("Invite management is unavailable");
35
+ result = await this.control.revokeServerOverviewInvite({
36
+ ...input,
37
+ actorUserId: requiredActor(actorUserId),
38
+ });
39
+ break;
40
+ }
21
41
  case "installation.health.read.v1":
22
42
  parseOperationInput(request.operation, request.input);
23
43
  result = {
@@ -7,6 +7,7 @@ const platformGlobalModuleIds = new Set([
7
7
  "helyx.permissions",
8
8
  "helyx.privacy",
9
9
  "helyx.process-info",
10
+ "helyx.help",
10
11
  "helyx.premium-activation",
11
12
  ]);
12
13
  const deploymentModuleIds = new Set(["helyx.cloud-connect"]);
@@ -0,0 +1,50 @@
1
+ import type { DiscordUserIdentityControl } from "@helyx/control-protocol/discord-user-identities";
2
+ import type { MessageResponse, PermissionActor, PublicAchievementProjectionPage } from "@helyx/sdk";
3
+ import type { OverviewDiscordDirectory } from "./server-overview-discord.js";
4
+ export interface BotControlGuildResources {
5
+ botGuildPermissions?: string[];
6
+ channels: Array<{
7
+ id: string;
8
+ name: string;
9
+ type: "guild_text" | "guild_announcement" | "guild_category" | "guild_forum" | "guild_media";
10
+ }>;
11
+ roles: Array<{
12
+ id: string;
13
+ name: string;
14
+ colour: string;
15
+ position: number;
16
+ }>;
17
+ }
18
+ export interface BotPublicModuleProjection {
19
+ server: {
20
+ id: string;
21
+ name: string;
22
+ memberCount: number;
23
+ };
24
+ projection: PublicAchievementProjectionPage;
25
+ }
26
+ export type BotGuildPresenceState = "installed" | "not_installed" | "unknown" | "unavailable";
27
+ export interface BotGuildPresenceObservation {
28
+ guildId: string;
29
+ state: BotGuildPresenceState;
30
+ observedAt: string;
31
+ }
32
+ export interface BotControlGuildResourceDirectory extends DiscordUserIdentityControl {
33
+ overview?: OverviewDiscordDirectory;
34
+ listGuildPresence(guildIds: readonly string[]): Promise<BotGuildPresenceObservation[]>;
35
+ getGuildResources(guildId: string): Promise<BotControlGuildResources>;
36
+ getGuildPublicSummary(guildId: string): Promise<{
37
+ id: string;
38
+ name: string;
39
+ memberCount: number;
40
+ }>;
41
+ sendToChannel(channelId: string, response: MessageResponse): Promise<{
42
+ messageId: string;
43
+ }>;
44
+ deleteChannelMessage(channelId: string, messageId: string): Promise<void>;
45
+ editChannelMessage(channelId: string, messageId: string, response: MessageResponse): Promise<void>;
46
+ addChannelMessageReaction(channelId: string, messageId: string, emojiIdentifier: string): Promise<void>;
47
+ resolveActor(guildId: string, actorUserId: string): Promise<PermissionActor>;
48
+ sendPrivateSupportNotification?(guildId: string, channelId: string, response: MessageResponse, notificationId: string): Promise<void>;
49
+ }
50
+ //# sourceMappingURL=control-directory-contracts.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=control-directory-contracts.js.map
@@ -8,9 +8,9 @@ export declare const permissionState: z.ZodObject<{
8
8
  module: z.ZodObject<{
9
9
  preset: z.ZodEnum<{
10
10
  everyone: "everyone";
11
+ custom: "custom";
11
12
  moderators: "moderators";
12
13
  administrators: "administrators";
13
- custom: "custom";
14
14
  }>;
15
15
  allowedRoleIds: z.ZodArray<z.ZodString>;
16
16
  deniedRoleIds: z.ZodArray<z.ZodString>;
@@ -22,9 +22,9 @@ export declare const permissionState: z.ZodObject<{
22
22
  policy: z.ZodObject<{
23
23
  preset: z.ZodEnum<{
24
24
  everyone: "everyone";
25
+ custom: "custom";
25
26
  moderators: "moderators";
26
27
  administrators: "administrators";
27
- custom: "custom";
28
28
  }>;
29
29
  allowedRoleIds: z.ZodArray<z.ZodString>;
30
30
  deniedRoleIds: z.ZodArray<z.ZodString>;
@@ -59,8 +59,8 @@ export declare const managedResourceDetailSchema: z.ZodObject<{
59
59
  availableActionIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
60
60
  notices: z.ZodOptional<z.ZodArray<z.ZodObject<{
61
61
  kind: z.ZodEnum<{
62
- info: "info";
63
62
  warning: "warning";
63
+ info: "info";
64
64
  }>;
65
65
  message: z.ZodString;
66
66
  }, z.core.$strict>>>;
@@ -1,9 +1,13 @@
1
+ import type { BotControlGuildResources, BotPublicModuleProjection, BotGuildPresenceObservation, BotControlGuildResourceDirectory } from "./control-directory-contracts.js";
2
+ export type { BotControlGuildResources, BotPublicModuleProjection, BotGuildPresenceState, BotGuildPresenceObservation, BotControlGuildResourceDirectory, } from "./control-directory-contracts.js";
1
3
  import { type DiscordUserIdentityControl, type DiscordUserIdentityRequest } from "@helyx/control-protocol/discord-user-identities";
4
+ import type { ServerOverviewControl } from "@helyx/control-protocol";
5
+ import { type OverviewDiscordDirectory } from "./server-overview-discord.js";
2
6
  import { AuditRepository, GuildAccessPolicyRepository, InstallationRepository, ModuleLoggingPolicyRepository, PermissionPolicyRepository, type DatabasePool, type GuildShardOwnership } from "@helyx/database";
3
7
  import type { MysqlPool } from "@helyx/database-mysql";
4
8
  import type { CreatorOnboardingRequest, CreatorOnboardingResponse } from "@helyx/sdk";
5
9
  export { mapBotControlError, safeBotControlFieldError, } from "./control-error-mapping.js";
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";
10
+ 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 ServiceAccess } from "@helyx/sdk";
7
11
  import { type ActionReceipt, type SignedAssessmentEnvelope } from "@helyx/verification-contracts";
8
12
  import { type Client } from "discord.js";
9
13
  import { type FastifyInstance } from "fastify";
@@ -29,53 +33,10 @@ interface DashboardLoggingPolicy {
29
33
  enabled: boolean;
30
34
  version: number;
31
35
  }
32
- export interface BotControlGuildResources {
33
- channels: Array<{
34
- id: string;
35
- name: string;
36
- type: "guild_text" | "guild_announcement" | "guild_category" | "guild_forum" | "guild_media";
37
- }>;
38
- roles: Array<{
39
- id: string;
40
- name: string;
41
- colour: string;
42
- position: number;
43
- }>;
44
- }
45
- export interface BotPublicModuleProjection {
46
- server: {
47
- id: string;
48
- name: string;
49
- memberCount: number;
50
- };
51
- projection: PublicAchievementProjectionPage;
52
- }
53
- export type BotGuildPresenceState = "installed" | "not_installed" | "unknown" | "unavailable";
54
- export interface BotGuildPresenceObservation {
55
- guildId: string;
56
- state: BotGuildPresenceState;
57
- observedAt: string;
58
- }
59
- export interface BotControlGuildResourceDirectory extends DiscordUserIdentityControl {
60
- listGuildPresence(guildIds: readonly string[]): Promise<BotGuildPresenceObservation[]>;
61
- getGuildResources(guildId: string): Promise<BotControlGuildResources>;
62
- getGuildPublicSummary(guildId: string): Promise<{
63
- id: string;
64
- name: string;
65
- memberCount: number;
66
- }>;
67
- sendToChannel(channelId: string, response: MessageResponse): Promise<{
68
- messageId: string;
69
- }>;
70
- deleteChannelMessage(channelId: string, messageId: string): Promise<void>;
71
- editChannelMessage(channelId: string, messageId: string, response: MessageResponse): Promise<void>;
72
- addChannelMessageReaction(channelId: string, messageId: string, emojiIdentifier: string): Promise<void>;
73
- resolveActor(guildId: string, actorUserId: string): Promise<PermissionActor>;
74
- sendPrivateSupportNotification?(guildId: string, channelId: string, response: MessageResponse, notificationId: string): Promise<void>;
75
- }
76
36
  export declare class DiscordGuildResourceDirectory implements BotControlGuildResourceDirectory {
77
37
  #private;
78
38
  private readonly client;
39
+ readonly overview: OverviewDiscordDirectory;
79
40
  constructor(client: Client);
80
41
  resolveDiscordUserIdentities(input: DiscordUserIdentityRequest): Promise<{
81
42
  guildId: string;
@@ -110,6 +71,7 @@ export interface SupportNotificationInput {
110
71
  source: "dashboard_bug" | "dashboard_support" | "dashboard_security" | "discord_command";
111
72
  }
112
73
  export interface BotControlState {
74
+ botGuildPermissions?: string[];
113
75
  discordCapabilities?: {
114
76
  messageContent: boolean;
115
77
  };
@@ -134,7 +96,7 @@ export interface DashboardGuildAccessState extends GuildAccessPolicy {
134
96
  actorHighestRolePosition: number | null;
135
97
  updatedAt: string | null;
136
98
  }
137
- export interface BotControlHandler extends FirstTimeCommandSetupHandler, DashboardActionHandler, ManagedResourceCreateDefaultsControl, ChannelAuditControl, DiscordUserIdentityControl {
99
+ export interface BotControlHandler extends ServerOverviewControl, FirstTimeCommandSetupHandler, DashboardActionHandler, ManagedResourceCreateDefaultsControl, ChannelAuditControl, DiscordUserIdentityControl {
138
100
  creatorOnboarding?(input: CreatorOnboardingRequest): Promise<CreatorOnboardingResponse>;
139
101
  cleanupOAuthConsumer(input: {
140
102
  consumerBindingId: string;
@@ -363,6 +325,8 @@ export declare class BotControlService implements BotControlHandler {
363
325
  private readonly supportNotifications?;
364
326
  private readonly presenceOwnership?;
365
327
  private readonly presenceShardCount?;
328
+ readonly readServerOverview: NonNullable<ServerOverviewControl["readServerOverview"]>;
329
+ readonly revokeServerOverviewInvite: NonNullable<ServerOverviewControl["revokeServerOverviewInvite"]>;
366
330
  readonly readChannelAuditConfiguration: Required<ChannelAuditControl>["readChannelAuditConfiguration"];
367
331
  readonly checkChannelAudit: Required<ChannelAuditControl>["checkChannelAudit"];
368
332
  constructor(databaseOrPersistence: DatabasePool | BotControlPersistence, modules: readonly ModuleDefinition[], resources: BotControlGuildResourceDirectory, services: ServiceAccess, privacyExecutor?: PrivacyProviderExecutor, supportNotifications?: {
@@ -1,4 +1,8 @@
1
+ import { overviewReadContext } from "./server-overview-profile.js";
1
2
  import { discordUserIdentityRequestSchema, } from "@helyx/control-protocol/discord-user-identities";
3
+ import { createServerOverviewControl } from "./server-overview-control.js";
4
+ import { createOverviewDiscordDirectory, } from "./server-overview-discord.js";
5
+ import { registerServerOverviewControlRoutes } from "./server-overview-routes.js";
2
6
  import { createDiscordUserIdentityLookup, } from "./discord-user-identities.js";
3
7
  import { AuditRepository, GuildAccessPolicyRepository, InstallationRepository, ModuleLoggingPolicyRepository, OptimisticConcurrencyError, PermissionPolicyRepository, withTransaction, } from "@helyx/database";
4
8
  import { safeError } from "@helyx/core";
@@ -44,9 +48,11 @@ import { validateDiscordUserSettingSelections, validateDiscordSettingSelections,
44
48
  import { configurationValue, guildAccessPolicy, managedResourceDeleteResultSchema, managedResourceDetailSchema, managedResourceFilters, managedResourceObject, managedResourcePageSchema, managedResourceRelationPageSchema, moduleId, mutationContext, permissionState, resourceActionId, resourceCursor, resourceId, snowflake, } from "./control-server-schemas.js";
45
49
  export class DiscordGuildResourceDirectory {
46
50
  client;
51
+ overview;
47
52
  #identities;
48
53
  constructor(client) {
49
54
  this.client = client;
55
+ this.overview = createOverviewDiscordDirectory(client);
50
56
  this.#identities = createDiscordUserIdentityLookup(client);
51
57
  }
52
58
  resolveDiscordUserIdentities(input) {
@@ -107,6 +113,9 @@ export class DiscordGuildResourceDirectory {
107
113
  colour: role.hexColor,
108
114
  position: role.position,
109
115
  })),
116
+ ...(guild.members?.me
117
+ ? { botGuildPermissions: guild.members.me.permissions.toArray() }
118
+ : {}),
110
119
  };
111
120
  }
112
121
  catch (error) {
@@ -242,6 +251,9 @@ export class BotControlService {
242
251
  #services;
243
252
  #privacyProviders;
244
253
  #persistence;
254
+ #overview;
255
+ readServerOverview;
256
+ revokeServerOverviewInvite;
245
257
  readChannelAuditConfiguration;
246
258
  checkChannelAudit;
247
259
  constructor(databaseOrPersistence, modules, resources, services, privacyExecutor, supportNotifications, presenceOwnership, presenceShardCount, statusModules) {
@@ -276,6 +288,33 @@ export class BotControlService {
276
288
  });
277
289
  this.readChannelAuditConfiguration = audit.readChannelAuditConfiguration;
278
290
  this.checkChannelAudit = audit.checkChannelAudit;
291
+ this.#overview = createServerOverviewControl({
292
+ modules: this.#modules,
293
+ services: this.#services,
294
+ permissions: this.#permissions,
295
+ directory: resources.overview,
296
+ auditInviteRevoke: async (input) => {
297
+ await this.#persistence.repositories.audit.append({
298
+ guildId: input.guildId,
299
+ actorUserId: input.actorUserId,
300
+ action: `server.invite.${input.outcome}`,
301
+ source: "dashboard",
302
+ correlationId: input.correlationId,
303
+ targetType: "discord-invite",
304
+ targetId: createHash("sha256")
305
+ .update(`${input.guildId}:${input.code}`)
306
+ .digest("hex"),
307
+ });
308
+ },
309
+ resolveActor: (guildId, actorUserId) => resources.resolveActor(guildId, actorUserId),
310
+ readGuildPolicy: (guildId) => this.#readGuildAccessPolicy(guildId),
311
+ listModules: (guildId) => this.#persistence.repositories.installations.listModules(guildId),
312
+ requireAccessible: (guildId, moduleId) => this.#requireModuleAccessible(guildId, moduleId),
313
+ context: (guildId, actor) => overviewReadContext(guildId, actor, this.#services),
314
+ });
315
+ this.readServerOverview = this.#overview.readServerOverview.bind(this.#overview);
316
+ this.revokeServerOverviewInvite =
317
+ this.#overview.revokeServerOverviewInvite.bind(this.#overview);
279
318
  }
280
319
  async creatorOnboarding(input) {
281
320
  return executeCreatorOnboarding(input, {
@@ -994,7 +1033,9 @@ export class BotControlService {
994
1033
  const declaredIntents = declaration?.creationIntents ?? ["publish"];
995
1034
  if (!declaredIntents.includes(input.intent))
996
1035
  throw new DashboardActionValidationError("This creation intent is not declared by the module.");
997
- return normalizeManagedResourceDetail(managedResourceDetailSchema.parse(await resource.create(context, input.value, input.intent)), managedResourceColumnKeys(module, input.resourceType));
1036
+ const result = normalizeManagedResourceDetail(managedResourceDetailSchema.parse(await resource.create(context, input.value, input.intent)), managedResourceColumnKeys(module, input.resourceType));
1037
+ this.#overview.invalidate(input.guildId);
1038
+ return result;
998
1039
  }
999
1040
  async updateManagedResource(input) {
1000
1041
  await this.#requireModuleAccessible(input.guildId, input.moduleId);
@@ -1005,11 +1046,13 @@ export class BotControlService {
1005
1046
  declaration?.featureId,
1006
1047
  ...resourceFieldFeatureIds(declaration?.fields ?? [], input.value),
1007
1048
  ]);
1008
- return normalizeManagedResourceDetail(managedResourceDetailSchema.parse(await resource.update(await this.#managedResourceContext(input, module, "update"), {
1049
+ const result = normalizeManagedResourceDetail(managedResourceDetailSchema.parse(await resource.update(await this.#managedResourceContext(input, module, "update"), {
1009
1050
  resourceId: input.resourceId,
1010
1051
  expectedRevision: input.expectedRevision,
1011
1052
  value: input.value,
1012
1053
  })), managedResourceColumnKeys(module, input.resourceType));
1054
+ this.#overview.invalidate(input.guildId);
1055
+ return result;
1013
1056
  }
1014
1057
  async deleteManagedResource(input) {
1015
1058
  await this.#requireModuleAccessible(input.guildId, input.moduleId);
@@ -1017,10 +1060,12 @@ export class BotControlService {
1017
1060
  this.#requireManagedResourceOperation(module, input.resourceType, "delete");
1018
1061
  const declaration = module.manifest.dashboard?.resources?.find(({ id }) => id === input.resourceType);
1019
1062
  await requireDashboardFeatures(this.#services, input.guildId, input.moduleId, [declaration?.featureId]);
1020
- return managedResourceDeleteResultSchema.parse(await resource.delete(await this.#managedResourceContext(input, module, "delete"), {
1063
+ const result = managedResourceDeleteResultSchema.parse(await resource.delete(await this.#managedResourceContext(input, module, "delete"), {
1021
1064
  resourceId: input.resourceId,
1022
1065
  expectedRevision: input.expectedRevision,
1023
1066
  }));
1067
+ this.#overview.invalidate(input.guildId);
1068
+ return result;
1024
1069
  }
1025
1070
  async executeManagedResourceAction(input) {
1026
1071
  await this.#requireModuleAccessible(input.guildId, input.moduleId);
@@ -1046,6 +1091,7 @@ export class BotControlService {
1046
1091
  expectedRevision: input.expectedRevision,
1047
1092
  value: input.value,
1048
1093
  });
1094
+ this.#overview.invalidate(input.guildId);
1049
1095
  return result === null
1050
1096
  ? null
1051
1097
  : normalizeManagedResourceDetail(managedResourceDetailSchema.parse(result), managedResourceColumnKeys(module, input.resourceType));
@@ -1523,6 +1569,7 @@ export async function createBotControlServer(input) {
1523
1569
  return input.service.setModuleStatus({ ...params, ...body });
1524
1570
  });
1525
1571
  registerDashboardActionRoutes(server, input.service);
1572
+ registerServerOverviewControlRoutes(server, input.service);
1526
1573
  registerChannelAuditRoutes(server, input.service);
1527
1574
  server.get("/v1/guilds/:guildId/modules/:moduleId/resources/:resourceType", async (request) => {
1528
1575
  const params = z
@@ -8,6 +8,13 @@ export declare class DiscordCreatorInviteService implements CreatorInviteService
8
8
  private readonly repository;
9
9
  private readonly services;
10
10
  private readonly module;
11
+ identifyInvites(input: {
12
+ guildId: string;
13
+ codes: readonly string[];
14
+ }): Promise<readonly {
15
+ code: string;
16
+ reference: string;
17
+ }[]>;
11
18
  constructor(client: Client, repository: CreatorInviteRepository, services: ServiceAccess, module: ModuleDefinition | undefined, encryption: string | {
12
19
  inviteEncryptionKey: string;
13
20
  });
@@ -16,6 +16,23 @@ export class DiscordCreatorInviteService {
16
16
  repository;
17
17
  services;
18
18
  module;
19
+ async identifyInvites(input) {
20
+ validateId(input.guildId);
21
+ if (input.codes.length > 15 ||
22
+ input.codes.some((code) => !/^[A-Za-z0-9_-]{1,100}$/u.test(code)))
23
+ throw new CreatorInviteOperationError("unavailable");
24
+ this.#ownedGuild(input.guildId);
25
+ if (!(await this.repository.isInstalled()))
26
+ return [];
27
+ const requested = new Set(input.codes);
28
+ const found = [];
29
+ for (const record of await this.repository.listOwnedForGuild(input.guildId)) {
30
+ const code = this.#decrypt(record);
31
+ if (requested.has(code))
32
+ found.push({ code, reference: record.reference });
33
+ }
34
+ return found;
35
+ }
19
36
  #key;
20
37
  #rest = null;
21
38
  #cleanupCursor;
@@ -163,8 +163,8 @@ export declare class DiscordGuildHealthInspection implements DiscordGuildHealthI
163
163
  resourceValidationValid: boolean | null;
164
164
  activationValid: boolean | null;
165
165
  catalogueMatches: boolean;
166
- requiredCapabilities: readonly ("message-content" | "guild-members")[];
167
- missingCapabilities: ("message-content" | "guild-members")[];
166
+ requiredCapabilities: readonly ("guild-members" | "message-content")[];
167
+ missingCapabilities: ("guild-members" | "message-content")[];
168
168
  missingPermissions: string[];
169
169
  healthStatus: "healthy" | "degraded" | "unhealthy";
170
170
  healthMessage: string | null;
package/dist/events.d.ts CHANGED
@@ -113,6 +113,7 @@ export declare class DiscordResourceInspection implements DiscordResourceInspect
113
113
  userId: string;
114
114
  displayName: string | null;
115
115
  avatarUrl?: string;
116
+ isBot?: boolean;
116
117
  }[]>;
117
118
  searchMembers(input: {
118
119
  guildId: string;
package/dist/events.js CHANGED
@@ -1802,6 +1802,7 @@ export class DiscordResourceInspection {
1802
1802
  const member = members.get(userId) ?? null;
1803
1803
  const cachedUser = this.client.users?.cache?.get(userId);
1804
1804
  const avatarUrl = memberAvatarUrl(member);
1805
+ const isBot = member?.user?.bot ?? cachedUser?.bot;
1805
1806
  return {
1806
1807
  userId,
1807
1808
  displayName: member?.displayName ??
@@ -1809,6 +1810,7 @@ export class DiscordResourceInspection {
1809
1810
  cachedUser?.username ??
1810
1811
  null,
1811
1812
  ...(avatarUrl ? { avatarUrl } : {}),
1813
+ ...(typeof isBot === "boolean" ? { isBot } : {}),
1812
1814
  };
1813
1815
  });
1814
1816
  }
@@ -0,0 +1,25 @@
1
+ import { z } from "zod";
2
+ export declare const HELP_API_ORIGIN = "https://api.helyx.gg";
3
+ declare const credentialSchema: z.ZodObject<{
4
+ installationId: z.ZodString;
5
+ token: z.ZodString;
6
+ expiresAt: z.ZodString;
7
+ }, z.core.$strict>;
8
+ export type HelpCredential = z.infer<typeof credentialSchema>;
9
+ export declare const parseHelpCredential: (value: unknown) => HelpCredential;
10
+ export declare class HelpCredentialError extends Error {
11
+ constructor();
12
+ }
13
+ /** Encrypted, opt-in file; separate from Cloud Connect and every provider secret. */
14
+ export declare class HelpCredentialStore {
15
+ readonly path: string;
16
+ private readonly key;
17
+ constructor(directory: string, dataKey: string);
18
+ private aad;
19
+ private checkDirectory;
20
+ read(): Promise<HelpCredential | null>;
21
+ save(input: HelpCredential): Promise<void>;
22
+ remove(): Promise<void>;
23
+ }
24
+ export {};
25
+ //# sourceMappingURL=help-credentials.d.ts.map