novaapp-sdk 1.4.0 → 1.4.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.js CHANGED
@@ -21,44 +21,70 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  ActionRowBuilder: () => ActionRowBuilder,
24
+ ArgumentParser: () => ArgumentParser,
24
25
  AuditLogAPI: () => AuditLogAPI,
26
+ AutoModAPI: () => AutoModAPI,
27
+ AutoModRuleBuilder: () => AutoModRuleBuilder,
25
28
  ButtonBuilder: () => ButtonBuilder,
29
+ CacheMap: () => CacheMap,
30
+ CategoriesAPI: () => CategoriesAPI,
31
+ ChannelBuilder: () => ChannelBuilder,
26
32
  ChannelsAPI: () => ChannelsAPI,
27
33
  Collection: () => Collection,
28
34
  CommandsAPI: () => CommandsAPI,
35
+ ConfirmationDialog: () => ConfirmationDialog,
36
+ ContextMenuCommandBuilder: () => ContextMenuCommandBuilder,
29
37
  Cooldown: () => Cooldown,
30
38
  CooldownManager: () => CooldownManager,
31
39
  EmbedBuilder: () => EmbedBuilder,
40
+ EmbedPaginator: () => EmbedPaginator,
41
+ EventBuilder: () => EventBuilder,
42
+ EventScheduler: () => EventScheduler,
43
+ EventsAPI: () => EventsAPI,
44
+ ForumAPI: () => ForumAPI,
45
+ ForumPostBuilder: () => ForumPostBuilder,
32
46
  HttpClient: () => HttpClient,
47
+ InteractionCollector: () => InteractionCollector,
33
48
  InteractionOptions: () => InteractionOptions,
34
49
  InteractionsAPI: () => InteractionsAPI,
50
+ InviteBuilder: () => InviteBuilder,
35
51
  InvitesAPI: () => InvitesAPI,
36
52
  Logger: () => Logger,
37
53
  MembersAPI: () => MembersAPI,
54
+ MentionParser: () => MentionParser,
38
55
  MessageBuilder: () => MessageBuilder,
56
+ MessageCollector: () => MessageCollector,
39
57
  MessagesAPI: () => MessagesAPI,
40
58
  ModalBuilder: () => ModalBuilder,
59
+ NovaBan: () => NovaBan,
60
+ NovaCategory: () => NovaCategory,
41
61
  NovaChannel: () => NovaChannel,
42
62
  NovaClient: () => NovaClient,
63
+ NovaForumPost: () => NovaForumPost,
43
64
  NovaInteraction: () => NovaInteraction,
44
65
  NovaInvite: () => NovaInvite,
45
66
  NovaMember: () => NovaMember,
46
67
  NovaMessage: () => NovaMessage,
47
68
  NovaRole: () => NovaRole,
69
+ NovaServerEvent: () => NovaServerEvent,
48
70
  NovaServerWrapper: () => NovaServerWrapper,
71
+ NovaWarning: () => NovaWarning,
49
72
  NovaWebhook: () => NovaWebhook,
50
73
  Paginator: () => Paginator,
74
+ ParsedArguments: () => ParsedArguments,
51
75
  Permissions: () => Permissions,
52
76
  PermissionsAPI: () => PermissionsAPI,
53
77
  PermissionsBitfield: () => PermissionsBitfield,
54
78
  PollBuilder: () => PollBuilder,
55
79
  ReactionsAPI: () => ReactionsAPI,
80
+ RoleBuilder: () => RoleBuilder,
56
81
  RolesAPI: () => RolesAPI,
57
82
  SelectMenuBuilder: () => SelectMenuBuilder,
58
83
  ServersAPI: () => ServersAPI,
59
84
  SlashCommandBuilder: () => SlashCommandBuilder,
60
85
  SlashCommandOptionBuilder: () => SlashCommandOptionBuilder,
61
86
  TextInputBuilder: () => TextInputBuilder,
87
+ UsersAPI: () => UsersAPI,
62
88
  WebhooksAPI: () => WebhooksAPI,
63
89
  countdown: () => countdown,
64
90
  formatDuration: () => formatDuration,
@@ -426,6 +452,74 @@ var MembersAPI = class {
426
452
  removeRole(serverId, userId, roleId) {
427
453
  return this.http.delete(`/servers/${serverId}/members/${userId}/roles/${roleId}`);
428
454
  }
455
+ /**
456
+ * Issue a warning to a member.
457
+ * Requires the `members.moderate` scope.
458
+ *
459
+ * @example
460
+ * await client.members.warn('server-id', 'user-id', 'Spamming')
461
+ */
462
+ warn(serverId, userId, reason) {
463
+ return this.http.post(`/servers/${serverId}/members/${userId}/warn`, { reason });
464
+ }
465
+ /**
466
+ * Fetch warnings for a server (or optionally a specific user).
467
+ * Requires the `members.moderate` scope.
468
+ *
469
+ * @example
470
+ * const all = await client.members.fetchWarnings('server-id')
471
+ * const userWarnings = await client.members.fetchWarnings('server-id', { userId: 'user-id' })
472
+ */
473
+ fetchWarnings(serverId, options = {}) {
474
+ const params = new URLSearchParams();
475
+ if (options.userId) params.set("userId", options.userId);
476
+ const qs = params.toString() ? `?${params.toString()}` : "";
477
+ return this.http.get(`/servers/${serverId}/warnings${qs}`);
478
+ }
479
+ /**
480
+ * Remove a warning by its ID.
481
+ * Requires the `members.moderate` scope.
482
+ *
483
+ * @example
484
+ * await client.members.removeWarning('warning-id')
485
+ */
486
+ removeWarning(warningId) {
487
+ return this.http.delete(`/warnings/${warningId}`);
488
+ }
489
+ /**
490
+ * Get a member's XP and level.
491
+ * Requires the `members.read` scope.
492
+ *
493
+ * @example
494
+ * const xpData = await client.members.getXP('server-id', 'user-id')
495
+ * console.log(`Level ${xpData.level} — ${xpData.xp} XP`)
496
+ */
497
+ getXP(serverId, userId) {
498
+ return this.http.get(`/servers/${serverId}/members/${userId}/xp`);
499
+ }
500
+ /**
501
+ * Set or add XP to a member.
502
+ * Requires the `members.manage` scope.
503
+ *
504
+ * @example
505
+ * // Add 50 XP
506
+ * await client.members.setXP('server-id', 'user-id', { add: 50 })
507
+ * // Set XP to a specific value
508
+ * await client.members.setXP('server-id', 'user-id', { xp: 1000 })
509
+ */
510
+ setXP(serverId, userId, options) {
511
+ return this.http.patch(`/servers/${serverId}/members/${userId}/xp`, options);
512
+ }
513
+ /**
514
+ * Get the XP leaderboard for a server.
515
+ * Requires the `members.read` scope.
516
+ *
517
+ * @example
518
+ * const top10 = await client.members.leaderboard('server-id', 10)
519
+ */
520
+ leaderboard(serverId, limit = 10) {
521
+ return this.http.get(`/servers/${serverId}/leaderboard?limit=${limit}`);
522
+ }
429
523
  };
430
524
 
431
525
  // src/api/servers.ts
@@ -475,6 +569,34 @@ var ServersAPI = class {
475
569
  listRoles(serverId) {
476
570
  return this.http.get(`/servers/${serverId}/roles`);
477
571
  }
572
+ /**
573
+ * Fetch all channel categories in a server.
574
+ *
575
+ * @example
576
+ * const categories = await client.servers.listCategories('server-id')
577
+ */
578
+ listCategories(serverId) {
579
+ return this.http.get(`/servers/${serverId}/categories`);
580
+ }
581
+ /**
582
+ * Get server statistics (member count, message count, etc.).
583
+ *
584
+ * @example
585
+ * const stats = await client.servers.getStats('server-id')
586
+ * console.log(`${stats.onlineCount} online / ${stats.memberCount} total`)
587
+ */
588
+ getStats(serverId) {
589
+ return this.http.get(`/servers/${serverId}/stats`);
590
+ }
591
+ /**
592
+ * Fetch all soundboard clips uploaded to a server.
593
+ *
594
+ * @example
595
+ * const clips = await client.servers.listSoundboard('server-id')
596
+ */
597
+ listSoundboard(serverId) {
598
+ return this.http.get(`/servers/${serverId}/soundboard`);
599
+ }
478
600
  };
479
601
 
480
602
  // src/api/interactions.ts
@@ -752,6 +874,18 @@ var ChannelsAPI = class {
752
874
  startTyping(channelId) {
753
875
  return this.http.post(`/channels/${channelId}/typing`);
754
876
  }
877
+ /**
878
+ * Bulk delete up to 100 messages from a channel at once.
879
+ * Requires the `messages.manage` scope.
880
+ * Soft-deletes all specified messages in a single operation.
881
+ *
882
+ * @example
883
+ * const result = await client.channels.bulkDelete('channel-id', ['msg1', 'msg2', 'msg3'])
884
+ * console.log(`Deleted ${result.deleted} messages`)
885
+ */
886
+ bulkDelete(channelId, messageIds) {
887
+ return this.http.post(`/channels/${channelId}/bulk-delete`, { messageIds });
888
+ }
755
889
  };
756
890
 
757
891
  // src/api/reactions.ts
@@ -1060,6 +1194,224 @@ var AuditLogAPI = class {
1060
1194
  }
1061
1195
  };
1062
1196
 
1197
+ // src/api/forum.ts
1198
+ var ForumAPI = class {
1199
+ constructor(http) {
1200
+ this.http = http;
1201
+ }
1202
+ /**
1203
+ * List forum posts in a FORUM channel.
1204
+ *
1205
+ * @example
1206
+ * const posts = await client.forum.list('channel-id')
1207
+ */
1208
+ async list(channelId, options = {}) {
1209
+ const params = new URLSearchParams();
1210
+ if (options.limit !== void 0) params.set("limit", String(options.limit));
1211
+ if (options.before) params.set("before", options.before);
1212
+ const qs = params.toString() ? `?${params.toString()}` : "";
1213
+ return this.http.get(`/channels/${channelId}/forum-posts${qs}`);
1214
+ }
1215
+ /**
1216
+ * Create a forum post in a FORUM channel.
1217
+ *
1218
+ * @example
1219
+ * const post = await client.forum.create('channel-id', {
1220
+ * title: 'Ideas thread',
1221
+ * body: 'Post your ideas here!',
1222
+ * tags: ['idea', 'discussion'],
1223
+ * })
1224
+ */
1225
+ async create(channelId, options) {
1226
+ return this.http.post(`/channels/${channelId}/forum-posts`, options);
1227
+ }
1228
+ /**
1229
+ * Edit a forum post.
1230
+ * The bot must be the author of the post.
1231
+ *
1232
+ * @example
1233
+ * await client.forum.edit('post-id', { closed: true })
1234
+ */
1235
+ async edit(postId, options) {
1236
+ return this.http.patch(`/forum-posts/${postId}`, options);
1237
+ }
1238
+ /**
1239
+ * Delete a forum post.
1240
+ * The bot must be the author of the post.
1241
+ *
1242
+ * @example
1243
+ * await client.forum.delete('post-id')
1244
+ */
1245
+ async delete(postId) {
1246
+ await this.http.delete(`/forum-posts/${postId}`);
1247
+ }
1248
+ };
1249
+
1250
+ // src/api/events.ts
1251
+ var EventsAPI = class {
1252
+ constructor(http) {
1253
+ this.http = http;
1254
+ }
1255
+ /**
1256
+ * List server events.
1257
+ *
1258
+ * @example
1259
+ * const events = await client.events.list('server-id', { upcoming: true })
1260
+ */
1261
+ async list(serverId, options = {}) {
1262
+ const params = new URLSearchParams();
1263
+ if (options.limit !== void 0) params.set("limit", String(options.limit));
1264
+ if (options.upcoming) params.set("upcoming", "true");
1265
+ const qs = params.toString() ? `?${params.toString()}` : "";
1266
+ return this.http.get(`/servers/${serverId}/events${qs}`);
1267
+ }
1268
+ /**
1269
+ * Fetch a single server event by ID.
1270
+ * Returns full attendee list.
1271
+ *
1272
+ * @example
1273
+ * const event = await client.events.fetch('event-id')
1274
+ */
1275
+ async fetch(eventId) {
1276
+ return this.http.get(`/events/${eventId}`);
1277
+ }
1278
+ /**
1279
+ * Create a server event.
1280
+ *
1281
+ * @example
1282
+ * const event = await client.events.create('server-id', {
1283
+ * title: 'Game Night',
1284
+ * description: 'Friday fun!',
1285
+ * startAt: new Date(Date.now() + 86400_000).toISOString(),
1286
+ * })
1287
+ */
1288
+ async create(serverId, options) {
1289
+ return this.http.post(`/servers/${serverId}/events`, options);
1290
+ }
1291
+ /**
1292
+ * Edit a server event.
1293
+ *
1294
+ * @example
1295
+ * await client.events.edit('event-id', { title: 'Game Night v2' })
1296
+ */
1297
+ async edit(eventId, options) {
1298
+ return this.http.patch(`/events/${eventId}`, options);
1299
+ }
1300
+ /**
1301
+ * Delete a server event.
1302
+ *
1303
+ * @example
1304
+ * await client.events.delete('event-id')
1305
+ */
1306
+ async delete(eventId) {
1307
+ await this.http.delete(`/events/${eventId}`);
1308
+ }
1309
+ };
1310
+
1311
+ // src/api/categories.ts
1312
+ var CategoriesAPI = class {
1313
+ constructor(http) {
1314
+ this.http = http;
1315
+ }
1316
+ /**
1317
+ * List all channel categories in a server, ordered by position.
1318
+ *
1319
+ * @example
1320
+ * const cats = await client.categories.list('server-id')
1321
+ */
1322
+ async list(serverId) {
1323
+ return this.http.get(`/servers/${serverId}/categories`);
1324
+ }
1325
+ /**
1326
+ * Create a new channel category.
1327
+ *
1328
+ * @example
1329
+ * const cat = await client.categories.create('server-id', { name: 'General', position: 0 })
1330
+ */
1331
+ async create(serverId, options) {
1332
+ return this.http.post(`/servers/${serverId}/categories`, options);
1333
+ }
1334
+ /**
1335
+ * Edit an existing channel category.
1336
+ *
1337
+ * @example
1338
+ * await client.categories.edit('cat-id', { name: 'Renamed' })
1339
+ */
1340
+ async edit(categoryId, options) {
1341
+ return this.http.patch(`/categories/${categoryId}`, options);
1342
+ }
1343
+ /**
1344
+ * Delete a channel category.
1345
+ *
1346
+ * @example
1347
+ * await client.categories.delete('cat-id')
1348
+ */
1349
+ async delete(categoryId) {
1350
+ await this.http.delete(`/categories/${categoryId}`);
1351
+ }
1352
+ };
1353
+
1354
+ // src/api/automod.ts
1355
+ var AutoModAPI = class {
1356
+ constructor(http) {
1357
+ this.http = http;
1358
+ }
1359
+ /**
1360
+ * List all automod rules for a server.
1361
+ *
1362
+ * @example
1363
+ * const rules = await client.automod.list('server-id')
1364
+ */
1365
+ async list(serverId) {
1366
+ return this.http.get(`/servers/${serverId}/automod`);
1367
+ }
1368
+ /**
1369
+ * Create a new automod rule.
1370
+ *
1371
+ * @example
1372
+ * await client.automod.create('server-id', { type: 'BLOCKED_WORD', value: 'badword' })
1373
+ * await client.automod.create('server-id', { type: 'BLOCKED_LINK', value: 'example.com' })
1374
+ */
1375
+ async create(serverId, options) {
1376
+ return this.http.post(`/servers/${serverId}/automod`, options);
1377
+ }
1378
+ /**
1379
+ * Enable / disable a rule or update the blocked value.
1380
+ *
1381
+ * @example
1382
+ * await client.automod.edit('rule-id', { enabled: false })
1383
+ */
1384
+ async edit(ruleId, options) {
1385
+ return this.http.patch(`/automod/${ruleId}`, options);
1386
+ }
1387
+ /**
1388
+ * Delete an automod rule.
1389
+ *
1390
+ * @example
1391
+ * await client.automod.delete('rule-id')
1392
+ */
1393
+ async delete(ruleId) {
1394
+ await this.http.delete(`/automod/${ruleId}`);
1395
+ }
1396
+ };
1397
+
1398
+ // src/api/users.ts
1399
+ var UsersAPI = class {
1400
+ constructor(http) {
1401
+ this.http = http;
1402
+ }
1403
+ /**
1404
+ * Fetch a user's public profile by ID.
1405
+ *
1406
+ * @example
1407
+ * const user = await client.users.fetch('user-id')
1408
+ * console.log(user.displayName)
1409
+ */
1410
+ async fetch(userId) {
1411
+ return this.http.get(`/users/${userId}`);
1412
+ }
1413
+ };
1414
+
1063
1415
  // src/structures/NovaInteraction.ts
