seedcord 0.7.1 → 0.8.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
@@ -34,8 +34,6 @@ var crypto2__namespace = /*#__PURE__*/_interopNamespace(crypto2);
34
34
 
35
35
  var __defProp = Object.defineProperty;
36
36
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
37
-
38
- // src/bot/decorators/Command.ts
39
37
  var CommandMetadataKey = Symbol("command:metadata");
40
38
  function RegisterCommand(scope, guilds = []) {
41
39
  return (ctor) => {
@@ -45,6 +43,20 @@ function RegisterCommand(scope, guilds = []) {
45
43
  scope,
46
44
  guilds
47
45
  };
46
+ const existingMeta = Reflect.getOwnMetadata(CommandMetadataKey, ctor);
47
+ if (existingMeta) {
48
+ throw new services.SeedcordError(services.SeedcordErrorCode.DecoratorCommandAlreadyRegistered, [
49
+ ctor.name,
50
+ existingMeta.scope,
51
+ scope
52
+ ]);
53
+ }
54
+ if (scope === "global" && guilds.length > 0) {
55
+ throw new services.SeedcordError(services.SeedcordErrorCode.DecoratorCommandGlobalWithGuilds);
56
+ }
57
+ if (scope === "guild" && (!Array.isArray(guilds) || guilds.length === 0)) {
58
+ throw new services.SeedcordError(services.SeedcordErrorCode.DecoratorCommandGuildWithoutGuilds);
59
+ }
48
60
  Reflect.defineMetadata(CommandMetadataKey, meta, ctor);
49
61
  };
50
62
  }
@@ -291,24 +303,20 @@ var CommandRegistry = class {
291
303
  await guild.commands.set(commands);
292
304
  const tag = commands.length === 1 ? "command" : "commands";
293
305
  this.logger.info(`${chalk3__default.default.bold.green("Configured")} ${chalk3__default.default.magenta.bold(commands.length)} ${tag} for guild ${chalk3__default.default.bold.yellow(guild.name)}`);
294
- this.logger.info(` \u2192 ${commands.map((command) => chalk3__default.default.bold.cyan(command.name)).join(", ")}`);
306
+ this.logger.info(`\u2192 ${commands.map((command) => chalk3__default.default.bold.cyan(command.name)).join(", ")}`);
295
307
  }
296
308
  }
297
309
  };
298
310
 
299
311
  // src/bot/decorators/Events.ts
300
312
  var EventMetadataKey = Symbol("event:metadata");
