@helyx/bot 0.4.44 → 0.5.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.
- package/CHANGELOG.md +43 -0
- package/dist/command-path-contract.d.ts +26 -0
- package/dist/command-path-contract.js +157 -0
- package/dist/control-permissions.js +1 -0
- package/dist/control-server.d.ts +1 -1
- package/dist/control-server.js +8 -2
- package/dist/dashboard-commands.js +5 -35
- package/dist/discord-analytics.d.ts +30 -0
- package/dist/discord-analytics.js +337 -0
- package/dist/discord-invites.d.ts +24 -0
- package/dist/discord-invites.js +91 -0
- package/dist/events.js +53 -2
- package/dist/index.js +11 -3
- package/dist/interactions.js +66 -42
- package/dist/management-module.d.ts +1 -1
- package/dist/modules.d.ts +2 -0
- package/dist/modules.js +27 -4
- package/dist/permissions-module.js +13 -16
- package/dist/shard-contracts.js +1 -1
- package/dist/shard-worker.js +11 -3
- package/package.json +4 -4
package/dist/interactions.js
CHANGED
|
@@ -4,6 +4,7 @@ import { canUseCommand } from "@helyx/core";
|
|
|
4
4
|
import { commandRegistrationPolicy, contributionRegistrationPolicy, } from "./command-registration-policy.js";
|
|
5
5
|
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelSelectMenuBuilder, EmbedBuilder, Events, GuildMember, ApplicationIntegrationType, ChannelType, InteractionContextType, LabelBuilder, MessageFlags, MentionableSelectMenuBuilder, ModalBuilder, RoleSelectMenuBuilder, Routes, StringSelectMenuBuilder, SlashCommandBuilder, TextInputBuilder, TextInputStyle, UserSelectMenuBuilder, TextDisplayBuilder, PermissionFlagsBits, escapeMarkdown, } from "discord.js";
|
|
6
6
|
import { collectCommandRegistrations, } from "./shared-commands.js";
|
|
7
|
+
import { resolveDirectCommandPath } from "./command-path-contract.js";
|
|
7
8
|
const buttonStyles = {
|
|
8
9
|
primary: ButtonStyle.Primary,
|
|
9
10
|
secondary: ButtonStyle.Secondary,
|
|
@@ -108,10 +109,7 @@ export function installInteractions(client, modules, registration, logger, servi
|
|
|
108
109
|
if (!registered ||
|
|
109
110
|
!subcommand?.autocomplete ||
|
|
110
111
|
!ownerModuleId ||
|
|
111
|
-
!(await authorizeAutocomplete(interaction, ownerModuleId, subcommand.permissionId ??
|
|
112
|
-
[registered.contribution.name, groupName, subcommand.name]
|
|
113
|
-
.filter(Boolean)
|
|
114
|
-
.join("."), services, registration.selfAuthorizingGuildModuleIds?.has(ownerModuleId) ??
|
|
112
|
+
!(await authorizeAutocomplete(interaction, ownerModuleId, resolveDirectCommandPath(registered.contribution, subcommand, groupName).permissionId, services, registration.selfAuthorizingGuildModuleIds?.has(ownerModuleId) ??
|
|
115
113
|
false, subcommand.authorization === "resource-scoped", isInstallationIndependentModule(modulesById, ownerModuleId), subcommand.authorization))) {
|
|
116
114
|
await response.respond([]);
|
|
117
115
|
return;
|
|
@@ -175,12 +173,7 @@ export function installInteractions(client, modules, registration, logger, servi
|
|
|
175
173
|
});
|
|
176
174
|
return;
|
|
177
175
|
}
|
|
178
|
-
const
|
|
179
|
-
? [command.name, groupName, subcommandName]
|
|
180
|
-
.filter(Boolean)
|
|
181
|
-
.join(".")
|
|
182
|
-
: command.name;
|
|
183
|
-
const permissionId = subcommand?.permissionId ?? command.permissionId ?? commandId;
|
|
176
|
+
const { permissionId } = resolveDirectCommandPath(command, subcommand, groupName);
|
|
184
177
|
const owner = resolveCommandOwner(registered, subcommandName, groupName);
|
|
185
178
|
const ownerModuleId = owner?.moduleId ?? moduleId;
|
|
186
179
|
const installationIndependentCommand = isInstallationIndependentModule(modulesById, ownerModuleId);
|
|
@@ -515,6 +508,8 @@ function addCommandOption(builder, option) {
|
|
|
515
508
|
guild_text: ChannelType.GuildText,
|
|
516
509
|
guild_announcement: ChannelType.GuildAnnouncement,
|
|
517
510
|
guild_category: ChannelType.GuildCategory,
|
|
511
|
+
guild_forum: ChannelType.GuildForum,
|
|
512
|
+
guild_media: ChannelType.GuildMedia,
|
|
518
513
|
};
|
|
519
514
|
builder.addChannelOption((optionBuilder) => {
|
|
520
515
|
optionBuilder
|
|
@@ -1003,7 +998,7 @@ export function normalizeDiscordPermission(permission) {
|
|
|
1003
998
|
return permission.replaceAll(/([a-z0-9])([A-Z])/g, "$1_$2").toUpperCase();
|
|
1004
999
|
}
|
|
1005
1000
|
export function toReplyOptions(response) {
|
|
1006
|
-
const allowedMentions =
|
|
1001
|
+
const allowedMentions = toAllowedMentions(response.allowedUserMentionIds, response.allowedRoleMentionIds);
|
|
1007
1002
|
const selectRows = toSelectRows(response);
|
|
1008
1003
|
if (response.componentsV2) {
|
|
1009
1004
|
if (response.content !== undefined) {
|
|
@@ -1053,27 +1048,37 @@ export function toReplyOptions(response) {
|
|
|
1053
1048
|
],
|
|
1054
1049
|
};
|
|
1055
1050
|
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1051
|
+
if (response.embed && response.embeds)
|
|
1052
|
+
throw new Error("Message responses cannot use both embed and embeds");
|
|
1053
|
+
const embeds = (response.embeds ?? (response.embed ? [response.embed] : [])).map((value) => {
|
|
1054
|
+
const embed = new EmbedBuilder().setColor(value.color ?? 0x7c3aed);
|
|
1055
|
+
if (value.title)
|
|
1056
|
+
embed.setTitle(value.title);
|
|
1057
|
+
if (value.description)
|
|
1058
|
+
embed.setDescription(value.description);
|
|
1059
|
+
if (value.footer)
|
|
1060
|
+
embed.setFooter({
|
|
1061
|
+
text: value.footer,
|
|
1062
|
+
...(value.footerIconUrl ? { iconURL: value.footerIconUrl } : {}),
|
|
1063
|
+
});
|
|
1064
|
+
if (value.url)
|
|
1065
|
+
embed.setURL(value.url);
|
|
1066
|
+
if (value.author)
|
|
1067
|
+
embed.setAuthor({
|
|
1068
|
+
name: value.author.name,
|
|
1069
|
+
...(value.author.url ? { url: value.author.url } : {}),
|
|
1070
|
+
...(value.author.iconUrl ? { iconURL: value.author.iconUrl } : {}),
|
|
1071
|
+
});
|
|
1072
|
+
if (value.timestamp)
|
|
1073
|
+
embed.setTimestamp(new Date(value.timestamp));
|
|
1074
|
+
if (value.imageUrl)
|
|
1075
|
+
embed.setImage(value.imageUrl);
|
|
1076
|
+
if (value.thumbnailUrl)
|
|
1077
|
+
embed.setThumbnail(value.thumbnailUrl);
|
|
1078
|
+
if (value.fields?.length)
|
|
1079
|
+
embed.addFields(...value.fields);
|
|
1080
|
+
return embed;
|
|
1081
|
+
});
|
|
1077
1082
|
const components = response.buttons?.length
|
|
1078
1083
|
? [
|
|
1079
1084
|
new ActionRowBuilder().addComponents(response.buttons.map((button) => {
|
|
@@ -1096,15 +1101,20 @@ export function toReplyOptions(response) {
|
|
|
1096
1101
|
: [];
|
|
1097
1102
|
components.push(...selectRows);
|
|
1098
1103
|
return {
|
|
1099
|
-
...(response.ephemeral ||
|
|
1104
|
+
...(response.ephemeral ||
|
|
1105
|
+
response.suppressEmbeds ||
|
|
1106
|
+
response.suppressNotifications
|
|
1100
1107
|
? {
|
|
1101
1108
|
flags: (response.ephemeral ? MessageFlags.Ephemeral : 0) |
|
|
1102
|
-
(response.suppressEmbeds ? MessageFlags.SuppressEmbeds : 0)
|
|
1109
|
+
(response.suppressEmbeds ? MessageFlags.SuppressEmbeds : 0) |
|
|
1110
|
+
(response.suppressNotifications
|
|
1111
|
+
? MessageFlags.SuppressNotifications
|
|
1112
|
+
: 0),
|
|
1103
1113
|
}
|
|
1104
1114
|
: {}),
|
|
1105
1115
|
...(allowedMentions ? { allowedMentions } : {}),
|
|
1106
1116
|
...(response.content === undefined ? {} : { content: response.content }),
|
|
1107
|
-
...(
|
|
1117
|
+
...(embeds.length > 0 ? { embeds } : {}),
|
|
1108
1118
|
components,
|
|
1109
1119
|
};
|
|
1110
1120
|
}
|
|
@@ -1154,15 +1164,25 @@ export function toUpdateOptions(response) {
|
|
|
1154
1164
|
ephemeral: false,
|
|
1155
1165
|
});
|
|
1156
1166
|
}
|
|
1157
|
-
function
|
|
1158
|
-
if (userIds === undefined)
|
|
1167
|
+
function toAllowedMentions(userIds, roleIds) {
|
|
1168
|
+
if (userIds === undefined && roleIds === undefined)
|
|
1159
1169
|
return undefined;
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1170
|
+
validateAllowedMentionIds(userIds, "user");
|
|
1171
|
+
validateAllowedMentionIds(roleIds, "role");
|
|
1172
|
+
return {
|
|
1173
|
+
parse: [],
|
|
1174
|
+
...(userIds === undefined ? {} : { users: [...userIds] }),
|
|
1175
|
+
...(roleIds === undefined ? {} : { roles: [...roleIds] }),
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
function validateAllowedMentionIds(ids, label) {
|
|
1179
|
+
if (ids === undefined)
|
|
1180
|
+
return;
|
|
1181
|
+
if (ids.length > 100 ||
|
|
1182
|
+
new Set(ids).size !== ids.length ||
|
|
1183
|
+
ids.some((id) => !/^\d{17,20}$/u.test(id))) {
|
|
1184
|
+
throw new Error(`Allowed ${label} mentions must be unique Discord snowflakes`);
|
|
1164
1185
|
}
|
|
1165
|
-
return { parse: [], users: [...userIds] };
|
|
1166
1186
|
}
|
|
1167
1187
|
function toButtonEmoji(emojiIdentifier) {
|
|
1168
1188
|
const custom = /^([A-Za-z0-9_]{2,32}):(\d{17,20})$/u.exec(emojiIdentifier);
|
|
@@ -1289,6 +1309,10 @@ function discordChannelType(type) {
|
|
|
1289
1309
|
return ChannelType.GuildAnnouncement;
|
|
1290
1310
|
if (type === "guild_category")
|
|
1291
1311
|
return ChannelType.GuildCategory;
|
|
1312
|
+
if (type === "guild_forum")
|
|
1313
|
+
return ChannelType.GuildForum;
|
|
1314
|
+
if (type === "guild_media")
|
|
1315
|
+
return ChannelType.GuildMedia;
|
|
1292
1316
|
return ChannelType.GuildText;
|
|
1293
1317
|
}
|
|
1294
1318
|
export function safeInteractionFailureMessage(error) {
|
|
@@ -2,7 +2,7 @@ import { type ModuleDefinition } from "@helyx/sdk";
|
|
|
2
2
|
export interface ManagementGuildResources {
|
|
3
3
|
channels: readonly {
|
|
4
4
|
id: string;
|
|
5
|
-
type: "guild_text" | "guild_announcement" | "guild_category";
|
|
5
|
+
type: "guild_text" | "guild_announcement" | "guild_category" | "guild_forum" | "guild_media";
|
|
6
6
|
}[];
|
|
7
7
|
roles: readonly {
|
|
8
8
|
id: string;
|
package/dist/modules.d.ts
CHANGED
|
@@ -20,8 +20,10 @@ export declare function deriveSecureRandomSelectionKey(encodedDataEncryptionKey:
|
|
|
20
20
|
export declare function platformServiceOptions(environment: PlatformServiceEnvironment, modules: readonly ModuleDefinition[]): {
|
|
21
21
|
applicationId?: string;
|
|
22
22
|
secureRandomSelectionKey?: Uint8Array;
|
|
23
|
+
inviteFingerprintKey?: Uint8Array;
|
|
23
24
|
dashboardBaseUrl?: string;
|
|
24
25
|
};
|
|
26
|
+
export declare function deriveInviteFingerprintKey(selectionKey: Uint8Array): Uint8Array;
|
|
25
27
|
export declare function loadConfiguredModules(specifiers: readonly string[], importer?: ModuleImporter): Promise<ModuleDefinition[]>;
|
|
26
28
|
export declare function loadConfiguredModulePackages(specifiers: readonly string[], importer?: ModuleImporter, resolvePackageJson?: PackageJsonResolver, readPackageJson?: PackageJsonReader): Promise<ConfiguredModulePackage[]>;
|
|
27
29
|
export declare function validateContributionParity(definition: ModuleDefinition): void;
|
package/dist/modules.js
CHANGED
|
@@ -4,10 +4,13 @@ import { createHmac } from "node:crypto";
|
|
|
4
4
|
import { readFile } from "node:fs/promises";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import { dirname } from "node:path";
|
|
7
|
+
import { assertCommandPathContract } from "./command-path-contract.js";
|
|
7
8
|
const officialModulePackage = /^@helyx\/module-[a-z0-9][a-z0-9-]*$/u;
|
|
8
9
|
const giveawaysModuleId = "helyx.giveaways";
|
|
10
|
+
const inviteTrackingModuleId = "helyx.invite-tracking";
|
|
9
11
|
export const secureRandomSelectionWorkerKeyName = "HELYX_INTERNAL_SECURE_RANDOM_SELECTION_KEY";
|
|
10
12
|
const secureRandomSelectionKeyContext = "helyx.secure-random-selection.worker.v1";
|
|
13
|
+
const inviteFingerprintKeyContext = "helyx.discord-invite-fingerprint.v1";
|
|
11
14
|
function decodeSecureRandomSelectionKey(encodedKey, variableName) {
|
|
12
15
|
const key = Buffer.from(encodedKey, "base64");
|
|
13
16
|
if (key.byteLength !== 32) {
|
|
@@ -30,25 +33,38 @@ export function platformServiceOptions(environment, modules) {
|
|
|
30
33
|
: {};
|
|
31
34
|
const internalSelectionKey = environment.HELYX_INTERNAL_SECURE_RANDOM_SELECTION_KEY;
|
|
32
35
|
if (internalSelectionKey) {
|
|
36
|
+
const selectionKey = decodeSecureRandomSelectionKey(internalSelectionKey, secureRandomSelectionWorkerKeyName);
|
|
33
37
|
return {
|
|
34
38
|
...application,
|
|
35
39
|
...urls,
|
|
36
|
-
secureRandomSelectionKey:
|
|
40
|
+
secureRandomSelectionKey: selectionKey,
|
|
41
|
+
inviteFingerprintKey: deriveInviteFingerprintKey(selectionKey),
|
|
37
42
|
};
|
|
38
43
|
}
|
|
39
44
|
const encodedKey = environment.HELYX_DATA_ENCRYPTION_KEY;
|
|
40
45
|
if (!encodedKey) {
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
const keyRequired = modules.some(({ manifest }) => manifest.id === giveawaysModuleId ||
|
|
47
|
+
manifest.id === inviteTrackingModuleId);
|
|
48
|
+
if (keyRequired) {
|
|
49
|
+
throw new Error("HELYX_DATA_ENCRYPTION_KEY is required when Giveaways or Invite Tracking is loaded");
|
|
43
50
|
}
|
|
44
51
|
return { ...application, ...urls };
|
|
45
52
|
}
|
|
53
|
+
const selectionKey = deriveSecureRandomSelectionKey(encodedKey);
|
|
46
54
|
return {
|
|
47
55
|
...application,
|
|
48
56
|
...urls,
|
|
49
|
-
secureRandomSelectionKey:
|
|
57
|
+
secureRandomSelectionKey: selectionKey,
|
|
58
|
+
inviteFingerprintKey: deriveInviteFingerprintKey(selectionKey),
|
|
50
59
|
};
|
|
51
60
|
}
|
|
61
|
+
export function deriveInviteFingerprintKey(selectionKey) {
|
|
62
|
+
if (selectionKey.byteLength !== 32)
|
|
63
|
+
throw new Error("Secure random selection key must contain exactly 32 bytes");
|
|
64
|
+
return createHmac("sha256", selectionKey)
|
|
65
|
+
.update(inviteFingerprintKeyContext, "utf8")
|
|
66
|
+
.digest();
|
|
67
|
+
}
|
|
52
68
|
function isModuleDefinition(value) {
|
|
53
69
|
if (typeof value !== "object" || value === null)
|
|
54
70
|
return false;
|
|
@@ -165,13 +181,20 @@ export function validateContributionParity(definition) {
|
|
|
165
181
|
...(definition.events?.guildEmojiUpdate ?? []),
|
|
166
182
|
].map(({ type }) => type);
|
|
167
183
|
const executableLogs = (definition.logging?.events ?? []).map(({ id }) => id);
|
|
184
|
+
const executableSchedulableActions = [
|
|
185
|
+
...new Set((definition.schedulableActions ?? []).map(({ id }) => id)),
|
|
186
|
+
];
|
|
168
187
|
const executableDashboardActions = (definition.dashboardActions ?? []).map(({ id }) => id);
|
|
169
188
|
const executableManagedResources = (definition.managedResources ?? []).map(({ id }) => id);
|
|
170
189
|
assertSameContributionIds(definition.manifest.contributions.commands, executableCommands, "commands");
|
|
171
190
|
assertSameContributionIds(definition.manifest.contributions.events, executableEvents, "events");
|
|
172
191
|
assertSameContributionIds(definition.manifest.contributions.logs ?? [], executableLogs, "logs");
|
|
192
|
+
assertSameContributionIds(definition.manifest.contributions.schedulableActions ?? [], executableSchedulableActions, "schedulable actions");
|
|
173
193
|
assertSameContributionIds(definition.manifest.dashboard?.actions?.map(({ id }) => id) ?? [], executableDashboardActions, "dashboard actions");
|
|
174
194
|
assertSameContributionIds(definition.manifest.dashboard?.resources?.map(({ id }) => id) ?? [], executableManagedResources, "dashboard resources");
|
|
195
|
+
if (definition.manifest.manifestVersion === "0.4") {
|
|
196
|
+
assertCommandPathContract(definition);
|
|
197
|
+
}
|
|
175
198
|
validatePrivacyParity(definition);
|
|
176
199
|
}
|
|
177
200
|
function validatePrivacyParity(definition) {
|
|
@@ -2,6 +2,7 @@ import { defineModule, HELYX_SERVICE_NAMES, } from "@helyx/sdk";
|
|
|
2
2
|
import { isCustomerSelectableModule } from "@helyx/module-manifest";
|
|
3
3
|
import { PermissionFlagsBits } from "discord.js";
|
|
4
4
|
import { canMutatePermissionPolicy, permissionMutationDenialMessage, } from "@helyx/core";
|
|
5
|
+
import { collectCommandPermissionTargets } from "./command-path-contract.js";
|
|
5
6
|
import { INTERNAL_MODULE_MANIFEST_FIELDS } from "./internal-module-manifest.js";
|
|
6
7
|
const MODULE_SELECT_ID = "helyx.permissions.module";
|
|
7
8
|
const TARGET_SELECT_ID = "helyx.permissions.target";
|
|
@@ -129,7 +130,8 @@ export function createPermissionsModule(configurableModules) {
|
|
|
129
130
|
if (!module || !target)
|
|
130
131
|
throw new Error("Permission target expired");
|
|
131
132
|
const commandId = target === "__module__" ? undefined : target;
|
|
132
|
-
if (commandId &&
|
|
133
|
+
if (commandId &&
|
|
134
|
+
!commandTargets(module).some(({ id }) => id === commandId)) {
|
|
133
135
|
throw new Error("Selected command is unavailable");
|
|
134
136
|
}
|
|
135
137
|
const current = await context.services
|
|
@@ -437,17 +439,12 @@ function restrictedPolicy() {
|
|
|
437
439
|
};
|
|
438
440
|
}
|
|
439
441
|
function commandTargets(module) {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
]
|
|
445
|
-
|
|
446
|
-
return nested;
|
|
447
|
-
return [command.name];
|
|
448
|
-
});
|
|
449
|
-
targets.push(...(module.sharedCommands ?? []).map((command) => command.permissionId ?? `${command.family}.${command.path}`), ...(module.nexusCommands ?? []).map((command) => `nexus.${command.path}`));
|
|
450
|
-
return [...new Set(targets)];
|
|
442
|
+
return [
|
|
443
|
+
...new Map(collectCommandPermissionTargets(module).map(({ id, name }) => [
|
|
444
|
+
id,
|
|
445
|
+
{ id, label: name },
|
|
446
|
+
])).values(),
|
|
447
|
+
];
|
|
451
448
|
}
|
|
452
449
|
function modulePickerResponse(modules, requestedPage) {
|
|
453
450
|
const pages = pageCount(modules.length, MODULE_PAGE_SIZE);
|
|
@@ -483,7 +480,7 @@ function targetPickerResponse(module, targets, requestedPage) {
|
|
|
483
480
|
ephemeral: true,
|
|
484
481
|
embed: {
|
|
485
482
|
title: `${module.manifest.name} permissions`,
|
|
486
|
-
description: "Choose the whole module or
|
|
483
|
+
description: "Choose the whole module, a command, or a declared command operation. Target policies override the module policy." +
|
|
487
484
|
(pages > 1 ? ` Page ${String(page + 1)} of ${String(pages)}.` : ""),
|
|
488
485
|
color: VIOLET,
|
|
489
486
|
},
|
|
@@ -495,11 +492,11 @@ function targetPickerResponse(module, targets, requestedPage) {
|
|
|
495
492
|
{
|
|
496
493
|
label: "Entire module",
|
|
497
494
|
value: "__module__",
|
|
498
|
-
description: "Default policy for every command",
|
|
495
|
+
description: "Default policy for every command and operation",
|
|
499
496
|
},
|
|
500
497
|
...visible.map((target) => ({
|
|
501
|
-
label:
|
|
502
|
-
value: target,
|
|
498
|
+
label: target.label,
|
|
499
|
+
value: target.id,
|
|
503
500
|
})),
|
|
504
501
|
],
|
|
505
502
|
},
|
package/dist/shard-contracts.js
CHANGED
|
@@ -4,7 +4,7 @@ export const SHARD_CONTRACT_VERSION = 1;
|
|
|
4
4
|
// Guild dashboard state includes the bounded module catalogue as well as the
|
|
5
5
|
// guild's Discord resources. Keep this aligned with the cloud-control message
|
|
6
6
|
// ceiling so hosted and self-hosted transports accept the same valid state.
|
|
7
|
-
export const SHARD_CONTROL_MAX_BYTES =
|
|
7
|
+
export const SHARD_CONTROL_MAX_BYTES = 768 * 1_024;
|
|
8
8
|
export const SHARD_CONTROL_TIMEOUT_MS = 30_000;
|
|
9
9
|
export const SHARD_HEALTH_STALE_MS = 30_000;
|
|
10
10
|
export const SHARD_LEASE_HEARTBEAT_TIMEOUT_MS = 20_000;
|
package/dist/shard-worker.js
CHANGED
|
@@ -2,7 +2,7 @@ import { parseBotEnvironment } from "@helyx/config";
|
|
|
2
2
|
import { safeError, ServiceRegistry } from "@helyx/core";
|
|
3
3
|
import { checkDatabaseConnection, createDatabasePool } from "@helyx/database";
|
|
4
4
|
import { createRuntime } from "@helyx/framework";
|
|
5
|
-
import { createPlatformServices } from "@helyx/platform";
|
|
5
|
+
import { createPlatformServices, PlatformMemberRoleGrantOwnershipService, PostgresMemberRoleGrantOwnershipStore, } from "@helyx/platform";
|
|
6
6
|
import { HELYX_SERVICE_NAMES, } from "@helyx/sdk";
|
|
7
7
|
import { Client, Events, Partials } from "discord.js";
|
|
8
8
|
import { monitorEventLoopDelay } from "node:perf_hooks";
|
|
@@ -21,6 +21,8 @@ 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
23
|
import { DiscordGuildHealthInspection } from "./discord-guild-health.js";
|
|
24
|
+
import { DiscordAnalyticsInspection } from "./discord-analytics.js";
|
|
25
|
+
import { DiscordInviteInspection } from "./discord-invites.js";
|
|
24
26
|
import { CoreResourceAuthorizationService } from "./resource-authorization.js";
|
|
25
27
|
import { PostgresTriviaDataProvider } from "./trivia-data-provider.js";
|
|
26
28
|
import { createDeveloperOperationsModule, WorkerShardHealthProvider, } from "./developer-operations-module.js";
|
|
@@ -181,8 +183,9 @@ export async function startShardWorker(processEnvironment = process.env, identit
|
|
|
181
183
|
];
|
|
182
184
|
const platformServices = new Map();
|
|
183
185
|
const serviceAccess = new ServiceRegistry(platformServices);
|
|
186
|
+
const serviceOptions = platformServiceOptions(environment, publicModules);
|
|
184
187
|
for (const [name, service] of createPlatformServices(database, publicModules, {
|
|
185
|
-
...
|
|
188
|
+
...serviceOptions,
|
|
186
189
|
activationServices: serviceAccess,
|
|
187
190
|
})) {
|
|
188
191
|
platformServices.set(name, service);
|
|
@@ -213,9 +216,14 @@ export async function startShardWorker(processEnvironment = process.env, identit
|
|
|
213
216
|
platformServices.set(HELYX_SERVICE_NAMES.discordMessages, new DiscordMessageDeliveryService(client));
|
|
214
217
|
platformServices.set(HELYX_SERVICE_NAMES.discordOwnedMessages, new DiscordOwnedMessageDeletionService(client));
|
|
215
218
|
platformServices.set(HELYX_SERVICE_NAMES.memberEnforcement, new DiscordMemberEnforcementService(client));
|
|
216
|
-
|
|
219
|
+
const memberRoles = new DiscordMemberRoleService(client);
|
|
220
|
+
platformServices.set(HELYX_SERVICE_NAMES.memberRoles, memberRoles);
|
|
221
|
+
platformServices.set(HELYX_SERVICE_NAMES.memberRoleGrants, new PlatformMemberRoleGrantOwnershipService(new PostgresMemberRoleGrantOwnershipStore(database), memberRoles));
|
|
217
222
|
platformServices.set(HELYX_SERVICE_NAMES.discordResources, new DiscordResourceInspection(client));
|
|
218
223
|
platformServices.set(HELYX_SERVICE_NAMES.discordGuildHealth, new DiscordGuildHealthInspection(client, publicModules, serviceAccess));
|
|
224
|
+
platformServices.set(HELYX_SERVICE_NAMES.discordAnalytics, new DiscordAnalyticsInspection(client));
|
|
225
|
+
if (serviceOptions.inviteFingerprintKey)
|
|
226
|
+
platformServices.set(HELYX_SERVICE_NAMES.discordInvites, new DiscordInviteInspection(client, serviceOptions.inviteFingerprintKey));
|
|
219
227
|
if (featureModules.some(({ manifest }) => manifest.id === "helyx.logging"))
|
|
220
228
|
platformServices.set(HELYX_SERVICE_NAMES.moduleLogging, new DiscordModuleLoggingService(client, database, publicModules, logger));
|
|
221
229
|
const scheduledTaskRoute = createLocalScheduledTaskRoute({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@helyx/bot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Production Discord runtime for the modular Helyx platform.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"helyx",
|
|
@@ -53,11 +53,11 @@
|
|
|
53
53
|
"@helyx/config": ">=0.5.0 <0.6.0",
|
|
54
54
|
"@helyx/control-protocol": ">=0.4.0 <0.5.0",
|
|
55
55
|
"@helyx/core": ">=0.1.0 <0.2.0",
|
|
56
|
-
"@helyx/database": ">=0.
|
|
56
|
+
"@helyx/database": ">=0.10.0",
|
|
57
57
|
"@helyx/framework": ">=0.1.0 <0.2.0",
|
|
58
58
|
"@helyx/module-manifest": ">=0.8.0 <0.9.0",
|
|
59
|
-
"@helyx/platform": ">=0.
|
|
60
|
-
"@helyx/sdk": ">=0.
|
|
59
|
+
"@helyx/platform": ">=0.7.0",
|
|
60
|
+
"@helyx/sdk": ">=0.10.0",
|
|
61
61
|
"@helyx/status-client": ">=0.1.0 <0.2.0",
|
|
62
62
|
"@helyx/verification-contracts": ">=0.3.0 <0.4.0",
|
|
63
63
|
"discord.js": "14.27.0",
|