@helyx/bot 0.4.30 → 0.4.32

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/dist/modules.d.ts CHANGED
@@ -12,11 +12,13 @@ export interface ConfiguredModulePackage extends LoadedDefinition {
12
12
  }
13
13
  export declare const secureRandomSelectionWorkerKeyName = "HELYX_INTERNAL_SECURE_RANDOM_SELECTION_KEY";
14
14
  interface PlatformServiceEnvironment extends Pick<BotEnvironment, "HELYX_DATA_ENCRYPTION_KEY"> {
15
+ HELYX_DASHBOARD_PUBLIC_URL?: string;
15
16
  HELYX_INTERNAL_SECURE_RANDOM_SELECTION_KEY?: string;
16
17
  }
17
18
  export declare function deriveSecureRandomSelectionKey(encodedDataEncryptionKey: string): Uint8Array;
18
19
  export declare function platformServiceOptions(environment: PlatformServiceEnvironment, modules: readonly ModuleDefinition[]): {
19
20
  secureRandomSelectionKey?: Uint8Array;
21
+ dashboardBaseUrl?: string;
20
22
  };
21
23
  export declare function loadConfiguredModules(specifiers: readonly string[], importer?: ModuleImporter): Promise<ModuleDefinition[]>;
22
24
  export declare function loadConfiguredModulePackages(specifiers: readonly string[], importer?: ModuleImporter, resolvePackageJson?: PackageJsonResolver, readPackageJson?: PackageJsonReader): Promise<ConfiguredModulePackage[]>;
package/dist/modules.js CHANGED
@@ -21,9 +21,13 @@ export function deriveSecureRandomSelectionKey(encodedDataEncryptionKey) {
21
21
  .digest();
22
22
  }
23
23
  export function platformServiceOptions(environment, modules) {
24
+ const urls = environment.HELYX_DASHBOARD_PUBLIC_URL
25
+ ? { dashboardBaseUrl: environment.HELYX_DASHBOARD_PUBLIC_URL }
26
+ : {};
24
27
  const internalSelectionKey = environment.HELYX_INTERNAL_SECURE_RANDOM_SELECTION_KEY;
25
28
  if (internalSelectionKey) {
26
29
  return {
30
+ ...urls,
27
31
  secureRandomSelectionKey: decodeSecureRandomSelectionKey(internalSelectionKey, secureRandomSelectionWorkerKeyName),
28
32
  };
29
33
  }
@@ -32,9 +36,10 @@ export function platformServiceOptions(environment, modules) {
32
36
  if (modules.some(({ manifest }) => manifest.id === giveawaysModuleId)) {
33
37
  throw new Error("HELYX_DATA_ENCRYPTION_KEY is required when Giveaways is loaded");
34
38
  }
35
- return {};
39
+ return urls;
36
40
  }
37
41
  return {
42
+ ...urls,
38
43
  secureRandomSelectionKey: deriveSecureRandomSelectionKey(encodedKey),
39
44
  };
40
45
  }
