@helyx/bot 0.4.31 → 0.4.33
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 +22 -0
- package/dist/control-permissions.d.ts +38 -0
- package/dist/control-permissions.js +139 -0
- package/dist/control-server.d.ts +24 -16
- package/dist/control-server.js +149 -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 +196 -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-contracts.d.ts +42 -0
- package/dist/shard-contracts.js +26 -0
- package/dist/shard-router.d.ts +1 -0
- package/dist/shard-router.js +3 -0
- package/dist/shard-supervisor.js +29 -3
- 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 ||
|
|
@@ -284,6 +286,26 @@ export class BotControlService {
|
|
|
284
286
|
})
|
|
285
287
|
: []);
|
|
286
288
|
}
|
|
289
|
+
async cleanupOAuthConsumer(input) {
|
|
290
|
+
const module = this.#modules.get(input.moduleId);
|
|
291
|
+
if (!module)
|
|
292
|
+
throw new BotControlNotFoundError("OAuth consumer module is unavailable");
|
|
293
|
+
const consumer = module.oauthConsumers?.find((candidate) => candidate.provider === input.provider &&
|
|
294
|
+
candidate.resourceType === input.resourceType &&
|
|
295
|
+
candidate.version === input.contractVersion);
|
|
296
|
+
if (!consumer)
|
|
297
|
+
throw new BotControlNotFoundError("OAuth consumer cleanup contract is unavailable");
|
|
298
|
+
return consumer.cleanup({
|
|
299
|
+
consumerBindingId: input.consumerBindingId,
|
|
300
|
+
provider: input.provider,
|
|
301
|
+
guildId: input.guildId,
|
|
302
|
+
resourceType: input.resourceType,
|
|
303
|
+
resourceId: input.resourceId,
|
|
304
|
+
subjectUserId: input.subjectUserId,
|
|
305
|
+
correlationId: input.correlationId,
|
|
306
|
+
services: this.#services,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
287
309
|
async listGuildPresence(input) {
|
|
288
310
|
const guildIds = [...new Set(input.guildIds)];
|
|
289
311
|
if (guildIds.length > 200)
|
|
@@ -389,11 +411,25 @@ export class BotControlService {
|
|
|
389
411
|
});
|
|
390
412
|
}
|
|
391
413
|
async getGuildState(guildId) {
|
|
392
|
-
const
|
|
414
|
+
const dashboardEntries = this.#dashboardModules.map((dashboardModule) => ({
|
|
415
|
+
dashboardModule,
|
|
416
|
+
module: this.#requireModule(dashboardModule.id),
|
|
417
|
+
}));
|
|
418
|
+
const moduleIds = dashboardEntries.map(({ dashboardModule }) => dashboardModule.id);
|
|
419
|
+
const permissionTargets = dashboardEntries.flatMap(({ dashboardModule, module }) => [
|
|
420
|
+
{ moduleId: dashboardModule.id },
|
|
421
|
+
...dashboardCommands(module).map(({ id }) => ({
|
|
422
|
+
moduleId: dashboardModule.id,
|
|
423
|
+
commandId: id,
|
|
424
|
+
})),
|
|
425
|
+
]);
|
|
426
|
+
const [resources, installedModules, storedLoggingPolicies, runtime, storedConfigurations, storedPermissions,] = await Promise.all([
|
|
393
427
|
this.resources.getGuildResources(guildId),
|
|
394
428
|
new InstallationRepository(this.database).listModules(guildId),
|
|
395
429
|
new ModuleLoggingPolicyRepository(this.database).listForGuild(guildId),
|
|
396
430
|
this.#inspectRuntimeCapabilities(),
|
|
431
|
+
this.#readConfigurations(guildId, moduleIds),
|
|
432
|
+
this.#readPermissionPolicies(guildId, permissionTargets),
|
|
397
433
|
]);
|
|
398
434
|
const installed = new Map(installedModules.map((module) => [module.moduleId, module]));
|
|
399
435
|
const moduleStatuses = {};
|
|
@@ -404,24 +440,20 @@ export class BotControlService {
|
|
|
404
440
|
`${policy.sourceModuleId}:${policy.eventId}`,
|
|
405
441
|
policy,
|
|
406
442
|
]));
|
|
407
|
-
|
|
408
|
-
const module = this.#requireModule(dashboardModule.id);
|
|
443
|
+
for (const { dashboardModule, module } of dashboardEntries) {
|
|
409
444
|
const installation = installed.get(dashboardModule.id);
|
|
410
445
|
moduleStatuses[dashboardModule.id] = installation
|
|
411
446
|
? installation.enabled
|
|
412
447
|
? "enabled"
|
|
413
448
|
: "disabled"
|
|
414
449
|
: "unavailable";
|
|
415
|
-
const
|
|
416
|
-
this.#configurations.get(guildId, dashboardModule.id),
|
|
417
|
-
this.#getPermissionState(guildId, module),
|
|
418
|
-
]);
|
|
450
|
+
const configuration = storedConfigurations.get(dashboardModule.id);
|
|
419
451
|
if (configuration)
|
|
420
452
|
configurations[dashboardModule.id] = {
|
|
421
453
|
version: safeVersion(configuration.version),
|
|
422
454
|
value: configuration.value,
|
|
423
455
|
};
|
|
424
|
-
permissions[dashboardModule.id] =
|
|
456
|
+
permissions[dashboardModule.id] = this.#buildPermissionState(module, storedPermissions);
|
|
425
457
|
logging[dashboardModule.id] = dashboardModule.logEvents.map((event) => {
|
|
426
458
|
const stored = loggingPolicies.get(`${dashboardModule.id}:${event.id}`);
|
|
427
459
|
return {
|
|
@@ -430,7 +462,7 @@ export class BotControlService {
|
|
|
430
462
|
version: stored ? safeVersion(stored.version) : 0,
|
|
431
463
|
};
|
|
432
464
|
});
|
|
433
|
-
}
|
|
465
|
+
}
|
|
434
466
|
return {
|
|
435
467
|
discordCapabilities: {
|
|
436
468
|
messageContent: runtime.messageContent,
|
|
@@ -444,12 +476,17 @@ export class BotControlService {
|
|
|
444
476
|
};
|
|
445
477
|
}
|
|
446
478
|
async canAccessDashboard(input) {
|
|
447
|
-
const actor = await
|
|
448
|
-
|
|
479
|
+
const [actor, policy] = await Promise.all([
|
|
480
|
+
this.resources.resolveActor(input.guildId, input.actorUserId),
|
|
481
|
+
this.#readGuildAccessPolicy(input.guildId),
|
|
482
|
+
]);
|
|
483
|
+
return canAccessGuildDashboard(actor, policy);
|
|
449
484
|
}
|
|
450
485
|
async getGuildAccessPolicy(input) {
|
|
451
|
-
const actor = await
|
|
452
|
-
|
|
486
|
+
const [actor, record] = await Promise.all([
|
|
487
|
+
this.resources.resolveActor(input.guildId, input.actorUserId),
|
|
488
|
+
new GuildAccessPolicyRepository(this.database).get(input.guildId),
|
|
489
|
+
]);
|
|
453
490
|
const policy = record ?? DEFAULT_GUILD_ACCESS_POLICY;
|
|
454
491
|
return {
|
|
455
492
|
administratorRoleIds: [...policy.administratorRoleIds],
|
|
@@ -560,9 +597,14 @@ export class BotControlService {
|
|
|
560
597
|
}
|
|
561
598
|
async canAccessManagedResource(input) {
|
|
562
599
|
try {
|
|
563
|
-
const { module } =
|
|
600
|
+
const { module, resource } = this.#prepareManagedResource(input);
|
|
564
601
|
this.#requireManagedResourceOperation(module, input.resourceType, input.operation);
|
|
565
|
-
await
|
|
602
|
+
await Promise.all([
|
|
603
|
+
this.#authorizeAction(input.guildId, module, input.actorUserId, managedResourcePermissionId(module, input.resourceType, input.operation, undefined, declaredManagedResourcePermission(module, input.resourceType, input.operation))),
|
|
604
|
+
resource.availableWhileDisabled
|
|
605
|
+
? Promise.resolve()
|
|
606
|
+
: this.#requireEnabledManagedResource(input.guildId, input.moduleId),
|
|
607
|
+
]);
|
|
566
608
|
return true;
|
|
567
609
|
}
|
|
568
610
|
catch (error) {
|
|
@@ -840,10 +882,13 @@ export class BotControlService {
|
|
|
840
882
|
if (!declared || !action) {
|
|
841
883
|
throw new BotControlNotFoundError("Dashboard action is not declared by this module");
|
|
842
884
|
}
|
|
843
|
-
|
|
885
|
+
const [enabled] = await Promise.all([
|
|
886
|
+
new InstallationRepository(this.database).isModuleEnabled(input.guildId, input.moduleId),
|
|
887
|
+
this.#authorizeAction(input.guildId, module, input.actorUserId, dashboardActionPermissionId(module, input.actionId)),
|
|
888
|
+
]);
|
|
889
|
+
if (!enabled) {
|
|
844
890
|
throw new BotControlValidationError("Enable this module before running its dashboard actions");
|
|
845
891
|
}
|
|
846
|
-
await this.#authorizeAction(input.guildId, module, input.actorUserId, dashboardActionPermissionId(module, input.actionId));
|
|
847
892
|
return action.execute({
|
|
848
893
|
guildId: input.guildId,
|
|
849
894
|
actorUserId: input.actorUserId,
|
|
@@ -854,7 +899,7 @@ export class BotControlService {
|
|
|
854
899
|
});
|
|
855
900
|
}
|
|
856
901
|
async listManagedResources(input) {
|
|
857
|
-
const { module, resource } =
|
|
902
|
+
const { module, resource } = this.#prepareManagedResource(input);
|
|
858
903
|
this.#requireManagedResourceOperation(module, input.resourceType, "list");
|
|
859
904
|
this.#validateManagedResourceQuery(module, input.resourceType, input);
|
|
860
905
|
const result = await resource.list(await this.#managedResourceContext(input, module, "list"), {
|
|
@@ -866,7 +911,7 @@ export class BotControlService {
|
|
|
866
911
|
return normalizeManagedResourcePage(managedResourcePageSchema.parse(result), managedResourceColumnKeys(module, input.resourceType));
|
|
867
912
|
}
|
|
868
913
|
async readManagedResource(input) {
|
|
869
|
-
const { module, resource } =
|
|
914
|
+
const { module, resource } = this.#prepareManagedResource(input);
|
|
870
915
|
this.#requireManagedResourceOperation(module, input.resourceType, "read");
|
|
871
916
|
const result = await resource.read(await this.#managedResourceContext(input, module, "read"), input.resourceId);
|
|
872
917
|
if (!result)
|
|
@@ -874,7 +919,7 @@ export class BotControlService {
|
|
|
874
919
|
return normalizeManagedResourceDetail(managedResourceDetailSchema.parse(result), managedResourceColumnKeys(module, input.resourceType));
|
|
875
920
|
}
|
|
876
921
|
async listManagedResourceRelation(input) {
|
|
877
|
-
const { module, resource } =
|
|
922
|
+
const { module, resource } = this.#prepareManagedResource(input);
|
|
878
923
|
this.#requireManagedResourceOperation(module, input.resourceType, "read");
|
|
879
924
|
const relation = module.manifest.dashboard?.resources
|
|
880
925
|
?.find(({ id }) => id === input.resourceType)
|
|
@@ -890,12 +935,12 @@ export class BotControlService {
|
|
|
890
935
|
})), relation.columns.map(({ key }) => key));
|
|
891
936
|
}
|
|
892
937
|
async previewManagedResource(input) {
|
|
893
|
-
const { module, resource } =
|
|
938
|
+
const { module, resource } = this.#prepareManagedResource(input);
|
|
894
939
|
this.#requireManagedResourceOperation(module, input.resourceType, "preview");
|
|
895
940
|
return resource.preview(await this.#managedResourceContext(input, module, "preview"), input.value);
|
|
896
941
|
}
|
|
897
942
|
async createManagedResource(input) {
|
|
898
|
-
const { module, resource } =
|
|
943
|
+
const { module, resource } = this.#prepareManagedResource(input);
|
|
899
944
|
this.#requireManagedResourceOperation(module, input.resourceType, "create");
|
|
900
945
|
const context = await this.#managedResourceContext(input, module, "create");
|
|
901
946
|
const declaredIntents = module.manifest.dashboard?.resources?.find(({ id }) => id === input.resourceType)?.creationIntents ?? ["publish"];
|
|
@@ -904,7 +949,7 @@ export class BotControlService {
|
|
|
904
949
|
return normalizeManagedResourceDetail(managedResourceDetailSchema.parse(await resource.create(context, input.value, input.intent)), managedResourceColumnKeys(module, input.resourceType));
|
|
905
950
|
}
|
|
906
951
|
async updateManagedResource(input) {
|
|
907
|
-
const { module, resource } =
|
|
952
|
+
const { module, resource } = this.#prepareManagedResource(input);
|
|
908
953
|
this.#requireManagedResourceOperation(module, input.resourceType, "update");
|
|
909
954
|
return normalizeManagedResourceDetail(managedResourceDetailSchema.parse(await resource.update(await this.#managedResourceContext(input, module, "update"), {
|
|
910
955
|
resourceId: input.resourceId,
|
|
@@ -913,7 +958,7 @@ export class BotControlService {
|
|
|
913
958
|
})), managedResourceColumnKeys(module, input.resourceType));
|
|
914
959
|
}
|
|
915
960
|
async deleteManagedResource(input) {
|
|
916
|
-
const { module, resource } =
|
|
961
|
+
const { module, resource } = this.#prepareManagedResource(input);
|
|
917
962
|
this.#requireManagedResourceOperation(module, input.resourceType, "delete");
|
|
918
963
|
return managedResourceDeleteResultSchema.parse(await resource.delete(await this.#managedResourceContext(input, module, "delete"), {
|
|
919
964
|
resourceId: input.resourceId,
|
|
@@ -921,7 +966,7 @@ export class BotControlService {
|
|
|
921
966
|
}));
|
|
922
967
|
}
|
|
923
968
|
async executeManagedResourceAction(input) {
|
|
924
|
-
const { module, resource } =
|
|
969
|
+
const { module, resource } = this.#prepareManagedResource(input);
|
|
925
970
|
const declared = module.manifest.dashboard?.resources
|
|
926
971
|
?.find(({ id }) => id === input.resourceType)
|
|
927
972
|
?.actions?.find(({ id }) => id === input.actionId);
|
|
@@ -941,11 +986,9 @@ export class BotControlService {
|
|
|
941
986
|
? null
|
|
942
987
|
: normalizeManagedResourceDetail(managedResourceDetailSchema.parse(result), managedResourceColumnKeys(module, input.resourceType));
|
|
943
988
|
}
|
|
944
|
-
|
|
989
|
+
#prepareManagedResource(input) {
|
|
945
990
|
const module = this.#requireDashboardModule(input.moduleId);
|
|
946
991
|
const resource = this.#requireManagedResource(module, input.resourceType);
|
|
947
|
-
if (!resource.availableWhileDisabled)
|
|
948
|
-
await this.#requireEnabledManagedResource(input.guildId, input.moduleId);
|
|
949
992
|
return { module, resource };
|
|
950
993
|
}
|
|
951
994
|
#requireManagedResource(module, resourceType) {
|
|
@@ -979,8 +1022,14 @@ export class BotControlService {
|
|
|
979
1022
|
if (!(await new InstallationRepository(this.database).isModuleEnabled(guildId, moduleIdValue)))
|
|
980
1023
|
throw new BotControlValidationError("Enable this module before managing its resources");
|
|
981
1024
|
}
|
|
982
|
-
async #managedResourceContext(input, module, operation, permissionId = managedResourcePermissionId(module, input.resourceType, operation)) {
|
|
983
|
-
const
|
|
1025
|
+
async #managedResourceContext(input, module, operation, permissionId = managedResourcePermissionId(module, input.resourceType, operation, undefined, declaredManagedResourcePermission(module, input.resourceType, operation))) {
|
|
1026
|
+
const resource = this.#requireManagedResource(module, input.resourceType);
|
|
1027
|
+
const [actor] = await Promise.all([
|
|
1028
|
+
this.#authorizeAction(input.guildId, module, input.actorUserId, permissionId),
|
|
1029
|
+
resource.availableWhileDisabled
|
|
1030
|
+
? Promise.resolve()
|
|
1031
|
+
: this.#requireEnabledManagedResource(input.guildId, module.manifest.id),
|
|
1032
|
+
]);
|
|
984
1033
|
return {
|
|
985
1034
|
guildId: input.guildId,
|
|
986
1035
|
actor,
|
|
@@ -997,9 +1046,11 @@ export class BotControlService {
|
|
|
997
1046
|
};
|
|
998
1047
|
}
|
|
999
1048
|
async #authorizeAction(guildId, module, actorUserId, permissionId) {
|
|
1000
|
-
const actor = await
|
|
1001
|
-
|
|
1002
|
-
|
|
1049
|
+
const [actor, policy, guildAccess] = await Promise.all([
|
|
1050
|
+
this.resources.resolveActor(guildId, actorUserId),
|
|
1051
|
+
this.#permissions.getEffectivePolicy(guildId, module.manifest.id, permissionId),
|
|
1052
|
+
this.#permissions.getGuildAccessPolicy?.(guildId),
|
|
1053
|
+
]);
|
|
1003
1054
|
if (!canUseCommand(actor, policy, guildAccess))
|
|
1004
1055
|
throw new BotControlForbiddenError();
|
|
1005
1056
|
return actor;
|
|
@@ -1026,12 +1077,50 @@ export class BotControlService {
|
|
|
1026
1077
|
}
|
|
1027
1078
|
async #getPermissionState(guildId, module) {
|
|
1028
1079
|
const commands = dashboardCommands(module);
|
|
1029
|
-
const
|
|
1080
|
+
const policies = await this.#readPermissionPolicies(guildId, [
|
|
1081
|
+
{ moduleId: module.manifest.id },
|
|
1082
|
+
...commands.map(({ id }) => ({
|
|
1083
|
+
moduleId: module.manifest.id,
|
|
1084
|
+
commandId: id,
|
|
1085
|
+
})),
|
|
1086
|
+
]);
|
|
1087
|
+
return this.#buildPermissionState(module, policies);
|
|
1088
|
+
}
|
|
1089
|
+
async #readConfigurations(guildId, moduleIds) {
|
|
1090
|
+
const values = this.#configurations.getMany
|
|
1091
|
+
? await this.#configurations.getMany(guildId, moduleIds)
|
|
1092
|
+
: await Promise.all(moduleIds.map((moduleId) => this.#configurations.get(guildId, moduleId)));
|
|
1093
|
+
return new Map(values
|
|
1094
|
+
.filter((value) => value !== null)
|
|
1095
|
+
.map((value) => [value.moduleId, value]));
|
|
1096
|
+
}
|
|
1097
|
+
async #readPermissionPolicies(guildId, targets) {
|
|
1098
|
+
const values = this.#permissions.getPolicies
|
|
1099
|
+
? await this.#permissions.getPolicies(guildId, targets)
|
|
1100
|
+
: await Promise.all(targets.map(async (target) => ({
|
|
1101
|
+
...target,
|
|
1102
|
+
...(await this.#permissions.getPolicy(guildId, target.moduleId, target.commandId)),
|
|
1103
|
+
})));
|
|
1104
|
+
const policies = new Map(values.map((value) => [permissionPolicyKey(value), value]));
|
|
1105
|
+
for (const target of targets)
|
|
1106
|
+
if (!policies.has(permissionPolicyKey(target)))
|
|
1107
|
+
throw new Error("Permission policy bulk response was incomplete");
|
|
1108
|
+
return policies;
|
|
1109
|
+
}
|
|
1110
|
+
#buildPermissionState(module, policies) {
|
|
1111
|
+
const modulePolicy = policies.get(permissionPolicyKey({ moduleId: module.manifest.id }));
|
|
1112
|
+
if (!modulePolicy)
|
|
1113
|
+
throw new Error("Module permission policy was not resolved");
|
|
1030
1114
|
return {
|
|
1031
1115
|
version: safeOptionalVersion(modulePolicy.version),
|
|
1032
1116
|
module: toDashboardPolicy(modulePolicy.policy),
|
|
1033
|
-
commands:
|
|
1034
|
-
const value =
|
|
1117
|
+
commands: dashboardCommands(module).map((command) => {
|
|
1118
|
+
const value = policies.get(permissionPolicyKey({
|
|
1119
|
+
moduleId: module.manifest.id,
|
|
1120
|
+
commandId: command.id,
|
|
1121
|
+
}));
|
|
1122
|
+
if (!value)
|
|
1123
|
+
throw new Error("Command permission policy was not resolved");
|
|
1035
1124
|
return {
|
|
1036
1125
|
commandId: command.id,
|
|
1037
1126
|
version: safeOptionalVersion(value.version),
|
|
@@ -1041,7 +1130,7 @@ export class BotControlService {
|
|
|
1041
1130
|
module.defaultPermissions?.commands[command.id] !== undefined),
|
|
1042
1131
|
policy: toDashboardPolicy(value.policy),
|
|
1043
1132
|
};
|
|
1044
|
-
})
|
|
1133
|
+
}),
|
|
1045
1134
|
};
|
|
1046
1135
|
}
|
|
1047
1136
|
async #validateDiscordSettings(guildId, module, value) {
|
|
@@ -1144,6 +1233,23 @@ export async function createBotControlServer(input) {
|
|
|
1144
1233
|
server.get("/health", (_request, reply) => readiness(reply));
|
|
1145
1234
|
server.get("/v1/health", (_request, reply) => readiness(reply));
|
|
1146
1235
|
server.post("/v1/verification/results", async (request) => input.service.acceptVerificationResult(signedAssessmentEnvelopeSchema.parse(request.body)));
|
|
1236
|
+
server.post("/v1/oauth/consumers/cleanup", async (request) => {
|
|
1237
|
+
const body = z
|
|
1238
|
+
.object({
|
|
1239
|
+
consumerBindingId: z.uuid(),
|
|
1240
|
+
moduleId,
|
|
1241
|
+
provider: z.enum(["youtube", "twitch"]),
|
|
1242
|
+
guildId: snowflake,
|
|
1243
|
+
resourceType: z.string().regex(/^[a-z][a-z0-9_-]*$/u),
|
|
1244
|
+
resourceId,
|
|
1245
|
+
subjectUserId: snowflake,
|
|
1246
|
+
contractVersion: z.number().int().min(1).max(32_767),
|
|
1247
|
+
correlationId: z.string().trim().min(1).max(200),
|
|
1248
|
+
})
|
|
1249
|
+
.strict()
|
|
1250
|
+
.parse(request.body);
|
|
1251
|
+
return input.service.cleanupOAuthConsumer(body);
|
|
1252
|
+
});
|
|
1147
1253
|
server.get("/v1/shards/health", async (request, reply) => {
|
|
1148
1254
|
reply.header("Cache-Control", "no-store");
|
|
1149
1255
|
if (!input.shardHealth)
|
|
@@ -1650,58 +1756,6 @@ function isUnknownMemberError(error) {
|
|
|
1650
1756
|
"code" in error &&
|
|
1651
1757
|
error.code === 10_007);
|
|
1652
1758
|
}
|
|
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
1759
|
function isBoundedManagedResourceValue(value, depth = 0, budget = { keys: 0 }) {
|
|
1706
1760
|
if (depth > 8)
|
|
1707
1761
|
return false;
|
|
@@ -1766,6 +1820,7 @@ function normalizeManagedResourcePage(value, attributeKeys) {
|
|
|
1766
1820
|
updatedAt: item.updatedAt,
|
|
1767
1821
|
})),
|
|
1768
1822
|
...(value.nextCursor === undefined ? {} : { nextCursor: value.nextCursor }),
|
|
1823
|
+
...(value.total === undefined ? {} : { total: value.total }),
|
|
1769
1824
|
};
|
|
1770
1825
|
}
|
|
1771
1826
|
function normalizeManagedResourceRelationPage(value, valueKeys) {
|
|
@@ -1871,81 +1926,9 @@ function dashboardCommands(module) {
|
|
|
1871
1926
|
...new Map(commands.map((command) => [command.id, command])).values(),
|
|
1872
1927
|
];
|
|
1873
1928
|
}
|
|
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
1929
|
function assertPermissionMutationAllowed(decision) {
|
|
1934
1930
|
if (decision.allowed)
|
|
1935
1931
|
return;
|
|
1936
1932
|
throw new BotControlForbiddenError(permissionMutationDenialMessage(decision.reason));
|
|
1937
1933
|
}
|
|
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
1934
|
//# sourceMappingURL=control-server.js.map
|