@helyx/bot 0.4.31 → 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/CHANGELOG.md +12 -0
- package/dist/control-permissions.d.ts +38 -0
- package/dist/control-permissions.js +139 -0
- package/dist/control-server.d.ts +1 -15
- package/dist/control-server.js +112 -166
- package/dist/discord-guild-health.d.ts +174 -0
- package/dist/discord-guild-health.js +370 -0
- package/dist/events.d.ts +15 -0
- package/dist/events.js +338 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +26 -7
- package/dist/interactions.js +36 -6
- package/dist/modules.d.ts +2 -0
- package/dist/modules.js +10 -1
- package/dist/oauth-module.d.ts +2 -0
- package/dist/oauth-module.js +192 -0
- package/dist/oauth-module.test.d.ts +2 -0
- package/dist/oauth-module.test.js +172 -0
- package/dist/oauth-service.d.ts +16 -0
- package/dist/oauth-service.js +40 -0
- package/dist/shard-supervisor.js +6 -0
- package/dist/shard-worker.d.ts +5 -0
- package/dist/shard-worker.js +16 -1
- package/package.json +1 -1
package/dist/control-server.js
CHANGED
|
@@ -9,6 +9,7 @@ import { z, ZodError } from "zod";
|
|
|
9
9
|
import { addChannelMessageReaction as addDiscordChannelMessageReaction, deleteChannelMessage as deleteDiscordChannelMessage, editChannelMessage as editDiscordChannelMessage, normalizeDiscordPermission, sendToChannel as sendDiscordMessageToChannel, } from "./interactions.js";
|
|
10
10
|
import { SHARD_HEALTH_STALE_MS, } from "./shard-contracts.js";
|
|
11
11
|
import { dashboardCategoryLabel } from "./dashboard-category.js";
|
|
12
|
+
import { assertVersion, dashboardActionPermissionId, declaredManagedResourcePermission, emptyPolicy, fromDashboardPolicy, managedResourcePermissionId, permissionPoliciesEqual, permissionPolicyKey, safeOptionalVersion, safeVersion, toDashboardPolicy, } from "./control-permissions.js";
|
|
12
13
|
const snowflake = z.string().regex(/^\d{17,20}$/u);
|
|
13
14
|
const moduleId = z.string().regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/u);
|
|
14
15
|
const MAX_DISCORD_SETTING_SELECTIONS = 25;
|
|
@@ -84,6 +85,7 @@ const managedResourcePageSchema = z
|
|
|
84
85
|
.object({
|
|
85
86
|
items: z.array(managedResourceSummarySchema).max(50),
|
|
86
87
|
nextCursor: resourceCursor.optional(),
|
|
88
|
+
total: z.number().int().min(0).optional(),
|
|
87
89
|
})
|
|
88
90
|
.strict()
|
|
89
91
|
.refine(hasBoundedManagedResourceBytes);
|
|
@@ -129,11 +131,11 @@ export class DiscordGuildResourceDirectory {
|
|
|
129
131
|
}
|
|
130
132
|
async getGuildResources(guildId) {
|
|
131
133
|
try {
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
const cachedGuild = this.client.guilds.cache.get(guildId);
|
|
135
|
+
const guild = cachedGuild ?? (await this.client.guilds.fetch(guildId));
|
|
136
|
+
const [channels, roles] = cachedGuild?.available === true
|
|
137
|
+
? [cachedGuild.channels.cache, cachedGuild.roles.cache]
|
|
138
|
+
: await Promise.all([guild.channels.fetch(), guild.roles.fetch()]);
|
|
137
139
|
return {
|
|
138
140
|
channels: [...channels.values()]
|
|
139
141
|
.filter((channel) => channel?.type === ChannelType.GuildText ||
|
|
@@ -389,11 +391,25 @@ export class BotControlService {
|
|
|
389
391
|
});
|
|
390
392
|
}
|
|
391
393
|
async getGuildState(guildId) {
|
|
392
|
-
const
|
|
394
|
+
const dashboardEntries = this.#dashboardModules.map((dashboardModule) => ({
|
|
395
|
+
dashboardModule,
|
|
396
|
+
module: this.#requireModule(dashboardModule.id),
|
|
397
|
+
}));
|
|
398
|
+
const moduleIds = dashboardEntries.map(({ dashboardModule }) => dashboardModule.id);
|
|
399
|
+
const permissionTargets = dashboardEntries.flatMap(({ dashboardModule, module }) => [
|
|
400
|
+
{ moduleId: dashboardModule.id },
|
|
401
|
+
...dashboardCommands(module).map(({ id }) => ({
|
|
402
|
+
moduleId: dashboardModule.id,
|
|
403
|
+
commandId: id,
|
|
404
|
+
})),
|
|
405
|
+
]);
|
|
406
|
+
const [resources, installedModules, storedLoggingPolicies, runtime, storedConfigurations, storedPermissions,] = await Promise.all([
|
|
393
407
|
this.resources.getGuildResources(guildId),
|
|
394
408
|
new InstallationRepository(this.database).listModules(guildId),
|
|
395
409
|
new ModuleLoggingPolicyRepository(this.database).listForGuild(guildId),
|
|
396
410
|
this.#inspectRuntimeCapabilities(),
|
|
411
|
+
this.#readConfigurations(guildId, moduleIds),
|
|
412
|
+
this.#readPermissionPolicies(guildId, permissionTargets),
|
|
397
413
|
]);
|
|
398
414
|
const installed = new Map(installedModules.map((module) => [module.moduleId, module]));
|
|
399
415
|
const moduleStatuses = {};
|
|
@@ -404,24 +420,20 @@ export class BotControlService {
|
|
|
404
420
|
`${policy.sourceModuleId}:${policy.eventId}`,
|
|
405
421
|
policy,
|
|
406
422
|
]));
|
|
407
|
-
|
|
408
|
-
const module = this.#requireModule(dashboardModule.id);
|
|
423
|
+
for (const { dashboardModule, module } of dashboardEntries) {
|
|
409
424
|
const installation = installed.get(dashboardModule.id);
|
|
410
425
|
moduleStatuses[dashboardModule.id] = installation
|
|
411
426
|
? installation.enabled
|
|
412
427
|
? "enabled"
|
|
413
428
|
: "disabled"
|
|
414
429
|
: "unavailable";
|
|
415
|
-
const
|
|
416
|
-
this.#configurations.get(guildId, dashboardModule.id),
|
|
417
|
-
this.#getPermissionState(guildId, module),
|
|
418
|
-
]);
|
|
430
|
+
const configuration = storedConfigurations.get(dashboardModule.id);
|
|
419
431
|
if (configuration)
|
|
420
432
|
configurations[dashboardModule.id] = {
|
|
421
433
|
version: safeVersion(configuration.version),
|
|
422
434
|
value: configuration.value,
|
|
423
435
|
};
|
|
424
|
-
permissions[dashboardModule.id] =
|
|
436
|
+
permissions[dashboardModule.id] = this.#buildPermissionState(module, storedPermissions);
|
|
425
437
|
logging[dashboardModule.id] = dashboardModule.logEvents.map((event) => {
|
|
426
438
|
const stored = loggingPolicies.get(`${dashboardModule.id}:${event.id}`);
|
|
427
439
|
return {
|
|
@@ -430,7 +442,7 @@ export class BotControlService {
|
|
|
430
442
|
version: stored ? safeVersion(stored.version) : 0,
|
|
431
443
|
};
|
|
432
444
|
});
|
|
433
|
-
}
|
|
445
|
+
}
|
|
434
446
|
return {
|
|
435
447
|
discordCapabilities: {
|
|
436
448
|
messageContent: runtime.messageContent,
|
|
@@ -444,12 +456,17 @@ export class BotControlService {
|
|
|
444
456
|
};
|
|
445
457
|
}
|
|
446
458
|
async canAccessDashboard(input) {
|
|
447
|
-
const actor = await
|
|
448
|
-
|
|
459
|
+
const [actor, policy] = await Promise.all([
|
|
460
|
+
this.resources.resolveActor(input.guildId, input.actorUserId),
|
|
461
|
+
this.#readGuildAccessPolicy(input.guildId),
|
|
462
|
+
]);
|
|
463
|
+
return canAccessGuildDashboard(actor, policy);
|
|
449
464
|
}
|
|
450
465
|
async getGuildAccessPolicy(input) {
|
|
451
|
-
const actor = await
|
|
452
|
-
|
|
466
|
+
const [actor, record] = await Promise.all([
|
|
467
|
+
this.resources.resolveActor(input.guildId, input.actorUserId),
|
|
468
|
+
new GuildAccessPolicyRepository(this.database).get(input.guildId),
|
|
469
|
+
]);
|
|
453
470
|
const policy = record ?? DEFAULT_GUILD_ACCESS_POLICY;
|
|
454
471
|
return {
|
|
455
472
|
administratorRoleIds: [...policy.administratorRoleIds],
|
|
@@ -560,9 +577,14 @@ export class BotControlService {
|
|
|
560
577
|
}
|
|
561
578
|
async canAccessManagedResource(input) {
|
|
562
579
|
try {
|
|
563
|
-
const { module } =
|
|
580
|
+
const { module, resource } = this.#prepareManagedResource(input);
|
|
564
581
|
this.#requireManagedResourceOperation(module, input.resourceType, input.operation);
|
|
565
|
-
await
|
|
582
|
+
await Promise.all([
|
|
583
|
+
this.#authorizeAction(input.guildId, module, input.actorUserId, managedResourcePermissionId(module, input.resourceType, input.operation, undefined, declaredManagedResourcePermission(module, input.resourceType, input.operation))),
|
|
584
|
+
resource.availableWhileDisabled
|
|
585
|
+
? Promise.resolve()
|
|
586
|
+
: this.#requireEnabledManagedResource(input.guildId, input.moduleId),
|
|
587
|
+
]);
|
|
566
588
|
return true;
|
|
567
589
|
}
|
|
568
590
|
catch (error) {
|
|
@@ -840,10 +862,13 @@ export class BotControlService {
|
|
|
840
862
|
if (!declared || !action) {
|
|
841
863
|
throw new BotControlNotFoundError("Dashboard action is not declared by this module");
|
|
842
864
|
}
|
|
843
|
-
|
|
865
|
+
const [enabled] = await Promise.all([
|
|
866
|
+
new InstallationRepository(this.database).isModuleEnabled(input.guildId, input.moduleId),
|
|
867
|
+
this.#authorizeAction(input.guildId, module, input.actorUserId, dashboardActionPermissionId(module, input.actionId)),
|
|
868
|
+
]);
|
|
869
|
+
if (!enabled) {
|
|
844
870
|
throw new BotControlValidationError("Enable this module before running its dashboard actions");
|
|
845
871
|
}
|
|
846
|
-
await this.#authorizeAction(input.guildId, module, input.actorUserId, dashboardActionPermissionId(module, input.actionId));
|
|
847
872
|
return action.execute({
|
|
848
873
|
guildId: input.guildId,
|
|
849
874
|
actorUserId: input.actorUserId,
|
|
@@ -854,7 +879,7 @@ export class BotControlService {
|
|
|
854
879
|
});
|
|
855
880
|
}
|
|
856
881
|
async listManagedResources(input) {
|
|
857
|
-
const { module, resource } =
|
|
882
|
+
const { module, resource } = this.#prepareManagedResource(input);
|
|
858
883
|
this.#requireManagedResourceOperation(module, input.resourceType, "list");
|
|
859
884
|
this.#validateManagedResourceQuery(module, input.resourceType, input);
|
|
860
885
|
const result = await resource.list(await this.#managedResourceContext(input, module, "list"), {
|
|
@@ -866,7 +891,7 @@ export class BotControlService {
|
|
|
866
891
|
return normalizeManagedResourcePage(managedResourcePageSchema.parse(result), managedResourceColumnKeys(module, input.resourceType));
|
|
867
892
|
}
|
|
868
893
|
async readManagedResource(input) {
|
|
869
|
-
const { module, resource } =
|
|
894
|
+
const { module, resource } = this.#prepareManagedResource(input);
|
|
870
895
|
this.#requireManagedResourceOperation(module, input.resourceType, "read");
|
|
871
896
|
const result = await resource.read(await this.#managedResourceContext(input, module, "read"), input.resourceId);
|
|
872
897
|
if (!result)
|
|
@@ -874,7 +899,7 @@ export class BotControlService {
|
|
|
874
899
|
return normalizeManagedResourceDetail(managedResourceDetailSchema.parse(result), managedResourceColumnKeys(module, input.resourceType));
|
|
875
900
|
}
|
|
876
901
|
async listManagedResourceRelation(input) {
|
|
877
|
-
const { module, resource } =
|
|
902
|
+
const { module, resource } = this.#prepareManagedResource(input);
|
|
878
903
|
this.#requireManagedResourceOperation(module, input.resourceType, "read");
|
|
879
904
|
const relation = module.manifest.dashboard?.resources
|
|
880
905
|
?.find(({ id }) => id === input.resourceType)
|
|
@@ -890,12 +915,12 @@ export class BotControlService {
|
|
|
890
915
|
})), relation.columns.map(({ key }) => key));
|
|
891
916
|
}
|
|
892
917
|
async previewManagedResource(input) {
|
|
893
|
-
const { module, resource } =
|
|
918
|
+
const { module, resource } = this.#prepareManagedResource(input);
|
|
894
919
|
this.#requireManagedResourceOperation(module, input.resourceType, "preview");
|
|
895
920
|
return resource.preview(await this.#managedResourceContext(input, module, "preview"), input.value);
|
|
896
921
|
}
|
|
897
922
|
async createManagedResource(input) {
|
|
898
|
-
const { module, resource } =
|
|
923
|
+
const { module, resource } = this.#prepareManagedResource(input);
|
|
899
924
|
this.#requireManagedResourceOperation(module, input.resourceType, "create");
|
|
900
925
|
const context = await this.#managedResourceContext(input, module, "create");
|
|
901
926
|
const declaredIntents = module.manifest.dashboard?.resources?.find(({ id }) => id === input.resourceType)?.creationIntents ?? ["publish"];
|
|
@@ -904,7 +929,7 @@ export class BotControlService {
|
|
|
904
929
|
return normalizeManagedResourceDetail(managedResourceDetailSchema.parse(await resource.create(context, input.value, input.intent)), managedResourceColumnKeys(module, input.resourceType));
|
|
905
930
|
}
|
|
906
931
|
async updateManagedResource(input) {
|
|
907
|
-
const { module, resource } =
|
|
932
|
+
const { module, resource } = this.#prepareManagedResource(input);
|
|
908
933
|
this.#requireManagedResourceOperation(module, input.resourceType, "update");
|
|
909
934
|
return normalizeManagedResourceDetail(managedResourceDetailSchema.parse(await resource.update(await this.#managedResourceContext(input, module, "update"), {
|
|
910
935
|
resourceId: input.resourceId,
|
|
@@ -913,7 +938,7 @@ export class BotControlService {
|
|
|
913
938
|
})), managedResourceColumnKeys(module, input.resourceType));
|
|
914
939
|
}
|
|
915
940
|
async deleteManagedResource(input) {
|
|
916
|
-
const { module, resource } =
|
|
941
|
+
const { module, resource } = this.#prepareManagedResource(input);
|
|
917
942
|
this.#requireManagedResourceOperation(module, input.resourceType, "delete");
|
|
918
943
|
return managedResourceDeleteResultSchema.parse(await resource.delete(await this.#managedResourceContext(input, module, "delete"), {
|
|
919
944
|
resourceId: input.resourceId,
|
|
@@ -921,7 +946,7 @@ export class BotControlService {
|
|
|
921
946
|
}));
|
|
922
947
|
}
|
|
923
948
|
async executeManagedResourceAction(input) {
|
|
924
|
-
const { module, resource } =
|
|
949
|
+
const { module, resource } = this.#prepareManagedResource(input);
|
|
925
950
|
const declared = module.manifest.dashboard?.resources
|
|
926
951
|
?.find(({ id }) => id === input.resourceType)
|
|
927
952
|
?.actions?.find(({ id }) => id === input.actionId);
|
|
@@ -941,11 +966,9 @@ export class BotControlService {
|
|
|
941
966
|
? null
|
|
942
967
|
: normalizeManagedResourceDetail(managedResourceDetailSchema.parse(result), managedResourceColumnKeys(module, input.resourceType));
|
|
943
968
|
}
|
|
944
|
-
|
|
969
|
+
#prepareManagedResource(input) {
|
|
945
970
|
const module = this.#requireDashboardModule(input.moduleId);
|
|
946
971
|
const resource = this.#requireManagedResource(module, input.resourceType);
|
|
947
|
-
if (!resource.availableWhileDisabled)
|
|
948
|
-
await this.#requireEnabledManagedResource(input.guildId, input.moduleId);
|
|
949
972
|
return { module, resource };
|
|
950
973
|
}
|
|
951
974
|
#requireManagedResource(module, resourceType) {
|
|
@@ -979,8 +1002,14 @@ export class BotControlService {
|
|
|
979
1002
|
if (!(await new InstallationRepository(this.database).isModuleEnabled(guildId, moduleIdValue)))
|
|
980
1003
|
throw new BotControlValidationError("Enable this module before managing its resources");
|
|
981
1004
|
}
|
|
982
|
-
async #managedResourceContext(input, module, operation, permissionId = managedResourcePermissionId(module, input.resourceType, operation)) {
|
|
983
|
-
const
|
|
1005
|
+
async #managedResourceContext(input, module, operation, permissionId = managedResourcePermissionId(module, input.resourceType, operation, undefined, declaredManagedResourcePermission(module, input.resourceType, operation))) {
|
|
1006
|
+
const resource = this.#requireManagedResource(module, input.resourceType);
|
|
1007
|
+
const [actor] = await Promise.all([
|
|
1008
|
+
this.#authorizeAction(input.guildId, module, input.actorUserId, permissionId),
|
|
1009
|
+
resource.availableWhileDisabled
|
|
1010
|
+
? Promise.resolve()
|
|
1011
|
+
: this.#requireEnabledManagedResource(input.guildId, module.manifest.id),
|
|
1012
|
+
]);
|
|
984
1013
|
return {
|
|
985
1014
|
guildId: input.guildId,
|
|
986
1015
|
actor,
|
|
@@ -997,9 +1026,11 @@ export class BotControlService {
|
|
|
997
1026
|
};
|
|
998
1027
|
}
|
|
999
1028
|
async #authorizeAction(guildId, module, actorUserId, permissionId) {
|
|
1000
|
-
const actor = await
|
|
1001
|
-
|
|
1002
|
-
|
|
1029
|
+
const [actor, policy, guildAccess] = await Promise.all([
|
|
1030
|
+
this.resources.resolveActor(guildId, actorUserId),
|
|
1031
|
+
this.#permissions.getEffectivePolicy(guildId, module.manifest.id, permissionId),
|
|
1032
|
+
this.#permissions.getGuildAccessPolicy?.(guildId),
|
|
1033
|
+
]);
|
|
1003
1034
|
if (!canUseCommand(actor, policy, guildAccess))
|
|
1004
1035
|
throw new BotControlForbiddenError();
|
|
1005
1036
|
return actor;
|
|
@@ -1026,12 +1057,50 @@ export class BotControlService {
|
|
|
1026
1057
|
}
|
|
1027
1058
|
async #getPermissionState(guildId, module) {
|
|
1028
1059
|
const commands = dashboardCommands(module);
|
|
1029
|
-
const
|
|
1060
|
+
const policies = await this.#readPermissionPolicies(guildId, [
|
|
1061
|
+
{ moduleId: module.manifest.id },
|
|
1062
|
+
...commands.map(({ id }) => ({
|
|
1063
|
+
moduleId: module.manifest.id,
|
|
1064
|
+
commandId: id,
|
|
1065
|
+
})),
|
|
1066
|
+
]);
|
|
1067
|
+
return this.#buildPermissionState(module, policies);
|
|
1068
|
+
}
|
|
1069
|
+
async #readConfigurations(guildId, moduleIds) {
|
|
1070
|
+
const values = this.#configurations.getMany
|
|
1071
|
+
? await this.#configurations.getMany(guildId, moduleIds)
|
|
1072
|
+
: await Promise.all(moduleIds.map((moduleId) => this.#configurations.get(guildId, moduleId)));
|
|
1073
|
+
return new Map(values
|
|
1074
|
+
.filter((value) => value !== null)
|
|
1075
|
+
.map((value) => [value.moduleId, value]));
|
|
1076
|
+
}
|
|
1077
|
+
async #readPermissionPolicies(guildId, targets) {
|
|
1078
|
+
const values = this.#permissions.getPolicies
|
|
1079
|
+
? await this.#permissions.getPolicies(guildId, targets)
|
|
1080
|
+
: await Promise.all(targets.map(async (target) => ({
|
|
1081
|
+
...target,
|
|
1082
|
+
...(await this.#permissions.getPolicy(guildId, target.moduleId, target.commandId)),
|
|
1083
|
+
})));
|
|
1084
|
+
const policies = new Map(values.map((value) => [permissionPolicyKey(value), value]));
|
|
1085
|
+
for (const target of targets)
|
|
1086
|
+
if (!policies.has(permissionPolicyKey(target)))
|
|
1087
|
+
throw new Error("Permission policy bulk response was incomplete");
|
|
1088
|
+
return policies;
|
|
1089
|
+
}
|
|
1090
|
+
#buildPermissionState(module, policies) {
|
|
1091
|
+
const modulePolicy = policies.get(permissionPolicyKey({ moduleId: module.manifest.id }));
|
|
1092
|
+
if (!modulePolicy)
|
|
1093
|
+
throw new Error("Module permission policy was not resolved");
|
|
1030
1094
|
return {
|
|
1031
1095
|
version: safeOptionalVersion(modulePolicy.version),
|
|
1032
1096
|
module: toDashboardPolicy(modulePolicy.policy),
|
|
1033
|
-
commands:
|
|
1034
|
-
const value =
|
|
1097
|
+
commands: dashboardCommands(module).map((command) => {
|
|
1098
|
+
const value = policies.get(permissionPolicyKey({
|
|
1099
|
+
moduleId: module.manifest.id,
|
|
1100
|
+
commandId: command.id,
|
|
1101
|
+
}));
|
|
1102
|
+
if (!value)
|
|
1103
|
+
throw new Error("Command permission policy was not resolved");
|
|
1035
1104
|
return {
|
|
1036
1105
|
commandId: command.id,
|
|
1037
1106
|
version: safeOptionalVersion(value.version),
|
|
@@ -1041,7 +1110,7 @@ export class BotControlService {
|
|
|
1041
1110
|
module.defaultPermissions?.commands[command.id] !== undefined),
|
|
1042
1111
|
policy: toDashboardPolicy(value.policy),
|
|
1043
1112
|
};
|
|
1044
|
-
})
|
|
1113
|
+
}),
|
|
1045
1114
|
};
|
|
1046
1115
|
}
|
|
1047
1116
|
async #validateDiscordSettings(guildId, module, value) {
|
|
@@ -1650,58 +1719,6 @@ function isUnknownMemberError(error) {
|
|
|
1650
1719
|
"code" in error &&
|
|
1651
1720
|
error.code === 10_007);
|
|
1652
1721
|
}
|
|
1653
|
-
function dashboardActionPermissionId(module, actionId) {
|
|
1654
|
-
const namespace = modulePermissionNamespace(module);
|
|
1655
|
-
return selectDeclaredPermission(module, [
|
|
1656
|
-
`${namespace}.${actionId.replaceAll("-", ".")}`,
|
|
1657
|
-
`${namespace}.configure`,
|
|
1658
|
-
]);
|
|
1659
|
-
}
|
|
1660
|
-
function managedResourcePermissionId(module, resourceType, operation, actionId, declaredPermissionId) {
|
|
1661
|
-
if (declaredPermissionId)
|
|
1662
|
-
return declaredPermissionId;
|
|
1663
|
-
const namespace = modulePermissionNamespace(module);
|
|
1664
|
-
const resourcePermission = `resource.${resourceType}`;
|
|
1665
|
-
const candidates = operation === "create" || operation === "preview"
|
|
1666
|
-
? [`${resourcePermission}.create`, `${namespace}.create`]
|
|
1667
|
-
: operation === "update"
|
|
1668
|
-
? [
|
|
1669
|
-
`${resourcePermission}.update`,
|
|
1670
|
-
`${namespace}.edit`,
|
|
1671
|
-
`${namespace}.manage`,
|
|
1672
|
-
]
|
|
1673
|
-
: operation === "delete"
|
|
1674
|
-
? [
|
|
1675
|
-
`${resourcePermission}.delete`,
|
|
1676
|
-
`${namespace}.delete`,
|
|
1677
|
-
`${namespace}.cancel`,
|
|
1678
|
-
]
|
|
1679
|
-
: operation === "action" && actionId
|
|
1680
|
-
? [
|
|
1681
|
-
`${resourcePermission}.${actionId}`,
|
|
1682
|
-
`${resourcePermission}.action.${actionId}`,
|
|
1683
|
-
`${resourcePermission}.action`,
|
|
1684
|
-
`${namespace}.${actionId.replaceAll("-", ".")}`,
|
|
1685
|
-
`${namespace}.manage`,
|
|
1686
|
-
]
|
|
1687
|
-
: [
|
|
1688
|
-
`${resourcePermission}.${operation}`,
|
|
1689
|
-
`${namespace}.read`,
|
|
1690
|
-
`${namespace}.list`,
|
|
1691
|
-
`${namespace}.manage`,
|
|
1692
|
-
`${namespace}.entries.read`,
|
|
1693
|
-
];
|
|
1694
|
-
return selectDeclaredPermission(module, candidates);
|
|
1695
|
-
}
|
|
1696
|
-
function modulePermissionNamespace(module) {
|
|
1697
|
-
return module.manifest.id.startsWith("helyx.")
|
|
1698
|
-
? module.manifest.id.slice("helyx.".length)
|
|
1699
|
-
: module.manifest.id;
|
|
1700
|
-
}
|
|
1701
|
-
function selectDeclaredPermission(module, candidates) {
|
|
1702
|
-
const declared = module.defaultPermissions?.commands ?? {};
|
|
1703
|
-
return (candidates.find((candidate) => candidate in declared) ?? candidates[0]);
|
|
1704
|
-
}
|
|
1705
1722
|
function isBoundedManagedResourceValue(value, depth = 0, budget = { keys: 0 }) {
|
|
1706
1723
|
if (depth > 8)
|
|
1707
1724
|
return false;
|
|
@@ -1766,6 +1783,7 @@ function normalizeManagedResourcePage(value, attributeKeys) {
|
|
|
1766
1783
|
updatedAt: item.updatedAt,
|
|
1767
1784
|
})),
|
|
1768
1785
|
...(value.nextCursor === undefined ? {} : { nextCursor: value.nextCursor }),
|
|
1786
|
+
...(value.total === undefined ? {} : { total: value.total }),
|
|
1769
1787
|
};
|
|
1770
1788
|
}
|
|
1771
1789
|
function normalizeManagedResourceRelationPage(value, valueKeys) {
|
|
@@ -1871,81 +1889,9 @@ function dashboardCommands(module) {
|
|
|
1871
1889
|
...new Map(commands.map((command) => [command.id, command])).values(),
|
|
1872
1890
|
];
|
|
1873
1891
|
}
|
|
1874
|
-
function toDashboardPolicy(policy) {
|
|
1875
|
-
const preset = policy.everyone
|
|
1876
|
-
? "everyone"
|
|
1877
|
-
: policy.accessLevel === "administrator"
|
|
1878
|
-
? "administrators"
|
|
1879
|
-
: policy.accessLevel === "moderator"
|
|
1880
|
-
? "moderators"
|
|
1881
|
-
: policy.allowedRoleIds.length > 0 &&
|
|
1882
|
-
policy.requiredPermissions.length === 0
|
|
1883
|
-
? "custom"
|
|
1884
|
-
: policy.requiredPermissions.includes("ADMINISTRATOR")
|
|
1885
|
-
? "administrators"
|
|
1886
|
-
: policy.requiredPermissions.includes("MANAGE_GUILD")
|
|
1887
|
-
? "moderators"
|
|
1888
|
-
: "custom";
|
|
1889
|
-
return {
|
|
1890
|
-
preset,
|
|
1891
|
-
allowedRoleIds: [...policy.allowedRoleIds],
|
|
1892
|
-
deniedRoleIds: [...policy.deniedRoleIds],
|
|
1893
|
-
};
|
|
1894
|
-
}
|
|
1895
|
-
function fromDashboardPolicy(input, current) {
|
|
1896
|
-
return {
|
|
1897
|
-
everyone: input.preset === "everyone",
|
|
1898
|
-
...(input.preset === "administrators"
|
|
1899
|
-
? { accessLevel: "administrator" }
|
|
1900
|
-
: input.preset === "moderators"
|
|
1901
|
-
? { accessLevel: "moderator" }
|
|
1902
|
-
: {}),
|
|
1903
|
-
requiredPermissions: [],
|
|
1904
|
-
allowedRoleIds: input.preset === "custom" ? input.allowedRoleIds : [],
|
|
1905
|
-
deniedRoleIds: input.deniedRoleIds,
|
|
1906
|
-
allowedUserIds: [...current.allowedUserIds],
|
|
1907
|
-
deniedUserIds: [...current.deniedUserIds],
|
|
1908
|
-
};
|
|
1909
|
-
}
|
|
1910
|
-
function emptyPolicy() {
|
|
1911
|
-
return {
|
|
1912
|
-
everyone: false,
|
|
1913
|
-
requiredPermissions: [],
|
|
1914
|
-
allowedRoleIds: [],
|
|
1915
|
-
deniedRoleIds: [],
|
|
1916
|
-
allowedUserIds: [],
|
|
1917
|
-
deniedUserIds: [],
|
|
1918
|
-
};
|
|
1919
|
-
}
|
|
1920
|
-
function permissionPoliciesEqual(left, right) {
|
|
1921
|
-
return (left.everyone === right.everyone &&
|
|
1922
|
-
left.accessLevel === right.accessLevel &&
|
|
1923
|
-
sameStrings(left.requiredPermissions, right.requiredPermissions) &&
|
|
1924
|
-
sameStrings(left.allowedRoleIds, right.allowedRoleIds) &&
|
|
1925
|
-
sameStrings(left.deniedRoleIds, right.deniedRoleIds) &&
|
|
1926
|
-
sameStrings(left.allowedUserIds, right.allowedUserIds) &&
|
|
1927
|
-
sameStrings(left.deniedUserIds, right.deniedUserIds));
|
|
1928
|
-
}
|
|
1929
|
-
function sameStrings(left, right) {
|
|
1930
|
-
return (left.length === right.length &&
|
|
1931
|
-
left.every((value, index) => value === right[index]));
|
|
1932
|
-
}
|
|
1933
1892
|
function assertPermissionMutationAllowed(decision) {
|
|
1934
1893
|
if (decision.allowed)
|
|
1935
1894
|
return;
|
|
1936
1895
|
throw new BotControlForbiddenError(permissionMutationDenialMessage(decision.reason));
|
|
1937
1896
|
}
|
|
1938
|
-
function safeVersion(version) {
|
|
1939
|
-
const value = Number(version);
|
|
1940
|
-
if (!Number.isSafeInteger(value) || value < 1)
|
|
1941
|
-
throw new Error("Configuration version exceeded the supported range");
|
|
1942
|
-
return value;
|
|
1943
|
-
}
|
|
1944
|
-
function safeOptionalVersion(version) {
|
|
1945
|
-
return version === null ? 0 : safeVersion(version);
|
|
1946
|
-
}
|
|
1947
|
-
function assertVersion(actual, requested) {
|
|
1948
|
-
if (safeOptionalVersion(actual) !== requested)
|
|
1949
|
-
throw new OptimisticConcurrencyError("Permission policy");
|
|
1950
|
-
}
|
|
1951
1897
|
//# sourceMappingURL=control-server.js.map
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type { DiscordGuildHealthInspectionService, DiscordGuildHealthUnavailable, ModuleDefinition, ServiceAccess } from "@helyx/sdk";
|
|
2
|
+
import { type Client } from "discord.js";
|
|
3
|
+
export declare function moduleCatalogueMatches(packagedManifest: unknown, executableManifest: unknown): boolean;
|
|
4
|
+
export declare function missingRequiredPermissions(grantedPermissions: readonly string[], requiredPermissions: readonly string[]): string[];
|
|
5
|
+
export declare function hasRequiredModuleConfiguration(exposesConfiguration: boolean, savedConfiguration: unknown): boolean;
|
|
6
|
+
export declare function isHealthInspectableModule(moduleId: string): boolean;
|
|
7
|
+
export declare class DiscordGuildHealthInspection implements DiscordGuildHealthInspectionService {
|
|
8
|
+
#private;
|
|
9
|
+
private readonly client;
|
|
10
|
+
private readonly modules;
|
|
11
|
+
private readonly services?;
|
|
12
|
+
constructor(client: Client, modules?: readonly ModuleDefinition[], services?: ServiceAccess | undefined);
|
|
13
|
+
inspectOverview(input: {
|
|
14
|
+
guildId: string;
|
|
15
|
+
}): Promise<DiscordGuildHealthUnavailable> | Promise<{
|
|
16
|
+
available: true;
|
|
17
|
+
guildId: string;
|
|
18
|
+
name: string;
|
|
19
|
+
ownerId: string;
|
|
20
|
+
createdAt: Date;
|
|
21
|
+
memberCount: number;
|
|
22
|
+
verificationLevel: string;
|
|
23
|
+
explicitContentFilter: string;
|
|
24
|
+
defaultMessageNotifications: string;
|
|
25
|
+
mfaLevel: string;
|
|
26
|
+
premiumTier: number;
|
|
27
|
+
features: ("ANIMATED_BANNER" | "ANIMATED_ICON" | "APPLICATION_COMMAND_PERMISSIONS_V2" | "AUTO_MODERATION" | "BANNER" | "COMMUNITY" | "CREATOR_MONETIZABLE_PROVISIONAL" | "CREATOR_STORE_PAGE" | "DEVELOPER_SUPPORT_SERVER" | "DISCOVERABLE" | "FEATURABLE" | "HAS_DIRECTORY_ENTRY" | "HUB" | "INVITES_DISABLED" | "INVITE_SPLASH" | "LINKED_TO_HUB" | "MEMBER_VERIFICATION_GATE_ENABLED" | "MORE_SOUNDBOARD" | "MONETIZATION_ENABLED" | "MORE_STICKERS" | "NEWS" | "PARTNERED" | "PREVIEW_ENABLED" | "PRIVATE_THREADS" | "RAID_ALERTS_DISABLED" | "RELAY_ENABLED" | "ROLE_ICONS" | "ROLE_SUBSCRIPTIONS_AVAILABLE_FOR_PURCHASE" | "ROLE_SUBSCRIPTIONS_ENABLED" | "SOUNDBOARD" | "TICKETED_EVENTS_ENABLED" | "VANITY_URL" | "VERIFIED" | "VIP_REGIONS" | "WELCOME_SCREEN_ENABLED" | "GUILD_TAGS" | "ENHANCED_ROLE_COLORS" | "GUESTS_ENABLED" | "PIN_PERMISSION_MIGRATION_COMPLETE")[];
|
|
28
|
+
botPermissions: ("CreateInstantInvite" | "KickMembers" | "BanMembers" | "Administrator" | "ManageChannels" | "ManageGuild" | "AddReactions" | "ViewAuditLog" | "PrioritySpeaker" | "Stream" | "ViewChannel" | "SendMessages" | "SendTTSMessages" | "ManageMessages" | "EmbedLinks" | "AttachFiles" | "ReadMessageHistory" | "MentionEveryone" | "UseExternalEmojis" | "ViewGuildInsights" | "Connect" | "Speak" | "MuteMembers" | "DeafenMembers" | "MoveMembers" | "UseVAD" | "ChangeNickname" | "ManageNicknames" | "ManageRoles" | "ManageWebhooks" | "ManageEmojisAndStickers" | "ManageGuildExpressions" | "UseApplicationCommands" | "RequestToSpeak" | "ManageEvents" | "ManageThreads" | "CreatePublicThreads" | "CreatePrivateThreads" | "UseExternalStickers" | "SendMessagesInThreads" | "UseEmbeddedActivities" | "ModerateMembers" | "ViewCreatorMonetizationAnalytics" | "UseSoundboard" | "CreateGuildExpressions" | "CreateEvents" | "UseExternalSounds" | "SendVoiceMessages" | "SetVoiceChannelStatus" | "SendPolls" | "UseExternalApps" | "PinMessages" | "BypassSlowmode")[];
|
|
29
|
+
channelCount: number;
|
|
30
|
+
roleCount: number;
|
|
31
|
+
emojiCount: number;
|
|
32
|
+
stickerCount: number;
|
|
33
|
+
rulesChannelId: string | null;
|
|
34
|
+
publicUpdatesChannelId: string | null;
|
|
35
|
+
safetyAlertsChannelId: string | null;
|
|
36
|
+
invitesDisabledUntil: Date | null;
|
|
37
|
+
dmsDisabledUntil: Date | null;
|
|
38
|
+
botHighestRolePosition: number;
|
|
39
|
+
}>;
|
|
40
|
+
listChannels(input: {
|
|
41
|
+
guildId: string;
|
|
42
|
+
}): Promise<DiscordGuildHealthUnavailable> | Promise<{
|
|
43
|
+
available: true;
|
|
44
|
+
items: {
|
|
45
|
+
channelId: string;
|
|
46
|
+
name: string;
|
|
47
|
+
type: string;
|
|
48
|
+
parentId: string | null;
|
|
49
|
+
ageRestricted: boolean;
|
|
50
|
+
everyonePermissions: ("CreateInstantInvite" | "KickMembers" | "BanMembers" | "Administrator" | "ManageChannels" | "ManageGuild" | "AddReactions" | "ViewAuditLog" | "PrioritySpeaker" | "Stream" | "ViewChannel" | "SendMessages" | "SendTTSMessages" | "ManageMessages" | "EmbedLinks" | "AttachFiles" | "ReadMessageHistory" | "MentionEveryone" | "UseExternalEmojis" | "ViewGuildInsights" | "Connect" | "Speak" | "MuteMembers" | "DeafenMembers" | "MoveMembers" | "UseVAD" | "ChangeNickname" | "ManageNicknames" | "ManageRoles" | "ManageWebhooks" | "ManageEmojisAndStickers" | "ManageGuildExpressions" | "UseApplicationCommands" | "RequestToSpeak" | "ManageEvents" | "ManageThreads" | "CreatePublicThreads" | "CreatePrivateThreads" | "UseExternalStickers" | "SendMessagesInThreads" | "UseEmbeddedActivities" | "ModerateMembers" | "ViewCreatorMonetizationAnalytics" | "UseSoundboard" | "CreateGuildExpressions" | "CreateEvents" | "UseExternalSounds" | "SendVoiceMessages" | "SetVoiceChannelStatus" | "SendPolls" | "UseExternalApps" | "PinMessages" | "BypassSlowmode")[];
|
|
51
|
+
botPermissions: ("CreateInstantInvite" | "KickMembers" | "BanMembers" | "Administrator" | "ManageChannels" | "ManageGuild" | "AddReactions" | "ViewAuditLog" | "PrioritySpeaker" | "Stream" | "ViewChannel" | "SendMessages" | "SendTTSMessages" | "ManageMessages" | "EmbedLinks" | "AttachFiles" | "ReadMessageHistory" | "MentionEveryone" | "UseExternalEmojis" | "ViewGuildInsights" | "Connect" | "Speak" | "MuteMembers" | "DeafenMembers" | "MoveMembers" | "UseVAD" | "ChangeNickname" | "ManageNicknames" | "ManageRoles" | "ManageWebhooks" | "ManageEmojisAndStickers" | "ManageGuildExpressions" | "UseApplicationCommands" | "RequestToSpeak" | "ManageEvents" | "ManageThreads" | "CreatePublicThreads" | "CreatePrivateThreads" | "UseExternalStickers" | "SendMessagesInThreads" | "UseEmbeddedActivities" | "ModerateMembers" | "ViewCreatorMonetizationAnalytics" | "UseSoundboard" | "CreateGuildExpressions" | "CreateEvents" | "UseExternalSounds" | "SendVoiceMessages" | "SetVoiceChannelStatus" | "SendPolls" | "UseExternalApps" | "PinMessages" | "BypassSlowmode")[];
|
|
52
|
+
permissionsLocked: boolean | null;
|
|
53
|
+
}[];
|
|
54
|
+
}>;
|
|
55
|
+
listRoles(input: {
|
|
56
|
+
guildId: string;
|
|
57
|
+
}): Promise<{
|
|
58
|
+
available: true;
|
|
59
|
+
items: {
|
|
60
|
+
roleId: string;
|
|
61
|
+
name: string;
|
|
62
|
+
position: number;
|
|
63
|
+
managed: boolean;
|
|
64
|
+
memberCount: number;
|
|
65
|
+
permissions: ("CreateInstantInvite" | "KickMembers" | "BanMembers" | "Administrator" | "ManageChannels" | "ManageGuild" | "AddReactions" | "ViewAuditLog" | "PrioritySpeaker" | "Stream" | "ViewChannel" | "SendMessages" | "SendTTSMessages" | "ManageMessages" | "EmbedLinks" | "AttachFiles" | "ReadMessageHistory" | "MentionEveryone" | "UseExternalEmojis" | "ViewGuildInsights" | "Connect" | "Speak" | "MuteMembers" | "DeafenMembers" | "MoveMembers" | "UseVAD" | "ChangeNickname" | "ManageNicknames" | "ManageRoles" | "ManageWebhooks" | "ManageEmojisAndStickers" | "ManageGuildExpressions" | "UseApplicationCommands" | "RequestToSpeak" | "ManageEvents" | "ManageThreads" | "CreatePublicThreads" | "CreatePrivateThreads" | "UseExternalStickers" | "SendMessagesInThreads" | "UseEmbeddedActivities" | "ModerateMembers" | "ViewCreatorMonetizationAnalytics" | "UseSoundboard" | "CreateGuildExpressions" | "CreateEvents" | "UseExternalSounds" | "SendVoiceMessages" | "SetVoiceChannelStatus" | "SendPolls" | "UseExternalApps" | "PinMessages" | "BypassSlowmode")[];
|
|
66
|
+
}[];
|
|
67
|
+
}>;
|
|
68
|
+
listMembers(input: {
|
|
69
|
+
guildId: string;
|
|
70
|
+
after?: string;
|
|
71
|
+
limit: number;
|
|
72
|
+
}): Promise<DiscordGuildHealthUnavailable | {
|
|
73
|
+
nextAfter?: string;
|
|
74
|
+
available: true;
|
|
75
|
+
items: {
|
|
76
|
+
userId: string;
|
|
77
|
+
bot: boolean;
|
|
78
|
+
system: boolean;
|
|
79
|
+
createdAt: Date;
|
|
80
|
+
joinedAt: Date | null;
|
|
81
|
+
timedOutUntil: Date | null;
|
|
82
|
+
publicFlags: ("Staff" | "Partner" | "Hypesquad" | "BugHunterLevel1" | "MFASMS" | "PremiumPromoDismissed" | "HypeSquadOnlineHouse1" | "HypeSquadOnlineHouse2" | "HypeSquadOnlineHouse3" | "PremiumEarlySupporter" | "TeamPseudoUser" | "HasUnreadUrgentMessages" | "BugHunterLevel2" | "VerifiedBot" | "VerifiedDeveloper" | "CertifiedModerator" | "BotHTTPInteractions" | "Spammer" | "DisablePremium" | "ActiveDeveloper" | "Quarantined" | "Collaborator" | "RestrictedCollaborator")[];
|
|
83
|
+
roleIds: string[];
|
|
84
|
+
}[];
|
|
85
|
+
}>;
|
|
86
|
+
listInvites(input: {
|
|
87
|
+
guildId: string;
|
|
88
|
+
}): Promise<DiscordGuildHealthUnavailable | {
|
|
89
|
+
available: true;
|
|
90
|
+
items: {
|
|
91
|
+
code: string;
|
|
92
|
+
channelId: string | null;
|
|
93
|
+
channelName: string | null;
|
|
94
|
+
channelExists: boolean;
|
|
95
|
+
inviterUserId: string | null;
|
|
96
|
+
createdAt: Date | null;
|
|
97
|
+
expiresAt: Date | null;
|
|
98
|
+
maxUses: number;
|
|
99
|
+
uses: number;
|
|
100
|
+
temporary: boolean;
|
|
101
|
+
}[];
|
|
102
|
+
}>;
|
|
103
|
+
listIntegrations(input: {
|
|
104
|
+
guildId: string;
|
|
105
|
+
}): Promise<DiscordGuildHealthUnavailable | {
|
|
106
|
+
available: true;
|
|
107
|
+
items: {
|
|
108
|
+
integrationId: string;
|
|
109
|
+
name: string;
|
|
110
|
+
type: import("discord.js").IntegrationType;
|
|
111
|
+
enabled: boolean;
|
|
112
|
+
syncing: boolean | null;
|
|
113
|
+
roleId: string | null;
|
|
114
|
+
roleExists: boolean;
|
|
115
|
+
}[];
|
|
116
|
+
}>;
|
|
117
|
+
listWebhooks(input: {
|
|
118
|
+
guildId: string;
|
|
119
|
+
}): Promise<DiscordGuildHealthUnavailable | {
|
|
120
|
+
available: true;
|
|
121
|
+
items: {
|
|
122
|
+
webhookId: string;
|
|
123
|
+
name: string;
|
|
124
|
+
channelId: string;
|
|
125
|
+
ownerUserId: string | null;
|
|
126
|
+
applicationId: null;
|
|
127
|
+
channelName: string | null;
|
|
128
|
+
}[];
|
|
129
|
+
}>;
|
|
130
|
+
listAutoModerationRules(input: {
|
|
131
|
+
guildId: string;
|
|
132
|
+
}): Promise<DiscordGuildHealthUnavailable | {
|
|
133
|
+
available: true;
|
|
134
|
+
items: {
|
|
135
|
+
ruleId: string;
|
|
136
|
+
name: string;
|
|
137
|
+
enabled: boolean;
|
|
138
|
+
triggerType: string;
|
|
139
|
+
eventType: string;
|
|
140
|
+
actionCount: number;
|
|
141
|
+
}[];
|
|
142
|
+
}>;
|
|
143
|
+
inspectAuditSummary(input: {
|
|
144
|
+
guildId: string;
|
|
145
|
+
}): Promise<DiscordGuildHealthUnavailable | {
|
|
146
|
+
available: true;
|
|
147
|
+
inspected: number;
|
|
148
|
+
highImpactActionsLastHour: number;
|
|
149
|
+
}>;
|
|
150
|
+
inspectHelyxModules(input: {
|
|
151
|
+
guildId: string;
|
|
152
|
+
batchSize?: number;
|
|
153
|
+
}): Promise<DiscordGuildHealthUnavailable | {
|
|
154
|
+
available: true;
|
|
155
|
+
items: {
|
|
156
|
+
moduleId: string;
|
|
157
|
+
name: string;
|
|
158
|
+
version: string;
|
|
159
|
+
enabled: boolean;
|
|
160
|
+
configured: boolean;
|
|
161
|
+
configurationValid: boolean;
|
|
162
|
+
resourceValidationValid: boolean | null;
|
|
163
|
+
activationValid: boolean | null;
|
|
164
|
+
catalogueMatches: boolean;
|
|
165
|
+
requiredCapabilities: readonly ("message-content" | "guild-members")[];
|
|
166
|
+
missingCapabilities: ("message-content" | "guild-members")[];
|
|
167
|
+
missingPermissions: string[];
|
|
168
|
+
healthStatus: "healthy" | "degraded" | "unhealthy";
|
|
169
|
+
healthMessage: string | null;
|
|
170
|
+
diagnosticsAvailable: boolean;
|
|
171
|
+
}[];
|
|
172
|
+
}>;
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=discord-guild-health.d.ts.map
|