@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.
@@ -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 guild = await this.client.guilds.fetch(guildId);
133
- const [channels, roles] = await Promise.all([
134
- guild.channels.fetch(),
135
- guild.roles.fetch(),
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 [resources, installedModules, storedLoggingPolicies, runtime] = await Promise.all([
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
- await Promise.all(this.#dashboardModules.map(async (dashboardModule) => {
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 [configuration, permission] = await Promise.all([
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] = permission;
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 this.resources.resolveActor(input.guildId, input.actorUserId);
448
- return canAccessGuildDashboard(actor, await this.#readGuildAccessPolicy(input.guildId));
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 this.resources.resolveActor(input.guildId, input.actorUserId);
452
- const record = await new GuildAccessPolicyRepository(this.database).get(input.guildId);
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 } = await this.#prepareManagedResource(input);
600
+ const { module, resource } = this.#prepareManagedResource(input);
564
601
  this.#requireManagedResourceOperation(module, input.resourceType, input.operation);
565
- await this.#authorizeAction(input.guildId, module, input.actorUserId, managedResourcePermissionId(module, input.resourceType, input.operation));
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
- if (!(await new InstallationRepository(this.database).isModuleEnabled(input.guildId, input.moduleId))) {
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 } = await this.#prepareManagedResource(input);
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 } = await this.#prepareManagedResource(input);
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 } = await this.#prepareManagedResource(input);
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 } = await this.#prepareManagedResource(input);
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 } = await this.#prepareManagedResource(input);
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 } = await this.#prepareManagedResource(input);
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 } = await this.#prepareManagedResource(input);
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 } = await this.#prepareManagedResource(input);
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
- async #prepareManagedResource(input) {
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 actor = await this.#authorizeAction(input.guildId, module, input.actorUserId, permissionId);
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 this.resources.resolveActor(guildId, actorUserId);
1001
- const policy = await this.#permissions.getEffectivePolicy(guildId, module.manifest.id, permissionId);
1002
- const guildAccess = await this.#permissions.getGuildAccessPolicy?.(guildId);
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 modulePolicy = await this.#permissions.getPolicy(guildId, module.manifest.id);
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: await Promise.all(commands.map(async (command) => {
1034
- const value = await this.#permissions.getPolicy(guildId, module.manifest.id, command.id);
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