seedcord 0.9.1 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -154,8 +154,7 @@ var BaseComponent = class {
154
154
  * @returns Formatted customId string
155
155
  */
156
156
  buildCustomId(prefix, ...args) {
157
- if (args.length === 0) return prefix;
158
- return `${prefix}:${args.join("-")}`;
157
+ return args.length === 0 ? prefix : `${prefix}:${args.join("-")}`;
159
158
  }
160
159
  };
161
160
  var BuilderComponent = class extends BaseComponent {
@@ -737,7 +736,7 @@ function buildSlashRoute(arg1, arg2, arg3) {
737
736
  }
738
737
  __name(buildSlashRoute, "buildSlashRoute");
739
738
 
740
- // src/bot/errors/Database.ts
739
+ // src/bot/defaults/errors/Database.ts
741
740
  var DatabaseError = class extends CustomError {
742
741
  static {
743
742
  __name(this, "DatabaseError");
@@ -1245,6 +1244,10 @@ var Pluggable = class _Pluggable extends services.StrictEventEmitter {
1245
1244
  }
1246
1245
  };
1247
1246
  var emojiStorage = {};
1247
+ function isEmojiTuple(v) {
1248
+ return Array.isArray(v) && v.length === 2 && typeof v[0] === "string" && typeof v[1] === "string";
1249
+ }
1250
+ __name(isEmojiTuple, "isEmojiTuple");
1248
1251
  var Emojis = emojiStorage;
1249
1252
  var EmojiInjector = class {
1250
1253
  static {
@@ -1264,23 +1267,57 @@ var EmojiInjector = class {
1264
1267
  const configEmojis = this.core.config.bot.emojis;
1265
1268
  await this.core.bot.client.application?.emojis.fetch();
1266
1269
  let foundCount = 0;
1267
- Object.entries(configEmojis).forEach(([key, emojiName]) => {
1268
- if (typeof emojiName !== "string") {
1269
- this.logger.warn(`${chalk3__default.default.bold.yellow("Invalid")}: ${chalk3__default.default.magenta.bold(String(key))} (expected string, received ${typeof emojiName})`);
1270
- return;
1270
+ const entries = Object.entries(configEmojis);
1271
+ for (const [key, value] of entries) {
1272
+ if (isEmojiTuple(value)) {
1273
+ foundCount += this.handleTuple(key, value);
1274
+ continue;
1271
1275
  }
1272
- const emoji = this.core.bot.client.application?.emojis.cache.find((e) => e.name === emojiName);
1273
- if (emoji) {
1274
- emojiStorage[key] = `<${emoji.identifier}>`;
1275
- foundCount++;
1276
- this.logger.debug(`${chalk3__default.default.bold.green("Found")}: ${chalk3__default.default.magenta.bold(emojiName)} (${emoji.id})`);
1277
- return;
1276
+ if (typeof value === "string") {
1277
+ foundCount += this.handleString(key, value);
1278
+ continue;
1278
1279
  }
1279
- emojiStorage[key] = emojiName;
1280
- this.logger.warn(`${chalk3__default.default.bold.yellow("Missing")}: ${chalk3__default.default.magenta.bold(emojiName)} (using configured value)`);
1281
- });
1280
+ this.logger.warn(`${chalk3__default.default.bold.yellow("Invalid")}: ${chalk3__default.default.magenta.bold(String(key))} (expected string or [string, string])`);
1281
+ }
1282
1282
  this.logger.info(`${chalk3__default.default.bold.green("Loaded")}: ${chalk3__default.default.magenta.bold(foundCount)} emoji(s)`);
1283
1283
  }
1284
+ /**
1285
+ * Handle emoji config values in tuple form: [emojiName, guildId]
1286
+ * Returns 1 when the emoji was found and stored as an emoji object, otherwise 0.
1287
+ */
1288
+ handleTuple(key, value) {
1289
+ const [emojiName, guildId] = value;
1290
+ const guild = this.core.bot.client.guilds.cache.get(guildId);
1291
+ if (!guild) {
1292
+ emojiStorage[key] = emojiName;
1293
+ this.logger.warn(`${chalk3__default.default.bold.yellow("Missing")}: ${chalk3__default.default.magenta.bold(emojiName)} in guild ${chalk3__default.default.gray(guildId)} (guild not in cache or not found, using provided string)`);
1294
+ return 0;
1295
+ }
1296
+ const guildEmoji = guild.emojis.cache.find((e) => e.name === emojiName);
1297
+ if (guildEmoji) {
1298
+ emojiStorage[key] = guildEmoji;
1299
+ this.logger.debug(`${chalk3__default.default.bold.green("Found")}: ${chalk3__default.default.magenta.bold(emojiName)} (${guildEmoji.id}) in guild ${chalk3__default.default.gray(guildId)}`);
1300
+ return 1;
1301
+ }
1302
+ emojiStorage[key] = emojiName;
1303
+ this.logger.warn(`${chalk3__default.default.bold.yellow("Missing")}: ${chalk3__default.default.magenta.bold(emojiName)} in guild ${chalk3__default.default.magenta.bold(guildId)} (using provided string)`);
1304
+ return 0;
1305
+ }
1306
+ /**
1307
+ * Handle emoji config values provided as a simple string (application emoji lookup).
1308
+ * Returns 1 when the emoji was found and stored as an emoji object, otherwise 0.
1309
+ */
1310
+ handleString(key, emojiName) {
1311
+ const appEmoji = this.core.bot.client.application?.emojis.cache.find((e) => e.name === emojiName);
1312
+ if (appEmoji) {
1313
+ emojiStorage[key] = appEmoji;
1314
+ this.logger.debug(`${chalk3__default.default.bold.green("Found")}: ${chalk3__default.default.magenta.bold(emojiName)} (${appEmoji.id})`);
1315
+ return 1;
1316
+ }
1317
+ emojiStorage[key] = emojiName;
1318
+ this.logger.warn(`${chalk3__default.default.bold.yellow("Missing")}: ${chalk3__default.default.magenta.bold(emojiName)} (using provided string)`);
1319
+ return 0;
1320
+ }
1284
1321
  clearEmojis() {
1285
1322
  for (const key of Object.keys(emojiStorage)) Reflect.deleteProperty(emojiStorage, key);
1286
1323
  }
@@ -1378,1106 +1415,1041 @@ _ts_decorate4([
1378
1415
  _ts_metadata4("design:type", String)
1379
1416
  ], Bot.prototype, "botToken", void 0);
1380
1417
 
1381
- // src/effects/decorators/RegisterEffect.ts
1382
- var EffectMetadataKey = Symbol("effect:metadata");
1383
- function RegisterEffect(effect, options) {
1384
- return function(constructor) {
1385
- const meta = {
1386
- effect,
1387
- frequency: options?.frequency
1388
- };
1389
- Reflect.defineMetadata(EffectMetadataKey, meta, constructor);
1418
+ // src/bot/decorators/Checkable.ts
1419
+ function Checkable(ctor) {
1420
+ return class extends ctor {
1421
+ static name = ctor.name;
1422
+ checkable = true;
1390
1423
  };
1391
1424
  }
1392
- __name(RegisterEffect, "RegisterEffect");
1393
-
1394
- // src/effects/EffectsHandler.ts
1395
- var EffectsHandler = class {
1396
- static {
1397
- __name(this, "EffectsHandler");
1398
- }
1399
- data;
1400
- core;
1401
- /**
1402
- * Creates a new effects handler instance.
1403
- *
1404
- * @param data - The effect event data
1405
- * @param core - The core framework instance
1406
- */
1407
- constructor(data, core) {
1408
- this.data = data;
1409
- this.core = core;
1410
- this.data = data;
1411
- this.core = core;
1412
- }
1413
- };
1414
-
1415
- // src/effects/bases/WebhookLog.ts
1416
- var WebhookLog = class extends EffectsHandler {
1417
- static {
1418
- __name(this, "WebhookLog");
1419
- }
1420
- constructor(data, core) {
1421
- super(data, core);
1422
- }
1423
- };
1424
-
1425
- // src/effects/default/UnknownException.ts
1426
- function _ts_decorate5(decorators, target, key, desc) {
1427
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1428
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1429
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1430
- return c > 3 && r && Object.defineProperty(target, key, r), r;
1431
- }
1432
- __name(_ts_decorate5, "_ts_decorate");
1433
- function _ts_metadata5(k, v) {
1434
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1435
- }
1436
- __name(_ts_metadata5, "_ts_metadata");
1437
- function webhookUrlValidator(raw, _fallback) {
1438
- if (raw === null) {
1439
- throw new services.SeedcordError(services.SeedcordErrorCode.ConfigUnknownExceptionWebhookMissing);
1425
+ __name(Checkable, "Checkable");
1426
+ var resolveFactory = /* @__PURE__ */ __name(async (input, ctx) => typeof input === "function" ? await input(ctx) : input, "resolveFactory");
1427
+ var isV2 = /* @__PURE__ */ __name((opts) => "container" in opts, "isV2");
1428
+ var usesResolver = /* @__PURE__ */ __name((opts) => "resolveDecision" in opts, "usesResolver");
1429
+ var usesCustomIds = /* @__PURE__ */ __name((opts) => "confirmCustomIds" in opts, "usesCustomIds");
1430
+ var isMessageComponentIx = /* @__PURE__ */ __name((ix) => "deferUpdate" in ix && "customId" in ix, "isMessageComponentIx");
1431
+ var buildPrompt = /* @__PURE__ */ __name(async (opts, ctx) => {
1432
+ if (isV2(opts)) {
1433
+ const container = await resolveFactory(opts.container, ctx);
1434
+ return {
1435
+ flags: "IsComponentsV2",
1436
+ components: [
1437
+ container.component
1438
+ ]
1439
+ };
1440
1440
  }
1441
- if (typeof raw !== "string") {
1442
- throw new services.SeedcordError(services.SeedcordErrorCode.ConfigUnknownExceptionWebhookInvalid);
1441
+ const prompt = await resolveFactory(opts.prompt, ctx);
1442
+ const rows = await resolveFactory(opts.rows, ctx);
1443
+ const components = [
1444
+ ...rows
1445
+ ];
1446
+ const payload = {
1447
+ components
1448
+ };
1449
+ if (typeof prompt === "string") payload.content = prompt;
1450
+ else payload.embeds = [
1451
+ prompt.component
1452
+ ];
1453
+ return payload;
1454
+ }, "buildPrompt");
1455
+ var clearedPayload = /* @__PURE__ */ __name((opts) => isV2(opts) ? {
1456
+ flags: "IsComponentsV2",
1457
+ components: []
1458
+ } : {
1459
+ components: []
1460
+ }, "clearedPayload");
1461
+ var decide = /* @__PURE__ */ __name(async (opts, i) => {
1462
+ if (usesResolver(opts)) return opts.resolveDecision(i);
1463
+ if (usesCustomIds(opts)) {
1464
+ const { confirmCustomIds, cancelCustomIds = [] } = opts;
1465
+ if (confirmCustomIds.includes(i.customId)) return true;
1466
+ if (cancelCustomIds.includes(i.customId)) return false;
1443
1467
  }
1444
- const value = raw.trim();
1445
- if (value === "") {
1446
- throw new services.SeedcordError(services.SeedcordErrorCode.ConfigUnknownExceptionWebhookMissing);
1468
+ return false;
1469
+ }, "decide");
1470
+ var shouldDefer = /* @__PURE__ */ __name((ix, opts, isSlash, isContext) => {
1471
+ if (ix.deferred) return false;
1472
+ if (opts.defer === false) return false;
1473
+ return isSlash || isContext;
1474
+ }, "shouldDefer");
1475
+ var maybeDefer = /* @__PURE__ */ __name(async (ix, opts, isSlash, isContext) => {
1476
+ if (!shouldDefer(ix, opts, isSlash, isContext)) return;
1477
+ if (isSlash || isContext) {
1478
+ const { ephemeral = true } = opts;
1479
+ const flags = ephemeral ? {
1480
+ flags: "Ephemeral"
1481
+ } : void 0;
1482
+ if (flags) await ix.deferReply(flags);
1483
+ else await ix.deferReply();
1484
+ } else if (isMessageComponentIx(ix)) {
1485
+ await ix.deferUpdate();
1486
+ } else {
1487
+ await ix.deferReply().catch(() => void 0);
1447
1488
  }
1448
- const pattern = String.raw`^https?:\/\/(?:canary\.|ptb\.)?discord(?:app)?\.com\/api\/webhooks\/\d+\/[\w$-]+$`;
1449
- const discordWebhookRegex = new RegExp(pattern);
1450
- if (!URL.canParse(value) || !discordWebhookRegex.test(value)) {
1451
- throw new services.SeedcordError(services.SeedcordErrorCode.ConfigUnknownExceptionWebhookInvalid);
1489
+ }, "maybeDefer");
1490
+ var sendPrompt = /* @__PURE__ */ __name(async (ix, payload, ephemeral, isSlash, isContext) => {
1491
+ if (isSlash || isContext) {
1492
+ if (ix.deferred) return ix.editReply(payload);
1493
+ const reply = {
1494
+ ...payload,
1495
+ ...ephemeral ? {
1496
+ flags: "Ephemeral"
1497
+ } : {}
1498
+ };
1499
+ await ix.reply(reply);
1500
+ return ix.fetchReply();
1452
1501
  }
1453
- return value;
1454
- }
1455
- __name(webhookUrlValidator, "webhookUrlValidator");
1456
- exports.UnknownException = class _UnknownException extends WebhookLog {
1457
- static {
1458
- __name(this, "UnknownException");
1502
+ const follow = {
1503
+ ...payload,
1504
+ ...ephemeral ? {
1505
+ flags: "Ephemeral"
1506
+ } : {}
1507
+ };
1508
+ return ix.followUp(follow);
1509
+ }, "sendPrompt");
1510
+ var awaitComponent = /* @__PURE__ */ __name(async (msg, original, opts) => {
1511
+ const componentType = opts.componentType ?? discord_js.ComponentType.Button;
1512
+ const timeoutMs = opts.timeoutMs ?? 1e4;
1513
+ try {
1514
+ const button = await msg.awaitMessageComponent({
1515
+ componentType,
1516
+ time: timeoutMs,
1517
+ filter: /* @__PURE__ */ __name((c) => {
1518
+ if (c.user.id !== original.user.id) return false;
1519
+ if (usesCustomIds(opts)) {
1520
+ const { confirmCustomIds, cancelCustomIds = [] } = opts;
1521
+ return confirmCustomIds.includes(c.customId) || cancelCustomIds.includes(c.customId);
1522
+ }
1523
+ return true;
1524
+ }, "filter")
1525
+ });
1526
+ return {
1527
+ button,
1528
+ timedOut: false
1529
+ };
1530
+ } catch {
1531
+ return {
1532
+ button: null,
1533
+ timedOut: true
1534
+ };
1459
1535
  }
1460
- static logger = new services.Logger("Effect: UnknownException");
1461
- webhook = new discord_js.WebhookClient({
1462
- url: _UnknownException.unknownExceptionWebhookUrl
1463
- });
1464
- async execute() {
1465
- const metadataFile = this.prepareMetadataFile();
1536
+ }, "awaitComponent");
1537
+ var clearUi = /* @__PURE__ */ __name(async (ix, msg, opts, isSlash) => {
1538
+ if (!isSlash) {
1466
1539
  try {
1467
- await this.webhook.send({
1468
- flags: "IsComponentsV2",
1469
- withComponents: true,
1470
- username: "Unknown Exception",
1471
- avatarURL: "https://cdn.discordapp.com/attachments/1351446034827579466/1351446912947191830/warning-2.png",
1472
- components: [
1473
- new UnhandledErrorContainer(this.data).component
1474
- ],
1475
- files: metadataFile ? [
1476
- metadataFile
1477
- ] : []
1478
- });
1479
- } catch (error) {
1480
- _UnknownException.logger.error("Failed to send unknown exception webhook", error);
1540
+ await msg.edit(clearedPayload(opts));
1541
+ } catch {
1542
+ await ix.deleteReply(msg).catch(() => void 0);
1481
1543
  }
1544
+ return;
1482
1545
  }
1483
- prepareMetadataFile() {
1484
- const { metadata } = this.data;
1485
- if (!metadata) return null;
1486
- const content = utils.filterCirculars(metadata);
1487
- return new discord_js.AttachmentBuilder(Buffer.from(JSON.stringify(content, void 0, 2), "utf-8"), {
1488
- name: "metadata.json",
1489
- description: "Metadata associated with the error"
1490
- });
1546
+ await ix.editReply(clearedPayload(opts)).catch(() => void 0);
1547
+ }, "clearUi");
1548
+ var normalizeClassicOutcome = /* @__PURE__ */ __name((payload) => ({
1549
+ ...payload,
1550
+ components: payload.components ?? []
1551
+ }), "normalizeClassicOutcome");
1552
+ var outcomeReplacement = /* @__PURE__ */ __name(async (opts, ctx, confirmed, timedOut) => {
1553
+ const outcomes = opts.outcomeUi;
1554
+ if (isV2(opts)) {
1555
+ const v2Outcomes = outcomes;
1556
+ if (timedOut) return await resolveFactory(v2Outcomes.onTimeout, ctx);
1557
+ if (!confirmed) return await resolveFactory(v2Outcomes.onCancel, ctx);
1558
+ if (v2Outcomes.onConfirm) return await resolveFactory(v2Outcomes.onConfirm, ctx);
1559
+ return null;
1491
1560
  }
1492
- };
1493
- _ts_decorate5([
1494
- envapt.Envapt("UNKNOWN_EXCEPTION_WEBHOOK_URL", {
1495
- converter: /* @__PURE__ */ __name((raw, fallback) => webhookUrlValidator(raw), "converter")
1496
- }),
1497
- _ts_metadata5("design:type", String)
1498
- ], exports.UnknownException, "unknownExceptionWebhookUrl", void 0);
1499
- exports.UnknownException = _ts_decorate5([
1500
- RegisterEffect("unknownException")
1501
- ], exports.UnknownException);
1502
- var DefaultSeparator = class DefaultSeparator2 extends BuilderComponent {
1503
- static {
1504
- __name(this, "DefaultSeparator");
1505
- }
1506
- constructor() {
1507
- super("separator");
1508
- this.instance.setSpacing(discord_js.SeparatorSpacingSize.Small).setDivider(true);
1561
+ const classicOutcomes = outcomes;
1562
+ if (timedOut) return normalizeClassicOutcome(await resolveFactory(classicOutcomes.onTimeout, ctx));
1563
+ if (!confirmed) return normalizeClassicOutcome(await resolveFactory(classicOutcomes.onCancel, ctx));
1564
+ if (classicOutcomes.onConfirm) {
1565
+ return normalizeClassicOutcome(await resolveFactory(classicOutcomes.onConfirm, ctx));
1509
1566
  }
1510
- };
1511
- var UnhandledErrorContainer = class UnhandledErrorContainer2 extends BuilderComponent {
1567
+ return null;
1568
+ }, "outcomeReplacement");
1569
+ function Confirmable(question, options) {
1570
+ return function(_target, _propertyKey, descriptor) {
1571
+ const original = descriptor.value;
1572
+ descriptor.value = async function(...args) {
1573
+ if (!original) throw new services.SeedcordError(services.SeedcordErrorCode.DecoratorMethodNotFound);
1574
+ const ix = this.getEvent();
1575
+ const isSlash = ix.isChatInputCommand();
1576
+ const isContext = ix.isContextMenuCommand();
1577
+ const { ephemeral = true } = options;
1578
+ await maybeDefer(ix, options, isSlash, isContext);
1579
+ const q = typeof question === "function" ? await question.apply(this) : question;
1580
+ const ctx = {
1581
+ handler: this,
1582
+ interaction: ix,
1583
+ question: q
1584
+ };
1585
+ const prompt = await buildPrompt(options, ctx);
1586
+ const promptMsg = await sendPrompt(ix, prompt, ephemeral, isSlash, isContext);
1587
+ const { button, timedOut } = await awaitComponent(promptMsg, ix, options);
1588
+ let confirmed = false;
1589
+ if (button) {
1590
+ await button.deferUpdate().catch(() => void 0);
1591
+ confirmed = await decide(options, button);
1592
+ }
1593
+ const replacement = await outcomeReplacement(options, ctx, confirmed, timedOut);
1594
+ if (replacement) {
1595
+ if (isSlash || isContext) {
1596
+ await ix.editReply(replacement).catch(() => void 0);
1597
+ } else {
1598
+ await promptMsg.edit(replacement).catch(async () => {
1599
+ await ix.deleteReply(promptMsg).catch(() => void 0);
1600
+ await ix.followUp(replacement).catch(() => void 0);
1601
+ });
1602
+ }
1603
+ } else {
1604
+ await clearUi(ix, promptMsg, options, isSlash);
1605
+ }
1606
+ if (options.onResolved) {
1607
+ await options.onResolved({
1608
+ confirmed,
1609
+ timedOut,
1610
+ handler: this,
1611
+ interaction: ix,
1612
+ question: q,
1613
+ ...button ? {
1614
+ button
1615
+ } : {}
1616
+ });
1617
+ }
1618
+ if (confirmed) {
1619
+ await original.apply(this, args);
1620
+ }
1621
+ };
1622
+ return descriptor;
1623
+ };
1624
+ }
1625
+ __name(Confirmable, "Confirmable");
1626
+ function EventCatchable(options) {
1627
+ return function(_target, _prop, descriptor) {
1628
+ const log = options?.log ?? false;
1629
+ const silent = options?.silent ?? false;
1630
+ const original = descriptor.value;
1631
+ descriptor.value = async function(...args) {
1632
+ if (!original) throw new services.SeedcordError(services.SeedcordErrorCode.DecoratorMethodNotFound);
1633
+ try {
1634
+ await original.apply(this, args);
1635
+ } catch (err) {
1636
+ if (!(err instanceof Error)) throw err;
1637
+ this.setErrored();
1638
+ if (log) console.error(err);
1639
+ const eventArgs = Array.isArray(this.getEvent()) ? this.getEvent() : [
1640
+ this.getEvent()
1641
+ ];
1642
+ const msg = eventArgs.find((x) => x instanceof discord_js.Message);
1643
+ const { response } = extractErrorResponse(err, this.core, msg?.guild ?? null, msg?.author ?? null, eventArgs);
1644
+ if (typeof silent === "boolean" && silent) return;
1645
+ if (typeof silent !== "boolean" && silent.some((errorType) => err instanceof errorType)) {
1646
+ return;
1647
+ }
1648
+ if (!msg) return;
1649
+ await msg.reply({
1650
+ embeds: [
1651
+ response
1652
+ ],
1653
+ components: []
1654
+ });
1655
+ }
1656
+ };
1657
+ };
1658
+ }
1659
+ __name(EventCatchable, "EventCatchable");
1660
+
1661
+ // src/bot/defaults/errors/Channels.ts
1662
+ var ChannelNotFoundError = class extends CustomError {
1512
1663
  static {
1513
- __name(this, "UnhandledErrorContainer");
1514
- }
1515
- constructor(data) {
1516
- super("container");
1517
- const { uuid, error, guild, user, metadata } = data;
1518
- this.instance.addTextDisplayComponents((text) => text.setContent(`### An unknown exception was thrown
1519
- **Guild ID:** \`${guild?.id ?? "Not used in a guild"}\`
1520
- **Guild Name:** ${guild?.name ?? "Not used in a guild"}
1521
- **User ID:** \`${user?.id ?? "Missing user info"}\`
1522
- **Username:** ${user?.username ?? "Missing user info"}
1523
- `)).addSeparatorComponents(new DefaultSeparator().component).addTextDisplayComponents((text) => text.setContent(`### UUID \`${uuid}\`
1524
- \`\`\`${error.stack}\`\`\``));
1525
- this.addTimestampsIfAvailable(error);
1526
- this.addMetadataIfAvailable(metadata);
1527
- }
1528
- addTimestampsIfAvailable(error) {
1529
- if (!(error instanceof discord_js.DiscordAPIError)) return;
1530
- const now = Date.now();
1531
- const snowflake = error.url.match(/\/interactions\/(\d+)\//)?.[1];
1532
- if (!snowflake) return void 0;
1533
- const interactionTs = Number(discord_js.SnowflakeUtil.deconstruct(snowflake).timestamp);
1534
- const diff = now - interactionTs;
1535
- const seconds = Math.floor(diff / 1e3);
1536
- const millis = diff % 1e3;
1537
- this.instance.addSeparatorComponents(new DefaultSeparator().component).addTextDisplayComponents((text) => text.setContent(`### Timestamps
1538
- - **\`Interaction sent\` :** ${new Date(interactionTs).toISOString()} (${interactionTs})
1539
- - **\`Error logged \` :** ${new Date(now).toISOString()} (${now})
1540
- - **\`Offset \` :** ${seconds}s ${millis}ms`));
1664
+ __name(this, "ChannelNotFoundError");
1541
1665
  }
1542
- addMetadataIfAvailable(metadata) {
1543
- if (!metadata) return;
1544
- this.instance.addSeparatorComponents(new DefaultSeparator().component).addTextDisplayComponents((text) => text.setContent("### Metadata")).addFileComponents((file) => file.setURL("attachment://metadata.json"));
1666
+ channelId;
1667
+ /**
1668
+ * Creates a new ChannelNotFoundError.
1669
+ *
1670
+ * @param message - The error message
1671
+ * @param channelId - The ID of the channel that could not be found
1672
+ */
1673
+ constructor(message, channelId) {
1674
+ super(message), this.channelId = channelId;
1675
+ this.response.setDescription(`Channel with ID \`${this.channelId}\` not found.`);
1545
1676
  }
1546
1677
  };
1547
- var EffectsEmitter = class {
1678
+ var CannotSendEmbedsError = class extends CustomError {
1548
1679
  static {
1549
- __name(this, "EffectsEmitter");
1680
+ __name(this, "CannotSendEmbedsError");
1550
1681
  }
1551
- emitter = new events.EventEmitter();
1682
+ channelId;
1552
1683
  /**
1553
- * Registers a listener for the specified side effect.
1684
+ * Creates a new CannotSendEmbedsError.
1554
1685
  *
1555
- * @typeParam KeyOfEffects - The side effect name type
1556
- * @param event - The side effect name to listen for
1557
- * @param listener - Function to call when the event is emitted
1558
- * @returns This EffectsEmitter instance for chaining
1686
+ * @param message - The error message
1687
+ * @param channelId - The ID of the channel where embeds cannot be sent
1559
1688
  */
1560
- on(event, listener) {
1561
- this.emitter.on(event, listener);
1562
- return this;
1689
+ constructor(message, channelId) {
1690
+ super(message), this.channelId = channelId;
1691
+ this.response.setDescription(`Cannot send embeds in <#${this.channelId}>.
1692
+
1693
+ Please ensure I have the following permissions:
1694
+ \u2022 View Channel
1695
+ \u2022 Send Messages
1696
+ \u2022 Embed Links
1697
+ \u2022 Attach Files
1698
+ \u2022 Read Message History
1699
+ \u2022 Use External Emojis
1700
+ `);
1701
+ }
1702
+ };
1703
+ var CouldNotFindChannel = class extends CustomError {
1704
+ static {
1705
+ __name(this, "CouldNotFindChannel");
1563
1706
  }
1707
+ channelId;
1564
1708
  /**
1565
- * Registers a one-time listener for the specified side effect.
1709
+ * Creates a new CouldNotFindChannel error.
1566
1710
  *
1567
- * @typeParam KeyOfEffects - The side effect name type
1568
- * @param event - The side effect name to listen for once
1569
- * @param listener - Function to call when the event is emitted
1570
- * @returns This EffectsEmitter instance for chaining
1711
+ * @param message - The error message
1712
+ * @param channelId - The ID of the channel that could not be found
1571
1713
  */
1572
- once(event, listener) {
1573
- this.emitter.once(event, listener);
1574
- return this;
1714
+ constructor(message, channelId) {
1715
+ super(message), this.channelId = channelId;
1716
+ this.response.setDescription(`Could not find channel with ID \`${this.channelId}\`. It could also be that the channel is not a text channel.`);
1717
+ }
1718
+ };
1719
+ var ChannelNotTextChannel = class extends CustomError {
1720
+ static {
1721
+ __name(this, "ChannelNotTextChannel");
1575
1722
  }
1723
+ channelId;
1576
1724
  /**
1577
- * Emits a side effect with the provided data.
1725
+ * Creates a new ChannelNotTextChannel error.
1578
1726
  *
1579
- * @typeParam KeyOfEffects - The side effect name type
1580
- * @param event - The side effect name to emit
1581
- * @param data - The data to pass to registered listeners
1582
- * @returns True if the event had listeners, false otherwise
1727
+ * @param message - The error message
1728
+ * @param channelId - The ID of the channel that is not a text channel
1583
1729
  */
1584
- emit(event, data) {
1585
- return this.emitter.emit(event, data);
1730
+ constructor(message, channelId) {
1731
+ super(message), this.channelId = channelId;
1732
+ this.response.setDescription(`Channel with ID \`${this.channelId}\` is not a text channel.`);
1586
1733
  }
1587
1734
  };
1588
1735
 
1589
- // src/effects/EffectsRegistry.ts
1590
- var EffectsRegistry = class extends Plugin {
1591
- static {
1592
- __name(this, "EffectsRegistry");
1593
- }
1594
- core;
1595
- logger = new services.Logger("Effects");
1596
- isInitialized = false;
1597
- effectsMap = new discord_js.Collection();
1598
- emitter = new EffectsEmitter();
1599
- constructor(core) {
1600
- super(core), this.core = core;
1736
+ // src/bot/utilities/channels/fetchText.ts
1737
+ async function fetchText(client, channelId) {
1738
+ if (channelId instanceof discord_js.TextChannel) {
1739
+ return channelId;
1601
1740
  }
1602
- async init() {
1603
- if (this.isInitialized) return;
1604
- this.isInitialized = true;
1605
- const effectsDir = this.core.config.effects.path;
1606
- this.logger.info(chalk3__default.default.bold(effectsDir));
1607
- this.registerEffect(exports.UnknownException, {
1608
- effect: "unknownException",
1609
- frequency: "on"
1610
- });
1611
- await this.loadEffects(effectsDir);
1612
- this.attachEffects();
1613
- const totalEffects = Array.from(this.effectsMap.values()).reduce((acc, handlers) => acc + handlers.length, 0);
1614
- this.logger.info(`${chalk3__default.default.bold.green("Loaded")}: ${chalk3__default.default.bold.magenta(totalEffects)} side effects`);
1741
+ let channel = client.channels.cache.get(channelId);
1742
+ if (!channel) {
1743
+ try {
1744
+ channel = await client.channels.fetch(channelId);
1745
+ } catch {
1746
+ throw new CouldNotFindChannel("Channel not found or not a text channel", channelId);
1747
+ }
1615
1748
  }
1616
- async loadEffects(dir) {
1617
- await utils.traverseDirectory(dir, (_fullPath, relativePath, imported) => {
1618
- for (const exportName of Object.keys(imported)) {
1619
- const val = imported[exportName];
1620
- if (this.isEffectHandler(val)) {
1621
- const meta = Reflect.getMetadata(EffectMetadataKey, val);
1622
- this.registerEffect(val, meta);
1623
- this.logger.info(`${chalk3__default.default.italic("Registered")} ${chalk3__default.default.bold.yellow(val.name)} from ${chalk3__default.default.gray(relativePath)}`);
1624
- }
1625
- }
1626
- }, this.logger);
1749
+ if (channel?.isTextBased()) {
1750
+ return channel;
1627
1751
  }
1628
- registerEffect(handler, options) {
1629
- let handlers = this.effectsMap.get(options.effect);
1630
- if (!handlers) {
1631
- handlers = [];
1632
- this.effectsMap.set(options.effect, handlers);
1752
+ throw new CouldNotFindChannel("Channel not found or not a text channel", channelId);
1753
+ }
1754
+ __name(fetchText, "fetchText");
1755
+
1756
+ // src/bot/utilities/channels/sendInText.ts
1757
+ async function sendInText(client, channelId, message) {
1758
+ const channel = await fetchText(client, channelId);
1759
+ return await channel.send(message);
1760
+ }
1761
+ __name(sendInText, "sendInText");
1762
+ function throwCustomError(error, message, CustomError2) {
1763
+ const uuid = crypto.randomUUID();
1764
+ services.Logger.Error("Throwing Custom Error", error.name);
1765
+ if (typeof CustomError2 === typeof DatabaseError) {
1766
+ const errorMessage = error instanceof Error ? error.message : message;
1767
+ throw new CustomError2(errorMessage, uuid);
1768
+ } else {
1769
+ if (error instanceof Error) {
1770
+ throw new CustomError2(`${message}: ${error.message ? error.message : error.toString()}`);
1771
+ } else {
1772
+ throw new CustomError2(message);
1633
1773
  }
1634
- handlers.push({
1635
- ctor: handler,
1636
- frequency: options.frequency ?? "on"
1637
- });
1638
- }
1639
- isEffectHandler(obj) {
1640
- if (typeof obj !== "function") return false;
1641
- return obj.prototype instanceof EffectsHandler && Reflect.hasMetadata(EffectMetadataKey, obj);
1642
1774
  }
1643
- attachEffects() {
1644
- for (const [effectName, handlerEntries] of this.effectsMap) {
1645
- for (const entry of handlerEntries) {
1646
- const register = entry.frequency === "once" ? this.emitter.once.bind(this.emitter) : this.emitter.on.bind(this.emitter);
1647
- register(effectName, (data) => {
1648
- try {
1649
- const instance = new entry.ctor(data, this.core);
1650
- void instance.execute();
1651
- } catch (err) {
1652
- this.logger.error(`Error in side effect ${String(effectName)} handler ${entry.ctor.name}:`, err);
1653
- }
1654
- });
1655
- }
1775
+ }
1776
+ __name(throwCustomError, "throwCustomError");
1777
+
1778
+ // src/bot/utilities/messages/attemptSendDM.ts
1779
+ async function attemptSendDM(user, content) {
1780
+ const payload = {
1781
+ ...content.content !== void 0 && {
1782
+ content: content.content
1783
+ },
1784
+ ...content.embeds !== void 0 && {
1785
+ embeds: [
1786
+ ...content.embeds
1787
+ ]
1788
+ },
1789
+ ...content.components !== void 0 && {
1790
+ components: [
1791
+ ...content.components
1792
+ ]
1656
1793
  }
1794
+ };
1795
+ try {
1796
+ return await user.send(payload);
1797
+ } catch {
1798
+ return null;
1657
1799
  }
1658
- emit(event, data) {
1659
- return this.emitter.emit(event, data);
1800
+ }
1801
+ __name(attemptSendDM, "attemptSendDM");
1802
+ var RoleHigherThanMe = class extends CustomError {
1803
+ static {
1804
+ __name(this, "RoleHigherThanMe");
1660
1805
  }
1661
- };
1806
+ role;
1807
+ botRole;
1808
+ /**
1809
+ * Creates a new RoleHigherThanMe error.
1810
+ *
1811
+ * @param message - The error message
1812
+ */
1813
+ constructor(message, role, botRole) {
1814
+ super(message), this.role = role, this.botRole = botRole;
1815
+ this.response.setDescription(`I cannot assign a role that is higher than me.
1662
1816
 
1663
- // src/Seedcord.ts
1664
- var Seedcord = class _Seedcord extends Pluggable {
1817
+ The role <@&${this.role.id}> is higher than my role <@&${this.botRole.id}> in the hierarchy.`);
1818
+ }
1819
+ };
1820
+ var CannotAssignBotRole = class extends CustomError {
1665
1821
  static {
1666
- __name(this, "Seedcord");
1822
+ __name(this, "CannotAssignBotRole");
1667
1823
  }
1668
- config;
1669
- static isInstantiated = false;
1670
- /** @see {@link CoordinatedShutdown} */
1671
- shutdown;
1672
- /** @see {@link CoordinatedStartup} */
1673
- startup;
1674
- /** @see {@link EffectsRegistry} */
1675
- effects;
1676
- /** @see {@link Bot} */
1677
- bot;
1678
- /** @see {@link HealthCheck} */
1679
- healthCheck;
1680
1824
  /**
1681
- * Creates a new Seedcord instance
1825
+ * Creates a new CannotAssignBotRole error.
1682
1826
  *
1683
- * @param config - Bot configuration including paths and Discord client options
1684
- * @throws An {@link SeedcordError} When attempting to create multiple instances (singleton)
1827
+ * @param message - The error message
1685
1828
  */
1686
- constructor(config) {
1687
- const shutdown = new services.CoordinatedShutdown();
1688
- const startup = new services.CoordinatedStartup();
1689
- super(shutdown, startup), this.config = config;
1690
- this.shutdown = shutdown;
1691
- this.startup = startup;
1692
- if (_Seedcord.isInstantiated) {
1693
- throw new services.SeedcordError(services.SeedcordErrorCode.CoreSingletonViolation);
1694
- }
1695
- _Seedcord.isInstantiated = true;
1696
- this.effects = new EffectsRegistry(this);
1697
- this.bot = new Bot(this);
1698
- this.healthCheck = new services.HealthCheck(this.shutdown);
1699
- this.registerStartupTasks();
1829
+ constructor(message = "I cannot assign a managed role.") {
1830
+ super(message);
1831
+ this.response.setDescription("I cannot assign a managed role.");
1832
+ }
1833
+ };
1834
+ var RoleDoesNotExist = class extends CustomError {
1835
+ static {
1836
+ __name(this, "RoleDoesNotExist");
1700
1837
  }
1838
+ roleId;
1701
1839
  /**
1702
- * Registers default startup tasks
1703
- * @internal
1840
+ * Creates a new RoleDoesNotExist error.
1841
+ *
1842
+ * @param message - The error message
1843
+ * @param roleId - The ID of the role that doesn't exist
1704
1844
  */
1705
- registerStartupTasks() {
1706
- this.startup.addTask(services.StartupPhase.Configuration, "Effect Initialization", async () => {
1707
- this.effects.logger.info(chalk3__default.default.bold("Initializing"));
1708
- await this.effects.init();
1709
- this.effects.logger.info(chalk3__default.default.bold("Initialized"));
1710
- });
1711
- this.startup.addTask(services.StartupPhase.Instantiation, "Bot Initialization", async () => {
1712
- this.bot.logger.info(chalk3__default.default.bold("Initializing"));
1713
- await this.bot.init();
1714
- this.bot.logger.info(chalk3__default.default.bold("Initialized"));
1715
- });
1716
- this.startup.addTask(services.StartupPhase.Ready, "Health Check", async () => {
1717
- this.healthCheck.logger.info(chalk3__default.default.bold("Initializing"));
1718
- await this.healthCheck.init();
1719
- this.healthCheck.logger.info(chalk3__default.default.bold("Initialized"));
1720
- });
1845
+ constructor(message, roleId) {
1846
+ super(message), this.roleId = roleId;
1847
+ this.response.setDescription(`The role with ID \`${this.roleId}\` does not exist.`);
1848
+ }
1849
+ };
1850
+ var MissingPermissions = class extends CustomError {
1851
+ static {
1852
+ __name(this, "MissingPermissions");
1721
1853
  }
1854
+ where;
1855
+ missingPerms;
1722
1856
  /**
1723
- * Starts the bot and runs all initialization tasks
1857
+ * Creates a new MissingPermissions error.
1724
1858
  *
1725
- * @returns This Seedcord instance when fully initialized
1859
+ * @param message - The error message
1860
+ * @param missingPerms - Array of missing permission names
1861
+ * @param where - Location or subject where permissions are missing
1726
1862
  */
1727
- async start() {
1728
- await super.init();
1729
- return this;
1863
+ constructor(message, where, missingPerms) {
1864
+ super(message), this.where = where, this.missingPerms = missingPerms;
1865
+ const bullets = this.missingPerms.map((perm) => `\u2022 ${perm}`).join("\n");
1866
+ const mention = this.where instanceof discord_js.Role ? `<@&${this.where.id}>` : this.where instanceof discord_js.TextChannel ? `<#${this.where.id}>` : this.where instanceof discord_js.GuildMember ? `<@${this.where.id}>` : `\`${this.where.name}\``;
1867
+ const label = this.where instanceof discord_js.Role ? "role" : this.where instanceof discord_js.TextChannel ? "channel" : this.where instanceof discord_js.GuildMember ? "member" : "guild";
1868
+ this.response.setDescription(`The ${label} ${mention} is missing the following permission entries:
1869
+
1870
+ ${bullets}`);
1730
1871
  }
1731
1872
  };
1873
+ var HasDangerousPermissions = class extends CustomError {
1874
+ static {
1875
+ __name(this, "HasDangerousPermissions");
1876
+ }
1877
+ target;
1878
+ dangerousPerms;
1879
+ /**
1880
+ * Creates a new HasDangerousPermissions error.
1881
+ *
1882
+ * @param message - The error message
1883
+ * @param target - The subject that has the unwanted permissions
1884
+ * @param dangerousPerms - Array of dangerous permission names
1885
+ */
1886
+ constructor(message, target, dangerousPerms) {
1887
+ super(message), this.target = target, this.dangerousPerms = dangerousPerms;
1888
+ const bullets = this.dangerousPerms.map((perm) => `\u2022 ${perm}`).join("\n");
1889
+ const mention = this.target instanceof discord_js.Role ? `<@&${this.target.id}>` : this.target instanceof discord_js.TextChannel ? `<#${this.target.id}>` : this.target instanceof discord_js.GuildMember ? `<@${this.target.id}>` : `\`${this.target.name}\``;
1890
+ const label = this.target instanceof discord_js.Role ? "role" : this.target instanceof discord_js.TextChannel ? "channel" : this.target instanceof discord_js.GuildMember ? "member" : "guild";
1891
+ this.response.setDescription(`The ${label} ${mention} has the following permission entries that must not be enabled:
1732
1892
 
1733
- // src/bot/decorators/Checkable.ts
1734
- function Checkable(ctor) {
1735
- return class extends ctor {
1736
- static name = ctor.name;
1737
- checkable = true;
1738
- };
1893
+ ${bullets}`);
1894
+ }
1895
+ };
1896
+ var PermissionNames = new Map(Object.entries(discord_js.PermissionFlagsBits).map(([key, bit]) => [
1897
+ bit,
1898
+ utils.prettify(key)
1899
+ ]));
1900
+ function checkPermissions(a, b, c, d, e) {
1901
+ const opts = a instanceof discord_js.Role || a instanceof discord_js.GuildMember ? {
1902
+ for: a,
1903
+ in: b,
1904
+ scope: c,
1905
+ inverse: d ?? false,
1906
+ ...e ?? {}
1907
+ } : a;
1908
+ const { for: pFor, in: pIn, scope, inverse = false, missing: missingCtor, dangerous: dangerousCtor } = opts;
1909
+ const Missing = missingCtor ?? MissingPermissions;
1910
+ const Dangerous = dangerousCtor ?? HasDangerousPermissions;
1911
+ const perms = pIn instanceof discord_js.Guild ? pFor.permissions : pIn.permissionsFor(pFor, true);
1912
+ const names = /* @__PURE__ */ __name((bits) => bits.map((bit) => PermissionNames.get(bit) ?? String(bit)), "names");
1913
+ const present = scope.filter((bit) => perms.has(bit, true));
1914
+ const presentNames = names(present);
1915
+ if (inverse) {
1916
+ if (present.length > 0) {
1917
+ const base = `${pFor instanceof discord_js.Role ? "Role" : "Member"} has dangerous permissions in this ${pIn instanceof discord_js.Guild ? "guild" : "channel"}`;
1918
+ throw new Dangerous(base, pFor, presentNames);
1919
+ }
1920
+ return;
1921
+ }
1922
+ const missingBits = scope.filter((bit) => !perms.has(bit, true));
1923
+ if (missingBits.length > 0) {
1924
+ throw new Missing("Missing Any/All/No Permissions", pIn, names(missingBits));
1925
+ }
1739
1926
  }
1740
- __name(Checkable, "Checkable");
1741
- function EventCatchable(options) {
1742
- return function(_target, _prop, descriptor) {
1743
- const log = options?.log ?? false;
1744
- const silent = options?.silent ?? false;
1745
- const original = descriptor.value;
1746
- descriptor.value = async function(...args) {
1747
- if (!original) throw new services.SeedcordError(services.SeedcordErrorCode.DecoratorMethodNotFound);
1748
- try {
1749
- await original.apply(this, args);
1750
- } catch (err) {
1751
- if (!(err instanceof Error)) throw err;
1752
- this.setErrored();
1753
- if (log) console.error(err);
1754
- const eventArgs = Array.isArray(this.getEvent()) ? this.getEvent() : [
1755
- this.getEvent()
1756
- ];
1757
- const msg = eventArgs.find((x) => x instanceof discord_js.Message);
1758
- const { response } = extractErrorResponse(err, this.core, msg?.guild ?? null, msg?.author ?? null, eventArgs);
1759
- if (typeof silent === "boolean" && silent) return;
1760
- if (typeof silent !== "boolean" && silent.some((errorType) => err instanceof errorType)) {
1761
- return;
1762
- }
1763
- if (!msg) return;
1764
- await msg.reply({
1765
- embeds: [
1766
- response
1767
- ],
1768
- components: []
1769
- });
1770
- }
1771
- };
1772
- };
1927
+ __name(checkPermissions, "checkPermissions");
1928
+ var ensurePermissions = checkPermissions;
1929
+
1930
+ // src/bot/utilities/permissions/checkBotPermissions.ts
1931
+ function checkBotPermissions(target, scope, inverse = false, errors) {
1932
+ if (target instanceof discord_js.Guild) {
1933
+ const me2 = target.members.me;
1934
+ if (!me2) {
1935
+ const names = scope.map((bit) => PermissionNames.get(bit) ?? String(bit));
1936
+ const Missing = errors?.missing ?? MissingPermissions;
1937
+ throw new Missing("Missing Permissions", target, names);
1938
+ }
1939
+ checkPermissions(me2, target, scope, inverse, errors);
1940
+ return;
1941
+ }
1942
+ const me = target.guild.members.me;
1943
+ if (!me) {
1944
+ const names = scope.map((bit) => PermissionNames.get(bit) ?? String(bit));
1945
+ const Missing = errors?.missing ?? MissingPermissions;
1946
+ services.Logger.Warn("checkBotPermissions", `Bot member is unavailable in guild ${target.guild.id} while checking permissions in channel ${target.id}`);
1947
+ throw new Missing("Missing Permissions", target, names);
1948
+ }
1949
+ checkPermissions(me, target, scope, inverse, errors);
1773
1950
  }
1774
- __name(EventCatchable, "EventCatchable");
1775
- var resolveFactory = /* @__PURE__ */ __name(async (input, ctx) => typeof input === "function" ? await input(ctx) : input, "resolveFactory");
1776
- var isV2 = /* @__PURE__ */ __name((opts) => "container" in opts, "isV2");
1777
- var usesResolver = /* @__PURE__ */ __name((opts) => "resolveDecision" in opts, "usesResolver");
1778
- var usesCustomIds = /* @__PURE__ */ __name((opts) => "confirmCustomIds" in opts, "usesCustomIds");
1779
- var isMessageComponentIx = /* @__PURE__ */ __name((ix) => "deferUpdate" in ix && "customId" in ix, "isMessageComponentIx");
1780
- var buildPrompt = /* @__PURE__ */ __name(async (opts, ctx) => {
1781
- if (isV2(opts)) {
1782
- const container = await resolveFactory(opts.container, ctx);
1783
- return {
1784
- flags: "IsComponentsV2",
1785
- components: [
1786
- container.component
1787
- ]
1788
- };
1951
+ __name(checkBotPermissions, "checkBotPermissions");
1952
+ var ensureBotPermissions = checkBotPermissions;
1953
+ function getBotRole(guild) {
1954
+ const botRole = guild.roles.botRoleFor(guild.client.user);
1955
+ if (!botRole) {
1956
+ throw new services.SeedcordError(services.SeedcordErrorCode.CoreBotRoleMissing, [
1957
+ guild.id
1958
+ ]);
1789
1959
  }
1790
- const prompt = await resolveFactory(opts.prompt, ctx);
1791
- const rows = await resolveFactory(opts.rows, ctx);
1792
- const components = [
1793
- ...rows
1794
- ];
1795
- const payload = {
1796
- components
1797
- };
1798
- if (typeof prompt === "string") payload.content = prompt;
1799
- else payload.embeds = [
1800
- prompt.component
1801
- ];
1802
- return payload;
1803
- }, "buildPrompt");
1804
- var clearedPayload = /* @__PURE__ */ __name((opts) => isV2(opts) ? {
1805
- flags: "IsComponentsV2",
1806
- components: []
1807
- } : {
1808
- components: []
1809
- }, "clearedPayload");
1810
- var decide = /* @__PURE__ */ __name(async (opts, i) => {
1811
- if (usesResolver(opts)) return opts.resolveDecision(i);
1812
- if (usesCustomIds(opts)) {
1813
- const { confirmCustomIds, cancelCustomIds = [] } = opts;
1814
- if (confirmCustomIds.includes(i.customId)) return true;
1815
- if (cancelCustomIds.includes(i.customId)) return false;
1960
+ return botRole;
1961
+ }
1962
+ __name(getBotRole, "getBotRole");
1963
+
1964
+ // src/bot/utilities/permissions/hasPermsToAssign.ts
1965
+ function hasPermsToAssign(roleOrOptions) {
1966
+ const { targetRole, errors } = roleOrOptions instanceof discord_js.Role ? {
1967
+ targetRole: roleOrOptions,
1968
+ errors: void 0
1969
+ } : roleOrOptions;
1970
+ const HigherErr = errors?.higher ?? RoleHigherThanMe;
1971
+ const ManagedErr = errors?.managed ?? CannotAssignBotRole;
1972
+ const Missing = errors?.missing ?? MissingPermissions;
1973
+ const botRole = getBotRole(targetRole.guild);
1974
+ if (targetRole.comparePositionTo(botRole) >= 0) {
1975
+ throw new HigherErr("Role is higher than me", targetRole, botRole);
1816
1976
  }
1817
- return false;
1818
- }, "decide");
1819
- var shouldDefer = /* @__PURE__ */ __name((ix, opts, isSlash, isContext) => {
1820
- if (ix.deferred) return false;
1821
- if (opts.defer === false) return false;
1822
- return isSlash || isContext;
1823
- }, "shouldDefer");
1824
- var maybeDefer = /* @__PURE__ */ __name(async (ix, opts, isSlash, isContext) => {
1825
- if (!shouldDefer(ix, opts, isSlash, isContext)) return;
1826
- if (isSlash || isContext) {
1827
- const { ephemeral = true } = opts;
1828
- const flags = ephemeral ? {
1829
- flags: "Ephemeral"
1830
- } : void 0;
1831
- if (flags) await ix.deferReply(flags);
1832
- else await ix.deferReply();
1833
- } else if (isMessageComponentIx(ix)) {
1834
- await ix.deferUpdate();
1835
- } else {
1836
- await ix.deferReply().catch(() => void 0);
1977
+ if (targetRole.managed) {
1978
+ throw new ManagedErr(`Cannot assign managed role ${targetRole.name}`);
1837
1979
  }
1838
- }, "maybeDefer");
1839
- var sendPrompt = /* @__PURE__ */ __name(async (ix, payload, ephemeral, isSlash, isContext) => {
1840
- if (isSlash || isContext) {
1841
- if (ix.deferred) return ix.editReply(payload);
1842
- const reply = {
1843
- ...payload,
1844
- ...ephemeral ? {
1845
- flags: "Ephemeral"
1846
- } : {}
1847
- };
1848
- await ix.reply(reply);
1849
- return ix.fetchReply();
1980
+ checkBotPermissions(targetRole.guild, [
1981
+ discord_js.PermissionFlagsBits.ManageRoles
1982
+ ], false, {
1983
+ missing: Missing
1984
+ });
1985
+ }
1986
+ __name(hasPermsToAssign, "hasPermsToAssign");
1987
+ async function fetchRole(clientOrGuild, roleId) {
1988
+ let role;
1989
+ if (!roleId) {
1990
+ throw new RoleDoesNotExist("Role ID is null or undefined", roleId);
1850
1991
  }
1851
- const follow = {
1852
- ...payload,
1853
- ...ephemeral ? {
1854
- flags: "Ephemeral"
1855
- } : {}
1856
- };
1857
- return ix.followUp(follow);
1858
- }, "sendPrompt");
1859
- var awaitComponent = /* @__PURE__ */ __name(async (msg, original, opts) => {
1860
- const componentType = opts.componentType ?? discord_js.ComponentType.Button;
1861
- const timeoutMs = opts.timeoutMs ?? 1e4;
1862
- try {
1863
- const button = await msg.awaitMessageComponent({
1864
- componentType,
1865
- time: timeoutMs,
1866
- filter: /* @__PURE__ */ __name((c) => {
1867
- if (c.user.id !== original.user.id) return false;
1868
- if (usesCustomIds(opts)) {
1869
- const { confirmCustomIds, cancelCustomIds = [] } = opts;
1870
- return confirmCustomIds.includes(c.customId) || cancelCustomIds.includes(c.customId);
1992
+ if (clientOrGuild instanceof discord_js.Guild) {
1993
+ const guild = clientOrGuild;
1994
+ role = guild.roles.cache.get(roleId);
1995
+ if (!role) {
1996
+ try {
1997
+ role = await guild.roles.fetch(roleId);
1998
+ } catch {
1999
+ throw new RoleDoesNotExist("Role not found in specified guild", roleId);
2000
+ }
2001
+ }
2002
+ } else {
2003
+ const client = clientOrGuild;
2004
+ role = client.guilds.cache.map((guild) => guild.roles.cache.get(roleId)).find((role2) => role2);
2005
+ if (!role) {
2006
+ const guilds = client.guilds.cache;
2007
+ for (const guild of guilds.values()) {
2008
+ try {
2009
+ role = await guild.roles.fetch(roleId);
2010
+ if (role) break;
2011
+ } catch {
2012
+ continue;
1871
2013
  }
1872
- return true;
1873
- }, "filter")
1874
- });
1875
- return {
1876
- button,
1877
- timedOut: false
1878
- };
1879
- } catch {
1880
- return {
1881
- button: null,
1882
- timedOut: true
1883
- };
1884
- }
1885
- }, "awaitComponent");
1886
- var clearUi = /* @__PURE__ */ __name(async (ix, msg, opts, isSlash) => {
1887
- if (!isSlash) {
1888
- try {
1889
- await msg.edit(clearedPayload(opts));
1890
- } catch {
1891
- await ix.deleteReply(msg).catch(() => void 0);
2014
+ }
1892
2015
  }
1893
- return;
1894
- }
1895
- await ix.editReply(clearedPayload(opts)).catch(() => void 0);
1896
- }, "clearUi");
1897
- var normalizeClassicOutcome = /* @__PURE__ */ __name((payload) => ({
1898
- ...payload,
1899
- components: payload.components ?? []
1900
- }), "normalizeClassicOutcome");
1901
- var outcomeReplacement = /* @__PURE__ */ __name(async (opts, ctx, confirmed, timedOut) => {
1902
- const outcomes = opts.outcomeUi;
1903
- if (isV2(opts)) {
1904
- const v2Outcomes = outcomes;
1905
- if (timedOut) return await resolveFactory(v2Outcomes.onTimeout, ctx);
1906
- if (!confirmed) return await resolveFactory(v2Outcomes.onCancel, ctx);
1907
- if (v2Outcomes.onConfirm) return await resolveFactory(v2Outcomes.onConfirm, ctx);
1908
- return null;
1909
2016
  }
1910
- const classicOutcomes = outcomes;
1911
- if (timedOut) return normalizeClassicOutcome(await resolveFactory(classicOutcomes.onTimeout, ctx));
1912
- if (!confirmed) return normalizeClassicOutcome(await resolveFactory(classicOutcomes.onCancel, ctx));
1913
- if (classicOutcomes.onConfirm) {
1914
- return normalizeClassicOutcome(await resolveFactory(classicOutcomes.onConfirm, ctx));
2017
+ if (!role) {
2018
+ throw new RoleDoesNotExist("Role not found", roleId);
1915
2019
  }
1916
- return null;
1917
- }, "outcomeReplacement");
1918
- function Confirmable(question, options) {
1919
- return function(_target, _propertyKey, descriptor) {
1920
- const original = descriptor.value;
1921
- descriptor.value = async function(...args) {
1922
- if (!original) throw new services.SeedcordError(services.SeedcordErrorCode.DecoratorMethodNotFound);
1923
- const ix = this.getEvent();
1924
- const isSlash = ix.isChatInputCommand();
1925
- const isContext = ix.isContextMenuCommand();
1926
- const { ephemeral = true } = options;
1927
- await maybeDefer(ix, options, isSlash, isContext);
1928
- const q = typeof question === "function" ? await question.apply(this) : question;
1929
- const ctx = {
1930
- handler: this,
1931
- interaction: ix,
1932
- question: q
1933
- };
1934
- const prompt = await buildPrompt(options, ctx);
1935
- const promptMsg = await sendPrompt(ix, prompt, ephemeral, isSlash, isContext);
1936
- const { button, timedOut } = await awaitComponent(promptMsg, ix, options);
1937
- let confirmed = false;
1938
- if (button) {
1939
- await button.deferUpdate().catch(() => void 0);
1940
- confirmed = await decide(options, button);
1941
- }
1942
- const replacement = await outcomeReplacement(options, ctx, confirmed, timedOut);
1943
- if (replacement) {
1944
- if (isSlash || isContext) {
1945
- await ix.editReply(replacement).catch(() => void 0);
1946
- } else {
1947
- await promptMsg.edit(replacement).catch(async () => {
1948
- await ix.deleteReply(promptMsg).catch(() => void 0);
1949
- await ix.followUp(replacement).catch(() => void 0);
1950
- });
1951
- }
1952
- } else {
1953
- await clearUi(ix, promptMsg, options, isSlash);
1954
- }
1955
- if (options.onResolved) {
1956
- await options.onResolved({
1957
- confirmed,
1958
- timedOut,
1959
- handler: this,
1960
- interaction: ix,
1961
- question: q,
1962
- ...button ? {
1963
- button
1964
- } : {}
1965
- });
1966
- }
1967
- if (confirmed) {
1968
- await original.apply(this, args);
1969
- }
1970
- };
1971
- return descriptor;
1972
- };
2020
+ return role;
1973
2021
  }
1974
- __name(Confirmable, "Confirmable");
2022
+ __name(fetchRole, "fetchRole");
1975
2023
 
1976
- // src/bot/errors/Channels.ts
1977
- var ChannelNotFoundError = class extends CustomError {
2024
+ // src/bot/defaults/errors/User.ts
2025
+ var UserNotInGuild = class extends CustomError {
1978
2026
  static {
1979
- __name(this, "ChannelNotFoundError");
2027
+ __name(this, "UserNotInGuild");
1980
2028
  }
1981
- channelId;
1982
2029
  /**
1983
- * Creates a new ChannelNotFoundError.
2030
+ * Creates a new UserNotInGuild error.
1984
2031
  *
1985
2032
  * @param message - The error message
1986
- * @param channelId - The ID of the channel that could not be found
1987
2033
  */
1988
- constructor(message, channelId) {
1989
- super(message), this.channelId = channelId;
1990
- this.response.setDescription(`Channel with ID \`${this.channelId}\` not found.`);
2034
+ constructor(message = "User is not in the guild.") {
2035
+ super(message);
2036
+ this.response.setDescription("User is not in the guild.");
1991
2037
  }
1992
2038
  };
1993
- var CannotSendEmbedsError = class extends CustomError {
2039
+ var UserNotFound = class extends CustomError {
1994
2040
  static {
1995
- __name(this, "CannotSendEmbedsError");
2041
+ __name(this, "UserNotFound");
1996
2042
  }
1997
- channelId;
2043
+ userArg;
1998
2044
  /**
1999
- * Creates a new CannotSendEmbedsError.
2045
+ * Creates a new UserNotFound error.
2000
2046
  *
2001
- * @param message - The error message
2002
- * @param channelId - The ID of the channel where embeds cannot be sent
2047
+ * @param userArg - The user argument that could not be resolved
2003
2048
  */
2004
- constructor(message, channelId) {
2005
- super(message), this.channelId = channelId;
2006
- this.response.setDescription(`Cannot send embeds in <#${this.channelId}>.
2007
-
2008
- Please ensure I have the following permissions:
2009
- \u2022 View Channel
2010
- \u2022 Send Messages
2011
- \u2022 Embed Links
2012
- \u2022 Attach Files
2013
- \u2022 Read Message History
2014
- \u2022 Use External Emojis
2015
- `);
2049
+ constructor(userArg) {
2050
+ super(`User not found: ${userArg}`), this.userArg = userArg;
2051
+ this.response.setTitle("User Not Found").setDescription(`User probably doesn't exist or was deleted.
2052
+ **User Argument:** \`${this.userArg}\`
2053
+ Please check the user ID and try again. Only pass valid user IDs as the argument.`);
2016
2054
  }
2017
2055
  };
2018
- var CouldNotFindChannel = class extends CustomError {
2056
+
2057
+ // src/bot/utilities/users/fetchGuildMember.ts
2058
+ async function fetchGuildMember(guild, userId) {
2059
+ let user = guild.members.cache.get(userId);
2060
+ user ??= await guild.members.fetch(userId).catch(() => {
2061
+ throw new UserNotInGuild(`User with ID ${userId} not found in guild`);
2062
+ });
2063
+ return user;
2064
+ }
2065
+ __name(fetchGuildMember, "fetchGuildMember");
2066
+
2067
+ // src/bot/utilities/users/fetchManyGuildMembers.ts
2068
+ async function fetchManyGuildMembers(guild, userIds) {
2069
+ const results = await Promise.allSettled(userIds.map((userId) => fetchGuildMember(guild, userId)));
2070
+ return results.filter((result) => result.status === "fulfilled").map((result) => result.value);
2071
+ }
2072
+ __name(fetchManyGuildMembers, "fetchManyGuildMembers");
2073
+ async function fetchUser(client, userId) {
2074
+ let user = client.users.cache.get(userId);
2075
+ user ??= await client.users.fetch(userId).catch((err) => {
2076
+ if (err instanceof discord_js.DiscordAPIError && err.code === discord_js.RESTJSONErrorCodes.UnknownUser) {
2077
+ throw new UserNotFound(userId);
2078
+ }
2079
+ throw err;
2080
+ });
2081
+ return user;
2082
+ }
2083
+ __name(fetchUser, "fetchUser");
2084
+
2085
+ // src/bot/utilities/users/fetchManyUsers.ts
2086
+ async function fetchManyUsers(client, userIds) {
2087
+ const results = await Promise.allSettled(userIds.map((userId) => fetchUser(client, userId)));
2088
+ return results.filter((result) => result.status === "fulfilled").map((result) => result.value);
2089
+ }
2090
+ __name(fetchManyUsers, "fetchManyUsers");
2091
+
2092
+ // src/bot/utilities/users/updateMemberRoles.ts
2093
+ async function updateMemberRoles(rolesToAdd, rolesToRemove, member) {
2094
+ const current = new Set(member.roles.cache.map((r) => r.id));
2095
+ const toAdd = new Set(rolesToAdd);
2096
+ const toRemove = new Set(rolesToRemove);
2097
+ const updated = current.union(toAdd).difference(toRemove);
2098
+ await member.roles.set([
2099
+ ...updated
2100
+ ]);
2101
+ }
2102
+ __name(updateMemberRoles, "updateMemberRoles");
2103
+ var EffectsEmitter = class {
2019
2104
  static {
2020
- __name(this, "CouldNotFindChannel");
2105
+ __name(this, "EffectsEmitter");
2021
2106
  }
2022
- channelId;
2107
+ emitter = new events.EventEmitter();
2023
2108
  /**
2024
- * Creates a new CouldNotFindChannel error.
2109
+ * Registers a listener for the specified side effect.
2025
2110
  *
2026
- * @param message - The error message
2027
- * @param channelId - The ID of the channel that could not be found
2111
+ * @typeParam KeyOfEffects - The side effect name type
2112
+ * @param event - The side effect name to listen for
2113
+ * @param listener - Function to call when the event is emitted
2114
+ * @returns This EffectsEmitter instance for chaining
2028
2115
  */
2029
- constructor(message, channelId) {
2030
- super(message), this.channelId = channelId;
2031
- this.response.setDescription(`Could not find channel with ID \`${this.channelId}\`. It could also be that the channel is not a text channel.`);
2032
- }
2033
- };
2034
- var ChannelNotTextChannel = class extends CustomError {
2035
- static {
2036
- __name(this, "ChannelNotTextChannel");
2116
+ on(event, listener) {
2117
+ this.emitter.on(event, listener);
2118
+ return this;
2037
2119
  }
2038
- channelId;
2039
2120
  /**
2040
- * Creates a new ChannelNotTextChannel error.
2121
+ * Registers a one-time listener for the specified side effect.
2041
2122
  *
2042
- * @param message - The error message
2043
- * @param channelId - The ID of the channel that is not a text channel
2123
+ * @typeParam KeyOfEffects - The side effect name type
2124
+ * @param event - The side effect name to listen for once
2125
+ * @param listener - Function to call when the event is emitted
2126
+ * @returns This EffectsEmitter instance for chaining
2044
2127
  */
2045
- constructor(message, channelId) {
2046
- super(message), this.channelId = channelId;
2047
- this.response.setDescription(`Channel with ID \`${this.channelId}\` is not a text channel.`);
2048
- }
2049
- };
2050
- var MissingPermissions = class extends CustomError {
2051
- static {
2052
- __name(this, "MissingPermissions");
2128
+ once(event, listener) {
2129
+ this.emitter.once(event, listener);
2130
+ return this;
2053
2131
  }
2054
- missingPerms;
2055
- roleOrChannel;
2056
2132
  /**
2057
- * Creates a new BotMissingPermissionsError.
2133
+ * Emits a side effect with the provided data.
2058
2134
  *
2059
- * @param message - The error message
2060
- * @param missingPerms - Array of missing permission names
2061
- * @param roleOrChannel - The role or channel where permissions are missing
2135
+ * @typeParam KeyOfEffects - The side effect name type
2136
+ * @param event - The side effect name to emit
2137
+ * @param data - The data to pass to registered listeners
2138
+ * @returns True if the event had listeners, false otherwise
2062
2139
  */
2063
- constructor(message, missingPerms, roleOrChannel) {
2064
- super(message), this.missingPerms = missingPerms, this.roleOrChannel = roleOrChannel;
2065
- const missing = this.missingPerms.map((perm) => `\u2022 ${perm}`).join("\n");
2066
- const errorSubtext = this.roleOrChannel instanceof discord_js.Role ? `My role, <@&${this.roleOrChannel.id}>, is missing the following permissions:` : `I am missing the following permissions in <#${this.roleOrChannel.id}>:`;
2067
- this.response.setDescription(`${errorSubtext}
2068
-
2069
- Please ensure I have the following missing permission(s):
2070
- ${missing}`);
2140
+ emit(event, data) {
2141
+ return this.emitter.emit(event, data);
2071
2142
  }
2072
2143
  };
2073
- var RoleHigherThanMe = class extends CustomError {
2074
- static {
2075
- __name(this, "RoleHigherThanMe");
2076
- }
2077
- role;
2078
- botRole;
2079
- /**
2080
- * Creates a new RoleHigherThanMe error.
2081
- *
2082
- * @param message - The error message
2083
- */
2084
- constructor(message, role, botRole) {
2085
- super(message), this.role = role, this.botRole = botRole;
2086
- this.response.setDescription(`I cannot assign a role that is higher than me.
2087
2144
 
2088
- The role <@&${this.role.id}> is higher than my role <@&${this.botRole.id}> in the hierarchy.`);
2089
- }
2090
- };
2091
- var CannotAssignBotRole = class extends CustomError {
2145
+ // src/effects/EffectsHandler.ts
2146
+ var EffectsHandler = class {
2092
2147
  static {
2093
- __name(this, "CannotAssignBotRole");
2148
+ __name(this, "EffectsHandler");
2094
2149
  }
2150
+ data;
2151
+ core;
2095
2152
  /**
2096
- * Creates a new CannotAssignBotRole error.
2153
+ * Creates a new effects handler instance.
2097
2154
  *
2098
- * @param message - The error message
2155
+ * @param data - The effect event data
2156
+ * @param core - The core framework instance
2099
2157
  */
2100
- constructor(message = "I cannot assign a managed role.") {
2101
- super(message);
2102
- this.response.setDescription("I cannot assign a managed role.");
2158
+ constructor(data, core) {
2159
+ this.data = data;
2160
+ this.core = core;
2161
+ this.data = data;
2162
+ this.core = core;
2103
2163
  }
2104
2164
  };
2105
- var RoleDoesNotExist = class extends CustomError {
2165
+
2166
+ // src/effects/decorators/RegisterEffect.ts
2167
+ var EffectMetadataKey = Symbol("effect:metadata");
2168
+ function RegisterEffect(effect, options) {
2169
+ return function(constructor) {
2170
+ const meta = {
2171
+ effect,
2172
+ frequency: options?.frequency
2173
+ };
2174
+ Reflect.defineMetadata(EffectMetadataKey, meta, constructor);
2175
+ };
2176
+ }
2177
+ __name(RegisterEffect, "RegisterEffect");
2178
+
2179
+ // src/effects/bases/WebhookLog.ts
2180
+ var WebhookLog = class extends EffectsHandler {
2106
2181
  static {
2107
- __name(this, "RoleDoesNotExist");
2182
+ __name(this, "WebhookLog");
2108
2183
  }
2109
- roleId;
2110
- /**
2111
- * Creates a new RoleDoesNotExist error.
2112
- *
2113
- * @param message - The error message
2114
- * @param roleId - The ID of the role that doesn't exist
2115
- */
2116
- constructor(message, roleId) {
2117
- super(message), this.roleId = roleId;
2118
- this.response.setDescription(`The role with ID \`${this.roleId}\` does not exist.`);
2184
+ constructor(data, core) {
2185
+ super(data, core);
2119
2186
  }
2120
2187
  };
2121
- var HasDangerousPermissions = class extends CustomError {
2122
- static {
2123
- __name(this, "HasDangerousPermissions");
2124
- }
2125
- role;
2126
- dangerousPerms;
2127
- /**
2128
- * Creates a new HasDangerousPermissions error.
2129
- *
2130
- * @param message - The error message
2131
- * @param role - The role with dangerous permissions
2132
- * @param dangerousPerms - Array of dangerous permission names
2133
- */
2134
- constructor(message, role, dangerousPerms) {
2135
- super(message), this.role = role, this.dangerousPerms = dangerousPerms;
2136
- const dangerous = this.dangerousPerms.map((perm) => `\u2022 ${perm}`).join("\n");
2137
- this.response.setDescription(`The role <@&${this.role.id}> has the following dangerous permissions:
2138
2188
 
2139
- Please ensure the following dangerous permission(s) are not enabled:
2140
- ${dangerous}`);
2189
+ // src/effects/default/UnknownException.ts
2190
+ function _ts_decorate5(decorators, target, key, desc) {
2191
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2192
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2193
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2194
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2195
+ }
2196
+ __name(_ts_decorate5, "_ts_decorate");
2197
+ function _ts_metadata5(k, v) {
2198
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2199
+ }
2200
+ __name(_ts_metadata5, "_ts_metadata");
2201
+ function webhookUrlValidator(raw, _fallback) {
2202
+ if (raw === null) {
2203
+ throw new services.SeedcordError(services.SeedcordErrorCode.ConfigUnknownExceptionWebhookMissing);
2141
2204
  }
2142
- };
2143
-
2144
- // src/bot/errors/User.ts
2145
- var UserNotInGuild = class extends CustomError {
2146
- static {
2147
- __name(this, "UserNotInGuild");
2205
+ if (typeof raw !== "string") {
2206
+ throw new services.SeedcordError(services.SeedcordErrorCode.ConfigUnknownExceptionWebhookInvalid);
2148
2207
  }
2149
- /**
2150
- * Creates a new UserNotInGuild error.
2151
- *
2152
- * @param message - The error message
2153
- */
2154
- constructor(message = "User is not in the guild.") {
2155
- super(message);
2156
- this.response.setDescription("User is not in the guild.");
2208
+ const value = raw.trim();
2209
+ if (value === "") {
2210
+ throw new services.SeedcordError(services.SeedcordErrorCode.ConfigUnknownExceptionWebhookMissing);
2157
2211
  }
2158
- };
2159
- var UserNotFound = class extends CustomError {
2212
+ const pattern = String.raw`^https?:\/\/(?:canary\.|ptb\.)?discord(?:app)?\.com\/api\/webhooks\/\d+\/[\w$-]+$`;
2213
+ const discordWebhookRegex = new RegExp(pattern);
2214
+ if (!URL.canParse(value) || !discordWebhookRegex.test(value)) {
2215
+ throw new services.SeedcordError(services.SeedcordErrorCode.ConfigUnknownExceptionWebhookInvalid);
2216
+ }
2217
+ return value;
2218
+ }
2219
+ __name(webhookUrlValidator, "webhookUrlValidator");
2220
+ exports.UnknownException = class _UnknownException extends WebhookLog {
2160
2221
  static {
2161
- __name(this, "UserNotFound");
2222
+ __name(this, "UnknownException");
2162
2223
  }
2163
- userArg;
2164
- /**
2165
- * Creates a new UserNotFound error.
2166
- *
2167
- * @param userArg - The user argument that could not be resolved
2168
- */
2169
- constructor(userArg) {
2170
- super(`User not found: ${userArg}`), this.userArg = userArg;
2171
- this.response.setTitle("User Not Found").setDescription(`User probably doesn't exist or was deleted.
2172
- **User Argument:** \`${this.userArg}\`
2173
- Please check the user ID and try again. Only pass valid user IDs as the argument.`);
2174
- }
2175
- };
2176
- async function fetchText(client, channelId) {
2177
- if (channelId instanceof discord_js.TextChannel) {
2178
- return channelId;
2179
- }
2180
- let channel = client.channels.cache.get(channelId);
2181
- if (!channel) {
2224
+ static logger = new services.Logger("Effect: UnknownException");
2225
+ webhook = new discord_js.WebhookClient({
2226
+ url: _UnknownException.unknownExceptionWebhookUrl
2227
+ });
2228
+ async execute() {
2229
+ const metadataFile = this.prepareMetadataFile();
2182
2230
  try {
2183
- channel = await client.channels.fetch(channelId);
2184
- } catch {
2185
- throw new CouldNotFindChannel("Channel not found or not a text channel", channelId);
2186
- }
2187
- }
2188
- if (channel?.isTextBased()) {
2189
- return channel;
2190
- }
2191
- throw new CouldNotFindChannel("Channel not found or not a text channel", channelId);
2192
- }
2193
- __name(fetchText, "fetchText");
2194
-
2195
- // src/bot/utilities/channels/sendInText.ts
2196
- async function sendInText(client, channelId, message) {
2197
- const channel = await fetchText(client, channelId);
2198
- return await channel.send(message);
2199
- }
2200
- __name(sendInText, "sendInText");
2201
- function throwCustomError(error, message, CustomError2) {
2202
- const uuid = crypto.randomUUID();
2203
- services.Logger.Error("Throwing Custom Error", error.name);
2204
- if (typeof CustomError2 === typeof DatabaseError) {
2205
- const errorMessage = error instanceof Error ? error.message : message;
2206
- throw new CustomError2(errorMessage, uuid);
2207
- } else {
2208
- if (error instanceof Error) {
2209
- throw new CustomError2(`${message}: ${error.message ? error.message : error.toString()}`);
2210
- } else {
2211
- throw new CustomError2(message);
2231
+ await this.webhook.send({
2232
+ flags: "IsComponentsV2",
2233
+ withComponents: true,
2234
+ username: "Unknown Exception",
2235
+ avatarURL: "https://cdn.discordapp.com/attachments/1351446034827579466/1351446912947191830/warning-2.png",
2236
+ components: [
2237
+ new UnhandledErrorContainer(this.data).component
2238
+ ],
2239
+ files: metadataFile ? [
2240
+ metadataFile
2241
+ ] : []
2242
+ });
2243
+ } catch (error) {
2244
+ _UnknownException.logger.error("Failed to send unknown exception webhook", error);
2212
2245
  }
2213
2246
  }
2214
- }
2215
- __name(throwCustomError, "throwCustomError");
2216
-
2217
- // src/bot/utilities/messages/attemptSendDM.ts
2218
- async function attemptSendDM(user, content) {
2219
- const payload = {
2220
- ...content.content !== void 0 && {
2221
- content: content.content
2222
- },
2223
- ...content.embeds !== void 0 && {
2224
- embeds: [
2225
- ...content.embeds
2226
- ]
2227
- },
2228
- ...content.components !== void 0 && {
2229
- components: [
2230
- ...content.components
2231
- ]
2232
- }
2233
- };
2234
- try {
2235
- return await user.send(payload);
2236
- } catch {
2237
- return null;
2247
+ prepareMetadataFile() {
2248
+ const { metadata } = this.data;
2249
+ if (!metadata) return null;
2250
+ const content = utils.filterCirculars(metadata);
2251
+ return new discord_js.AttachmentBuilder(Buffer.from(JSON.stringify(content, void 0, 2), "utf-8"), {
2252
+ name: "metadata.json",
2253
+ description: "Metadata associated with the error"
2254
+ });
2238
2255
  }
2239
- }
2240
- __name(attemptSendDM, "attemptSendDM");
2241
- var PermissionNames = new Map(Object.entries(discord_js.PermissionFlagsBits).map(([key, bit]) => [
2242
- bit,
2243
- utils.prettify(key)
2244
- ]));
2245
- var PERM_GROUPS = {
2246
- manage: /* @__PURE__ */ new Map([
2247
- [
2248
- discord_js.PermissionFlagsBits.ManageChannels,
2249
- "Manage Channels"
2250
- ],
2251
- [
2252
- discord_js.PermissionFlagsBits.ManageRoles,
2253
- "Manage Roles"
2254
- ],
2255
- [
2256
- discord_js.PermissionFlagsBits.ManageWebhooks,
2257
- "Manage Webhooks"
2258
- ],
2259
- [
2260
- discord_js.PermissionFlagsBits.ManageMessages,
2261
- "Manage Messages"
2262
- ],
2263
- [
2264
- discord_js.PermissionFlagsBits.ManageNicknames,
2265
- "Manage Nicknames"
2266
- ]
2267
- ]),
2268
- embed: /* @__PURE__ */ new Map([
2269
- [
2270
- discord_js.PermissionFlagsBits.ViewChannel,
2271
- "View Channel"
2272
- ],
2273
- [
2274
- discord_js.PermissionFlagsBits.SendMessages,
2275
- "Send Messages"
2276
- ],
2277
- [
2278
- discord_js.PermissionFlagsBits.EmbedLinks,
2279
- "Embed Links"
2280
- ],
2281
- [
2282
- discord_js.PermissionFlagsBits.AttachFiles,
2283
- "Attach Files"
2284
- ],
2285
- [
2286
- discord_js.PermissionFlagsBits.UseExternalEmojis,
2287
- "Use External Emojis"
2288
- ],
2289
- [
2290
- discord_js.PermissionFlagsBits.ReadMessageHistory,
2291
- "Read Message History"
2292
- ]
2293
- ]),
2294
- others: /* @__PURE__ */ new Map([
2295
- [
2296
- discord_js.PermissionFlagsBits.AddReactions,
2297
- "Add Reactions"
2298
- ],
2299
- [
2300
- discord_js.PermissionFlagsBits.UseApplicationCommands,
2301
- "Use Application Commands"
2302
- ]
2303
- ])
2304
2256
  };
2305
- function checkPermissions(client, roleOrChannel, scope = "all", inverse = false) {
2306
- let required;
2307
- if (Array.isArray(scope)) {
2308
- required = /* @__PURE__ */ new Map();
2309
- for (const bit of scope) {
2310
- const name = PermissionNames.get(bit);
2311
- if (name) required.set(bit, name);
2312
- }
2313
- } else {
2314
- switch (scope) {
2315
- case "manage":
2316
- required = PERM_GROUPS.manage;
2317
- break;
2318
- case "embed":
2319
- required = PERM_GROUPS.embed;
2320
- break;
2321
- case "others":
2322
- required = new Map([
2323
- ...PERM_GROUPS.others,
2324
- ...PERM_GROUPS.embed
2325
- ]);
2326
- break;
2327
- default:
2328
- required = new Map([
2329
- ...PERM_GROUPS.manage,
2330
- ...PERM_GROUPS.others,
2331
- ...PERM_GROUPS.embed
2332
- ]);
2333
- break;
2334
- }
2257
+ _ts_decorate5([
2258
+ envapt.Envapt("UNKNOWN_EXCEPTION_WEBHOOK_URL", {
2259
+ converter: /* @__PURE__ */ __name((raw, fallback) => webhookUrlValidator(raw), "converter")
2260
+ }),
2261
+ _ts_metadata5("design:type", String)
2262
+ ], exports.UnknownException, "unknownExceptionWebhookUrl", void 0);
2263
+ exports.UnknownException = _ts_decorate5([
2264
+ RegisterEffect("unknownException")
2265
+ ], exports.UnknownException);
2266
+ var DefaultSeparator = class DefaultSeparator2 extends BuilderComponent {
2267
+ static {
2268
+ __name(this, "DefaultSeparator");
2335
2269
  }
2336
- let permissions;
2337
- if (roleOrChannel instanceof discord_js.Role) {
2338
- permissions = roleOrChannel.permissions;
2339
- } else {
2340
- if (!client.user) {
2341
- throw new services.SeedcordError(services.SeedcordErrorCode.CoreClientUserUnavailable);
2342
- }
2343
- permissions = roleOrChannel.permissionsFor(client.user, true);
2270
+ constructor() {
2271
+ super("separator");
2272
+ this.instance.setSpacing(discord_js.SeparatorSpacingSize.Small).setDivider(true);
2344
2273
  }
2345
- if (!permissions) {
2346
- throw new MissingPermissions("Missing Permissions", Array.from(required.values()), roleOrChannel);
2274
+ };
2275
+ var UnhandledErrorContainer = class UnhandledErrorContainer2 extends BuilderComponent {
2276
+ static {
2277
+ __name(this, "UnhandledErrorContainer");
2347
2278
  }
2348
- if (inverse) {
2349
- const dangerous = Array.from(required.entries()).filter(([bit]) => permissions.has(bit, true)).map(([, name]) => name);
2350
- if (dangerous.length > 0) {
2351
- throw new HasDangerousPermissions("Role has dangerous permissions", roleOrChannel, dangerous);
2352
- }
2353
- } else {
2354
- const missing = Array.from(required.entries()).filter(([bit]) => !permissions.has(bit, true)).map(([, name]) => name);
2355
- if (missing.length > 0) {
2356
- throw new MissingPermissions("Missing Permissions", missing, roleOrChannel);
2357
- }
2279
+ constructor(data) {
2280
+ super("container");
2281
+ const { uuid, error, guild, user, metadata } = data;
2282
+ this.instance.addTextDisplayComponents((text) => text.setContent(`### An unknown exception was thrown
2283
+ **Guild ID:** \`${guild?.id ?? "Not used in a guild"}\`
2284
+ **Guild Name:** ${guild?.name ?? "Not used in a guild"}
2285
+ **User ID:** \`${user?.id ?? "Missing user info"}\`
2286
+ **Username:** ${user?.username ?? "Missing user info"}
2287
+ `)).addSeparatorComponents(new DefaultSeparator().component).addTextDisplayComponents((text) => text.setContent(`### UUID \`${uuid}\`
2288
+ \`\`\`${error.stack}\`\`\``));
2289
+ this.addTimestampsIfAvailable(error);
2290
+ this.addMetadataIfAvailable(metadata);
2358
2291
  }
2359
- }
2360
- __name(checkPermissions, "checkPermissions");
2361
- function getBotRole(client, guild) {
2362
- if (!client.user) {
2363
- throw new services.SeedcordError(services.SeedcordErrorCode.CoreClientUserUnavailable);
2292
+ addTimestampsIfAvailable(error) {
2293
+ if (!(error instanceof discord_js.DiscordAPIError)) return;
2294
+ const now = Date.now();
2295
+ const snowflake = error.url.match(/\/interactions\/(\d+)\//)?.[1];
2296
+ if (!snowflake) return void 0;
2297
+ const interactionTs = Number(discord_js.SnowflakeUtil.deconstruct(snowflake).timestamp);
2298
+ const diff = now - interactionTs;
2299
+ const seconds = Math.floor(diff / 1e3);
2300
+ const millis = diff % 1e3;
2301
+ this.instance.addSeparatorComponents(new DefaultSeparator().component).addTextDisplayComponents((text) => text.setContent(`### Timestamps
2302
+ - **\`Interaction sent\` :** ${new Date(interactionTs).toISOString()} (${interactionTs})
2303
+ - **\`Error logged \` :** ${new Date(now).toISOString()} (${now})
2304
+ - **\`Offset \` :** ${seconds}s ${millis}ms`));
2364
2305
  }
2365
- const botRole = guild.roles.botRoleFor(client.user);
2366
- if (!botRole) {
2367
- throw new services.SeedcordError(services.SeedcordErrorCode.CoreBotRoleMissing, [
2368
- guild.id
2369
- ]);
2306
+ addMetadataIfAvailable(metadata) {
2307
+ if (!metadata) return;
2308
+ this.instance.addSeparatorComponents(new DefaultSeparator().component).addTextDisplayComponents((text) => text.setContent("### Metadata")).addFileComponents((file) => file.setURL("attachment://metadata.json"));
2370
2309
  }
2371
- return botRole;
2372
- }
2373
- __name(getBotRole, "getBotRole");
2310
+ };
2374
2311
 
2375
- // src/bot/utilities/roles/checkBotPermissions.ts
2376
- function checkBotPermissions(client, guildOrChannel, scope = "all", inverse = false) {
2377
- if (guildOrChannel instanceof discord_js.Guild) {
2378
- const botRole = getBotRole(client, guildOrChannel);
2379
- checkPermissions(client, botRole, scope, inverse);
2380
- } else {
2381
- checkPermissions(client, guildOrChannel, scope);
2312
+ // src/effects/EffectsRegistry.ts
2313
+ var EffectsRegistry = class extends Plugin {
2314
+ static {
2315
+ __name(this, "EffectsRegistry");
2382
2316
  }
2383
- }
2384
- __name(checkBotPermissions, "checkBotPermissions");
2385
- async function fetchRole(clientOrGuild, roleId) {
2386
- let role;
2387
- if (!roleId) {
2388
- throw new RoleDoesNotExist("Role ID is null or undefined", roleId);
2317
+ core;
2318
+ logger = new services.Logger("Effects");
2319
+ isInitialized = false;
2320
+ effectsMap = new discord_js.Collection();
2321
+ emitter = new EffectsEmitter();
2322
+ constructor(core) {
2323
+ super(core), this.core = core;
2389
2324
  }
2390
- if (clientOrGuild instanceof discord_js.Guild) {
2391
- const guild = clientOrGuild;
2392
- role = guild.roles.cache.get(roleId);
2393
- if (!role) {
2394
- try {
2395
- role = await guild.roles.fetch(roleId);
2396
- } catch {
2397
- throw new RoleDoesNotExist("Role not found in specified guild", roleId);
2398
- }
2399
- }
2400
- } else {
2401
- const client = clientOrGuild;
2402
- role = client.guilds.cache.map((guild) => guild.roles.cache.get(roleId)).find((role2) => role2);
2403
- if (!role) {
2404
- const guilds = client.guilds.cache;
2405
- for (const guild of guilds.values()) {
2406
- try {
2407
- role = await guild.roles.fetch(roleId);
2408
- if (role) break;
2409
- } catch {
2410
- continue;
2325
+ async init() {
2326
+ if (this.isInitialized) return;
2327
+ this.isInitialized = true;
2328
+ const effectsDir = this.core.config.effects.path;
2329
+ this.logger.info(chalk3__default.default.bold(effectsDir));
2330
+ this.registerEffect(exports.UnknownException, {
2331
+ effect: "unknownException",
2332
+ frequency: "on"
2333
+ });
2334
+ await this.loadEffects(effectsDir);
2335
+ this.attachEffects();
2336
+ const totalEffects = Array.from(this.effectsMap.values()).reduce((acc, handlers) => acc + handlers.length, 0);
2337
+ this.logger.info(`${chalk3__default.default.bold.green("Loaded")}: ${chalk3__default.default.bold.magenta(totalEffects)} side effects`);
2338
+ }
2339
+ async loadEffects(dir) {
2340
+ await utils.traverseDirectory(dir, (_fullPath, relativePath, imported) => {
2341
+ for (const exportName of Object.keys(imported)) {
2342
+ const val = imported[exportName];
2343
+ if (this.isEffectHandler(val)) {
2344
+ const meta = Reflect.getMetadata(EffectMetadataKey, val);
2345
+ this.registerEffect(val, meta);
2346
+ this.logger.info(`${chalk3__default.default.italic("Registered")} ${chalk3__default.default.bold.yellow(val.name)} from ${chalk3__default.default.gray(relativePath)}`);
2411
2347
  }
2412
2348
  }
2349
+ }, this.logger);
2350
+ }
2351
+ registerEffect(handler, options) {
2352
+ let handlers = this.effectsMap.get(options.effect);
2353
+ if (!handlers) {
2354
+ handlers = [];
2355
+ this.effectsMap.set(options.effect, handlers);
2413
2356
  }
2357
+ handlers.push({
2358
+ ctor: handler,
2359
+ frequency: options.frequency ?? "on"
2360
+ });
2414
2361
  }
2415
- if (!role) {
2416
- throw new RoleDoesNotExist("Role not found", roleId);
2362
+ isEffectHandler(obj) {
2363
+ if (typeof obj !== "function") return false;
2364
+ return obj.prototype instanceof EffectsHandler && Reflect.hasMetadata(EffectMetadataKey, obj);
2417
2365
  }
2418
- return role;
2419
- }
2420
- __name(fetchRole, "fetchRole");
2421
- function hasPermsToAssign(targetRole) {
2422
- const botRole = getBotRole(targetRole.client, targetRole.guild);
2423
- if (targetRole.comparePositionTo(botRole) >= 0) {
2424
- throw new RoleHigherThanMe("Role is higher than me", targetRole, botRole);
2366
+ attachEffects() {
2367
+ for (const [effectName, handlerEntries] of this.effectsMap) {
2368
+ for (const entry of handlerEntries) {
2369
+ const register = entry.frequency === "once" ? this.emitter.once.bind(this.emitter) : this.emitter.on.bind(this.emitter);
2370
+ register(effectName, (data) => {
2371
+ try {
2372
+ const instance = new entry.ctor(data, this.core);
2373
+ void instance.execute();
2374
+ } catch (err) {
2375
+ this.logger.error(`Error in side effect ${String(effectName)} handler ${entry.ctor.name}:`, err);
2376
+ }
2377
+ });
2378
+ }
2379
+ }
2425
2380
  }
2426
- if (targetRole.managed) {
2427
- throw new CannotAssignBotRole(`Cannot assign bot role ${targetRole.name}`);
2381
+ emit(event, data) {
2382
+ return this.emitter.emit(event, data);
2428
2383
  }
2429
- checkBotPermissions(targetRole.client, targetRole.guild, [
2430
- discord_js.PermissionFlagsBits.ManageRoles
2431
- ]);
2432
- }
2433
- __name(hasPermsToAssign, "hasPermsToAssign");
2434
-
2435
- // src/bot/utilities/users/fetchGuildMember.ts
2436
- async function fetchGuildMember(guild, userId) {
2437
- let user = guild.members.cache.get(userId);
2438
- user ??= await guild.members.fetch(userId).catch(() => {
2439
- throw new UserNotInGuild(`User with ID ${userId} not found in guild`);
2440
- });
2441
- return user;
2442
- }
2443
- __name(fetchGuildMember, "fetchGuildMember");
2444
-
2445
- // src/bot/utilities/users/fetchManyGuildMembers.ts
2446
- async function fetchManyGuildMembers(guild, userIds) {
2447
- const results = await Promise.allSettled(userIds.map((userId) => fetchGuildMember(guild, userId)));
2448
- return results.filter((result) => result.status === "fulfilled").map((result) => result.value);
2449
- }
2450
- __name(fetchManyGuildMembers, "fetchManyGuildMembers");
2451
- async function fetchUser(client, userId) {
2452
- let user = client.users.cache.get(userId);
2453
- user ??= await client.users.fetch(userId).catch((err) => {
2454
- if (err instanceof discord_js.DiscordAPIError && err.code === discord_js.RESTJSONErrorCodes.UnknownUser) {
2455
- throw new UserNotFound(userId);
2384
+ };
2385
+ var Seedcord = class _Seedcord extends Pluggable {
2386
+ static {
2387
+ __name(this, "Seedcord");
2388
+ }
2389
+ config;
2390
+ static isInstantiated = false;
2391
+ /** @see {@link CoordinatedShutdown} */
2392
+ shutdown;
2393
+ /** @see {@link CoordinatedStartup} */
2394
+ startup;
2395
+ /** @see {@link EffectsRegistry} */
2396
+ effects;
2397
+ /** @see {@link Bot} */
2398
+ bot;
2399
+ /** @see {@link HealthCheck} */
2400
+ healthCheck;
2401
+ /**
2402
+ * Creates a new Seedcord instance
2403
+ *
2404
+ * @param config - Bot configuration including paths and Discord client options
2405
+ * @throws An {@link SeedcordError} When attempting to create multiple instances (singleton)
2406
+ */
2407
+ constructor(config) {
2408
+ const shutdown = new services.CoordinatedShutdown();
2409
+ const startup = new services.CoordinatedStartup();
2410
+ super(shutdown, startup), this.config = config;
2411
+ this.shutdown = shutdown;
2412
+ this.startup = startup;
2413
+ if (_Seedcord.isInstantiated) {
2414
+ throw new services.SeedcordError(services.SeedcordErrorCode.CoreSingletonViolation);
2456
2415
  }
2457
- throw err;
2458
- });
2459
- return user;
2460
- }
2461
- __name(fetchUser, "fetchUser");
2462
-
2463
- // src/bot/utilities/users/fetchManyUsers.ts
2464
- async function fetchManyUsers(client, userIds) {
2465
- const results = await Promise.allSettled(userIds.map((userId) => fetchUser(client, userId)));
2466
- return results.filter((result) => result.status === "fulfilled").map((result) => result.value);
2467
- }
2468
- __name(fetchManyUsers, "fetchManyUsers");
2469
-
2470
- // src/bot/utilities/users/updateMemberRoles.ts
2471
- async function updateMemberRoles(rolesToAdd, rolesToRemove, member) {
2472
- const current = new Set(member.roles.cache.map((r) => r.id));
2473
- const toAdd = new Set(rolesToAdd);
2474
- const toRemove = new Set(rolesToRemove);
2475
- const updated = current.union(toAdd).difference(toRemove);
2476
- await member.roles.set([
2477
- ...updated
2478
- ]);
2479
- }
2480
- __name(updateMemberRoles, "updateMemberRoles");
2416
+ _Seedcord.isInstantiated = true;
2417
+ this.effects = new EffectsRegistry(this);
2418
+ this.bot = new Bot(this);
2419
+ this.healthCheck = new services.HealthCheck(this.shutdown);
2420
+ this.registerStartupTasks();
2421
+ }
2422
+ /**
2423
+ * Registers default startup tasks
2424
+ * @internal
2425
+ */
2426
+ registerStartupTasks() {
2427
+ this.startup.addTask(services.StartupPhase.Configuration, "Effect Initialization", async () => {
2428
+ this.effects.logger.info(chalk3__default.default.bold("Initializing"));
2429
+ await this.effects.init();
2430
+ this.effects.logger.info(chalk3__default.default.bold("Initialized"));
2431
+ });
2432
+ this.startup.addTask(services.StartupPhase.Instantiation, "Bot Initialization", async () => {
2433
+ this.bot.logger.info(chalk3__default.default.bold("Initializing"));
2434
+ await this.bot.init();
2435
+ this.bot.logger.info(chalk3__default.default.bold("Initialized"));
2436
+ });
2437
+ this.startup.addTask(services.StartupPhase.Ready, "Health Check", async () => {
2438
+ this.healthCheck.logger.info(chalk3__default.default.bold("Initializing"));
2439
+ await this.healthCheck.init();
2440
+ this.healthCheck.logger.info(chalk3__default.default.bold("Initialized"));
2441
+ });
2442
+ }
2443
+ /**
2444
+ * Starts the bot and runs all initialization tasks
2445
+ *
2446
+ * @returns This Seedcord instance when fully initialized
2447
+ */
2448
+ async start() {
2449
+ await super.init();
2450
+ return this;
2451
+ }
2452
+ };
2481
2453
 
2482
2454
  exports.AutocompleteHandler = AutocompleteHandler;
2483
2455
  exports.AutocompleteRoute = AutocompleteRoute;
@@ -2524,7 +2496,6 @@ exports.MiddlewareMetadataKey = MiddlewareMetadataKey;
2524
2496
  exports.MiddlewareType = MiddlewareType;
2525
2497
  exports.MissingPermissions = MissingPermissions;
2526
2498
  exports.ModalRoute = ModalRoute;
2527
- exports.PERM_GROUPS = PERM_GROUPS;
2528
2499
  exports.PermissionNames = PermissionNames;
2529
2500
  exports.Pluggable = Pluggable;
2530
2501
  exports.Plugin = Plugin;
@@ -2547,6 +2518,8 @@ exports.attemptSendDM = attemptSendDM;
2547
2518
  exports.buildSlashRoute = buildSlashRoute;
2548
2519
  exports.checkBotPermissions = checkBotPermissions;
2549
2520
  exports.checkPermissions = checkPermissions;
2521
+ exports.ensureBotPermissions = ensureBotPermissions;
2522
+ exports.ensurePermissions = ensurePermissions;
2550
2523
  exports.extractErrorResponse = extractErrorResponse;
2551
2524
  exports.fetchGuildMember = fetchGuildMember;
2552
2525
  exports.fetchManyGuildMembers = fetchManyGuildMembers;