seedcord 0.7.1 → 0.8.0

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,15 +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
- this.logger.debug(`${chalk3__default.default.bold.green("Found")}: ${chalk3__default.default.magenta.bold(emojiName)} (${emoji.id})`);
1276
+ return;
1241
1277
  }
1278
+ emojiStorage[key] = emojiName;
1279
+ this.logger.warn(`${chalk3__default.default.bold.yellow("Missing")}: ${chalk3__default.default.magenta.bold(emojiName)} (using configured value)`);
1242
1280
  });
1243
1281
  this.logger.info(`${chalk3__default.default.bold.green("Loaded")}: ${chalk3__default.default.magenta.bold(foundCount)} emojis`);
1244
1282
  }
1283
+ clearEmojis() {
1284
+ for (const key of Object.keys(emojiStorage)) Reflect.deleteProperty(emojiStorage, key);
1285
+ }
1245
1286
  };
1246
1287
 
1247
1288
  // src/bot/Bot.ts
@@ -1268,6 +1309,7 @@ var Bot = class extends Plugin {
1268
1309
  events;
1269
1310
  commands;
1270
1311
  emojiInjector;
1312
+ emojis = Emojis;
1271
1313
  constructor(core) {
1272
1314
  super(core), this.core = core;
1273
1315
  this._client = new discord_js.Client(core.config.bot.clientOptions);
@@ -1319,11 +1361,16 @@ var Bot = class extends Plugin {
1319
1361
  get client() {
1320
1362
  return this._client;
1321
1363
  }
1364
+ emit(event, ...args) {
1365
+ return super.emit(event, ...args);
1366
+ }
1322
1367
  };
1323
1368
  _ts_decorate4([
1324
1369
  envapt.Envapt("DISCORD_BOT_TOKEN", {
1325
1370
  converter(raw, _fallback) {
1326
- if (typeof raw !== "string") throw new Error("Missing DISCORD_BOT_TOKEN");
1371
+ if (typeof raw !== "string") {
1372
+ throw new services.SeedcordError(services.SeedcordErrorCode.ConfigMissingDiscordToken);
1373
+ }
1327
1374
  return raw;
1328
1375
  }
1329
1376
  }),
@@ -1386,6 +1433,25 @@ function _ts_metadata5(k, v) {
1386
1433
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1387
1434
  }
1388
1435
  __name(_ts_metadata5, "_ts_metadata");
1436
+ function webhookUrlValidator(raw, _fallback) {
1437
+ if (raw === null) {
1438
+ throw new services.SeedcordError(services.SeedcordErrorCode.ConfigUnknownExceptionWebhookMissing);
1439
+ }
1440
+ if (typeof raw !== "string") {
1441
+ throw new services.SeedcordError(services.SeedcordErrorCode.ConfigUnknownExceptionWebhookInvalid);
1442
+ }
1443
+ const value = raw.trim();
1444
+ if (value === "") {
1445
+ throw new services.SeedcordError(services.SeedcordErrorCode.ConfigUnknownExceptionWebhookMissing);
1446
+ }
1447
+ const pattern = String.raw`^https?:\/\/(?:canary\.|ptb\.)?discord(?:app)?\.com\/api\/webhooks\/\d+\/[\w$-]+$`;
1448
+ const discordWebhookRegex = new RegExp(pattern);
1449
+ if (!URL.canParse(value) || !discordWebhookRegex.test(value)) {
1450
+ throw new services.SeedcordError(services.SeedcordErrorCode.ConfigUnknownExceptionWebhookInvalid);
1451
+ }
1452
+ return value;
1453
+ }
1454
+ __name(webhookUrlValidator, "webhookUrlValidator");
1389
1455
  exports.UnknownException = class _UnknownException extends WebhookLog {
1390
1456
  static {
1391
1457
  __name(this, "UnknownException");
@@ -1425,11 +1491,7 @@ exports.UnknownException = class _UnknownException extends WebhookLog {
1425
1491
  };
1426
1492
  _ts_decorate5([
1427
1493
  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
- }
1494
+ converter: /* @__PURE__ */ __name((raw, fallback) => webhookUrlValidator(raw), "converter")
1433
1495
  }),
1434
1496
  _ts_metadata5("design:type", String)
1435
1497
  ], exports.UnknownException, "unknownExceptionWebhookUrl", void 0);
@@ -1618,7 +1680,7 @@ var Seedcord = class _Seedcord extends Pluggable {
1618
1680
  * Creates a new Seedcord instance
1619
1681
  *
1620
1682
  * @param config - Bot configuration including paths and Discord client options
1621
- * @throws An {@link Error} When attempting to create multiple instances (singleton)
1683
+ * @throws An {@link SeedcordError} When attempting to create multiple instances (singleton)
1622
1684
  */
1623
1685
  constructor(config) {
1624
1686
  const shutdown = new services.CoordinatedShutdown();
@@ -1627,7 +1689,7 @@ var Seedcord = class _Seedcord extends Pluggable {
1627
1689
  this.shutdown = shutdown;
1628
1690
  this.startup = startup;
1629
1691
  if (_Seedcord.isInstantiated) {
1630
- throw new Error("Seedcord can only be instantiated once. Use the existing instance instead.");
1692
+ throw new services.SeedcordError(services.SeedcordErrorCode.CoreSingletonViolation);
1631
1693
  }
1632
1694
  _Seedcord.isInstantiated = true;
1633
1695
  this.effects = new EffectsRegistry(this);
@@ -1679,7 +1741,7 @@ function EventCatchable(log) {
1679
1741
  return function(_target, _prop, descriptor) {
1680
1742
  const original = descriptor.value;
1681
1743
  descriptor.value = async function(...args) {
1682
- if (!original) throw new Error("Method not found");
1744
+ if (!original) throw new services.SeedcordError(services.SeedcordErrorCode.DecoratorMethodNotFound);
1683
1745
  try {
1684
1746
  await original.apply(this, args);
1685
1747
  } catch (err) {
@@ -1703,6 +1765,206 @@ function EventCatchable(log) {
1703
1765
  };
1704
1766
  }
1705
1767
  __name(EventCatchable, "EventCatchable");
1768
+ var resolveFactory = /* @__PURE__ */ __name(async (input, ctx) => typeof input === "function" ? await input(ctx) : input, "resolveFactory");
1769
+ var isV2 = /* @__PURE__ */ __name((opts) => "container" in opts, "isV2");
1770
+ var usesResolver = /* @__PURE__ */ __name((opts) => "resolveDecision" in opts, "usesResolver");
1771
+ var usesCustomIds = /* @__PURE__ */ __name((opts) => "confirmCustomIds" in opts, "usesCustomIds");
1772
+ var isMessageComponentIx = /* @__PURE__ */ __name((ix) => "deferUpdate" in ix && "customId" in ix, "isMessageComponentIx");
1773
+ var buildPrompt = /* @__PURE__ */ __name(async (opts, ctx) => {
1774
+ if (isV2(opts)) {
1775
+ const container = await resolveFactory(opts.container, ctx);
1776
+ return {
1777
+ flags: "IsComponentsV2",
1778
+ components: [
1779
+ container.component
1780
+ ]
1781
+ };
1782
+ }
1783
+ const prompt = await resolveFactory(opts.prompt, ctx);
1784
+ const rows = await resolveFactory(opts.rows, ctx);
1785
+ const components = [
1786
+ ...rows
1787
+ ];
1788
+ const payload = {
1789
+ components
1790
+ };
1791
+ if (typeof prompt === "string") payload.content = prompt;
1792
+ else payload.embeds = [
1793
+ prompt.component
1794
+ ];
1795
+ return payload;
1796
+ }, "buildPrompt");
1797
+ var clearedPayload = /* @__PURE__ */ __name((opts) => isV2(opts) ? {
1798
+ flags: "IsComponentsV2",
1799
+ components: []
1800
+ } : {
1801
+ components: []
1802
+ }, "clearedPayload");
1803
+ var decide = /* @__PURE__ */ __name(async (opts, i) => {
1804
+ if (usesResolver(opts)) return opts.resolveDecision(i);
1805
+ if (usesCustomIds(opts)) {
1806
+ const { confirmCustomIds, cancelCustomIds = [] } = opts;
1807
+ if (confirmCustomIds.includes(i.customId)) return true;
1808
+ if (cancelCustomIds.includes(i.customId)) return false;
1809
+ }
1810
+ return false;
1811
+ }, "decide");
1812
+ var shouldDefer = /* @__PURE__ */ __name((ix, opts, isSlash, isContext) => {
1813
+ if (ix.deferred) return false;
1814
+ if (opts.defer === false) return false;
1815
+ return isSlash || isContext;
1816
+ }, "shouldDefer");
1817
+ var maybeDefer = /* @__PURE__ */ __name(async (ix, opts, isSlash, isContext) => {
1818
+ if (!shouldDefer(ix, opts, isSlash, isContext)) return;
1819
+ if (isSlash || isContext) {
1820
+ const { ephemeral = true } = opts;
1821
+ const flags = ephemeral ? {
1822
+ flags: "Ephemeral"
1823
+ } : void 0;
1824
+ if (flags) await ix.deferReply(flags);
1825
+ else await ix.deferReply();
1826
+ } else if (isMessageComponentIx(ix)) {
1827
+ await ix.deferUpdate();
1828
+ } else {
1829
+ await ix.deferReply().catch(() => void 0);
1830
+ }
1831
+ }, "maybeDefer");
1832
+ var sendPrompt = /* @__PURE__ */ __name(async (ix, payload, ephemeral, isSlash, isContext) => {
1833
+ if (isSlash || isContext) {
1834
+ if (ix.deferred) return ix.editReply(payload);
1835
+ const reply = {
1836
+ ...payload,
1837
+ ...ephemeral ? {
1838
+ flags: "Ephemeral"
1839
+ } : {}
1840
+ };
1841
+ await ix.reply(reply);
1842
+ return ix.fetchReply();
1843
+ }
1844
+ const follow = {
1845
+ ...payload,
1846
+ ...ephemeral ? {
1847
+ flags: "Ephemeral"
1848
+ } : {}
1849
+ };
1850
+ return ix.followUp(follow);
1851
+ }, "sendPrompt");
1852
+ var awaitComponent = /* @__PURE__ */ __name(async (msg, original, opts) => {
1853
+ const componentType = opts.componentType ?? discord_js.ComponentType.Button;
1854
+ const timeoutMs = opts.timeoutMs ?? 1e4;
1855
+ try {
1856
+ const button = await msg.awaitMessageComponent({
1857
+ componentType,
1858
+ time: timeoutMs,
1859
+ filter: /* @__PURE__ */ __name((c) => {
1860
+ if (c.user.id !== original.user.id) return false;
1861
+ if (usesCustomIds(opts)) {
1862
+ const { confirmCustomIds, cancelCustomIds = [] } = opts;
1863
+ return confirmCustomIds.includes(c.customId) || cancelCustomIds.includes(c.customId);
1864
+ }
1865
+ return true;
1866
+ }, "filter")
1867
+ });
1868
+ return {
1869
+ button,
1870
+ timedOut: false
1871
+ };
1872
+ } catch {
1873
+ return {
1874
+ button: null,
1875
+ timedOut: true
1876
+ };
1877
+ }
1878
+ }, "awaitComponent");
1879
+ var clearUi = /* @__PURE__ */ __name(async (ix, msg, opts, isSlash) => {
1880
+ if (!isSlash) {
1881
+ try {
1882
+ await msg.edit(clearedPayload(opts));
1883
+ } catch {
1884
+ await ix.deleteReply(msg).catch(() => void 0);
1885
+ }
1886
+ return;
1887
+ }
1888
+ await ix.editReply(clearedPayload(opts)).catch(() => void 0);
1889
+ }, "clearUi");
1890
+ var normalizeClassicOutcome = /* @__PURE__ */ __name((payload) => ({
1891
+ ...payload,
1892
+ components: payload.components ?? []
1893
+ }), "normalizeClassicOutcome");
1894
+ var outcomeReplacement = /* @__PURE__ */ __name(async (opts, ctx, confirmed, timedOut) => {
1895
+ const outcomes = opts.outcomeUi;
1896
+ if (isV2(opts)) {
1897
+ const v2Outcomes = outcomes;
1898
+ if (timedOut) return await resolveFactory(v2Outcomes.onTimeout, ctx);
1899
+ if (!confirmed) return await resolveFactory(v2Outcomes.onCancel, ctx);
1900
+ if (v2Outcomes.onConfirm) return await resolveFactory(v2Outcomes.onConfirm, ctx);
1901
+ return null;
1902
+ }
1903
+ const classicOutcomes = outcomes;
1904
+ if (timedOut) return normalizeClassicOutcome(await resolveFactory(classicOutcomes.onTimeout, ctx));
1905
+ if (!confirmed) return normalizeClassicOutcome(await resolveFactory(classicOutcomes.onCancel, ctx));
1906
+ if (classicOutcomes.onConfirm) {
1907
+ return normalizeClassicOutcome(await resolveFactory(classicOutcomes.onConfirm, ctx));
1908
+ }
1909
+ return null;
1910
+ }, "outcomeReplacement");
1911
+ function Confirmable(question, options) {
1912
+ return function(_target, _propertyKey, descriptor) {
1913
+ const original = descriptor.value;
1914
+ descriptor.value = async function(...args) {
1915
+ if (!original) throw new services.SeedcordError(services.SeedcordErrorCode.DecoratorMethodNotFound);
1916
+ const ix = this.getEvent();
1917
+ const isSlash = ix.isChatInputCommand();
1918
+ const isContext = ix.isContextMenuCommand();
1919
+ const { ephemeral = true } = options;
1920
+ await maybeDefer(ix, options, isSlash, isContext);
1921
+ const q = typeof question === "function" ? await question.apply(this) : question;
1922
+ const ctx = {
1923
+ handler: this,
1924
+ interaction: ix,
1925
+ question: q
1926
+ };
1927
+ const prompt = await buildPrompt(options, ctx);
1928
+ const promptMsg = await sendPrompt(ix, prompt, ephemeral, isSlash, isContext);
1929
+ const { button, timedOut } = await awaitComponent(promptMsg, ix, options);
1930
+ let confirmed = false;
1931
+ if (button) {
1932
+ await button.deferUpdate().catch(() => void 0);
1933
+ confirmed = await decide(options, button);
1934
+ }
1935
+ const replacement = await outcomeReplacement(options, ctx, confirmed, timedOut);
1936
+ if (replacement) {
1937
+ if (isSlash || isContext) {
1938
+ await ix.editReply(replacement).catch(() => void 0);
1939
+ } else {
1940
+ await promptMsg.edit(replacement).catch(async () => {
1941
+ await ix.deleteReply(promptMsg).catch(() => void 0);
1942
+ await ix.followUp(replacement).catch(() => void 0);
1943
+ });
1944
+ }
1945
+ } else {
1946
+ await clearUi(ix, promptMsg, options, isSlash);
1947
+ }
1948
+ if (options.onResolved) {
1949
+ await options.onResolved({
1950
+ confirmed,
1951
+ timedOut,
1952
+ handler: this,
1953
+ interaction: ix,
1954
+ question: q,
1955
+ ...button ? {
1956
+ button
1957
+ } : {}
1958
+ });
1959
+ }
1960
+ if (confirmed) {
1961
+ await original.apply(this, args);
1962
+ }
1963
+ };
1964
+ return descriptor;
1965
+ };
1966
+ }
1967
+ __name(Confirmable, "Confirmable");
1706
1968
 
1707
1969
  // src/bot/errors/Channels.ts
1708
1970
  var ChannelNotFoundError = class extends CustomError {
@@ -2068,7 +2330,9 @@ function checkPermissions(client, roleOrChannel, scope = "all", inverse = false)
2068
2330
  if (roleOrChannel instanceof discord_js.Role) {
2069
2331
  permissions = roleOrChannel.permissions;
2070
2332
  } else {
2071
- if (!client.user) throw new Error("Client user is not available");
2333
+ if (!client.user) {
2334
+ throw new services.SeedcordError(services.SeedcordErrorCode.CoreClientUserUnavailable);
2335
+ }
2072
2336
  permissions = roleOrChannel.permissionsFor(client.user, true);
2073
2337
  }
2074
2338
  if (!permissions) {
@@ -2087,12 +2351,16 @@ function checkPermissions(client, roleOrChannel, scope = "all", inverse = false)
2087
2351
  }
2088
2352
  }
2089
2353
  __name(checkPermissions, "checkPermissions");
2090
-
2091
- // src/bot/utilities/roles/getBotRole.ts
2092
2354
  function getBotRole(client, guild) {
2093
- if (!client.user) throw new Error("Client user is not available");
2355
+ if (!client.user) {
2356
+ throw new services.SeedcordError(services.SeedcordErrorCode.CoreClientUserUnavailable);
2357
+ }
2094
2358
  const botRole = guild.roles.botRoleFor(client.user);
2095
- if (!botRole) throw new Error("Bot role not found in guild");
2359
+ if (!botRole) {
2360
+ throw new services.SeedcordError(services.SeedcordErrorCode.CoreBotRoleMissing, [
2361
+ guild.id
2362
+ ]);
2363
+ }
2096
2364
  return botRole;
2097
2365
  }
2098
2366
  __name(getBotRole, "getBotRole");
@@ -2221,6 +2489,7 @@ exports.ChannelNotTextChannel = ChannelNotTextChannel;
2221
2489
  exports.Checkable = Checkable;
2222
2490
  exports.CommandMetadataKey = CommandMetadataKey;
2223
2491
  exports.CommandRegistry = CommandRegistry;
2492
+ exports.Confirmable = Confirmable;
2224
2493
  exports.ContextMenuRoute = ContextMenuRoute;
2225
2494
  exports.CouldNotFindChannel = CouldNotFindChannel;
2226
2495
  exports.CustomError = CustomError;
@@ -2230,6 +2499,7 @@ exports.EffectsEmitter = EffectsEmitter;
2230
2499
  exports.EffectsHandler = EffectsHandler;
2231
2500
  exports.EffectsRegistry = EffectsRegistry;
2232
2501
  exports.EmojiInjector = EmojiInjector;
2502
+ exports.Emojis = Emojis;
2233
2503
  exports.EventCatchable = EventCatchable;
2234
2504
  exports.EventController = EventController;
2235
2505
  exports.EventHandler = EventHandler;