@@ -103,7 +108,11 @@ export function validateContributionParity(definition) {
103
108
  ...(definition.events?.guildVoiceSession ?? []),
104
109
  ...(definition.events?.messageReactionAdd ?? []),
105
110
  ...(definition.events?.messageReactionRemove ?? []),
111
+ ...(definition.events?.messageReactionRemoveAll ?? []),
112
+ ...(definition.events?.messageReactionRemoveEmoji ?? []),
113
+ ...(definition.events?.guildMessageUpdate ?? []),
106
114
  ...(definition.events?.messageDelete ?? []),
115
+ ...(definition.events?.guildMessageDeleteBulk ?? []),
107
116
  ...(definition.events?.guildMemberAdd ?? []),
108
117
  ...(definition.events?.guildMemberRemove ?? []),
109
118
  ...(definition.events?.guildRoleDelete ?? []),
@@ -0,0 +1,2 @@
1
+ export declare function createOAuthModule(): import("@helyx/sdk").ModuleDefinition;
2
+ //# sourceMappingURL=oauth-module.d.ts.map
@@ -0,0 +1,192 @@
1
+ import { defineModule, HELYX_SERVICE_NAMES, } from "@helyx/sdk";
2
+ const MODULE_ID = "helyx.oauth";
3
+ const MODAL_ID = "helyx.oauth.revoke";
4
+ const SERVICE_FIELD_ID = "helyx.oauth.revoke.service";
5
+ const CONFIRM_FIELD_ID = "helyx.oauth.revoke.confirm";
6
+ const COMMENT_FIELD_ID = "helyx.oauth.revoke.comment";
7
+ const everyonePolicy = {
8
+ everyone: true,
9
+ requiredPermissions: [],
10
+ allowedRoleIds: [],
11
+ deniedRoleIds: [],
12
+ allowedUserIds: [],
13
+ deniedUserIds: [],
14
+ };
15
+ export function createOAuthModule() {
16
+ return defineModule({
17
+ manifest: {
18
+ manifestVersion: "0.1",
19
+ releaseStatus: "live",
20
+ id: MODULE_ID,
21
+ name: "Helyx OAuth",
22
+ version: "0.1.0",
23
+ description: "Provides self-service OAuth connection revocation.",
24
+ entry: "./dist/oauth-module.js",
25
+ framework: "^0.1.0",
26
+ dependencies: [],
27
+ permissions: [],
28
+ contributions: { commands: ["oauth"], events: [] },
29
+ },
30
+ defaultPermissions: {
31
+ module: everyonePolicy,
32
+ commands: { "oauth.revoke": everyonePolicy },
33
+ },
34
+ interactions: {
35
+ commands: [
36
+ {
37
+ name: "oauth",
38
+ description: "Manage OAuth access granted to Helyx.",
39
+ registration: "global",
40
+ contexts: ["guild"],
41
+ integrationTypes: ["guild_install"],
42
+ subcommands: [
43
+ {
44
+ name: "revoke",
45
+ description: "Revoke a service and delete the OAuth details Helyx stores for you.",
46
+ rateLimit: { scope: "user", limit: 5, windowSeconds: 60 },
47
+ execute: async (context) => {
48
+ const oauth = context.services.get(HELYX_SERVICE_NAMES.oauth);
49
+ let connections;
50
+ try {
51
+ connections = await oauth.listConnections({
52
+ discordUserId: context.userId,
53
+ });
54
+ }
55
+ catch {
56
+ await context.reply(unavailable());
57
+ return;
58
+ }
59
+ if (connections.length === 0) {
60
+ await context.reply({
61
+ ephemeral: true,
62
+ componentsV2: {
63
+ text: [
64
+ {
65
+ content: "You do not have any OAuth services connected to Helyx.",
66
+ },
67
+ ],
68
+ },
69
+ });
70
+ return;
71
+ }
72
+ await context.showModal({
73
+ customId: MODAL_ID,
74
+ title: "Revoke OAuth access",
75
+ items: [
76
+ {
77
+ type: "text-display",
78
+ content: "Choose one connection. This disconnects the service from Helyx everywhere. Any Helyx module, server or panel using it will lose access.",
79
+ },
80
+ {
81
+ type: "string-select",
82
+ customId: SERVICE_FIELD_ID,
83
+ label: "Connected service",
84
+ placeholder: "Choose a connection",
85
+ required: true,
86
+ minValues: 1,
87
+ maxValues: 1,
88
+ options: connections.slice(0, 25).map((connection) => ({
89
+ label: `${providerName(connection.provider)} — ${connection.displayName}`.slice(0, 100),
90
+ value: connection.selectionRef,
91
+ description: "Remove this connection",
92
+ })),
93
+ },
94
+ {
95
+ type: "string-select",
96
+ customId: CONFIRM_FIELD_ID,
97
+ label: "Confirm removal",
98
+ placeholder: "Select Remove access",
99
+ required: true,
100
+ minValues: 1,
101
+ maxValues: 1,
102
+ options: [
103
+ {
104
+ label: "Remove access",
105
+ value: "remove_access",
106
+ description: "Revoke access and delete my stored details",
107
+ },
108
+ ],
109
+ },
110
+ {
111
+ customId: COMMENT_FIELD_ID,
112
+ label: "Reason or comment (optional)",
113
+ description: "Stored only in Helyx's restricted security audit.",
114
+ style: "paragraph",
115
+ required: false,
116
+ maxLength: 250,
117
+ },
118
+ ],
119
+ fields: [],
120
+ });
121
+ },
122
+ },
123
+ ],
124
+ },
125
+ ],
126
+ modals: [
127
+ {
128
+ customId: MODAL_ID,
129
+ execute: async (context) => {
130
+ const selectionRef = context.getStringSelectValues(SERVICE_FIELD_ID)[0];
131
+ const confirmation = context.getStringSelectValues(CONFIRM_FIELD_ID)[0];
132
+ if (!selectionRef || confirmation !== "remove_access") {
133
+ await context.reply({
134
+ ephemeral: true,
135
+ componentsV2: {
136
+ text: [{ content: "Access was not removed." }],
137
+ },
138
+ });
139
+ return;
140
+ }
141
+ const comment = context.getField(COMMENT_FIELD_ID).trim();
142
+ await context.deferReply?.(true);
143
+ try {
144
+ const result = await context.services
145
+ .get(HELYX_SERVICE_NAMES.oauth)
146
+ .revoke({
147
+ discordUserId: context.userId,
148
+ selectionRef,
149
+ confirmation: "remove_access",
150
+ ...(comment ? { comment } : {}),
151
+ correlationId: context.interactionId,
152
+ });
153
+ await context.reply({
154
+ ephemeral: true,
155
+ componentsV2: {
156
+ text: [
157
+ {
158
+ content: result.providerRevocation === "completed"
159
+ ? `${providerName(result.provider)} access has been removed from Helyx. Local credentials were deleted.`
160
+ : `${providerName(result.provider)} access has been removed from Helyx and local credentials were deleted. Helyx could not confirm provider-side revocation, so you may also remove Helyx from your ${providerName(result.provider)} connected-app settings.`,
161
+ },
162
+ ],
163
+ },
164
+ });
165
+ }
166
+ catch {
167
+ await context.reply(unavailable());
168
+ }
169
+ },
170
+ },
171
+ ],
172
+ },
173
+ start: () => Promise.resolve(),
174
+ stop: () => Promise.resolve(),
175
+ });
176
+ }
177
+ function providerName(provider) {
178
+ return provider === "youtube" ? "YouTube" : "Twitch";
179
+ }
180
+ function unavailable() {
181
+ return {
182
+ ephemeral: true,
183
+ componentsV2: {
184
+ text: [
185
+ {
186
+ content: "OAuth access could not be updated right now. Nothing new was retained; please try again.",
187
+ },
188
+ ],
189
+ },
190
+ };
191
+ }
192
+ //# sourceMappingURL=oauth-module.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=oauth-module.test.d.ts.map
@@ -0,0 +1,172 @@
1
+ import { HELYX_SERVICE_NAMES, } from "@helyx/sdk";
2
+ import { describe, expect, it, vi } from "vitest";
3
+ import { createOAuthModule } from "./oauth-module.js";
4
+ describe("global OAuth command", () => {
5
+ it("is global, guild-scoped and available without management permissions", () => {
6
+ const module = createOAuthModule();
7
+ const command = module.interactions?.commands?.[0];
8
+ const revoke = command?.subcommands?.[0];
9
+ expect(command).toMatchObject({
10
+ name: "oauth",
11
+ registration: "global",
12
+ contexts: ["guild"],
13
+ integrationTypes: ["guild_install"],
14
+ });
15
+ expect(revoke).toMatchObject({
16
+ name: "revoke",
17
+ rateLimit: { scope: "user", limit: 5, windowSeconds: 60 },
18
+ });
19
+ expect(module.manifest.permissions).toEqual([]);
20
+ expect(module.defaultPermissions?.commands["oauth.revoke"]).toMatchObject({
21
+ everyone: true,
22
+ requiredPermissions: [],
23
+ });
24
+ });
25
+ it("does not open a modal when the acting user has no connections", async () => {
26
+ const oauth = oauthService({ connections: [] });
27
+ const showModal = vi.fn();
28
+ const replies = [];
29
+ const command = createOAuthModule().interactions?.commands?.[0]?.subcommands?.[0];
30
+ await command?.execute(commandContext(oauth, {
31
+ showModal,
32
+ reply: (response) => {
33
+ replies.push(response);
34
+ return Promise.resolve();
35
+ },
36
+ }));
37
+ expect(showModal).not.toHaveBeenCalled();
38
+ expect(replies).toEqual([expect.objectContaining({ ephemeral: true })]);
39
+ expect(replies[0]?.componentsV2?.text?.[0]?.content).toContain("do not have any OAuth services");
40
+ });
41
+ it("uses opaque selections and requires explicit removal confirmation", async () => {
42
+ const oauth = oauthService({
43
+ connections: [
44
+ {
45
+ selectionRef: "opaque-selection-reference-123",
46
+ provider: "youtube",
47
+ displayName: "Example creator",
48
+ connectedAt: new Date().toISOString(),
49
+ },
50
+ ],
51
+ });
52
+ const showModal = vi.fn();
53
+ const command = createOAuthModule().interactions?.commands?.[0]?.subcommands?.[0];
54
+ await command?.execute(commandContext(oauth, { showModal }));
55
+ const modal = showModal.mock.calls[0]?.[0];
56
+ expect(modal?.title).toBe("Revoke OAuth access");
57
+ const service = modal?.items?.find((item) => "customId" in item && item.customId.endsWith("service"));
58
+ const confirmation = modal?.items?.find((item) => "customId" in item && item.customId.endsWith("confirm"));
59
+ expect(service).toMatchObject({
60
+ type: "string-select",
61
+ required: true,
62
+ options: [
63
+ expect.objectContaining({ value: "opaque-selection-reference-123" }),
64
+ ],
65
+ });
66
+ expect(confirmation).toMatchObject({
67
+ type: "string-select",
68
+ required: true,
69
+ options: [expect.objectContaining({ value: "remove_access" })],
70
+ });
71
+ });
72
+ it("revokes only for the acting user and returns an ephemeral advisory", async () => {
73
+ const revoke = vi.fn().mockResolvedValue({
74
+ provider: "twitch",
75
+ outcome: "completed",
76
+ providerRevocation: "failed",
77
+ });
78
+ const oauth = oauthService({ connections: [], revoke });
79
+ const replies = [];
80
+ const modal = createOAuthModule().interactions?.modals?.[0];
81
+ await modal?.execute(modalContext(oauth, {
82
+ reply: (response) => {
83
+ replies.push(response);
84
+ return Promise.resolve();
85
+ },
86
+ }));
87
+ expect(revoke).toHaveBeenCalledWith({
88
+ discordUserId: "12345678901234567",
89
+ selectionRef: "opaque-selection-reference-123",
90
+ confirmation: "remove_access",
91
+ comment: "No longer needed",
92
+ correlationId: "interaction-1",
93
+ });
94
+ expect(replies[0]).toMatchObject({ ephemeral: true });
95
+ expect(replies[0]?.componentsV2?.text?.[0]?.content).toContain("connected-app settings");
96
+ });
97
+ });
98
+ function oauthService(input) {
99
+ return {
100
+ connectionStatus: () => Promise.resolve([]),
101
+ listConnections: () => Promise.resolve(input.connections),
102
+ createAuthorization: () => Promise.resolve({
103
+ authorizationUrl: "https://example.com",
104
+ expiresAt: "",
105
+ }),
106
+ verifyEligibility: () => Promise.resolve({
107
+ provider: "youtube",
108
+ status: "unknown",
109
+ connected: false,
110
+ checkedAt: new Date().toISOString(),
111
+ }),
112
+ revoke: input.revoke ??
113
+ (() => Promise.resolve({
114
+ provider: "youtube",
115
+ outcome: "completed",
116
+ providerRevocation: "completed",
117
+ })),
118
+ };
119
+ }
120
+ function services(oauth) {
121
+ return {
122
+ has: (name) => name === HELYX_SERVICE_NAMES.oauth,
123
+ get: (name) => {
124
+ if (name !== HELYX_SERVICE_NAMES.oauth)
125
+ throw new Error("unknown service");
126
+ return oauth;
127
+ },
128
+ };
129
+ }
130
+ function commandContext(oauth, overrides = {}) {
131
+ return {
132
+ ...interactionBase(oauth),
133
+ showModal: () => Promise.resolve(),
134
+ getStringOption: () => null,
135
+ getBooleanOption: () => null,
136
+ getUserOption: () => null,
137
+ getChannelOption: () => null,
138
+ getRoleOption: () => null,
139
+ ...overrides,
140
+ };
141
+ }
142
+ function modalContext(oauth, overrides = {}) {
143
+ return {
144
+ ...interactionBase(oauth),
145
+ getField: (customId) => customId.endsWith("comment") ? "No longer needed" : "",
146
+ getStringSelectValues: (customId) => customId.endsWith("service")
147
+ ? ["opaque-selection-reference-123"]
148
+ : ["remove_access"],
149
+ getSelectedUserIds: () => [],
150
+ getSelectedRoleIds: () => [],
151
+ getSelectedMentionables: () => ({ userIds: [], roleIds: [] }),
152
+ getSelectedChannelIds: () => [],
153
+ ...overrides,
154
+ };
155
+ }
156
+ function interactionBase(oauth) {
157
+ return {
158
+ interactionId: "interaction-1",
159
+ userId: "12345678901234567",
160
+ userDisplayName: "Member",
161
+ userRoleIds: [],
162
+ isAdministrator: false,
163
+ serverId: "12345678901234568",
164
+ services: services(oauth),
165
+ reply: () => Promise.resolve(),
166
+ sendToChannel: () => Promise.resolve({ messageId: "message-1" }),
167
+ deleteChannelMessage: () => Promise.resolve(),
168
+ editChannelMessage: () => Promise.resolve(),
169
+ addChannelMessageReaction: () => Promise.resolve(),
170
+ };
171
+ }
172
+ //# sourceMappingURL=oauth-module.test.js.map
@@ -0,0 +1,16 @@
1
+ import type { OAuthConnectionSummary, OAuthEligibilityResult, OAuthService } from "@helyx/sdk";
2
+ export declare class HttpOAuthService implements OAuthService {
3
+ #private;
4
+ private readonly internalSecret;
5
+ constructor(baseUrl: string, internalSecret: string);
6
+ listConnections(input: {
7
+ discordUserId: string;
8
+ }): Promise<readonly OAuthConnectionSummary[]>;
9
+ connectionStatus(input: {
10
+ discordUserId: string;
11
+ }): Promise<readonly ("youtube" | "twitch")[]>;
12
+ createAuthorization(input: Parameters<OAuthService["createAuthorization"]>[0]): ReturnType<OAuthService["createAuthorization"]>;
13
+ verifyEligibility(input: Parameters<OAuthService["verifyEligibility"]>[0]): Promise<OAuthEligibilityResult>;
14
+ revoke(input: Parameters<OAuthService["revoke"]>[0]): ReturnType<OAuthService["revoke"]>;
15
+ }
16
+ //# sourceMappingURL=oauth-service.d.ts.map
@@ -0,0 +1,40 @@
1
+ export class HttpOAuthService {
2
+ internalSecret;
3
+ #baseUrl;
4
+ constructor(baseUrl, internalSecret) {
5
+ this.internalSecret = internalSecret;
6
+ this.#baseUrl = new URL(baseUrl);
7
+ }
8
+ async listConnections(input) {
9
+ const result = await this.#request("/v1/internal/oauth/connections", input);
10
+ return result.connections;
11
+ }
12
+ async connectionStatus(input) {
13
+ const result = await this.#request("/v1/internal/oauth/status", input);
14
+ return result.providers;
15
+ }
16
+ createAuthorization(input) {
17
+ return this.#request("/v1/internal/oauth/authorizations", input);
18
+ }
19
+ verifyEligibility(input) {
20
+ return this.#request("/v1/internal/oauth/eligibility", input);
21
+ }
22
+ revoke(input) {
23
+ return this.#request("/v1/internal/oauth/revocations", input);
24
+ }
25
+ async #request(path, body) {
26
+ const response = await fetch(new URL(path, this.#baseUrl), {
27
+ method: "POST",
28
+ headers: {
29
+ authorization: `Bearer ${this.internalSecret}`,
30
+ "content-type": "application/json",
31
+ },
32
+ body: JSON.stringify(body),
33
+ signal: AbortSignal.timeout(6_000),
34
+ });
35
+ if (!response.ok)
36
+ throw new Error(`oauth_service_http_${response.status}`);
37
+ return (await response.json());
38
+ }
39
+ }
40
+ //# sourceMappingURL=oauth-service.js.map
@@ -22,6 +22,7 @@ import { ShardControlRouter } from "./shard-router.js";
22
22
  import { createDeveloperOperationsModule } from "./developer-operations-module.js";