1064
1416
  var InteractionOptions = class {
1065
1417
  constructor(data) {
@@ -1384,6 +1736,63 @@ var NovaMessage = class _NovaMessage {
1384
1736
  const raw = await this._messages.fetchOne(this.id);
1385
1737
  return new _NovaMessage(raw, this._messages, this._reactions);
1386
1738
  }
1739
+ /**
1740
+ * Forward this message's content (and embed if present) to another channel.
1741
+ * Creates a new message in the target channel.
1742
+ *
1743
+ * @example
1744
+ * const forwarded = await msg.forward('target-channel-id')
1745
+ * console.log('Forwarded to:', forwarded.channelId)
1746
+ */
1747
+ async forward(channelId) {
1748
+ const opts = {
1749
+ content: this.content || void 0,
1750
+ ...this.embed ? { embed: this.embed } : {}
1751
+ };
1752
+ const raw = await this._messages.send(channelId, opts);
1753
+ return new _NovaMessage(raw, this._messages, this._reactions);
1754
+ }
1755
+ /**
1756
+ * Fetch detailed reaction data for a specific emoji on this message.
1757
+ * Returns the users who reacted with that emoji.
1758
+ *
1759
+ * @example
1760
+ * const reactors = await msg.fetchReactionDetails('👍')
1761
+ * for (const r of reactors) console.log(r.username, 'reacted 👍')
1762
+ */
1763
+ fetchReactionDetails(emoji) {
1764
+ return this._reactions.fetchEmoji(this.id, emoji);
1765
+ }
1766
+ /**
1767
+ * Remove **all** reactions from this message.
1768
+ * Requires the `messages.manage` scope.
1769
+ *
1770
+ * @example
1771
+ * await msg.clearAllReactions()
1772
+ */
1773
+ clearAllReactions() {
1774
+ return this._reactions.removeAll(this.id);
1775
+ }
1776
+ /**
1777
+ * Remove all reactions for a specific emoji from this message.
1778
+ * Requires the `messages.manage` scope.
1779
+ *
1780
+ * @example
1781
+ * await msg.clearReactionsFor('👍')
1782
+ */
1783
+ clearReactionsFor(emoji) {
1784
+ return this._reactions.removeEmoji(this.id, emoji);
1785
+ }
1786
+ /**
1787
+ * Fetch a breakdown of all reactions on this message, grouped by emoji.
1788
+ *
1789
+ * @example
1790
+ * const details = await msg.fetchAllReactions()
1791
+ * for (const d of details) console.log(`${d.emoji} — ${d.count}`)
1792
+ */
1793
+ fetchAllReactions() {
1794
+ return this._reactions.fetch(this.id);
1795
+ }
1387
1796
  /**
1388
1797
  * Get a URL to this message (deep link).
1389
1798
  */
@@ -1415,7 +1824,7 @@ var NovaMessage = class _NovaMessage {
1415
1824
 
1416
1825
  // src/structures/NovaChannel.ts
1417
1826
  var NovaChannel = class _NovaChannel {
1418
- constructor(raw, channels, messages) {
1827
+ constructor(raw, channels, messages, webhooks, forum) {
1419
1828
  this.id = raw.id;
1420
1829
  this.name = raw.name;
1421
1830
  this.type = raw.type;
@@ -1426,6 +1835,8 @@ var NovaChannel = class _NovaChannel {
1426
1835
  this.createdAt = new Date(raw.createdAt);
1427
1836
  this._channels = channels;
1428
1837
  this._messages = messages;
1838
+ this._webhooks = webhooks;
1839
+ this._forum = forum;
1429
1840
  }
1430
1841
  // ─── Type guards ────────────────────────────────────────────────────────────
1431
1842
  /** `true` for text channels. */
@@ -1502,7 +1913,7 @@ var NovaChannel = class _NovaChannel {
1502
1913
  */
1503
1914
  async edit(options) {
1504
1915
  const raw = await this._channels.edit(this.id, options);
1505
- return new _NovaChannel(raw, this._channels, this._messages);
1916
+ return new _NovaChannel(raw, this._channels, this._messages, this._webhooks, this._forum);
1506
1917
  }
1507
1918
  /**
1508
1919
  * Delete this channel.
@@ -1514,6 +1925,67 @@ var NovaChannel = class _NovaChannel {
1514
1925
  delete() {
1515
1926
  return this._channels.delete(this.id);
1516
1927
  }
1928
+ /**
1929
+ * Bulk-delete multiple messages in this channel (max 100 at once).
1930
+ * Requires the `messages.manage` scope.
1931
+ *
1932
+ * @example
1933
+ * const ids = messages.map(m => m.id)
1934
+ * const result = await channel.bulkDelete(ids)
1935
+ * console.log(`Deleted ${result.deleted} messages`)
1936
+ */
1937
+ bulkDelete(messageIds) {
1938
+ return this._channels.bulkDelete(this.id, messageIds);
1939
+ }
1940
+ // ─── Webhooks ────────────────────────────────────────────────────────────────
1941
+ /**
1942
+ * Create a webhook in this channel.
1943
+ * Requires the `webhooks.manage` scope.
1944
+ *
1945
+ * @example
1946
+ * const wh = await channel.createWebhook({ name: 'Notifications' })
1947
+ * console.log('Token:', wh.token)
1948
+ */
1949
+ createWebhook(options) {
1950
+ if (!this._webhooks) throw new Error("[NovaChannel] WebhooksAPI not available \u2014 ensure you obtained this channel from client.fetchChannel()");
1951
+ return this._webhooks.create(this.id, options);
1952
+ }
1953
+ /**
1954
+ * Fetch all webhooks in this channel.
1955
+ * Requires the `webhooks.manage` scope.
1956
+ *
1957
+ * @example
1958
+ * const webhooks = await channel.fetchWebhooks()
1959
+ */
1960
+ fetchWebhooks() {
1961
+ if (!this._webhooks) throw new Error("[NovaChannel] WebhooksAPI not available \u2014 ensure you obtained this channel from client.fetchChannel()");
1962
+ return this._webhooks.list(this.id);
1963
+ }
1964
+ // ─── Forum ───────────────────────────────────────────────────────────────────
1965
+ /**
1966
+ * Create a new post in this FORUM channel.
1967
+ * Requires the `channels.write` scope.
1968
+ *
1969
+ * @example
1970
+ * const post = await channel.createForumPost({
1971
+ * title: 'Announcement',
1972
+ * content: 'Welcome everyone!',
1973
+ * })
1974
+ */
1975
+ createForumPost(options) {
1976
+ if (!this._forum) throw new Error("[NovaChannel] ForumAPI not available \u2014 ensure you obtained this channel from client.fetchChannel()");
1977
+ return this._forum.create(this.id, options);
1978
+ }
1979
+ /**
1980
+ * Fetch posts from this FORUM channel.
1981
+ *
1982
+ * @example
1983
+ * const posts = await channel.fetchForumPosts({ limit: 20 })
1984
+ */
1985
+ fetchForumPosts(options) {
1986
+ if (!this._forum) throw new Error("[NovaChannel] ForumAPI not available \u2014 ensure you obtained this channel from client.fetchChannel()");
1987
+ return this._forum.list(this.id, options);
1988
+ }
1517
1989
  // ─── Serialisation ──────────────────────────────────────────────────────────
1518
1990
  /**
1519
1991
  * Returns the channel as a mention-style string: `#name`.
@@ -1631,29 +2103,106 @@ var NovaMember = class {
1631
2103
  removeRole(roleId) {
1632
2104
  return this._members.removeRole(this.serverId, this.userId, roleId);
1633
2105
  }
1634
- // ─── Serialisation ──────────────────────────────────────────────────────────
2106
+ // ─── Moderation ─────────────────────────────────────────────────────────────
1635
2107
  /**
1636
- * Returns a mention-style string: `@displayName`.
2108
+ * Issue a warning to this member.
2109
+ * Requires the `members.moderate` scope.
2110
+ *
2111
+ * @example
2112
+ * await member.warn('Excessive spamming')
1637
2113
  */
1638
- toString() {
1639
- return `@${this.displayName}`;
2114
+ warn(reason) {
2115
+ return this._members.warn(this.serverId, this.userId, reason);
1640
2116
  }
1641
- /** Returns the raw member data. */
1642
- toJSON() {
1643
- return {
1644
- role: this.role,
1645
- joinedAt: this.joinedAt.toISOString(),
1646
- user: {
1647
- id: this.userId,
1648
- username: this.username,
1649
- displayName: this.displayName,
1650
- avatar: this.avatar,
1651
- status: this.status,
1652
- isBot: this.isBot
1653
- }
1654
- };
1655
- }
1656
- };
2117
+ /**
2118
+ * Fetch all warnings for this member in this server.
2119
+ * Requires the `members.moderate` scope.
2120
+ *
2121
+ * @example
2122
+ * const warnings = await member.fetchWarnings()
2123
+ * console.log(`${warnings.length} warnings on record`)
2124
+ */
2125
+ fetchWarnings() {
2126
+ return this._members.fetchWarnings(this.serverId, { userId: this.userId });
2127
+ }
2128
+ // ─── XP & Levels ────────────────────────────────────────────────────────────
2129
+ /**
2130
+ * Get this member's current XP and level.
2131
+ * Requires the `members.read` scope.
2132
+ *
2133
+ * @example
2134
+ * const xp = await member.getXP()
2135
+ * console.log(`Level ${xp.level} — ${xp.xp} XP`)
2136
+ */
2137
+ getXP() {
2138
+ return this._members.getXP(this.serverId, this.userId);
2139
+ }
2140
+ /**
2141
+ * Set this member's XP to a specific value.
2142
+ * Requires the `members.manage` scope.
2143
+ *
2144
+ * @example
2145
+ * await member.setXP(500)
2146
+ */
2147
+ setXP(xp) {
2148
+ return this._members.setXP(this.serverId, this.userId, { xp });
2149
+ }
2150
+ /**
2151
+ * Add (or subtract) XP to this member's current total.
2152
+ * Requires the `members.manage` scope.
2153
+ *
2154
+ * @example
2155
+ * await member.addXP(100) // reward 100 XP
2156
+ * await member.addXP(-50) // deduct 50 XP
2157
+ */
2158
+ addXP(amount) {
2159
+ return this._members.setXP(this.serverId, this.userId, { add: amount });
2160
+ }
2161
+ /**
2162
+ * Delete all warnings on record for this member in this server.
2163
+ * Requires the `members.moderate` scope.
2164
+ *
2165
+ * @example
2166
+ * await member.clearWarnings()
2167
+ */
2168
+ async clearWarnings() {
2169
+ const warnings = await this.fetchWarnings();
2170
+ await Promise.all(warnings.map((w) => this._members.removeWarning(w.id)));
2171
+ return warnings.length;
2172
+ }
2173
+ /**
2174
+ * Reset this member's XP back to zero.
2175
+ * Requires the `members.manage` scope.
2176
+ *
2177
+ * @example
2178
+ * await member.resetXP()
2179
+ */
2180
+ resetXP() {
2181
+ return this._members.setXP(this.serverId, this.userId, { xp: 0 });
2182
+ }
2183
+ // ─── Serialisation ──────────────────────────────────────────────────────────
2184
+ /**
2185
+ * Returns a mention-style string: `@displayName`.
2186
+ */
2187
+ toString() {
2188
+ return `@${this.displayName}`;
2189
+ }
2190
+ /** Returns the raw member data. */
2191
+ toJSON() {
2192
+ return {
2193
+ role: this.role,
2194
+ joinedAt: this.joinedAt.toISOString(),
2195
+ user: {
2196
+ id: this.userId,
2197
+ username: this.username,
2198
+ displayName: this.displayName,
2199
+ avatar: this.avatar,
2200
+ status: this.status,
2201
+ isBot: this.isBot
2202
+ }
2203
+ };
2204
+ }
2205
+ };
1657
2206
 
1658
2207
  // src/structures/NovaRole.ts
1659
2208
  var NovaRole = class {
@@ -1730,7 +2279,7 @@ var NovaRole = class {
1730
2279
 
1731
2280
  // src/structures/NovaServer.ts
1732
2281
  var NovaServerWrapper = class {
1733
- constructor(raw, servers, channels, members, invites, roles, messages) {
2282
+ constructor(raw, servers, channels, members, invites, roles, messages, categories, events, automod, auditLog) {
1734
2283
  this.id = raw.id;
1735
2284
  this.name = raw.name;
1736
2285
  this.icon = raw.icon;
@@ -1744,6 +2293,10 @@ var NovaServerWrapper = class {
1744
2293
  this._invites = invites;
1745
2294
  this._roles = roles;
1746
2295
  this._messages = messages;
2296
+ this._categories = categories;
2297
+ this._events = events;
2298
+ this._automod = automod;
2299
+ this._auditLog = auditLog;
1747
2300
  }
1748
2301
  // ─── Server management ────────────────────────────────────────────────────
1749
2302
  /**
@@ -1822,14 +2375,25 @@ var NovaServerWrapper = class {
1822
2375
  }
1823
2376
  // ─── Roles ────────────────────────────────────────────────────────────────
1824
2377
  /**
1825
- * Fetch all custom roles in this server.
2378
+ * List all custom roles in this server, sorted by position.
1826
2379
  *
1827
2380
  * @example
1828
2381
  * const roles = await server.fetchRoles()
2382
+ * const admins = roles.filter(r => r.name === 'Admin')
1829
2383
  */
1830
2384
  fetchRoles() {
1831
2385
  return this._roles.list(this.id);
1832
2386
  }
2387
+ /**
2388
+ * Create a new custom role in this server.
2389
+ * Requires the `roles.manage` scope.
2390
+ *
2391
+ * @example
2392
+ * const role = await server.createRole({ name: 'Supporter', color: '#00d4ff', hoist: true })
2393
+ */
2394
+ createRole(options) {
2395
+ return this._roles.create(this.id, options);
2396
+ }
1833
2397
  // ─── Invites ──────────────────────────────────────────────────────────────
1834
2398
  /**
1835
2399
  * Fetch all active invites for this server.
@@ -1860,6 +2424,133 @@ var NovaServerWrapper = class {
1860
2424
  send(channelId, content) {
1861
2425
  return this._messages.send(channelId, { content });
1862
2426
  }
2427
+ // ─── Categories ───────────────────────────────────────────────────────────
2428
+ /**
2429
+ * Fetch all channel categories in this server.
2430
+ *
2431
+ * @example
2432
+ * const cats = await server.fetchCategories()
2433
+ * const general = cats.find(c => c.name === 'General')
2434
+ */
2435
+ fetchCategories() {
2436
+ if (!this._categories) throw new Error("[NovaServerWrapper] CategoriesAPI not available");
2437
+ return this._categories.list(this.id);
2438
+ }
2439
+ /**
2440
+ * Create a new channel category in this server.
2441
+ * Requires the `channels.manage` scope.
2442
+ *
2443
+ * @example
2444
+ * const cat = await server.createCategory({ name: 'Bot Channels' })
2445
+ */
2446
+ createCategory(options) {
2447
+ if (!this._categories) throw new Error("[NovaServerWrapper] CategoriesAPI not available");
2448
+ return this._categories.create(this.id, options);
2449
+ }
2450
+ // ─── Events ───────────────────────────────────────────────────────────────
2451
+ /**
2452
+ * Fetch server events. Pass `{ upcoming: true }` to only return future events.
2453
+ *
2454
+ * @example
2455
+ * const events = await server.fetchEvents({ upcoming: true })
2456
+ */
2457
+ fetchEvents(options = {}) {
2458
+ if (!this._events) throw new Error("[NovaServerWrapper] EventsAPI not available");
2459
+ return this._events.list(this.id, options);
2460
+ }
2461
+ /**
2462
+ * Create a new server event.
2463
+ * Requires the `server.manage` scope.
2464
+ *
2465
+ * @example
2466
+ * const event = await server.createEvent({
2467
+ * title: 'Game Night',
2468
+ * startAt: new Date('2025-09-01T20:00:00Z').toISOString(),
2469
+ * })
2470
+ */
2471
+ createEvent(options) {
2472
+ if (!this._events) throw new Error("[NovaServerWrapper] EventsAPI not available");
2473
+ return this._events.create(this.id, options);
2474
+ }
2475
+ // ─── Stats & Audit Log ────────────────────────────────────────────────────
2476
+ /**
2477
+ * Fetch server statistics (member count, message count, etc.).
2478
+ *
2479
+ * @example
2480
+ * const stats = await server.fetchStats()
2481
+ * console.log(`Online: ${stats.onlineCount} / ${stats.memberCount}`)
2482
+ */
2483
+ fetchStats() {
2484
+ return this._servers.getStats(this.id);
2485
+ }
2486
+ /**
2487
+ * Fetch the audit log for this server.
2488
+ * Requires the `audit-log.read` scope.
2489
+ *
2490
+ * @example
2491
+ * const log = await server.fetchAuditLog({ action: 'member.banned', limit: 20 })
2492
+ */
2493
+ fetchAuditLog(options = {}) {
2494
+ if (!this._auditLog) throw new Error("[NovaServerWrapper] AuditLogAPI not available");
2495
+ return this._auditLog.fetch(this.id, options);
2496
+ }
2497
+ // ─── Warnings & Leaderboard ───────────────────────────────────────────────
2498
+ /**
2499
+ * Issue a warning to a member in this server.
2500
+ * Requires the `members.moderate` scope.
2501
+ *
2502
+ * @example
2503
+ * await server.warn('user-id', 'Insulting other members')
2504
+ */
2505
+ warn(userId, reason) {
2506
+ return this._members.warn(this.id, userId, reason);
2507
+ }
2508
+ /**
2509
+ * Fetch warnings for this server, optionally filtered by user.
2510
+ * Requires the `members.moderate` scope.
2511
+ *
2512
+ * @example
2513
+ * const all = await server.fetchWarnings()
2514
+ * const userWarns = await server.fetchWarnings({ userId: 'user-id' })
2515
+ */
2516
+ fetchWarnings(options = {}) {
2517
+ return this._members.fetchWarnings(this.id, options);
2518
+ }
2519
+ /**
2520
+ * Fetch the XP leaderboard for this server.
2521
+ * Requires the `members.read` scope.
2522
+ *
2523
+ * @example
2524
+ * const top = await server.fetchLeaderboard(10)
2525
+ * top.forEach(e => console.log(`#${e.rank} ${e.user.displayName} — ${e.xp} XP`))
2526
+ */
2527
+ fetchLeaderboard(limit = 10) {
2528
+ return this._members.leaderboard(this.id, limit);
2529
+ }
2530
+ // ─── AutoMod ─────────────────────────────────────────────────────────────
2531
+ /**
2532
+ * Fetch all AutoMod rules for this server.
2533
+ * Requires the `server.manage` scope.
2534
+ *
2535
+ * @example
2536
+ * const rules = await server.fetchAutoModRules()
2537
+ * const active = rules.filter(r => r.enabled)
2538
+ */
2539
+ fetchAutoModRules() {
2540
+ if (!this._automod) throw new Error("[NovaServerWrapper] AutoModAPI not available");
2541
+ return this._automod.list(this.id);
2542
+ }
2543
+ /**
2544
+ * Create a new AutoMod rule for this server.
2545
+ * Requires the `server.manage` scope.
2546
+ *
2547
+ * @example
2548
+ * await server.createAutoModRule({ type: 'BLOCKED_WORD', value: 'badword' })
2549
+ */
2550
+ createAutoModRule(options) {
2551
+ if (!this._automod) throw new Error("[NovaServerWrapper] AutoModAPI not available");
2552
+ return this._automod.create(this.id, options);
2553
+ }
1863
2554
  // ─── Helpers ─────────────────────────────────────────────────────────────
1864
2555
  toJSON() {
1865
2556
  return {
@@ -1987,6 +2678,265 @@ var NovaWebhook = class {
1987
2678
  }
1988
2679
  };
1989
2680
 
2681
+ // src/structures/NovaForumPost.ts
2682
+ var NovaForumPost = class _NovaForumPost {
2683
+ constructor(raw, forum) {
2684
+ this.raw = raw;
2685
+ this.forum = forum;
2686
+ this.id = raw.id;
2687
+ this.channelId = raw.channelId;
2688
+ this.authorId = raw.authorId;
2689
+ this.title = raw.title;
2690
+ this.body = raw.body;
2691
+ this.tags = raw.tags;
2692
+ this.pinned = raw.pinned;
2693
+ this.closed = raw.closed;
2694
+ this.upvoteCount = raw.upvoteCount;
2695
+ this.replyCount = raw.replyCount;
2696
+ this.author = raw.author;
2697
+ this.createdAt = raw.createdAt;
2698
+ this.updatedAt = raw.updatedAt;
2699
+ }
2700
+ /**
2701
+ * Edit this forum post.
2702
+ *
2703
+ * @example
2704
+ * await post.edit({ title: 'Updated title' })
2705
+ */
2706
+ async edit(options) {
2707
+ const updated = await this.forum.edit(this.id, options);
2708
+ return new _NovaForumPost(updated, this.forum);
2709
+ }
2710
+ /**
2711
+ * Close the forum post (no new replies).
2712
+ *
2713
+ * @example
2714
+ * await post.close()
2715
+ */
2716
+ async close() {
2717
+ return this.edit({ closed: true });
2718
+ }
2719
+ /**
2720
+ * Re-open a closed forum post.
2721
+ *
2722
+ * @example
2723
+ * await post.open()
2724
+ */
2725
+ async open() {
2726
+ return this.edit({ closed: false });
2727
+ }
2728
+ /**
2729
+ * Pin this forum post in the channel.
2730
+ *
2731
+ * @example
2732
+ * await post.pin()
2733
+ */
2734
+ async pin() {
2735
+ return this.edit({ pinned: true });
2736
+ }
2737
+ /**
2738
+ * Unpin this forum post.
2739
+ *
2740
+ * @example
2741
+ * await post.unpin()
2742
+ */
2743
+ async unpin() {
2744
+ return this.edit({ pinned: false });
2745
+ }
2746
+ /**
2747
+ * Delete this forum post.
2748
+ *
2749
+ * @example
2750
+ * await post.delete()
2751
+ */
2752
+ async delete() {
2753
+ await this.forum.delete(this.id);
2754
+ }
2755
+ /** Whether this post was created by the currently connected bot. */
2756
+ get isOwnPost() {
2757
+ return this.authorId === this.author.id;
2758
+ }
2759
+ /** Returns `true` if the post has been closed. */
2760
+ get isClosed() {
2761
+ return this.closed;
2762
+ }
2763
+ /** Returns `true` if the post is pinned. */
2764
+ get isPinned() {
2765
+ return this.pinned;
2766
+ }
2767
+ toJSON() {
2768
+ return { ...this.raw };
2769
+ }
2770
+ };
2771
+
2772
+ // src/structures/NovaServerEvent.ts
2773
+ var NovaServerEvent = class _NovaServerEvent {
2774
+ constructor(raw, events) {
2775
+ this.raw = raw;
2776
+ this.events = events;
2777
+ this.id = raw.id;
2778
+ this.serverId = raw.serverId;
2779
+ this.channelId = raw.channelId;
2780
+ this.createdById = raw.createdById;
2781
+ this.title = raw.title;
2782
+ this.description = raw.description;
2783
+ this.imageUrl = raw.imageUrl;
2784
+ this.location = raw.location;
2785
+ this.startAt = raw.startAt;
2786
+ this.endAt = raw.endAt;
2787
+ this.attendeeCount = raw.attendeeCount;
2788
+ this.createdAt = raw.createdAt;
2789
+ }
2790
+ /**
2791
+ * Edit this event.
2792
+ *
2793
+ * @example
2794
+ * await event.edit({ title: 'New Title' })
2795
+ */
2796
+ async edit(options) {
2797
+ const updated = await this.events.edit(this.id, options);
2798
+ return new _NovaServerEvent(updated, this.events);
2799
+ }
2800
+ /**
2801
+ * Fetch full event details including the attendee list.
2802
+ *
2803
+ * @example
2804
+ * const full = await event.fetchAttendees()
2805
+ * console.log(`${full.attendees.length} attendees`)
2806
+ */
2807
+ async fetchAttendees() {
2808
+ const data = await this.events.fetch(this.id);
2809
+ return {
2810
+ event: new _NovaServerEvent(data, this.events),
2811
+ attendees: data.attendees
2812
+ };
2813
+ }
2814
+ /**
2815
+ * Delete this event.
2816
+ *
2817
+ * @example
2818
+ * await event.delete()
2819
+ */
2820
+ async delete() {
2821
+ await this.events.delete(this.id);
2822
+ }
2823
+ /**
2824
+ * Whether the event has already started.
2825
+ */
2826
+ get hasStarted() {
2827
+ return new Date(this.startAt) <= /* @__PURE__ */ new Date();
2828
+ }
2829
+ /**
2830
+ * Whether the event has ended.
2831
+ * Returns `false` if no `endAt` is set.
2832
+ */
2833
+ get hasEnded() {
2834
+ return this.endAt ? new Date(this.endAt) <= /* @__PURE__ */ new Date() : false;
2835
+ }
2836
+ /**
2837
+ * Whether this is an upcoming event that has not yet started.
2838
+ */
2839
+ get isUpcoming() {
2840
+ return new Date(this.startAt) > /* @__PURE__ */ new Date();
2841
+ }
2842
+ toJSON() {
2843
+ return { ...this.raw };
2844
+ }
2845
+ };
2846
+
2847
+ // src/structures/NovaBan.ts
2848
+ var NovaBan = class {
2849
+ constructor(raw, serverId, members) {
2850
+ this.userId = raw.userId;
2851
+ this.username = raw.username;
2852
+ this.displayName = raw.displayName;
2853
+ this.avatar = raw.avatar;
2854
+ this.reason = raw.reason;
2855
+ this.bannedAt = raw.bannedAt;
2856
+ this.moderatorId = raw.moderatorId;
2857
+ this.serverId = serverId;
2858
+ this._members = members;
2859
+ }
2860
+ /**
2861
+ * Revoke this ban, allowing the user to rejoin the server.
2862
+ * Requires the `members.ban` scope.
2863
+ *
2864
+ * @example
2865
+ * await ban.unban()
2866
+ */
2867
+ unban() {
2868
+ return this._members.unban(this.serverId, this.userId);
2869
+ }
2870
+ /**
2871
+ * When the ban was issued as a `Date` object.
2872
+ */
2873
+ get bannedAtDate() {
2874
+ return new Date(this.bannedAt);
2875
+ }
2876
+ /**
2877
+ * Human-readable label: `"displayName (username)"`.
2878
+ */
2879
+ get label() {
2880
+ return `${this.displayName} (${this.username})`;
2881
+ }
2882
+ toJSON() {
2883
+ return {
2884
+ userId: this.userId,
2885
+ username: this.username,
2886
+ displayName: this.displayName,
2887
+ avatar: this.avatar,
2888
+ reason: this.reason,
2889
+ bannedAt: this.bannedAt,
2890
+ moderatorId: this.moderatorId
2891
+ };
2892
+ }
2893
+ };
2894
+
2895
+ // src/structures/NovaWarning.ts
2896
+ var NovaWarning = class {
2897
+ constructor(raw, members) {
2898
+ this.id = raw.id;
2899
+ this.serverId = raw.serverId;
2900
+ this.userId = raw.userId;
2901
+ this.moderatorId = raw.moderatorId;
2902
+ this.reason = raw.reason;
2903
+ this.createdAt = raw.createdAt;
2904
+ this._members = members;
2905
+ }
2906
+ /**
2907
+ * Delete (expunge) this warning from the record.
2908
+ * Requires the `members.moderate` scope.
2909
+ *
2910
+ * @example
2911
+ * await warning.delete()
2912
+ */
2913
+ delete() {
2914
+ return this._members.removeWarning(this.id);
2915
+ }
2916
+ /**
2917
+ * When the warning was issued as a `Date` object.
2918
+ */
2919
+ get issuedAt() {
2920
+ return new Date(this.createdAt);
2921
+ }
2922
+ /**
2923
+ * How long ago the warning was issued, in milliseconds.
2924
+ */
2925
+ get ageMs() {
2926
+ return Date.now() - this.issuedAt.getTime();
2927
+ }
2928
+ toJSON() {
2929
+ return {
2930
+ id: this.id,
2931
+ serverId: this.serverId,
2932
+ userId: this.userId,
2933
+ moderatorId: this.moderatorId,
2934
+ reason: this.reason,
2935
+ createdAt: this.createdAt
2936
+ };
2937
+ }
2938
+ };
2939
+
1990
2940
  // src/client.ts
1991
2941
  var NovaClient = class extends import_node_events.EventEmitter {
1992
2942
  constructor(options) {
@@ -2024,6 +2974,11 @@ var NovaClient = class extends import_node_events.EventEmitter {
2024
2974
  this.invites = new InvitesAPI(this.http);
2025
2975
  this.webhooks = new WebhooksAPI(this.http);
2026
2976
  this.auditLog = new AuditLogAPI(this.http);
2977
+ this.forum = new ForumAPI(this.http);
2978
+ this.events = new EventsAPI(this.http);
2979
+ this.categories = new CategoriesAPI(this.http);
2980
+ this.automod = new AutoModAPI(this.http);
2981
+ this.users = new UsersAPI(this.http);
2027
2982
  this.on("error", () => {
2028
2983
  });
2029
2984
  const cleanup = () => this.disconnect();
@@ -2235,16 +3190,53 @@ var NovaClient = class extends import_node_events.EventEmitter {
2235
3190
  case "server.updated":
2236
3191
  this.emit("serverUpdate", event.data);
2237
3192
  break;
2238
- }
2239
- });
2240
- this.socket.on("bot:error", (err) => {
2241
- this.emit("error", err);
2242
- });
2243
- this.socket.on("disconnect", (reason) => {
2244
- this.emit("disconnect", reason);
2245
- });
2246
- });
2247
- }
3193
+ case "invite.created":
3194
+ this.emit("inviteCreate", event.data);
3195
+ break;
3196
+ case "invite.deleted":
3197
+ this.emit("inviteDelete", event.data);
3198
+ break;
3199
+ case "forum.post.created": {
3200
+ const rawPost = event.data;
3201
+ this.emit("forumPostCreate", new NovaForumPost(rawPost, this.forum));
3202
+ break;
3203
+ }
3204
+ case "event.created": {
3205
+ const rawEvent = event.data;
3206
+ this.emit("eventCreate", new NovaServerEvent(rawEvent, this.events));
3207
+ break;
3208
+ }
3209
+ case "event.updated":
3210
+ this.emit("eventUpdate", event.data);
3211
+ break;
3212
+ case "event.deleted":
3213
+ this.emit("eventDelete", event.data);
3214
+ break;
3215
+ case "category.created":
3216
+ this.emit("categoryCreate", event.data);
3217
+ break;
3218
+ case "category.updated":
3219
+ this.emit("categoryUpdate", event.data);
3220
+ break;
3221
+ case "category.deleted":
3222
+ this.emit("categoryDelete", event.data);
3223
+ break;
3224
+ case "member.warned":
3225
+ this.emit("memberWarned", event.data);
3226
+ break;
3227
+ case "messages.bulk_deleted":
3228
+ this.emit("messagesBulkDelete", event.data);
3229
+ break;
3230
+ }
3231
+ });
3232
+ this.socket.on("bot:error", (err) => {
3233
+ this.emit("error", err);
3234
+ });
3235
+ this.socket.on("disconnect", (reason) => {
3236
+ this.emit("disconnect", reason);
3237
+ });
3238
+ });
3239
+ }
2248
3240
  /**
2249
3241
  * Register a recurring task that fires at a set interval.
2250
3242
  * All cron tasks are automatically cancelled on `disconnect()`.
@@ -2301,7 +3293,7 @@ var NovaClient = class extends import_node_events.EventEmitter {
2301
3293
  */
2302
3294
  async fetchChannel(channelId) {
2303
3295
  const raw = await this.channels.fetch(channelId);
2304
- return new NovaChannel(raw, this.channels, this.messages);
3296
+ return new NovaChannel(raw, this.channels, this.messages, this.webhooks, this.forum);
2305
3297
  }
2306
3298
  /**
2307
3299
  * Fetch all channels in a server and return them as `NovaChannel` wrappers.
@@ -2312,7 +3304,7 @@ var NovaClient = class extends import_node_events.EventEmitter {
2312
3304
  */
2313
3305
  async fetchChannels(serverId) {
2314
3306
  const list = await this.channels.list(serverId);
2315
- return list.map((raw) => new NovaChannel(raw, this.channels, this.messages));
3307
+ return list.map((raw) => new NovaChannel(raw, this.channels, this.messages, this.webhooks, this.forum));
2316
3308
  }
2317
3309
  /**
2318
3310
  * Fetch all members in a server and return them as `NovaMember` wrappers.
@@ -2337,6 +3329,36 @@ var NovaClient = class extends import_node_events.EventEmitter {
2337
3329
  const raw = await this.members.fetch(serverId, userId);
2338
3330
  return new NovaMember(raw, serverId, this.members);
2339
3331
  }
3332
+ /**
3333
+ * Fetch all bans in a server and return them as `NovaBan` wrappers.
3334
+ * Each `NovaBan` has an `.unban()` convenience method.
3335
+ * Requires the `members.ban` scope.
3336
+ *
3337
+ * @example
3338
+ * const bans = await client.fetchBans('server-id')
3339
+ * for (const ban of bans) {
3340
+ * if (ban.reason === 'test') await ban.unban()
3341
+ * }
3342
+ */
3343
+ async fetchBans(serverId) {
3344
+ const list = await this.members.listBans(serverId);
3345
+ return list.map((raw) => new NovaBan(raw, serverId, this.members));
3346
+ }
3347
+ /**
3348
+ * Fetch warnings for a member (or all members) in a server and return them
3349
+ * as `NovaWarning` wrappers. Each wrapper has a `.delete()` convenience method.
3350
+ * Requires the `members.moderate` scope.
3351
+ *
3352
+ * @example
3353
+ * const warnings = await client.fetchMemberWarnings('server-id', 'user-id')
3354
+ * for (const w of warnings) {
3355
+ * if (w.ageMs > 30 * 24 * 60 * 60_000) await w.delete() // older than 30 days
3356
+ * }
3357
+ */
3358
+ async fetchMemberWarnings(serverId, userId) {
3359
+ const list = await this.members.fetchWarnings(serverId, userId ? { userId } : {});
3360
+ return list.map((raw) => new NovaWarning(raw, this.members));
3361
+ }
2340
3362
  /**
2341
3363
  * Fetch a single server by ID and return it as a `NovaServerWrapper`.
2342
3364
  *
@@ -2346,7 +3368,7 @@ var NovaClient = class extends import_node_events.EventEmitter {
2346
3368
  */
2347
3369
  async fetchServer(serverId) {
2348
3370
  const raw = await this.servers.fetch(serverId);
2349
- return new NovaServerWrapper(raw, this.servers, this.channels, this.members, this.invites, this.roles, this.messages);
3371
+ return new NovaServerWrapper(raw, this.servers, this.channels, this.members, this.invites, this.roles, this.messages, this.categories, this.events, this.automod, this.auditLog);
2350
3372
  }
2351
3373
  /**
2352
3374
  * Fetch all servers the bot is in and return them as `NovaServerWrapper` objects.
@@ -2357,7 +3379,7 @@ var NovaClient = class extends import_node_events.EventEmitter {
2357
3379
  */
2358
3380
  async fetchServers() {
2359
3381
  const list = await this.servers.list();
2360
- return list.map((raw) => new NovaServerWrapper(raw, this.servers, this.channels, this.members, this.invites, this.roles, this.messages));
3382
+ return list.map((raw) => new NovaServerWrapper(raw, this.servers, this.channels, this.members, this.invites, this.roles, this.messages, this.categories, this.events, this.automod, this.auditLog));
2361
3383
  }
2362
3384
  /**
2363
3385
  * Fetch all custom roles in a server and return them as `NovaRole` wrappers.
@@ -2413,6 +3435,116 @@ var NovaClient = class extends import_node_events.EventEmitter {
2413
3435
  const raw = await this.webhooks.fetch(webhookId);
2414
3436
  return new NovaWebhook(raw, this.webhooks);
2415
3437
  }
3438
+ /**
3439
+ * Fetch forum posts from a FORUM channel and return them as `NovaForumPost` wrappers.
3440
+ *
3441
+ * @example
3442
+ * const posts = await client.fetchForumPosts('channel-id', { limit: 20 })
3443
+ * const open = posts.filter(p => !p.isClosed)
3444
+ */
3445
+ async fetchForumPosts(channelId, options) {
3446
+ const list = await this.forum.list(channelId, options);
3447
+ return list.map((raw) => new NovaForumPost(raw, this.forum));
3448
+ }
3449
+ /**
3450
+ * Fetch server events and return them as `NovaServerEvent` wrappers.
3451
+ *
3452
+ * @example
3453
+ * const upcoming = await client.fetchEvents('server-id', { upcoming: true })
3454
+ * for (const event of upcoming) console.log(event.title)
3455
+ */
3456
+ async fetchEvents(serverId, options) {
3457
+ const list = await this.events.list(serverId, options);
3458
+ return list.map((raw) => new NovaServerEvent(raw, this.events));
3459
+ }
3460
+ /**
3461
+ * Fetch a single server event by ID and return it as a `NovaServerEvent` wrapper.
3462
+ *
3463
+ * @example
3464
+ * const event = await client.fetchEvent('event-id')
3465
+ * if (event.isUpcoming) await event.edit({ title: 'Updated!' })
3466
+ */
3467
+ async fetchEvent(eventId) {
3468
+ const raw = await this.events.fetch(eventId);
3469
+ return new NovaServerEvent(raw, this.events);
3470
+ }
3471
+ /**
3472
+ * Fetch a user's public profile.
3473
+ *
3474
+ * @example
3475
+ * const user = await client.fetchUserProfile('user-id')
3476
+ * console.log(user.bio)
3477
+ */
3478
+ fetchUserProfile(userId) {
3479
+ return this.users.fetch(userId);
3480
+ }
3481
+ /**
3482
+ * Fetch the XP leaderboard for a server.
3483
+ *
3484
+ * @example
3485
+ * const top = await client.fetchLeaderboard('server-id', 10)
3486
+ * top.forEach(entry => console.log(`#${entry.rank} ${entry.user.username} — ${entry.xp} XP`))
3487
+ */
3488
+ fetchLeaderboard(serverId, limit = 10) {
3489
+ return this.members.leaderboard(serverId, limit);
3490
+ }
3491
+ /**
3492
+ * Fetch warnings in a server, optionally filtered by user.
3493
+ *
3494
+ * @example
3495
+ * const warnings = await client.fetchWarnings('server-id', { userId: 'user-id' })
3496
+ */
3497
+ fetchWarnings(serverId, options) {
3498
+ return this.members.fetchWarnings(serverId, options);
3499
+ }
3500
+ /**
3501
+ * Fetch all automod rules for a server.
3502
+ *
3503
+ * @example
3504
+ * const rules = await client.fetchAutoModRules('server-id')
3505
+ */
3506
+ fetchAutoModRules(serverId) {
3507
+ return this.automod.list(serverId);
3508
+ }
3509
+ /**
3510
+ * Fetch all channel categories for a server.
3511
+ *
3512
+ * @example
3513
+ * const cats = await client.fetchCategories('server-id')
3514
+ */
3515
+ fetchCategories(serverId) {
3516
+ return this.categories.list(serverId);
3517
+ }
3518
+ /**
3519
+ * Fetch soundboard clips for a server.
3520
+ *
3521
+ * @example
3522
+ * const clips = await client.fetchSoundboard('server-id')
3523
+ */
3524
+ fetchSoundboard(serverId) {
3525
+ return this.servers.listSoundboard(serverId);
3526
+ }
3527
+ /**
3528
+ * Fetch statistics for a server (member count, message count, etc.).
3529
+ *
3530
+ * @example
3531
+ * const stats = await client.fetchServerStats('server-id')
3532
+ * console.log(`${stats.onlineCount}/${stats.memberCount} online`)
3533
+ */
3534
+ fetchServerStats(serverId) {
3535
+ return this.servers.getStats(serverId);
3536
+ }
3537
+ /**
3538
+ * Bulk delete up to 100 messages in a channel.
3539
+ * Requires the `messages.manage` scope.
3540
+ *
3541
+ * @example
3542
+ * const result = await client.bulkDelete('channel-id', messageIds)
3543
+ * console.log(`Deleted ${result.deleted} messages`)
3544
+ */
3545
+ bulkDelete(channelId, messageIds) {
3546
+ return this.channels.bulkDelete(channelId, messageIds);
3547
+ }
2416
3548
  // ─── Event fence ──────────────────────────────────────────────────────────────
2417
3549
  /**
2418
3550
  * Wait for a specific event to be emitted, optionally filtered.
@@ -2442,6 +3574,58 @@ var NovaClient = class extends import_node_events.EventEmitter {
2442
3574
  this.on(event, handler);
2443
3575
  });
2444
3576
  }
3577
+ /**
3578
+ * Wait for the next message matching an optional filter.
3579
+ * Short for `waitFor('messageCreate', filter, timeoutMs)`.
3580
+ *
3581
+ * @example
3582
+ * // Wait for any message in a specific channel
3583
+ * const msg = await client.waitForMessage(
3584
+ * m => m.channelId === channelId && !m.isFromBot(),
3585
+ * 30_000,
3586
+ * )
3587
+ * console.log(msg.content)
3588
+ */
3589
+ waitForMessage(filter, timeoutMs = 3e4) {
3590
+ return this.waitFor("messageCreate", filter, timeoutMs);
3591
+ }
3592
+ /**
3593
+ * Wait for the next button-click interaction matching an optional filter.
3594
+ * Short for `waitFor('interactionCreate', i => i.isButton() && filter(i), timeoutMs)`.
3595
+ *
3596
+ * @example
3597
+ * // Wait for any button on a specific message
3598
+ * const click = await client.waitForButton(
3599
+ * i => i.triggerMsgId === message.id,
3600
+ * 60_000,
3601
+ * )
3602
+ * await click.reply('Button clicked!')
3603
+ */
3604
+ waitForButton(filter, timeoutMs = 3e4) {
3605
+ return this.waitFor(
3606
+ "interactionCreate",
3607
+ (i) => i.isButton() && (!filter || filter(i)),
3608
+ timeoutMs
3609
+ );
3610
+ }
3611
+ /**
3612
+ * Wait for the next select-menu interaction matching an optional filter.
3613
+ * Short for `waitFor('interactionCreate', i => i.isSelectMenu() && filter(i), timeoutMs)`.
3614
+ *
3615
+ * @example
3616
+ * const pick = await client.waitForSelectMenu(
3617
+ * i => i.customId === 'colour_pick',
3618
+ * 60_000,
3619
+ * )
3620
+ * await pick.reply(`You picked: ${pick.values.join(', ')}`)
3621
+ */
3622
+ waitForSelectMenu(filter, timeoutMs = 3e4) {
3623
+ return this.waitFor(
3624
+ "interactionCreate",
3625
+ (i) => i.isSelectMenu() && (!filter || filter(i)),
3626
+ timeoutMs
3627
+ );
3628
+ }
2445
3629
  /**
2446
3630
  * Disconnect from the gateway and clean up.
2447
3631
  */
@@ -3809,47 +4993,1376 @@ function countdown(target) {
3809
4993
  seconds: Math.floor(total % MINUTE / SECOND)
3810
4994
  };
3811
4995
  }
4996
+
4997
+ // src/structures/NovaCategory.ts
4998
+ var NovaCategory = class _NovaCategory {
4999
+ constructor(raw, categories) {
5000
+ this.id = raw.id;
5001
+ this.name = raw.name;
5002
+ this.position = raw.position;
5003
+ this.serverId = raw.serverId;
5004
+ this.createdAt = raw.createdAt;
5005
+ this._categories = categories;
5006
+ }
5007
+ // ─── Convenience methods ──────────────────────────────────────────────────
5008
+ /**
5009
+ * Edit this category's name and/or position.
5010
+ * Requires the `channels.manage` scope.
5011
+ *
5012
+ * @example
5013
+ * await cat.edit({ name: 'Lounges', position: 0 })
5014
+ */
5015
+ async edit(options) {
5016
+ const updated = await this._categories.edit(this.id, options);
5017
+ return new _NovaCategory(updated, this._categories);
5018
+ }
5019
+ /**
5020
+ * Rename this category.
5021
+ * Requires the `channels.manage` scope.
5022
+ *
5023
+ * @example
5024
+ * await cat.rename('Lounges')
5025
+ */
5026
+ rename(name) {
5027
+ return this.edit({ name });
5028
+ }
5029
+ /**
5030
+ * Move this category to a specific position.
5031
+ * Requires the `channels.manage` scope.
5032
+ *
5033
+ * @example
5034
+ * await cat.setPosition(0)
5035
+ */
5036
+ setPosition(position) {
5037
+ return this.edit({ position });
5038
+ }
5039
+ /**
5040
+ * Delete this category.
5041
+ * Requires the `channels.manage` scope.
5042
+ *
5043
+ * @example
5044
+ * await cat.delete()
5045
+ */
5046
+ async delete() {
5047
+ await this._categories.delete(this.id);
5048
+ }
5049
+ // ─── Serialisation ─────────────────────────────────────────────────────────
5050
+ toJSON() {
5051
+ return {
5052
+ id: this.id,
5053
+ name: this.name,
5054
+ position: this.position,
5055
+ serverId: this.serverId,
5056
+ createdAt: this.createdAt
5057
+ };
5058
+ }
5059
+ toString() {
5060
+ return `NovaCategory(${this.id}): ${this.name}`;
5061
+ }
5062
+ };
5063
+
5064
+ // src/builders/ForumPostBuilder.ts
5065
+ var ForumPostBuilder = class {
5066
+ constructor() {
5067
+ this._title = "";
5068
+ this._body = "";
5069
+ this._tags = [];
5070
+ }
5071
+ /**
5072
+ * Set the post title.
5073
+ */
5074
+ setTitle(title) {
5075
+ this._title = title;
5076
+ return this;
5077
+ }
5078
+ /**
5079
+ * Set the post body / content.
5080
+ */
5081
+ setBody(body) {
5082
+ this._body = body;
5083
+ return this;
5084
+ }
5085
+ /**
5086
+ * Add a tag to the post.
5087
+ * Tags are used to categorise forum posts.
5088
+ */
5089
+ addTag(tag) {
5090
+ this._tags.push(tag);
5091
+ return this;
5092
+ }
5093
+ /**
5094
+ * Set all tags at once, replacing any previously added tags.
5095
+ */
5096
+ setTags(tags) {
5097
+ this._tags = [...tags];
5098
+ return this;
5099
+ }
5100
+ /**
5101
+ * Validate and return the final `CreateForumPostOptions` object.
5102
+ *
5103
+ * @throws {Error} if title or body is missing.
5104
+ */
5105
+ build() {
5106
+ if (!this._title.trim()) throw new Error("[ForumPostBuilder] title is required");
5107
+ if (!this._body.trim()) throw new Error("[ForumPostBuilder] body is required");
5108
+ return {
5109
+ title: this._title.trim(),
5110
+ body: this._body.trim(),
5111
+ tags: this._tags
5112
+ };
5113
+ }
5114
+ };
5115
+
5116
+ // src/builders/EventBuilder.ts
5117
+ var EventBuilder = class {
5118
+ constructor() {
5119
+ this._title = "";
5120
+ }
5121
+ /** Set the event title (required). */
5122
+ setTitle(title) {
5123
+ this._title = title;
5124
+ return this;
5125
+ }
5126
+ /** Set the event description. */
5127
+ setDescription(description) {
5128
+ this._description = description;
5129
+ return this;
5130
+ }
5131
+ /** Set a banner/cover image URL for the event. */
5132
+ setImage(url) {
5133
+ this._imageUrl = url;
5134
+ return this;
5135
+ }
5136
+ /** Set the physical or virtual location for the event. */
5137
+ setLocation(location) {
5138
+ this._location = location;
5139
+ return this;
5140
+ }
5141
+ /** Set when the event starts. Accepts a Date or ISO string. */
5142
+ setStartAt(date) {
5143
+ this._startAt = date instanceof Date ? date.toISOString() : date;
5144
+ return this;
5145
+ }
5146
+ /** Set when the event ends. Accepts a Date or ISO string. */
5147
+ setEndAt(date) {
5148
+ this._endAt = date instanceof Date ? date.toISOString() : date;
5149
+ return this;
5150
+ }
5151
+ /** Link the event to a specific channel. */
5152
+ setChannel(channelId) {
5153
+ this._channelId = channelId;
5154
+ return this;
5155
+ }
5156
+ /**
5157
+ * Validate and return the final `CreateEventOptions` object.
5158
+ *
5159
+ * @throws {Error} if title or startAt is missing.
5160
+ */
5161
+ build() {
5162
+ if (!this._title.trim()) throw new Error("[EventBuilder] title is required");
5163
+ if (!this._startAt) throw new Error("[EventBuilder] startAt is required \u2014 call setStartAt()");
5164
+ return {
5165
+ title: this._title.trim(),
5166
+ ...this._description !== void 0 ? { description: this._description } : {},
5167
+ ...this._imageUrl !== void 0 ? { imageUrl: this._imageUrl } : {},
5168
+ ...this._location !== void 0 ? { location: this._location } : {},
5169
+ startAt: this._startAt,
5170
+ ...this._endAt !== void 0 ? { endAt: this._endAt } : {},
5171
+ ...this._channelId !== void 0 ? { channelId: this._channelId } : {}
5172
+ };
5173
+ }
5174
+ };
5175
+
5176
+ // src/builders/RoleBuilder.ts
5177
+ var RoleBuilder = class {
5178
+ constructor() {
5179
+ this._name = "";
5180
+ }
5181
+ /** Set the role's display name (required). */
5182
+ setName(name) {
5183
+ this._name = name;
5184
+ return this;
5185
+ }
5186
+ /**
5187
+ * Set the role's accent colour.
5188
+ * @param color Hex string — e.g. `'#5865F2'` or `'5865F2'`
5189
+ */
5190
+ setColor(color) {
5191
+ this._color = color.startsWith("#") ? color : `#${color}`;
5192
+ return this;
5193
+ }
5194
+ /**
5195
+ * Whether this role is shown separately in the member sidebar.
5196
+ * Default: `true` when called without arguments.
5197
+ */
5198
+ setHoist(hoist = true) {
5199
+ this._hoist = hoist;
5200
+ return this;
5201
+ }
5202
+ /** Set a specific permission key to true or false. */
5203
+ addPermission(key, value = true) {
5204
+ if (!this._permissions) this._permissions = {};
5205
+ this._permissions[key] = value;
5206
+ return this;
5207
+ }
5208
+ /** Replace all permission overrides at once. */
5209
+ setPermissions(permissions) {
5210
+ this._permissions = { ...permissions };
5211
+ return this;
5212
+ }
5213
+ // ─── Common permission shorthands ─────────────────────────────────────────
5214
+ /** Grant the `sendMessages` permission. */
5215
+ allowSendMessages() {
5216
+ return this.addPermission("sendMessages");
5217
+ }
5218
+ /** Deny the `sendMessages` permission. */
5219
+ denySendMessages() {
5220
+ return this.addPermission("sendMessages", false);
5221
+ }
5222
+ /** Grant the `addReactions` permission. */
5223
+ allowAddReactions() {
5224
+ return this.addPermission("addReactions");
5225
+ }
5226
+ /** Grant the `manageMessages` permission (delete/pin messages). */
5227
+ allowManageMessages() {
5228
+ return this.addPermission("manageMessages");
5229
+ }
5230
+ /** Grant the `manageChannels` permission. */
5231
+ allowManageChannels() {
5232
+ return this.addPermission("manageChannels");
5233
+ }
5234
+ /** Grant the `kickMembers` permission. */
5235
+ allowKickMembers() {
5236
+ return this.addPermission("kickMembers");
5237
+ }
5238
+ /** Grant the `banMembers` permission. */
5239
+ allowBanMembers() {
5240
+ return this.addPermission("banMembers");
5241
+ }
5242
+ /** Grant the `manageServer` permission. */
5243
+ allowManageServer() {
5244
+ return this.addPermission("manageServer");
5245
+ }
5246
+ /** Grant the `manageRoles` permission. */
5247
+ allowManageRoles() {
5248
+ return this.addPermission("manageRoles");
5249
+ }
5250
+ /**
5251
+ * Validate and build the `RoleCreateOptions` object.
5252
+ *
5253
+ * @throws if name is not set.
5254
+ */
5255
+ build() {
5256
+ if (!this._name.trim()) throw new Error("[RoleBuilder] name is required \u2014 call .setName()");
5257
+ return {
5258
+ name: this._name.trim(),
5259
+ ...this._color !== void 0 ? { color: this._color } : {},
5260
+ ...this._hoist !== void 0 ? { hoist: this._hoist } : {},
5261
+ ...this._permissions !== void 0 ? { permissions: this._permissions } : {}
5262
+ };
5263
+ }
5264
+ };
5265
+
5266
+ // src/builders/ChannelBuilder.ts
5267
+ var ChannelBuilder = class {
5268
+ constructor() {
5269
+ this._name = "";
5270
+ this._type = "TEXT";
5271
+ }
5272
+ /** Set the channel name (required). No # prefix needed. */
5273
+ setName(name) {
5274
+ this._name = name;
5275
+ return this;
5276
+ }
5277
+ /**
5278
+ * Set the channel type.
5279
+ * @default 'TEXT'
5280
+ */
5281
+ setType(type) {
5282
+ this._type = type;
5283
+ return this;
5284
+ }
5285
+ // ─── Type shorthands ──────────────────────────────────────────────────────
5286
+ /** Create a TEXT channel. */
5287
+ asText() {
5288
+ return this.setType("TEXT");
5289
+ }
5290
+ /** Create a VOICE channel. */
5291
+ asVoice() {
5292
+ return this.setType("VOICE");
5293
+ }
5294
+ /** Create an ANNOUNCEMENT channel. */
5295
+ asAnnouncement() {
5296
+ return this.setType("ANNOUNCEMENT");
5297
+ }
5298
+ /** Create a FORUM channel. */
5299
+ asForum() {
5300
+ return this.setType("FORUM");
5301
+ }
5302
+ /** Create a STAGE channel. */
5303
+ asStage() {
5304
+ return this.setType("STAGE");
5305
+ }
5306
+ // ─── Properties ───────────────────────────────────────────────────────────
5307
+ /** Set the channel topic / description. */
5308
+ setTopic(topic) {
5309
+ this._topic = topic;
5310
+ return this;
5311
+ }
5312
+ /** Set the channel's sort position. */
5313
+ setPosition(position) {
5314
+ this._position = position;
5315
+ return this;
5316
+ }
5317
+ /** Place the channel inside an existing category by its ID. */
5318
+ setCategory(categoryId) {
5319
+ this._categoryId = categoryId;
5320
+ return this;
5321
+ }
5322
+ /**
5323
+ * Set a slow-mode interval in seconds.
5324
+ * Members must wait this long between messages.
5325
+ * Pass `0` to disable.
5326
+ */
5327
+ setSlowMode(seconds) {
5328
+ this._slowMode = seconds;
5329
+ return this;
5330
+ }
5331
+ /**
5332
+ * Mark the channel as private.
5333
+ * Private channels are only visible to members with explicit access.
5334
+ */
5335
+ setPrivate(isPrivate = true) {
5336
+ this._isPrivate = isPrivate;
5337
+ return this;
5338
+ }
5339
+ /**
5340
+ * Validate and build the `CreateChannelOptions` object.
5341
+ *
5342
+ * @throws if name is not set.
5343
+ */
5344
+ build() {
5345
+ if (!this._name.trim()) throw new Error("[ChannelBuilder] name is required \u2014 call .setName()");
5346
+ return {
5347
+ name: this._name.trim(),
5348
+ type: this._type,
5349
+ ...this._topic !== void 0 ? { topic: this._topic } : {},
5350
+ ...this._position !== void 0 ? { position: this._position } : {},
5351
+ ...this._categoryId !== void 0 ? { categoryId: this._categoryId } : {},
5352
+ ...this._slowMode !== void 0 ? { slowMode: this._slowMode } : {},
5353
+ ...this._isPrivate !== void 0 ? { isPrivate: this._isPrivate } : {}
5354
+ };
5355
+ }
5356
+ };
5357
+
5358
+ // src/builders/InviteBuilder.ts
5359
+ var InviteBuilder = class {
5360
+ /**
5361
+ * Set the maximum number of times this invite can be used.
5362
+ * Pass `null` for unlimited uses.
5363
+ */
5364
+ setMaxUses(maxUses) {
5365
+ this._maxUses = maxUses;
5366
+ return this;
5367
+ }
5368
+ /**
5369
+ * Set an absolute expiry date/time for this invite.
5370
+ * Pass a `Date`, an ISO string, or `null` to never expire.
5371
+ */
5372
+ setExpiresAt(date) {
5373
+ if (date === null) {
5374
+ this._expiresAt = null;
5375
+ } else {
5376
+ this._expiresAt = date instanceof Date ? date.toISOString() : date;
5377
+ }
5378
+ return this;
5379
+ }
5380
+ /**
5381
+ * Set expiry as a duration from now.
5382
+ * @param ms Duration in milliseconds.
5383
+ *
5384
+ * @example
5385
+ * builder.expiresIn(7 * 24 * 60 * 60 * 1000) // 7 days
5386
+ */
5387
+ expiresIn(ms) {
5388
+ this._expiresAt = new Date(Date.now() + ms).toISOString();
5389
+ return this;
5390
+ }
5391
+ /**
5392
+ * Create a one-time use invite (`maxUses = 1`).
5393
+ */
5394
+ setOneTimeUse() {
5395
+ return this.setMaxUses(1);
5396
+ }
5397
+ /**
5398
+ * Create a permanent invite with no expiry or usage limit.
5399
+ */
5400
+ setPermanent() {
5401
+ this._maxUses = null;
5402
+ this._expiresAt = null;
5403
+ return this;
5404
+ }
5405
+ /**
5406
+ * Build the `InviteCreateOptions` object.
5407
+ */
5408
+ build() {
5409
+ return {
5410
+ ...this._maxUses !== void 0 ? { maxUses: this._maxUses } : {},
5411
+ ...this._expiresAt !== void 0 ? { expiresAt: this._expiresAt } : {}
5412
+ };
5413
+ }
5414
+ };
5415
+
5416
+ // src/builders/AutoModRuleBuilder.ts
5417
+ var AutoModRuleBuilder = class {
5418
+ constructor() {
5419
+ this._value = "";
5420
+ this._enabled = true;
5421
+ }
5422
+ /** Set the rule type explicitly. */
5423
+ setType(type) {
5424
+ this._type = type;
5425
+ return this;
5426
+ }
5427
+ /**
5428
+ * Set the blocked value (word or domain).
5429
+ */
5430
+ setValue(value) {
5431
+ this._value = value;
5432
+ return this;
5433
+ }
5434
+ /**
5435
+ * Shorthand: set type to BLOCKED_WORD and optionally set the value.
5436
+ *
5437
+ * @example
5438
+ * builder.blockWord('badword')
5439
+ */
5440
+ blockWord(word) {
5441
+ this._type = "BLOCKED_WORD";
5442
+ if (word !== void 0) this._value = word;
5443
+ return this;
5444
+ }
5445
+ /**
5446
+ * Shorthand: set type to BLOCKED_LINK and optionally set the domain.
5447
+ *
5448
+ * @example
5449
+ * builder.blockLink('spam.com')
5450
+ */
5451
+ blockLink(domain) {
5452
+ this._type = "BLOCKED_LINK";
5453
+ if (domain !== void 0) this._value = domain;
5454
+ return this;
5455
+ }
5456
+ /**
5457
+ * Whether the rule is active immediately.
5458
+ * @default true
5459
+ */
5460
+ setEnabled(enabled) {
5461
+ this._enabled = enabled;
5462
+ return this;
5463
+ }
5464
+ /**
5465
+ * Validate and build the `CreateAutoModRuleOptions` object.
5466
+ *
5467
+ * @throws if type or value is not set.
5468
+ */
5469
+ build() {
5470
+ if (!this._type) {
5471
+ throw new Error("[AutoModRuleBuilder] type is required \u2014 call .setType(), .blockWord(), or .blockLink()");
5472
+ }
5473
+ if (!this._value.trim()) {
5474
+ throw new Error("[AutoModRuleBuilder] value is required \u2014 call .setValue(), .blockWord(word), or .blockLink(domain)");
5475
+ }
5476
+ return {
5477
+ type: this._type,
5478
+ value: this._value.trim(),
5479
+ enabled: this._enabled
5480
+ };
5481
+ }
5482
+ };
5483
+
5484
+ // src/builders/ContextMenuCommandBuilder.ts
5485
+ var ContextMenuCommandBuilder = class {
5486
+ constructor() {
5487
+ this._name = "";
5488
+ this._target = "MESSAGE";
5489
+ this._guildId = null;
5490
+ }
5491
+ /**
5492
+ * Display name of the command (shown in the context menu).
5493
+ * Max 32 characters. May contain spaces.
5494
+ *
5495
+ * @example
5496
+ * builder.setName('Report Message')
5497
+ */
5498
+ setName(name) {
5499
+ this._name = name.trim();
5500
+ return this;
5501
+ }
5502
+ /**
5503
+ * Set whether the command targets messages or users.
5504
+ *
5505
+ * @example
5506
+ * builder.setTarget('USER')
5507
+ */
5508
+ setTarget(target) {
5509
+ this._target = target;
5510
+ return this;
5511
+ }
5512
+ /**
5513
+ * Shorthand for `setTarget('MESSAGE')`.
5514
+ *
5515
+ * @example
5516
+ * builder.forMessages()
5517
+ */
5518
+ forMessages() {
5519
+ return this.setTarget("MESSAGE");
5520
+ }
5521
+ /**
5522
+ * Shorthand for `setTarget('USER')`.
5523
+ *
5524
+ * @example
5525
+ * builder.forUsers()
5526
+ */
5527
+ forUsers() {
5528
+ return this.setTarget("USER");
5529
+ }
5530
+ /**
5531
+ * Register this command only for a specific server (guild).
5532
+ * Omit (or pass `null`) to make the command available globally.
5533
+ *
5534
+ * @example
5535
+ * builder.setGuildId('server-id')
5536
+ */
5537
+ setGuildId(guildId) {
5538
+ this._guildId = guildId;
5539
+ return this;
5540
+ }
5541
+ /**
5542
+ * Validate and return the final command definition object.
5543
+ * Throws if `name` has not been set.
5544
+ */
5545
+ build() {
5546
+ if (!this._name) {
5547
+ throw new Error("[ContextMenuCommandBuilder] Command name is required");
5548
+ }
5549
+ return {
5550
+ name: this._name,
5551
+ target: this._target,
5552
+ guildId: this._guildId
5553
+ };
5554
+ }
5555
+ /** Alias for `build()`. */
5556
+ toJSON() {
5557
+ return this.build();
5558
+ }
5559
+ };
5560
+
5561
+ // src/utils/MessageCollector.ts
5562
+ var MessageCollector = class {
5563
+ constructor(emitter, options = {}) {
5564
+ this._emitter = emitter;
5565
+ this._options = {
5566
+ count: options.count ?? 1,
5567
+ timeout: options.timeout ?? 6e4,
5568
+ filter: options.filter ?? (() => true)
5569
+ };
5570
+ }
5571
+ /**
5572
+ * Start collecting messages.
5573
+ * Returns a promise that resolves with the collected `NovaMessage` array.
5574
+ */
5575
+ run() {
5576
+ return new Promise((resolve) => {
5577
+ const collected = [];
5578
+ const cleanup = () => {
5579
+ clearTimeout(timer);
5580
+ this._emitter.off("messageCreate", handler);
5581
+ };
5582
+ const handler = (msg) => {
5583
+ if (!this._options.filter(msg)) return;
5584
+ collected.push(msg);
5585
+ if (collected.length >= this._options.count) {
5586
+ cleanup();
5587
+ resolve([...collected]);
5588
+ }
5589
+ };
5590
+ const timer = setTimeout(() => {
5591
+ this._emitter.off("messageCreate", handler);
5592
+ resolve([...collected]);
5593
+ }, this._options.timeout);
5594
+ this._emitter.on("messageCreate", handler);
5595
+ });
5596
+ }
5597
+ };
5598
+
5599
+ // src/utils/InteractionCollector.ts
5600
+ var InteractionCollector = class {
5601
+ constructor(emitter, options = {}) {
5602
+ this._emitter = emitter;
5603
+ this._options = {
5604
+ count: options.count ?? 1,
5605
+ timeout: options.timeout ?? 6e4,
5606
+ filter: options.filter ?? (() => true),
5607
+ messageId: options.messageId
5608
+ };
5609
+ }
5610
+ /**
5611
+ * Start collecting interactions.
5612
+ * Returns a promise that resolves with the collected `NovaInteraction` array.
5613
+ */
5614
+ run() {
5615
+ return new Promise((resolve) => {
5616
+ const collected = [];
5617
+ const cleanup = () => {
5618
+ clearTimeout(timer);
5619
+ this._emitter.off("interactionCreate", handler);
5620
+ };
5621
+ const handler = (interaction) => {
5622
+ if (this._options.messageId && interaction.triggerMsgId !== this._options.messageId) return;
5623
+ if (!this._options.filter(interaction)) return;
5624
+ collected.push(interaction);
5625
+ if (collected.length >= this._options.count) {
5626
+ cleanup();
5627
+ resolve([...collected]);
5628
+ }
5629
+ };
5630
+ const timer = setTimeout(() => {
5631
+ this._emitter.off("interactionCreate", handler);
5632
+ resolve([...collected]);
5633
+ }, this._options.timeout);
5634
+ this._emitter.on("interactionCreate", handler);
5635
+ });
5636
+ }
5637
+ };
5638
+
5639
+ // src/utils/MentionParser.ts
5640
+ var MentionParser = class {
5641
+ // ─── Extraction ────────────────────────────────────────────────────────────
5642
+ /**
5643
+ * Extract all user IDs mentioned in the content (`<@userId>`).
5644
+ *
5645
+ * @example
5646
+ * MentionParser.userIds('Hello <@abc> and <@xyz>!') // ['abc', 'xyz']
5647
+ */
5648
+ static userIds(content) {
5649
+ return [...content.matchAll(/<@([a-zA-Z0-9_-]+)>/g)].map((m) => m[1]);
5650
+ }
5651
+ /**
5652
+ * Extract all channel IDs mentioned in the content (`<#channelId>`).
5653
+ *
5654
+ * @example
5655
+ * MentionParser.channelIds('Go to <#general>!') // ['general']
5656
+ */
5657
+ static channelIds(content) {
5658
+ return [...content.matchAll(/<#([a-zA-Z0-9_-]+)>/g)].map((m) => m[1]);
5659
+ }
5660
+ /**
5661
+ * Extract all role IDs mentioned in the content (`<&roleId>`).
5662
+ *
5663
+ * @example
5664
+ * MentionParser.roleIds('Paging <&mods>!') // ['mods']
5665
+ */
5666
+ static roleIds(content) {
5667
+ return [...content.matchAll(/<&([a-zA-Z0-9_-]+)>/g)].map((m) => m[1]);
5668
+ }
5669
+ /**
5670
+ * Extract all custom emoji IDs mentioned in the content (`<:name:id>`).
5671
+ *
5672
+ * @example
5673
+ * MentionParser.emojiIds('Love it <:nova:12345>!')
5674
+ * // [{ name: 'nova', id: '12345' }]
5675
+ */
5676
+ static emojiIds(content) {
5677
+ return [...content.matchAll(/<:([a-zA-Z0-9_]+):([a-zA-Z0-9_-]+)>/g)].map((m) => ({
5678
+ name: m[1],
5679
+ id: m[2]
5680
+ }));
5681
+ }
5682
+ // ─── Format ─────────────────────────────────────────────────────────────────
5683
+ /**
5684
+ * Format a user ID into a mention string `<@userId>`.
5685
+ *
5686
+ * @example
5687
+ * MentionParser.user('abc123') // '<@abc123>'
5688
+ */
5689
+ static user(userId) {
5690
+ return `<@${userId}>`;
5691
+ }
5692
+ /**
5693
+ * Format a channel ID into a mention string `<#channelId>`.
5694
+ *
5695
+ * @example
5696
+ * MentionParser.channel('general') // '<#general>'
5697
+ */
5698
+ static channel(channelId) {
5699
+ return `<#${channelId}>`;
5700
+ }
5701
+ /**
5702
+ * Format a role ID into a mention string `<&roleId>`.
5703
+ *
5704
+ * @example
5705
+ * MentionParser.role('mods') // '<&mods>'
5706
+ */
5707
+ static role(roleId) {
5708
+ return `<&${roleId}>`;
5709
+ }
5710
+ /**
5711
+ * Format a custom emoji mention `<:name:id>`.
5712
+ *
5713
+ * @example
5714
+ * MentionParser.emoji('nova', '12345') // '<:nova:12345>'
5715
+ */
5716
+ static emoji(name, id) {
5717
+ return `<:${name}:${id}>`;
5718
+ }
5719
+ // ─── Clean ──────────────────────────────────────────────────────────────────
5720
+ /**
5721
+ * Remove all mention tokens from `content` and return the cleaned string.
5722
+ *
5723
+ * @example
5724
+ * MentionParser.stripMentions('Hello <@u1> in <#c1>!')
5725
+ * // 'Hello in !'
5726
+ */
5727
+ static stripMentions(content) {
5728
+ return content.replace(/<@[a-zA-Z0-9_-]+>/g, "").replace(/<#[a-zA-Z0-9_-]+>/g, "").replace(/<&[a-zA-Z0-9_-]+>/g, "").replace(/<:[a-zA-Z0-9_]+:[a-zA-Z0-9_-]+>/g, "");
5729
+ }
5730
+ /**
5731
+ * Returns `true` if `content` mentions at least one user.
5732
+ * Optionally checks for a specific `userId`.
5733
+ *
5734
+ * @example
5735
+ * MentionParser.hasMention('Hi <@u1>') // true
5736
+ * MentionParser.hasMention('Hi <@u1>', 'u1') // true
5737
+ * MentionParser.hasMention('Hi <@u1>', 'u2') // false
5738
+ */
5739
+ static hasMention(content, userId) {
5740
+ if (userId) return content.includes(`<@${userId}>`);
5741
+ return /<@[a-zA-Z0-9_-]+>/.test(content);
5742
+ }
5743
+ /**
5744
+ * Returns `true` if `content` mentions a specific bot by its ID.
5745
+ *
5746
+ * @example
5747
+ * if (MentionParser.mentionsBotId(message.content, client.user.id)) {
5748
+ * await message.reply('You called?')
5749
+ * }
5750
+ */
5751
+ static mentionsBotId(content, botId) {
5752
+ return content.includes(`<@${botId}>`);
5753
+ }
5754
+ };
5755
+
5756
+ // src/utils/EmbedPaginator.ts
5757
+ var EmbedPaginator = class {
5758
+ constructor(emitter, messages, interactions, options) {
5759
+ this.emitter = emitter;
5760
+ this.messages = messages;
5761
+ this.interactions = interactions;
5762
+ this.options = options;
5763
+ this.page = 0;
5764
+ const size = Math.max(1, options.pageSize ?? 10);
5765
+ const pages = [];
5766
+ for (let i = 0; i < options.items.length; i += size) {
5767
+ pages.push(options.items.slice(i, i + size));
5768
+ }
5769
+ this.pages = pages;
5770
+ }
5771
+ /** Total number of pages. */
5772
+ get totalPages() {
5773
+ return this.pages.length;
5774
+ }
5775
+ /**
5776
+ * Send the first page to the configured channel.
5777
+ * If there are multiple pages, attaches ◀ ▶ navigation buttons and
5778
+ * listens for clicks for up to `options.timeout` ms.
5779
+ */
5780
+ async send() {
5781
+ if (this.pages.length === 0) {
5782
+ await this.messages.send(this.options.channelId, { content: "*Nothing to display.*" });
5783
+ return;
5784
+ }
5785
+ const msg = await this.messages.send(this.options.channelId, this._buildPayload());
5786
+ if (this.pages.length <= 1) return;
5787
+ const timeout = this.options.timeout ?? 6e4;
5788
+ const deadline = Date.now() + timeout;
5789
+ while (true) {
5790
+ const remaining = deadline - Date.now();
5791
+ if (remaining <= 0) break;
5792
+ const click = await this._awaitClick(msg.id, remaining);
5793
+ if (!click) break;
5794
+ if (click.customId === "_pg_prev" && this.page > 0) this.page--;
5795
+ else if (click.customId === "_pg_next" && this.page < this.pages.length - 1) this.page++;
5796
+ this.interactions.respond(click.id, { content: "\u200B", ephemeral: true }).catch(() => void 0);
5797
+ await this.messages.edit(msg.id, this._buildPayload());
5798
+ }
5799
+ }
5800
+ // ── Internal helpers ────────────────────────────────────────────────────────
5801
+ _awaitClick(msgId, timeoutMs) {
5802
+ return new Promise((resolve) => {
5803
+ const timer = setTimeout(() => {
5804
+ this.emitter.off("interactionCreate", handler);
5805
+ resolve(null);
5806
+ }, timeoutMs);
5807
+ const handler = (i) => {
5808
+ const isOurs = i.triggerMsgId === msgId && (i.customId === "_pg_prev" || i.customId === "_pg_next");
5809
+ const isAllowed = !this.options.userId || i.userId === this.options.userId;
5810
+ if (isOurs && isAllowed) {
5811
+ clearTimeout(timer);
5812
+ this.emitter.off("interactionCreate", handler);
5813
+ resolve({ id: i.id, customId: i.customId });
5814
+ }
5815
+ };
5816
+ this.emitter.on("interactionCreate", handler);
5817
+ });
5818
+ }
5819
+ _buildPayload() {
5820
+ const page = this.pages[this.page];
5821
+ const embed = this.options.renderPage(page, this.page, this.pages.length);
5822
+ if (this.options.showPageCounter !== false && this.pages.length > 1) {
5823
+ const counter = `Page ${this.page + 1} / ${this.pages.length}`;
5824
+ embed.footer = embed.footer ? `${embed.footer} \u2022 ${counter}` : counter;
5825
+ }
5826
+ const row = new ActionRowBuilder().addComponent(
5827
+ new ButtonBuilder().setCustomId("_pg_prev").setLabel("\u25C0").setStyle("secondary").setDisabled(this.page === 0)
5828
+ ).addComponent(
5829
+ new ButtonBuilder().setCustomId("_pg_next").setLabel("\u25B6").setStyle("secondary").setDisabled(this.page === this.pages.length - 1)
5830
+ );
5831
+ return { embed, components: row.toJSON() };
5832
+ }
5833
+ };
5834
+
5835
+ // src/utils/ConfirmationDialog.ts
5836
+ var ConfirmationDialog = class {
5837
+ constructor(emitter, messages, interactions) {
5838
+ this.emitter = emitter;
5839
+ this.messages = messages;
5840
+ this.interactions = interactions;
5841
+ }
5842
+ /**
5843
+ * Send the confirmation prompt and await the user's choice.
5844
+ * Returns `true` for confirm, `false` for cancel or timeout.
5845
+ */
5846
+ async ask(options) {
5847
+ const content = options.content ?? "Are you sure?";
5848
+ const confirmLabel = options.confirmLabel ?? "Confirm";
5849
+ const cancelLabel = options.cancelLabel ?? "Cancel";
5850
+ const timeout = options.timeout ?? 3e4;
5851
+ const row = new ActionRowBuilder().addComponent(
5852
+ new ButtonBuilder().setCustomId("_confirm_yes").setLabel(confirmLabel).setStyle("success")
5853
+ ).addComponent(
5854
+ new ButtonBuilder().setCustomId("_confirm_no").setLabel(cancelLabel).setStyle("danger")
5855
+ );
5856
+ const msg = await this.messages.send(options.channelId, {
5857
+ content,
5858
+ components: row.toJSON()
5859
+ });
5860
+ const click = await this._awaitClick(msg.id, options.userId, timeout);
5861
+ const disabledRow = new ActionRowBuilder().addComponent(
5862
+ new ButtonBuilder().setCustomId("_confirm_yes").setLabel(confirmLabel).setStyle("success").setDisabled(true)
5863
+ ).addComponent(
5864
+ new ButtonBuilder().setCustomId("_confirm_no").setLabel(cancelLabel).setStyle("danger").setDisabled(true)
5865
+ );
5866
+ await this.messages.edit(msg.id, { content, components: disabledRow.toJSON() }).catch(() => void 0);
5867
+ if (click) {
5868
+ this.interactions.respond(click.id, { content: "\u200B", ephemeral: true }).catch(() => void 0);
5869
+ return click.customId === "_confirm_yes";
5870
+ }
5871
+ return false;
5872
+ }
5873
+ _awaitClick(msgId, userId, timeoutMs) {
5874
+ return new Promise((resolve) => {
5875
+ const timer = setTimeout(() => {
5876
+ this.emitter.off("interactionCreate", handler);
5877
+ resolve(null);
5878
+ }, timeoutMs);
5879
+ const handler = (i) => {
5880
+ const isOurs = i.triggerMsgId === msgId && (i.customId === "_confirm_yes" || i.customId === "_confirm_no");
5881
+ const isAllowed = !userId || i.userId === userId;
5882
+ if (isOurs && isAllowed && typeof i.customId === "string") {
5883
+ clearTimeout(timer);
5884
+ this.emitter.off("interactionCreate", handler);
5885
+ resolve({ id: i.id, customId: i.customId });
5886
+ }
5887
+ };
5888
+ this.emitter.on("interactionCreate", handler);
5889
+ });
5890
+ }
5891
+ };
5892
+
5893
+ // src/utils/ArgumentParser.ts
5894
+ var ParsedArguments = class {
5895
+ constructor(positional, flags) {
5896
+ this.positional = positional;
5897
+ this._flags = flags;
5898
+ }
5899
+ /**
5900
+ * Whether a named flag is present (regardless of value).
5901
+ *
5902
+ * @example
5903
+ * args.has('notify') // true for --notify or --notify true
5904
+ */
5905
+ has(flag) {
5906
+ return this._flags.has(flag);
5907
+ }
5908
+ /**
5909
+ * Get the string value of a named flag, or `null` if absent.
5910
+ *
5911
+ * @example
5912
+ * args.get('reason') // 'rule violation'
5913
+ */
5914
+ get(flag) {
5915
+ return this._flags.get(flag) ?? null;
5916
+ }
5917
+ /**
5918
+ * Get the string value of a named flag, throwing if missing.
5919
+ *
5920
+ * @example
5921
+ * const reason = args.require('reason') // throws if --reason not supplied
5922
+ */
5923
+ require(flag) {
5924
+ const v = this._flags.get(flag);
5925
+ if (v == null) throw new Error(`[ArgumentParser] Required flag "--${flag}" is missing`);
5926
+ return v;
5927
+ }
5928
+ /**
5929
+ * Parse the flag value as an integer, or `null` if absent / not a number.
5930
+ *
5931
+ * @example
5932
+ * args.getInt('days') // 7 from '--days 7'
5933
+ */
5934
+ getInt(flag) {
5935
+ const v = this._flags.get(flag);
5936
+ if (v == null) return null;
5937
+ const n = parseInt(v, 10);
5938
+ return isNaN(n) ? null : n;
5939
+ }
5940
+ /**
5941
+ * Parse the flag value as a float, or `null` if absent / not a number.
5942
+ */
5943
+ getFloat(flag) {
5944
+ const v = this._flags.get(flag);
5945
+ if (v == null) return null;
5946
+ const n = parseFloat(v);
5947
+ return isNaN(n) ? null : n;
5948
+ }
5949
+ /**
5950
+ * Get a positional argument by index (0-based), or `null` if absent.
5951
+ *
5952
+ * @example
5953
+ * args.at(0) // first positional token
5954
+ */
5955
+ at(index) {
5956
+ return this.positional[index] ?? null;
5957
+ }
5958
+ /**
5959
+ * Get positional argument at `index` as an integer, or `null`.
5960
+ *
5961
+ * @example
5962
+ * args.getInt(1) // 7 when positional is ['nick123', '7']
5963
+ */
5964
+ getIntAt(index) {
5965
+ const v = this.positional[index];
5966
+ if (v == null) return null;
5967
+ const n = parseInt(v, 10);
5968
+ return isNaN(n) ? null : n;
5969
+ }
5970
+ /**
5971
+ * All flag names that were parsed.
5972
+ *
5973
+ * @example
5974
+ * args.flags() // ['reason', 'notify']
5975
+ */
5976
+ flags() {
5977
+ return [...this._flags.keys()];
5978
+ }
5979
+ toJSON() {
5980
+ return {
5981
+ positional: this.positional,
5982
+ flags: Object.fromEntries(this._flags)
5983
+ };
5984
+ }
5985
+ };
5986
+ var ArgumentParser = class _ArgumentParser {
5987
+ /**
5988
+ * Parse a raw string of arguments.
5989
+ * Returns a `ParsedArguments` instance.
5990
+ */
5991
+ static parse(input) {
5992
+ const positional = [];
5993
+ const flags = /* @__PURE__ */ new Map();
5994
+ const tokens = _ArgumentParser._tokenise(input);
5995
+ let i = 0;
5996
+ while (i < tokens.length) {
5997
+ const token = tokens[i];
5998
+ if (token.startsWith("--")) {
5999
+ const key = token.slice(2);
6000
+ const next = tokens[i + 1];
6001
+ if (next && !next.startsWith("-")) {
6002
+ flags.set(key, next);
6003
+ i += 2;
6004
+ } else {
6005
+ flags.set(key, "true");
6006
+ i++;
6007
+ }
6008
+ } else if (token.startsWith("-") && token.length === 2) {
6009
+ flags.set(token.slice(1), "true");
6010
+ i++;
6011
+ } else {
6012
+ positional.push(token);
6013
+ i++;
6014
+ }
6015
+ }
6016
+ return new ParsedArguments(positional, flags);
6017
+ }
6018
+ /**
6019
+ * Strip a user mention like `<@123456>` from the start of the input string
6020
+ * and return the remainder trimmed. Returns the original string unchanged if
6021
+ * no leading mention is found.
6022
+ *
6023
+ * @example
6024
+ * ArgumentParser.stripMention('<@98765> hello world') // 'hello world'
6025
+ */
6026
+ static stripMention(input) {
6027
+ return input.replace(/^<@!?\d+>\s*/, "").trim();
6028
+ }
6029
+ /**
6030
+ * Split `input` into tokens. Quoted strings are treated as single tokens.
6031
+ */
6032
+ static _tokenise(input) {
6033
+ const tokens = [];
6034
+ let current = "";
6035
+ let inDouble = false;
6036
+ let inSingle = false;
6037
+ for (let i = 0; i < input.length; i++) {
6038
+ const ch = input[i];
6039
+ if (ch === '"' && !inSingle) {
6040
+ inDouble = !inDouble;
6041
+ continue;
6042
+ }
6043
+ if (ch === "'" && !inDouble) {
6044
+ inSingle = !inSingle;
6045
+ continue;
6046
+ }
6047
+ if (ch === " " && !inDouble && !inSingle) {
6048
+ if (current.length > 0) {
6049
+ tokens.push(current);
6050
+ current = "";
6051
+ }
6052
+ continue;
6053
+ }
6054
+ current += ch;
6055
+ }
6056
+ if (current.length > 0) tokens.push(current);
6057
+ return tokens;
6058
+ }
6059
+ };
6060
+
6061
+ // src/utils/CacheMap.ts
6062
+ var CacheMap = class {
6063
+ constructor() {
6064
+ this._store = /* @__PURE__ */ new Map();
6065
+ }
6066
+ /**
6067
+ * Store a value.
6068
+ *
6069
+ * @param key - Cache key.
6070
+ * @param value - Value to store.
6071
+ * @param ttlMs - Optional time-to-live in milliseconds. Omit for permanent storage.
6072
+ */
6073
+ set(key, value, ttlMs) {
6074
+ this._store.set(key, {
6075
+ value,
6076
+ expiresAt: ttlMs != null ? Date.now() + ttlMs : null
6077
+ });
6078
+ return this;
6079
+ }
6080
+ /**
6081
+ * Retrieve a value, or `undefined` if absent or expired.
6082
+ * Expired entries are automatically deleted on access.
6083
+ */
6084
+ get(key) {
6085
+ const entry = this._store.get(key);
6086
+ if (!entry) return void 0;
6087
+ if (entry.expiresAt !== null && Date.now() >= entry.expiresAt) {
6088
+ this._store.delete(key);
6089
+ return void 0;
6090
+ }
6091
+ return entry.value;
6092
+ }
6093
+ /**
6094
+ * Check if a live (non-expired) entry exists for this key.
6095
+ */
6096
+ has(key) {
6097
+ return this.get(key) !== void 0;
6098
+ }
6099
+ /**
6100
+ * Delete an entry.
6101
+ * Returns `true` if the entry existed (even if it was expired).
6102
+ */
6103
+ delete(key) {
6104
+ return this._store.delete(key);
6105
+ }
6106
+ /** Remove all entries. */
6107
+ clear() {
6108
+ this._store.clear();
6109
+ }
6110
+ /**
6111
+ * Number of **live** (non-expired) entries.
6112
+ * This prunes expired entries as a side-effect.
6113
+ */
6114
+ get size() {
6115
+ this.prune();
6116
+ return this._store.size;
6117
+ }
6118
+ /**
6119
+ * Remove all expired entries immediately.
6120
+ * Useful to call occasionally to free memory.
6121
+ *
6122
+ * @example
6123
+ * // Prune every 10 minutes
6124
+ * setInterval(() => cache.prune(), 10 * 60_000)
6125
+ */
6126
+ prune() {
6127
+ const now = Date.now();
6128
+ let removed = 0;
6129
+ for (const [key, entry] of this._store) {
6130
+ if (entry.expiresAt !== null && now >= entry.expiresAt) {
6131
+ this._store.delete(key);
6132
+ removed++;
6133
+ }
6134
+ }
6135
+ return removed;
6136
+ }
6137
+ /**
6138
+ * Return all **live** keys.
6139
+ */
6140
+ keys() {
6141
+ this.prune();
6142
+ return [...this._store.keys()];
6143
+ }
6144
+ /**
6145
+ * Return all **live** values.
6146
+ */
6147
+ values() {
6148
+ this.prune();
6149
+ return [...this._store.values()].map((e) => e.value);
6150
+ }
6151
+ /**
6152
+ * Return all **live** `[key, value]` pairs.
6153
+ */
6154
+ entries() {
6155
+ this.prune();
6156
+ return [...this._store.entries()].map(([k, e]) => [k, e.value]);
6157
+ }
6158
+ /**
6159
+ * Get a value or compute & cache it if missing.
6160
+ * Very useful for "cache-aside" data loading.
6161
+ *
6162
+ * @param key - Cache key.
6163
+ * @param loader - Async function that produces the value when the cache is cold.
6164
+ * @param ttlMs - Optional TTL to apply to the newly cached value.
6165
+ *
6166
+ * @example
6167
+ * const profile = await cache.getOrSet(userId, () => client.fetchUserProfile(userId), 60_000)
6168
+ */
6169
+ async getOrSet(key, loader, ttlMs) {
6170
+ const existing = this.get(key);
6171
+ if (existing !== void 0) return existing;
6172
+ const value = await loader();
6173
+ this.set(key, value, ttlMs);
6174
+ return value;
6175
+ }
6176
+ /**
6177
+ * Return the number of milliseconds until a key expires.
6178
+ * Returns `null` if the key has no TTL, `0` if it is already expired.
6179
+ */
6180
+ ttl(key) {
6181
+ const entry = this._store.get(key);
6182
+ if (!entry) return null;
6183
+ if (entry.expiresAt === null) return null;
6184
+ return Math.max(0, entry.expiresAt - Date.now());
6185
+ }
6186
+ [Symbol.iterator]() {
6187
+ return this.entries()[Symbol.iterator]();
6188
+ }
6189
+ };
6190
+
6191
+ // src/utils/EventScheduler.ts
6192
+ var _idCounter = 0;
6193
+ function nextId() {
6194
+ return `task_${++_idCounter}`;
6195
+ }
6196
+ var EventScheduler = class {
6197
+ constructor() {
6198
+ this._tasks = /* @__PURE__ */ new Map();
6199
+ }
6200
+ /**
6201
+ * Schedule a one-shot task to run after `delayMs` milliseconds.
6202
+ * Returns a task ID that can be passed to `cancel()`.
6203
+ *
6204
+ * @example
6205
+ * const id = scheduler.schedule(5_000, () => console.log('5 seconds later'))
6206
+ * // Change your mind:
6207
+ * scheduler.cancel(id)
6208
+ */
6209
+ schedule(delayMs, fn) {
6210
+ const id = nextId();
6211
+ const handle = setTimeout(() => {
6212
+ this._tasks.delete(id);
6213
+ this._run(fn, id);
6214
+ }, delayMs);
6215
+ this._tasks.set(id, { id, handle, type: "once" });
6216
+ return id;
6217
+ }
6218
+ /**
6219
+ * Schedule a one-shot task to run at a specific `Date`.
6220
+ * If the date is in the past, the callback fires on the next event-loop tick.
6221
+ *
6222
+ * @example
6223
+ * const nextMidnight = new Date()
6224
+ * nextMidnight.setHours(24, 0, 0, 0)
6225
+ * scheduler.scheduleAt(nextMidnight, () => console.log('Midnight!'))
6226
+ */
6227
+ scheduleAt(date, fn) {
6228
+ const delay = Math.max(0, date.getTime() - Date.now());
6229
+ return this.schedule(delay, fn);
6230
+ }
6231
+ /**
6232
+ * Schedule a recurring task that fires every `intervalMs` milliseconds.
6233
+ * Returns a task ID that can be passed to `cancel()`.
6234
+ *
6235
+ * @example
6236
+ * // Check incoming messages every minute
6237
+ * const id = scheduler.repeat(60_000, () => checkForAnnouncements())
6238
+ */
6239
+ repeat(intervalMs, fn) {
6240
+ const id = nextId();
6241
+ const handle = setInterval(() => this._run(fn, id), intervalMs);
6242
+ this._tasks.set(id, { id, handle, type: "repeat" });
6243
+ return id;
6244
+ }
6245
+ /**
6246
+ * Cancel a previously scheduled task by its ID.
6247
+ * Returns `true` if the task existed, `false` if it had already fired or
6248
+ * was not found.
6249
+ *
6250
+ * @example
6251
+ * const id = scheduler.schedule(30_000, doSomething)
6252
+ * // …maybe cancel it:
6253
+ * scheduler.cancel(id)
6254
+ */
6255
+ cancel(id) {
6256
+ const task = this._tasks.get(id);
6257
+ if (!task) return false;
6258
+ if (task.type === "once") clearTimeout(task.handle);
6259
+ else clearInterval(task.handle);
6260
+ this._tasks.delete(id);
6261
+ return true;
6262
+ }
6263
+ /**
6264
+ * Cancel all pending tasks.
6265
+ * Useful during graceful shutdown.
6266
+ *
6267
+ * @example
6268
+ * client.once('disconnect', () => scheduler.cancelAll())
6269
+ */
6270
+ cancelAll() {
6271
+ for (const id of this._tasks.keys()) this.cancel(id);
6272
+ }
6273
+ /**
6274
+ * Number of currently active (pending or recurring) tasks.
6275
+ */
6276
+ get pendingCount() {
6277
+ return this._tasks.size;
6278
+ }
6279
+ /**
6280
+ * IDs of all currently registered tasks.
6281
+ */
6282
+ taskIds() {
6283
+ return [...this._tasks.keys()];
6284
+ }
6285
+ _run(fn, id) {
6286
+ try {
6287
+ const result = fn();
6288
+ if (result && typeof result.catch === "function") {
6289
+ ;
6290
+ result.catch((e) => {
6291
+ console.error(`[EventScheduler] Task ${id} threw:`, e);
6292
+ });
6293
+ }
6294
+ } catch (e) {
6295
+ console.error(`[EventScheduler] Task ${id} threw:`, e);
6296
+ }
6297
+ }
6298
+ };
3812
6299
  // Annotate the CommonJS export names for ESM import in node:
3813
6300
  0 && (module.exports = {
3814
6301
  ActionRowBuilder,
6302
+ ArgumentParser,
3815
6303
  AuditLogAPI,
6304
+ AutoModAPI,
6305
+ AutoModRuleBuilder,
3816
6306
  ButtonBuilder,
6307
+ CacheMap,
6308
+ CategoriesAPI,
6309
+ ChannelBuilder,
3817
6310
  ChannelsAPI,
3818
6311
  Collection,
3819
6312
  CommandsAPI,
6313
+ ConfirmationDialog,
6314
+ ContextMenuCommandBuilder,
3820
6315
  Cooldown,
3821
6316
  CooldownManager,
3822
6317
  EmbedBuilder,
6318
+ EmbedPaginator,
6319
+ EventBuilder,
6320
+ EventScheduler,
6321
+ EventsAPI,
6322
+ ForumAPI,
6323
+ ForumPostBuilder,
3823
6324
  HttpClient,
6325
+ InteractionCollector,
3824
6326
  InteractionOptions,
3825
6327
  InteractionsAPI,
6328
+ InviteBuilder,
3826
6329
  InvitesAPI,
3827
6330
  Logger,
3828
6331
  MembersAPI,
6332
+ MentionParser,
3829
6333
  MessageBuilder,
6334
+ MessageCollector,
3830
6335
  MessagesAPI,
3831
6336
  ModalBuilder,
6337
+ NovaBan,
6338
+ NovaCategory,
3832
6339
  NovaChannel,
3833
6340
  NovaClient,
6341
+ NovaForumPost,
3834
6342
  NovaInteraction,
3835
6343
  NovaInvite,
3836
6344
  NovaMember,
3837
6345
  NovaMessage,
3838
6346
  NovaRole,
6347
+ NovaServerEvent,
3839
6348
  NovaServerWrapper,
6349
+ NovaWarning,
3840
6350
  NovaWebhook,
3841
6351
  Paginator,
6352
+ ParsedArguments,
3842
6353
  Permissions,
3843
6354
  PermissionsAPI,
3844
6355
  PermissionsBitfield,
3845
6356
  PollBuilder,
3846
6357
  ReactionsAPI,
6358
+ RoleBuilder,
3847
6359
  RolesAPI,
3848
6360
  SelectMenuBuilder,
3849
6361
  ServersAPI,
3850
6362
  SlashCommandBuilder,
3851
6363
  SlashCommandOptionBuilder,
3852
6364
  TextInputBuilder,
6365
+ UsersAPI,
3853
6366
  WebhooksAPI,
3854
6367
  countdown,
3855
6368
  formatDuration,