301
- function RegisterEvent(events, options) {
313
+ function RegisterEvent(...defs) {
302
314
  return function(constructor) {
303
315
  const saved = Reflect.getMetadata(EventMetadataKey, constructor);
304
316
  const existing = saved ?? [];
305
- const toStore = Array.isArray(events) ? events : [
306
- events
307
- ];
308
- const frequency = options?.frequency ?? "on";
309
- const entries = toStore.map((event) => ({
317
+ const entries = defs.map(([event, options]) => ({
310
318
  event,
311
- frequency
319
+ frequency: options?.frequency ?? "on"
312
320
  }));
313
321
  Reflect.defineMetadata(EventMetadataKey, [
314
322
  ...existing,
@@ -317,8 +325,6 @@ function RegisterEvent(events, options) {
317
325
  };
318
326
  }
319
327
  __name(RegisterEvent, "RegisterEvent");
320
-
321
- // src/bot/decorators/Middlewares.ts
322
328
  var MiddlewareType = /* @__PURE__ */ (function(MiddlewareType2) {
323
329
  MiddlewareType2["Interaction"] = "middleware:interaction";
324
330
  MiddlewareType2["Event"] = "middleware:event";
@@ -329,10 +335,10 @@ function Middleware(type, priority = 0, options = {}) {
329
335
  return (ctor) => {
330
336
  const normalizedPriority = Number(priority);
331
337
  if (!Number.isFinite(normalizedPriority)) {
332
- throw new TypeError("Middleware priority must be a finite number");
338
+ throw new services.SeedcordTypeError(services.SeedcordErrorCode.DecoratorInvalidMiddlewarePriority);
333
339
  }
334
- if (type === "middleware:interaction" && options.events?.length) {
335
- throw new Error("Interaction middleware cannot specify event filters");
340
+ if (type === "middleware:interaction" && Array.isArray(options.events) && options.events.length > 0) {
341
+ throw new services.SeedcordError(services.SeedcordErrorCode.DecoratorInteractionEventFilter);
336
342
  }
337
343
  const metadata = {
338
344
  priority: normalizedPriority,
@@ -345,8 +351,6 @@ function Middleware(type, priority = 0, options = {}) {
345
351
  };
346
352
  }
347
353
  __name(Middleware, "Middleware");
348
-
349
- // src/interfaces/Handler.ts
350
354
  var BaseHandler = class {
351
355
  static {
352
356
  __name(this, "BaseHandler");
@@ -357,10 +361,18 @@ var BaseHandler = class {
357
361
  errored = false;
358
362
  event;
359
363
  args = [];
364
+ logger;
360
365
  constructor(event, core, args) {
361
366
  this.core = core;
362
367
  this.event = event;
363
368
  this.args = args ?? [];
369
+ this.logger = new services.Logger(this.constructor.name);
370
+ this.populate();
371
+ }
372
+ /**
373
+ * Populates the handler with necessary data before execution. Override this method in your handler classes to customize population logic. This method is called at the end of the constructor before all async operations.
374
+ */
375
+ populate() {
364
376
  }
365
377
  hasChecks() {
366
378
  return this.checkable;
@@ -575,8 +587,12 @@ var EventController = class {
575
587
  for (const entry of handlerEntries) {
576
588
  const register = entry.frequency === "once" ? this.core.bot.client.once.bind(this.core.bot.client) : this.core.bot.client.on.bind(this.core.bot.client);
577
589
  register(eventName, (...args) => {
590
+ this.core.bot.emit("any:event", eventName, ...args);
578
591
  void (async () => {
579
- await this.processEvent(eventName, args, entry.ctor);
592
+ await this.processEvent(eventName, args, entry.ctor).catch((err) => {
593
+ this.logger.error(`[${chalk3__default.default.bold.red("UNHANDLED ERROR AT ROOT")}] ${err.name}`, err.stack);
594
+ this.core.bot.emit("error:unhandled:event", err);
595
+ });
580
596
  })();
581
597
  });
582
598
  }
@@ -713,7 +729,7 @@ function buildSlashRoute(arg1, arg2, arg3) {
713
729
  group = arg1.options.getSubcommandGroup(false) ?? void 0;
714
730
  sub = arg1.options.getSubcommand(false) ?? void 0;
715
731
  } else {
716
- throw new TypeError("Invalid argument passed to buildSlashRoute");
732
+ throw new services.SeedcordTypeError(services.SeedcordErrorCode.UtilInvalidSlashRouteArgument);
717
733
  }
718
734
  if (sub && group) return `${command}/${group}/${sub}`;
719
735
  if (sub) return `${command}/${sub}`;
@@ -816,7 +832,7 @@ function Catchable(options) {
816
832
  const originalMethod = descriptor.value;
817
833
  descriptor.value = async function(...args) {
818
834
  const interaction = this.getEvent();
819
- if (!originalMethod) throw new Error("Method not found");
835
+ if (!originalMethod) throw new services.SeedcordError(services.SeedcordErrorCode.DecoratorMethodNotFound);
820
836
  try {
821
837
  await originalMethod.apply(this, args);
822
838
  } catch (error) {
@@ -1033,8 +1049,10 @@ var InteractionController = class {
1033
1049
  }
1034
1050
  attachToClient() {
1035
1051
  this.core.bot.client.on(discord_js.Events.InteractionCreate, (interaction) => {
1052
+ this.core.bot.emit("any:interaction", interaction);
1036
1053
  this.handleInteraction(interaction).catch((err) => {
1037
1054
  this.logger.error(`[${chalk3__default.default.bold.red("UNHANDLED ERROR AT ROOT")}] ${err.name}`, err.stack);
1055
+ this.core.bot.emit("error:unhandled:interaction", err);
1038
1056
  });
1039
1057
  });
1040
1058
  }
@@ -1055,7 +1073,11 @@ var InteractionController = class {
1055
1073
  }
1056
1074
  async processInteraction(interaction, extractKey, getHandler, args) {
1057
1075
  const key = extractKey(interaction);
1058
- if (this.keysToIgnore.has(key)) return;
1076
+ if ([
1077
+ ...this.keysToIgnore
1078
+ ].some((pattern) => typeof pattern === "string" ? pattern === key : pattern.test(key))) {
1079
+ return;
1080
+ }
1059
1081
  for (const { ctor } of this.middlewares) {
1060
1082
  const middleware = new ctor(interaction, this.core, args);
1061
1083
  if (middleware.hasChecks()) await middleware.runChecks();
@@ -1152,16 +1174,16 @@ var InteractionController = class {
1152
1174
  await this.processInteraction(interaction, () => autocompleteKey, (key) => this.autocompleteMap.get(key));
1153
1175
  }
1154
1176
  };
1155
- var Plugin = class {
1177
+ var Plugin = class extends services.StrictEventEmitter {
1156
1178
  static {
1157
1179
  __name(this, "Plugin");
1158
1180
  }
1159
1181
  pluggable;
1160
1182
  constructor(pluggable) {
1161
- this.pluggable = pluggable;
1183
+ super(), this.pluggable = pluggable;
1162
1184
  }
1163
1185
  };
1164
- var Pluggable = class _Pluggable {
1186
+ var Pluggable = class _Pluggable extends services.StrictEventEmitter {
1165
1187
  static {
1166
1188
  __name(this, "Pluggable");
1167
1189
  }
@@ -1170,6 +1192,7 @@ var Pluggable = class _Pluggable {
1170
1192
  startup;
1171
1193
  static PLUGIN_INIT_TIMEOUT_MS = 15e3;
1172
1194
  constructor(shutdown, startup) {
1195
+ super();
1173
1196
  this.shutdown = shutdown;
1174
1197
  this.startup = startup;
1175
1198
  }
@@ -1194,15 +1217,21 @@ var Pluggable = class _Pluggable {
1194
1217
  * @param startupPhase - When during startup to initialize this plugin ({@link StartupPhase})
1195
1218
  * @param args - Additional arguments to pass to the plugin constructor
1196
1219
  * @returns This instance with the plugin attached as a typed property
1197
- * @throws An {@link Error} When called after initialization or if key already exists
1220
+ * @throws A {@link SeedcordError} When called after initialization or if key already exists
1198
1221
  * @example
1199
1222
  * ```typescript
1200
1223
  * seedcord.attach('db', Mongo, StartupPhase.Configuration, { uri: 'mongodb://...', name: 'seedcord', dir: ... })
1201
1224
  * ```
1202
1225
  */
1203
1226
  attach(key, Plugin2, startupPhase, ...args) {
1204
- if (this.isInitialized) throw new Error("Cannot attach a plugin after initialization.");
1205
- if (this[key]) throw new Error(`Plugin with key "${key}" already exists.`);
1227
+ if (this.isInitialized) {
1228
+ throw new services.SeedcordError(services.SeedcordErrorCode.CorePluginAfterInit);
1229
+ }
1230
+ if (this[key]) {
1231
+ throw new services.SeedcordError(services.SeedcordErrorCode.CorePluginKeyExists, [
1232
+ key
1233
+ ]);
1234
+ }
1206
1235
  const instance = new Plugin2(this, ...args);
1207
1236
  const entry = {
1208
1237
  [key]: instance
@@ -1215,6 +1244,8 @@ var Pluggable = class _Pluggable {
1215
1244
  return Object.assign(this, entry);
1216
1245
  }
1217
1246
  };
1247
+ var emojiStorage = {};
1248
+ var Emojis = emojiStorage;
1218
1249
  var EmojiInjector = class {
1219
1250
  static {
1220
1251
  __name(this, "EmojiInjector");
@@ -1225,6 +1256,7 @@ var EmojiInjector = class {
1225
1256
  this.core = core;
1226
1257
  }
1227
1258
  async init() {
1259
+ this.clearEmojis();
1228
1260
  if (!this.core.config.bot.emojis || Object.keys(this.core.config.bot.emojis).length === 0) {
1229
1261
  this.logger.info(`${chalk3__default.default.bold.green("Loaded")}: ${chalk3__default.default.magenta.bold("0")} emojis`);
1230
1262
  return;
@@ -1233,14 +1265,24 @@ var EmojiInjector = class {
1233
1265
  await this.core.bot.client.application?.emojis.fetch();
1234
1266
  let foundCount = 0;
1235
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;
1271
+ }
1236
1272
  const emoji = this.core.bot.client.application?.emojis.cache.find((e) => e.name === emojiName);
1237
1273
  if (emoji) {
1238
- configEmojis[key] = `<${emoji.identifier}>`;
1274
+ emojiStorage[key] = `<${emoji.identifier}>`;
1239
1275
  foundCount++;
1240
1276
  this.logger.debug(`${chalk3__default.default.bold.green("Found")}: ${chalk3__default.default.magenta.bold(emojiName)} (${emoji.id})`);
1277
+ return;
1241
1278
  }
1279
+ emojiStorage[key] = emojiName;
1280
+ this.logger.warn(`${chalk3__default.default.bold.yellow("Missing")}: ${chalk3__default.default.magenta.bold(emojiName)} (using configured value)`);
1242
1281
  });
1243
- this.logger.info(`${chalk3__default.default.bold.green("Loaded")}: ${chalk3__default.default.magenta.bold(foundCount)} emojis`);
1282
+ this.logger.info(`${chalk3__default.default.bold.green("Loaded")}: ${chalk3__default.default.magenta.bold(foundCount)} emoji(s)`);
1283
+ }
1284
+ clearEmojis() {
1285
+ for (const key of Object.keys(emojiStorage)) Reflect.deleteProperty(emojiStorage, key);
1244
1286
  }
1245
1287
  };
1246
1288
 
@@ -1268,6 +1310,7 @@ var Bot = class extends Plugin {
1268
1310
  events;
1269
1311
  commands;
1270
1312
  emojiInjector;
1313
+ emojis = Emojis;
1271
1314
  constructor(core) {
1272
1315
  super(core), this.core = core;
1273
1316
  this._client = new discord_js.Client(core.config.bot.clientOptions);
@@ -1319,11 +1362,16 @@ var Bot = class extends Plugin {
1319
1362
  get client() {
1320
1363
  return this._client;
1321
1364
  }
1365
+ emit(event, ...args) {
1366
+ return super.emit(event, ...args);
1367
+ }
1322
1368
  };
1323
1369
  _ts_decorate4([
1324
1370
  envapt.Envapt("DISCORD_BOT_TOKEN", {
1325
1371
  converter(raw, _fallback) {
1326
- if (typeof raw !== "string") throw new Error("Missing DISCORD_BOT_TOKEN");
1372
+ if (typeof raw !== "string") {
1373
+ throw new services.SeedcordError(services.SeedcordErrorCode.ConfigMissingDiscordToken);
1374
+ }
1327
1375
  return raw;
1328
1376
  }
1329
1377
  }),
@@ -1386,6 +1434,25 @@ function _ts_metadata5(k, v) {
1386
1434
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1387
1435
  }
1388
1436
  __name(_ts_metadata5, "_ts_metadata");
1437
+ function webhookUrlValidator(raw, _fallback) {
1438
+ if (raw === null) {
1439
+ throw new services.SeedcordError(services.SeedcordErrorCode.ConfigUnknownExceptionWebhookMissing);
1440
+ }
1441
+ if (typeof raw !== "string") {
1442
+ throw new services.SeedcordError(services.SeedcordErrorCode.ConfigUnknownExceptionWebhookInvalid);
1443
+ }
1444
+ const value = raw.trim();
1445
+ if (value === "") {
1446
+ throw new services.SeedcordError(services.SeedcordErrorCode.ConfigUnknownExceptionWebhookMissing);
1447
+ }
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);
1452
+ }
1453
+ return value;
1454
+ }
1455
+ __name(webhookUrlValidator, "webhookUrlValidator");
1389
1456
  exports.UnknownException = class _UnknownException extends WebhookLog {
1390
1457
  static {
1391
1458
  __name(this, "UnknownException");
@@ -1425,11 +1492,7 @@ exports.UnknownException = class _UnknownException extends WebhookLog {
1425
1492
  };
1426
1493
  _ts_decorate5([
1427
1494
  envapt.Envapt("UNKNOWN_EXCEPTION_WEBHOOK_URL", {
1428
- converter(raw, _fallback) {
1429
- if (!raw) throw new Error("Missing UNKNOWN_EXCEPTION_WEBHOOK_URL");
1430
- if (!URL.canParse(String(raw))) throw new Error("Invalid UNKNOWN_EXCEPTION_WEBHOOK_URL");
1431
- return raw;
1432
- }
1495
+ converter: /* @__PURE__ */ __name((raw, fallback) => webhookUrlValidator(raw), "converter")
1433
1496
  }),
1434
1497
  _ts_metadata5("design:type", String)
1435
1498
  ], exports.UnknownException, "unknownExceptionWebhookUrl", void 0);
@@ -1618,7 +1681,7 @@ var Seedcord = class _Seedcord extends Pluggable {
1618
1681
  * Creates a new Seedcord instance
1619
1682
  *
1620
1683
  * @param config - Bot configuration including paths and Discord client options
1621
- * @throws An {@link Error} When attempting to create multiple instances (singleton)
1684
+ * @throws An {@link SeedcordError} When attempting to create multiple instances (singleton)
1622
1685
  */
1623
1686
  constructor(config) {
1624
1687
  const shutdown = new services.CoordinatedShutdown();
@@ -1627,7 +1690,7 @@ var Seedcord = class _Seedcord extends Pluggable {
1627
1690
  this.shutdown = shutdown;
1628
1691
  this.startup = startup;
1629
1692
  if (_Seedcord.isInstantiated) {
1630
- throw new Error("Seedcord can only be instantiated once. Use the existing instance instead.");
1693
+ throw new services.SeedcordError(services.SeedcordErrorCode.CoreSingletonViolation);
1631
1694
  }
1632
1695
  _Seedcord.isInstantiated = true;
1633
1696
  this.effects = new EffectsRegistry(this);
@@ -1679,7 +1742,7 @@ function EventCatchable(log) {
1679
1742
  return function(_target, _prop, descriptor) {
1680
1743
  const original = descriptor.value;
1681
1744
  descriptor.value = async function(...args) {
1682
- if (!original) throw new Error("Method not found");
1745
+ if (!original) throw new services.SeedcordError(services.SeedcordErrorCode.DecoratorMethodNotFound);
1683
1746
  try {
1684
1747
  await original.apply(this, args);
1685
1748
  } catch (err) {
@@ -1703,6 +1766,206 @@ function EventCatchable(log) {
1703
1766
  };
1704
1767
  }
1705
1768
  __name(EventCatchable, "EventCatchable");
1769
+ var resolveFactory = /* @__PURE__ */ __name(async (input, ctx) => typeof input === "function" ? await input(ctx) : input, "resolveFactory");
1770
+ var isV2 = /* @__PURE__ */ __name((opts) => "container" in opts, "isV2");
1771
+ var usesResolver = /* @__PURE__ */ __name((opts) => "resolveDecision" in opts, "usesResolver");
1772
+ var usesCustomIds = /* @__PURE__ */ __name((opts) => "confirmCustomIds" in opts, "usesCustomIds");
1773
+ var isMessageComponentIx = /* @__PURE__ */ __name((ix) => "deferUpdate" in ix && "customId" in ix, "isMessageComponentIx");
1774
+ var buildPrompt = /* @__PURE__ */ __name(async (opts, ctx) => {
1775
+ if (isV2(opts)) {
1776
+ const container = await resolveFactory(opts.container, ctx);
1777
+ return {
1778
+ flags: "IsComponentsV2",
1779
+ components: [
1780
+ container.component
1781
+ ]
1782
+ };
1783
+ }
1784
+ const prompt = await resolveFactory(opts.prompt, ctx);
1785
+ const rows = await resolveFactory(opts.rows, ctx);
1786
+ const components = [
1787
+ ...rows
1788
+ ];
1789
+ const payload = {
1790
+ components
1791
+ };
1792
+ if (typeof prompt === "string") payload.content = prompt;
1793
+ else payload.embeds = [
1794
+ prompt.component
1795
+ ];
1796
+ return payload;
1797
+ }, "buildPrompt");
1798
+ var clearedPayload = /* @__PURE__ */ __name((opts) => isV2(opts) ? {
1799
+ flags: "IsComponentsV2",
1800
+ components: []
1801
+ } : {
1802
+ components: []
1803
+ }, "clearedPayload");
1804
+ var decide = /* @__PURE__ */ __name(async (opts, i) => {
1805
+ if (usesResolver(opts)) return opts.resolveDecision(i);
1806
+ if (usesCustomIds(opts)) {
1807
+ const { confirmCustomIds, cancelCustomIds = [] } = opts;
1808
+ if (confirmCustomIds.includes(i.customId)) return true;
1809
+ if (cancelCustomIds.includes(i.customId)) return false;
1810
+ }
1811
+ return false;
1812
+ }, "decide");
1813
+ var shouldDefer = /* @__PURE__ */ __name((ix, opts, isSlash, isContext) => {
1814
+ if (ix.deferred) return false;
1815
+ if (opts.defer === false) return false;
1816
+ return isSlash || isContext;
1817
+ }, "shouldDefer");
1818
+ var maybeDefer = /* @__PURE__ */ __name(async (ix, opts, isSlash, isContext) => {
1819
+ if (!shouldDefer(ix, opts, isSlash, isContext)) return;
1820
+ if (isSlash || isContext) {
1821
+ const { ephemeral = true } = opts;
1822
+ const flags = ephemeral ? {
1823
+ flags: "Ephemeral"
1824
+ } : void 0;
1825
+ if (flags) await ix.deferReply(flags);
1826
+ else await ix.deferReply();
1827
+ } else if (isMessageComponentIx(ix)) {
1828
+ await ix.deferUpdate();
1829
+ } else {
1830
+ await ix.deferReply().catch(() => void 0);
1831
+ }
1832
+ }, "maybeDefer");
1833
+ var sendPrompt = /* @__PURE__ */ __name(async (ix, payload, ephemeral, isSlash, isContext) => {
1834
+ if (isSlash || isContext) {
1835
+ if (ix.deferred) return ix.editReply(payload);
1836
+ const reply = {
1837
+ ...payload,
1838
+ ...ephemeral ? {
1839
+ flags: "Ephemeral"
1840
+ } : {}
1841
+ };
1842
+ await ix.reply(reply);
1843
+ return ix.fetchReply();
1844
+ }
1845
+ const follow = {
1846
+ ...payload,
1847
+ ...ephemeral ? {
1848
+ flags: "Ephemeral"
1849
+ } : {}
1850
+ };
1851
+ return ix.followUp(follow);
1852
+ }, "sendPrompt");
1853
+ var awaitComponent = /* @__PURE__ */ __name(async (msg, original, opts) => {
1854
+ const componentType = opts.componentType ?? discord_js.ComponentType.Button;
1855
+ const timeoutMs = opts.timeoutMs ?? 1e4;
1856
+ try {
1857
+ const button = await msg.awaitMessageComponent({
1858
+ componentType,
1859
+ time: timeoutMs,
1860
+ filter: /* @__PURE__ */ __name((c) => {
1861
+ if (c.user.id !== original.user.id) return false;
1862
+ if (usesCustomIds(opts)) {
1863
+ const { confirmCustomIds, cancelCustomIds = [] } = opts;
1864
+ return confirmCustomIds.includes(c.customId) || cancelCustomIds.includes(c.customId);
1865
+ }
1866
+ return true;
1867
+ }, "filter")
1868
+ });
1869
+ return {
1870
+ button,
1871
+ timedOut: false
1872
+ };
1873
+ } catch {
1874
+ return {
1875
+ button: null,
1876
+ timedOut: true
1877
+ };
1878
+ }
1879
+ }, "awaitComponent");
1880
+ var clearUi = /* @__PURE__ */ __name(async (ix, msg, opts, isSlash) => {
1881
+ if (!isSlash) {
1882
+ try {
1883
+ await msg.edit(clearedPayload(opts));
1884
+ } catch {
1885
+ await ix.deleteReply(msg).catch(() => void 0);
1886
+ }
1887
+ return;
1888
+ }
1889
+ await ix.editReply(clearedPayload(opts)).catch(() => void 0);
1890
+ }, "clearUi");
1891
+ var normalizeClassicOutcome = /* @__PURE__ */ __name((payload) => ({
1892
+ ...payload,
1893
+ components: payload.components ?? []
1894
+ }), "normalizeClassicOutcome");
1895
+ var outcomeReplacement = /* @__PURE__ */ __name(async (opts, ctx, confirmed, timedOut) => {
1896
+ const outcomes = opts.outcomeUi;
1897
+ if (isV2(opts)) {
1898
+ const v2Outcomes = outcomes;
1899
+ if (timedOut) return await resolveFactory(v2Outcomes.onTimeout, ctx);
1900
+ if (!confirmed) return await resolveFactory(v2Outcomes.onCancel, ctx);
1901
+ if (v2Outcomes.onConfirm) return await resolveFactory(v2Outcomes.onConfirm, ctx);
1902
+ return null;
1903
+ }
1904
+ const classicOutcomes = outcomes;
1905
+ if (timedOut) return normalizeClassicOutcome(await resolveFactory(classicOutcomes.onTimeout, ctx));
1906
+ if (!confirmed) return normalizeClassicOutcome(await resolveFactory(classicOutcomes.onCancel, ctx));
1907
+ if (classicOutcomes.onConfirm) {
1908
+ return normalizeClassicOutcome(await resolveFactory(classicOutcomes.onConfirm, ctx));
1909
+ }
1910
+ return null;
1911
+ }, "outcomeReplacement");
1912
+ function Confirmable(question, options) {
1913
+ return function(_target, _propertyKey, descriptor) {
1914
+ const original = descriptor.value;
1915
+ descriptor.value = async function(...args) {
1916
+ if (!original) throw new services.SeedcordError(services.SeedcordErrorCode.DecoratorMethodNotFound);
1917
+ const ix = this.getEvent();
1918
+ const isSlash = ix.isChatInputCommand();
1919
+ const isContext = ix.isContextMenuCommand();
1920
+ const { ephemeral = true } = options;
1921
+ await maybeDefer(ix, options, isSlash, isContext);
1922
+ const q = typeof question === "function" ? await question.apply(this) : question;
1923
+ const ctx = {
1924
+ handler: this,
1925
+ interaction: ix,
1926
+ question: q
1927
+ };
1928
+ const prompt = await buildPrompt(options, ctx);
1929
+ const promptMsg = await sendPrompt(ix, prompt, ephemeral, isSlash, isContext);
1930
+ const { button, timedOut } = await awaitComponent(promptMsg, ix, options);
1931
+ let confirmed = false;
1932
+ if (button) {
1933
+ await button.deferUpdate().catch(() => void 0);
1934
+ confirmed = await decide(options, button);
1935
+ }
1936
+ const replacement = await outcomeReplacement(options, ctx, confirmed, timedOut);
1937
+ if (replacement) {
1938
+ if (isSlash || isContext) {
1939
+ await ix.editReply(replacement).catch(() => void 0);
1940
+ } else {
1941
+ await promptMsg.edit(replacement).catch(async () => {
1942
+ await ix.deleteReply(promptMsg).catch(() => void 0);
1943
+ await ix.followUp(replacement).catch(() => void 0);
1944
+ });
1945
+ }
1946
+ } else {
1947
+ await clearUi(ix, promptMsg, options, isSlash);
1948
+ }
1949
+ if (options.onResolved) {
1950
+ await options.onResolved({
1951
+ confirmed,
1952
+ timedOut,
1953
+ handler: this,
1954
+ interaction: ix,
1955
+ question: q,
1956
+ ...button ? {
1957
+ button
1958
+ } : {}
1959
+ });
1960
+ }
1961
+ if (confirmed) {
1962
+ await original.apply(this, args);
1963
+ }
1964
+ };
1965
+ return descriptor;
1966
+ };
1967
+ }
1968
+ __name(Confirmable, "Confirmable");
1706
1969
 
1707
1970
  // src/bot/errors/Channels.ts
1708
1971
  var ChannelNotFoundError = class extends CustomError {
@@ -2068,7 +2331,9 @@ function checkPermissions(client, roleOrChannel, scope = "all", inverse = false)
2068
2331
  if (roleOrChannel instanceof discord_js.Role) {
2069
2332
  permissions = roleOrChannel.permissions;
2070
2333
  } else {
2071
- if (!client.user) throw new Error("Client user is not available");
2334
+ if (!client.user) {
2335
+ throw new services.SeedcordError(services.SeedcordErrorCode.CoreClientUserUnavailable);
2336
+ }
2072
2337
  permissions = roleOrChannel.permissionsFor(client.user, true);
2073
2338
  }
2074
2339
  if (!permissions) {
@@ -2087,12 +2352,16 @@ function checkPermissions(client, roleOrChannel, scope = "all", inverse = false)
2087
2352
  }
2088
2353
  }
2089
2354
  __name(checkPermissions, "checkPermissions");
2090
-
2091
- // src/bot/utilities/roles/getBotRole.ts
2092
2355
  function getBotRole(client, guild) {
2093
- if (!client.user) throw new Error("Client user is not available");
2356
+ if (!client.user) {
2357
+ throw new services.SeedcordError(services.SeedcordErrorCode.CoreClientUserUnavailable);
2358
+ }
2094
2359
  const botRole = guild.roles.botRoleFor(client.user);
2095
- if (!botRole) throw new Error("Bot role not found in guild");
2360
+ if (!botRole) {
2361
+ throw new services.SeedcordError(services.SeedcordErrorCode.CoreBotRoleMissing, [
2362
+ guild.id
2363
+ ]);
2364
+ }
2096
2365
  return botRole;
2097
2366
  }
2098
2367
  __name(getBotRole, "getBotRole");
@@ -2221,6 +2490,7 @@ exports.ChannelNotTextChannel = ChannelNotTextChannel;
2221
2490
  exports.Checkable = Checkable;
2222
2491
  exports.CommandMetadataKey = CommandMetadataKey;
2223
2492
  exports.CommandRegistry = CommandRegistry;
2493
+ exports.Confirmable = Confirmable;
2224
2494
  exports.ContextMenuRoute = ContextMenuRoute;
2225
2495
  exports.CouldNotFindChannel = CouldNotFindChannel;
2226
2496
  exports.CustomError = CustomError;
@@ -2230,6 +2500,7 @@ exports.EffectsEmitter = EffectsEmitter;
2230
2500
  exports.EffectsHandler = EffectsHandler;
2231
2501
  exports.EffectsRegistry = EffectsRegistry;
2232
2502
  exports.EmojiInjector = EmojiInjector;
2503
+ exports.Emojis = Emojis;
2233
2504
  exports.EventCatchable = EventCatchable;
2234
2505
  exports.EventController = EventController;
2235
2506
  exports.EventHandler = EventHandler;