23
23
  import { SupervisorCloudDiscordDirectory } from "./supervisor-cloud-directory.js";
24
24
  import { createSupportModule } from "./support-module.js";
25
+ import { createOAuthModule } from "./oauth-module.js";
25
26
  import { createScheduledTaskRunner, schedulerOperationalHealth, } from "./scheduled-task-runner.js";
26
27
  import { numericWriteBehindRegistrations } from "./numeric-write-behind.js";
27
28
  import { executeRedisNumericOperation, RedisNumericWriteBehindService, } from "./numeric-write-behind-redis.js";
@@ -187,10 +188,14 @@ export async function startShardSupervisor(environment, processEnvironment, work
187
188
  })
188
189
  : undefined;
189
190
  const supportModule = createSupportModule(cloudConnector);
191
+ const oauthModule = environment.HELYX_OAUTH_ENABLED
192
+ ? createOAuthModule()
193
+ : undefined;
190
194
  const publicModules = [
191
195
  managementModule,
192
196
  permissionsModule,
193
197
  supportModule,
198
+ ...(oauthModule ? [oauthModule] : []),
194
199
  ...(cloudManagementModule ? [cloudManagementModule] : []),
195
200
  ...featureModules,
196
201
  ];
@@ -210,6 +215,7 @@ export async function startShardSupervisor(environment, processEnvironment, work
210
215
  managementModule,
211
216
  permissionsModule,
212
217
  supportModule,
218
+ ...(oauthModule ? [oauthModule] : []),
213
219
  ...(cloudManagementModule ? [cloudManagementModule] : []),
214
220
  ].map(({ manifest }) => manifest),
215
221
  modules: featureModules.map(({ manifest }) => manifest),
@@ -34,6 +34,7 @@ export declare function parseShardWorkerEnvironment(processEnvironment: NodeJS.P
34
34
  APPROVED_ROLES: string[];
35
35
  HELYX_MODULE_PACKAGES: string[];
36
36
  HELYX_MODULE_CATALOGUE_PATH: string | undefined;
37
+ HELYX_DASHBOARD_PUBLIC_URL: string;
37
38
  HELYX_SUPPORT_STAFF_CHANNEL_ID: string | undefined;
38
39
  HELYX_VERIFICATION_ENABLED: boolean;
39
40
  HELYX_VERIFICATION_SIGNING_PUBLIC_KEYS: Record<string, string>;
@@ -41,10 +42,12 @@ export declare function parseShardWorkerEnvironment(processEnvironment: NodeJS.P
41
42
  HELYX_GUILD_RECONCILIATION_INTERVAL_SECONDS: number;
42
43
  HELYX_PRIVACY_ENABLED: boolean;
43
44
  HELYX_MESSAGE_CONTENT_ENABLED: boolean;
45
+ HELYX_GUILD_MEMBERS_ENABLED: boolean;
44
46
  HELYX_CLOUD_CONNECT_ENABLED: boolean;
45
47
  HELYX_CLOUD_ENROLMENT_TOKEN: string | undefined;
46
48
  HELYX_CLOUD_ENROLMENT_FILE: string;
47
49
  HELYX_CLOUD_API_URL: string;
50
+ HELYX_OAUTH_ENABLED: boolean;
48
51
  HELYX_CLOUD_ENDPOINT: string;
49
52
  HELYX_OWNER_USER_IDS: string[];
50
53
  HELYX_SHARD_MODE: "off" | "auto" | "fixed";
@@ -60,6 +63,7 @@ export declare function parseShardWorkerEnvironment(processEnvironment: NodeJS.P
60
63
  HELYX_VERIFICATION_INTERNAL_SECRET?: string | undefined;
61
64
  HELYX_PRIVACY_EXECUTOR_DATABASE_URL?: string | undefined;
62
65
  HELYX_PRIVACY_EXECUTOR_DATABASE_SSL?: "disable" | "require" | "verify-full" | undefined;
66
+ HELYX_OAUTH_INTERNAL_SECRET?: string | undefined;
63
67
  HELYX_SHARD_COUNT?: number | undefined;
64
68
  HELYX_SHARD_REDIS_URL?: string | undefined;
65
69
  HELYX_SHARD_REDIS_NAMESPACE?: string | undefined;
@@ -71,5 +75,6 @@ export declare function shardWorkerCommandRegistration(input: {
71
75
  prepare: NonNullable<CommandRegistration["prepare"]>;
72
76
  operationsModuleId?: string;
73
77
  supportModuleId: string;
78
+ oauthModuleId?: string;
74
79
  }): CommandRegistration;
75
80
  //# sourceMappingURL=shard-worker.d.ts.map
@@ -20,11 +20,14 @@ import { loadConfiguredModulePackages, platformServiceOptions, secureRandomSelec
20
20
  import { createPermissionsModule } from "./permissions-module.js";
21
21
  import { WorkerCloudConnectorProxy } from "./shard-cloud-proxy.js";
22
22
  import { DiscordOwnedMessageDeletionService } from "./discord-owned-message-service.js";
23
+ import { DiscordGuildHealthInspection } from "./discord-guild-health.js";
23
24
  import { CoreResourceAuthorizationService } from "./resource-authorization.js";
24
25
  import { createDeveloperOperationsModule, WorkerShardHealthProvider, } from "./developer-operations-module.js";
25
26
  import { assertShardMessageSize, controlOperationGuildId, executeShardControlOperation, guildIdToShardId, SHARD_LEASE_HEARTBEAT_TIMEOUT_MS, shardIdentitySchema, supervisorMessageSchema, } from "./shard-contracts.js";
26
27
  import { gatewayIntentsForModules, readBotVersion, } from "./index.js";
27
28
  import { createSupportModule } from "./support-module.js";
29
+ import { createOAuthModule } from "./oauth-module.js";
30
+ import { HttpOAuthService } from "./oauth-service.js";
28
31
  import { createLocalScheduledTaskRoute, } from "./scheduled-task-runner.js";
29
32
  import { createLocalNumericWriteBehind } from "./numeric-write-behind.js";
30
33
  import { WorkerNumericWriteBehindProxy } from "./numeric-write-behind-proxy.js";
@@ -111,7 +114,7 @@ export async function startShardWorker(processEnvironment = process.env, identit
111
114
  const modulePackages = await loadConfiguredModulePackages(moduleSpecifiers);
112
115
  const featureModules = modulePackages.map(({ definition }) => definition);
113
116
  client = new Client({
114
- intents: gatewayIntentsForModules(featureModules, environment.HELYX_MESSAGE_CONTENT_ENABLED),
117
+ intents: gatewayIntentsForModules(featureModules, environment.HELYX_MESSAGE_CONTENT_ENABLED, environment.HELYX_GUILD_MEMBERS_ENABLED),
115
118
  partials: [Partials.Message, Partials.Channel, Partials.Reaction],
116
119
  allowedMentions: { parse: [] },
117
120
  shards: [identity.shardId],
@@ -130,10 +133,14 @@ export async function startShardWorker(processEnvironment = process.env, identit
130
133
  })
131
134
  : undefined;
132
135
  const supportModule = createSupportModule(cloudProxy);
136
+ const oauthModule = environment.HELYX_OAUTH_ENABLED
137
+ ? createOAuthModule()
138
+ : undefined;
133
139
  const publicModules = [
134
140
  managementModule,
135
141
  permissionsModule,
136
142
  supportModule,
143
+ ...(oauthModule ? [oauthModule] : []),
137
144
  ...(cloudManagementModule ? [cloudManagementModule] : []),
138
145
  ...featureModules,
139
146
  ];
@@ -152,6 +159,7 @@ export async function startShardWorker(processEnvironment = process.env, identit
152
159
  managementModule,
153
160
  permissionsModule,
154
161
  supportModule,
162
+ ...(oauthModule ? [oauthModule] : []),
155
163
  ...(cloudManagementModule ? [cloudManagementModule] : []),
156
164
  ].map(({ manifest }) => manifest),
157
165
  modules: featureModules.map(({ manifest }) => manifest),
@@ -182,6 +190,9 @@ export async function startShardWorker(processEnvironment = process.env, identit
182
190
  }));
183
191
  platformServices.set(HELYX_SERVICE_NAMES.verificationMembers, new DiscordVerificationMemberService(client));
184
192
  }
193
+ if (environment.HELYX_OAUTH_ENABLED &&
194
+ environment.HELYX_OAUTH_INTERNAL_SECRET)
195
+ platformServices.set(HELYX_SERVICE_NAMES.oauth, new HttpOAuthService(environment.HELYX_CLOUD_API_URL, environment.HELYX_OAUTH_INTERNAL_SECRET));
185
196
  numericWriteBehind =
186
197
  identity.scope === "coordinated"
187
198
  ? new WorkerNumericWriteBehindProxy(identity)
@@ -193,6 +204,7 @@ export async function startShardWorker(processEnvironment = process.env, identit
193
204
  platformServices.set(HELYX_SERVICE_NAMES.memberEnforcement, new DiscordMemberEnforcementService(client));
194
205
  platformServices.set(HELYX_SERVICE_NAMES.memberRoles, new DiscordMemberRoleService(client));
195
206
  platformServices.set(HELYX_SERVICE_NAMES.discordResources, new DiscordResourceInspection(client));
207
+ platformServices.set(HELYX_SERVICE_NAMES.discordGuildHealth, new DiscordGuildHealthInspection(client, publicModules, serviceAccess));
196
208
  if (featureModules.some(({ manifest }) => manifest.id === "helyx.logging"))
197
209
  platformServices.set(HELYX_SERVICE_NAMES.moduleLogging, new DiscordModuleLoggingService(client, database, publicModules, logger));
198
210
  const scheduledTaskRoute = createLocalScheduledTaskRoute({
@@ -205,6 +217,7 @@ export async function startShardWorker(processEnvironment = process.env, identit
205
217
  managementModule.manifest.id,
206
218
  permissionsModule.manifest.id,
207
219
  supportModule.manifest.id,
220
+ ...(oauthModule ? [oauthModule.manifest.id] : []),
208
221
  ...(cloudManagementModule ? [cloudManagementModule.manifest.id] : []),
209
222
  ...(operationsModule ? [operationsModule.manifest.id] : []),
210
223
  ]);
@@ -226,6 +239,7 @@ export async function startShardWorker(processEnvironment = process.env, identit
226
239
  ? { operationsModuleId: operationsModule.manifest.id }
227
240
  : {}),
228
241
  supportModuleId: supportModule.manifest.id,
242
+ ...(oauthModule ? { oauthModuleId: oauthModule.manifest.id } : {}),
229
243
  }), logger, serviceAccess);
230
244
  moduleEvents = installModuleEvents(client, publicModules, logger, serviceAccess);
231
245
  const controlService = new BotControlService(database, publicModules, guildResources, serviceAccess, undefined, environment.DISCORD_INTERNAL_GUILD_ID &&
@@ -419,6 +433,7 @@ export function shardWorkerCommandRegistration(input) {
419
433
  synchronise: false,
420
434
  selfAuthorizingGuildModuleIds: new Set([
421
435
  input.supportModuleId,
436
+ ...(input.oauthModuleId ? [input.oauthModuleId] : []),
422
437
  ...(input.operationsModuleId ? [input.operationsModuleId] : []),
423
438
  ]),
424
439
  };