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.d.mts +2378 -138
- package/dist/index.d.ts +2378 -138
- package/dist/index.js +2551 -38
- package/dist/index.mjs +2525 -38
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -355,6 +355,74 @@ var MembersAPI = class {
|
|
|
355
355
|
removeRole(serverId, userId, roleId) {
|
|
356
356
|
return this.http.delete(`/servers/${serverId}/members/${userId}/roles/${roleId}`);
|
|
357
357
|
}
|
|
358
|
+
/**
|
|
359
|
+
* Issue a warning to a member.
|
|
360
|
+
* Requires the `members.moderate` scope.
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* await client.members.warn('server-id', 'user-id', 'Spamming')
|
|
364
|
+
*/
|
|
365
|
+
warn(serverId, userId, reason) {
|
|
366
|
+
return this.http.post(`/servers/${serverId}/members/${userId}/warn`, { reason });
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Fetch warnings for a server (or optionally a specific user).
|
|
370
|
+
* Requires the `members.moderate` scope.
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
* const all = await client.members.fetchWarnings('server-id')
|
|
374
|
+
* const userWarnings = await client.members.fetchWarnings('server-id', { userId: 'user-id' })
|
|
375
|
+
*/
|
|
376
|
+
fetchWarnings(serverId, options = {}) {
|
|
377
|
+
const params = new URLSearchParams();
|
|
378
|
+
if (options.userId) params.set("userId", options.userId);
|
|
379
|
+
const qs = params.toString() ? `?${params.toString()}` : "";
|
|
380
|
+
return this.http.get(`/servers/${serverId}/warnings${qs}`);
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Remove a warning by its ID.
|
|
384
|
+
* Requires the `members.moderate` scope.
|
|
385
|
+
*
|
|
386
|
+
* @example
|
|
387
|
+
* await client.members.removeWarning('warning-id')
|
|
388
|
+
*/
|
|
389
|
+
removeWarning(warningId) {
|
|
390
|
+
return this.http.delete(`/warnings/${warningId}`);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Get a member's XP and level.
|
|
394
|
+
* Requires the `members.read` scope.
|
|
395
|
+
*
|
|
396
|
+
* @example
|
|
397
|
+
* const xpData = await client.members.getXP('server-id', 'user-id')
|
|
398
|
+
* console.log(`Level ${xpData.level} — ${xpData.xp} XP`)
|
|
399
|
+
*/
|
|
400
|
+
getXP(serverId, userId) {
|
|
401
|
+
return this.http.get(`/servers/${serverId}/members/${userId}/xp`);
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Set or add XP to a member.
|
|
405
|
+
* Requires the `members.manage` scope.
|
|
406
|
+
*
|
|
407
|
+
* @example
|
|
408
|
+
* // Add 50 XP
|
|
409
|
+
* await client.members.setXP('server-id', 'user-id', { add: 50 })
|
|
410
|
+
* // Set XP to a specific value
|
|
411
|
+
* await client.members.setXP('server-id', 'user-id', { xp: 1000 })
|
|
412
|
+
*/
|
|
413
|
+
setXP(serverId, userId, options) {
|
|
414
|
+
return this.http.patch(`/servers/${serverId}/members/${userId}/xp`, options);
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Get the XP leaderboard for a server.
|
|
418
|
+
* Requires the `members.read` scope.
|
|
419
|
+
*
|
|
420
|
+
* @example
|
|
421
|
+
* const top10 = await client.members.leaderboard('server-id', 10)
|
|
422
|
+
*/
|
|
423
|
+
leaderboard(serverId, limit = 10) {
|
|
424
|
+
return this.http.get(`/servers/${serverId}/leaderboard?limit=${limit}`);
|
|
425
|
+
}
|
|
358
426
|
};
|
|
359
427
|
|
|
360
428
|
// src/api/servers.ts
|
|
@@ -404,6 +472,34 @@ var ServersAPI = class {
|
|
|
404
472
|
listRoles(serverId) {
|
|
405
473
|
return this.http.get(`/servers/${serverId}/roles`);
|
|
406
474
|
}
|
|
475
|
+
/**
|
|
476
|
+
* Fetch all channel categories in a server.
|
|
477
|
+
*
|
|
478
|
+
* @example
|
|
479
|
+
* const categories = await client.servers.listCategories('server-id')
|
|
480
|
+
*/
|
|
481
|
+
listCategories(serverId) {
|
|
482
|
+
return this.http.get(`/servers/${serverId}/categories`);
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Get server statistics (member count, message count, etc.).
|
|
486
|
+
*
|
|
487
|
+
* @example
|
|
488
|
+
* const stats = await client.servers.getStats('server-id')
|
|
489
|
+
* console.log(`${stats.onlineCount} online / ${stats.memberCount} total`)
|
|
490
|
+
*/
|
|
491
|
+
getStats(serverId) {
|
|
492
|
+
return this.http.get(`/servers/${serverId}/stats`);
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Fetch all soundboard clips uploaded to a server.
|
|
496
|
+
*
|
|
497
|
+
* @example
|
|
498
|
+
* const clips = await client.servers.listSoundboard('server-id')
|
|
499
|
+
*/
|
|
500
|
+
listSoundboard(serverId) {
|
|
501
|
+
return this.http.get(`/servers/${serverId}/soundboard`);
|
|
502
|
+
}
|
|
407
503
|
};
|
|
408
504
|
|
|
409
505
|
// src/api/interactions.ts
|
|
@@ -681,6 +777,18 @@ var ChannelsAPI = class {
|
|
|
681
777
|
startTyping(channelId) {
|
|
682
778
|
return this.http.post(`/channels/${channelId}/typing`);
|
|
683
779
|
}
|
|
780
|
+
/**
|
|
781
|
+
* Bulk delete up to 100 messages from a channel at once.
|
|
782
|
+
* Requires the `messages.manage` scope.
|
|
783
|
+
* Soft-deletes all specified messages in a single operation.
|
|
784
|
+
*
|
|
785
|
+
* @example
|
|
786
|
+
* const result = await client.channels.bulkDelete('channel-id', ['msg1', 'msg2', 'msg3'])
|
|
787
|
+
* console.log(`Deleted ${result.deleted} messages`)
|
|
788
|
+
*/
|
|
789
|
+
bulkDelete(channelId, messageIds) {
|
|
790
|
+
return this.http.post(`/channels/${channelId}/bulk-delete`, { messageIds });
|
|
791
|
+
}
|
|
684
792
|
};
|
|
685
793
|
|
|
686
794
|
// src/api/reactions.ts
|
|
@@ -989,6 +1097,224 @@ var AuditLogAPI = class {
|
|
|
989
1097
|
}
|
|
990
1098
|
};
|
|
991
1099
|
|
|
1100
|
+
// src/api/forum.ts
|
|
1101
|
+
var ForumAPI = class {
|
|
1102
|
+
constructor(http) {
|
|
1103
|
+
this.http = http;
|
|
1104
|
+
}
|
|
1105
|
+
/**
|
|
1106
|
+
* List forum posts in a FORUM channel.
|
|
1107
|
+
*
|
|
1108
|
+
* @example
|
|
1109
|
+
* const posts = await client.forum.list('channel-id')
|
|
1110
|
+
*/
|
|
1111
|
+
async list(channelId, options = {}) {
|
|
1112
|
+
const params = new URLSearchParams();
|
|
1113
|
+
if (options.limit !== void 0) params.set("limit", String(options.limit));
|
|
1114
|
+
if (options.before) params.set("before", options.before);
|
|
1115
|
+
const qs = params.toString() ? `?${params.toString()}` : "";
|
|
1116
|
+
return this.http.get(`/channels/${channelId}/forum-posts${qs}`);
|
|
1117
|
+
}
|
|
1118
|
+
/**
|
|
1119
|
+
* Create a forum post in a FORUM channel.
|
|
1120
|
+
*
|
|
1121
|
+
* @example
|
|
1122
|
+
* const post = await client.forum.create('channel-id', {
|
|
1123
|
+
* title: 'Ideas thread',
|
|
1124
|
+
* body: 'Post your ideas here!',
|
|
1125
|
+
* tags: ['idea', 'discussion'],
|
|
1126
|
+
* })
|
|
1127
|
+
*/
|
|
1128
|
+
async create(channelId, options) {
|
|
1129
|
+
return this.http.post(`/channels/${channelId}/forum-posts`, options);
|
|
1130
|
+
}
|
|
1131
|
+
/**
|
|
1132
|
+
* Edit a forum post.
|
|
1133
|
+
* The bot must be the author of the post.
|
|
1134
|
+
*
|
|
1135
|
+
* @example
|
|
1136
|
+
* await client.forum.edit('post-id', { closed: true })
|
|
1137
|
+
*/
|
|
1138
|
+
async edit(postId, options) {
|
|
1139
|
+
return this.http.patch(`/forum-posts/${postId}`, options);
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Delete a forum post.
|
|
1143
|
+
* The bot must be the author of the post.
|
|
1144
|
+
*
|
|
1145
|
+
* @example
|
|
1146
|
+
* await client.forum.delete('post-id')
|
|
1147
|
+
*/
|
|
1148
|
+
async delete(postId) {
|
|
1149
|
+
await this.http.delete(`/forum-posts/${postId}`);
|
|
1150
|
+
}
|
|
1151
|
+
};
|
|
1152
|
+
|
|
1153
|
+
// src/api/events.ts
|
|
1154
|
+
var EventsAPI = class {
|
|
1155
|
+
constructor(http) {
|
|
1156
|
+
this.http = http;
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* List server events.
|
|
1160
|
+
*
|
|
1161
|
+
* @example
|
|
1162
|
+
* const events = await client.events.list('server-id', { upcoming: true })
|
|
1163
|
+
*/
|
|
1164
|
+
async list(serverId, options = {}) {
|
|
1165
|
+
const params = new URLSearchParams();
|
|
1166
|
+
if (options.limit !== void 0) params.set("limit", String(options.limit));
|
|
1167
|
+
if (options.upcoming) params.set("upcoming", "true");
|
|
1168
|
+
const qs = params.toString() ? `?${params.toString()}` : "";
|
|
1169
|
+
return this.http.get(`/servers/${serverId}/events${qs}`);
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Fetch a single server event by ID.
|
|
1173
|
+
* Returns full attendee list.
|
|
1174
|
+
*
|
|
1175
|
+
* @example
|
|
1176
|
+
* const event = await client.events.fetch('event-id')
|
|
1177
|
+
*/
|
|
1178
|
+
async fetch(eventId) {
|
|
1179
|
+
return this.http.get(`/events/${eventId}`);
|
|
1180
|
+
}
|
|
1181
|
+
/**
|
|
1182
|
+
* Create a server event.
|
|
1183
|
+
*
|
|
1184
|
+
* @example
|
|
1185
|
+
* const event = await client.events.create('server-id', {
|
|
1186
|
+
* title: 'Game Night',
|
|
1187
|
+
* description: 'Friday fun!',
|
|
1188
|
+
* startAt: new Date(Date.now() + 86400_000).toISOString(),
|
|
1189
|
+
* })
|
|
1190
|
+
*/
|
|
1191
|
+
async create(serverId, options) {
|
|
1192
|
+
return this.http.post(`/servers/${serverId}/events`, options);
|
|
1193
|
+
}
|
|
1194
|
+
/**
|
|
1195
|
+
* Edit a server event.
|
|
1196
|
+
*
|
|
1197
|
+
* @example
|
|
1198
|
+
* await client.events.edit('event-id', { title: 'Game Night v2' })
|
|
1199
|
+
*/
|
|
1200
|
+
async edit(eventId, options) {
|
|
1201
|
+
return this.http.patch(`/events/${eventId}`, options);
|
|
1202
|
+
}
|
|
1203
|
+
/**
|
|
1204
|
+
* Delete a server event.
|
|
1205
|
+
*
|
|
1206
|
+
* @example
|
|
1207
|
+
* await client.events.delete('event-id')
|
|
1208
|
+
*/
|
|
1209
|
+
async delete(eventId) {
|
|
1210
|
+
await this.http.delete(`/events/${eventId}`);
|
|
1211
|
+
}
|
|
1212
|
+
};
|
|
1213
|
+
|
|
1214
|
+
// src/api/categories.ts
|
|
1215
|
+
var CategoriesAPI = class {
|
|
1216
|
+
constructor(http) {
|
|
1217
|
+
this.http = http;
|
|
1218
|
+
}
|
|
1219
|
+
/**
|
|
1220
|
+
* List all channel categories in a server, ordered by position.
|
|
1221
|
+
*
|
|
1222
|
+
* @example
|
|
1223
|
+
* const cats = await client.categories.list('server-id')
|
|
1224
|
+
*/
|
|
1225
|
+
async list(serverId) {
|
|
1226
|
+
return this.http.get(`/servers/${serverId}/categories`);
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Create a new channel category.
|
|
1230
|
+
*
|
|
1231
|
+
* @example
|
|
1232
|
+
* const cat = await client.categories.create('server-id', { name: 'General', position: 0 })
|
|
1233
|
+
*/
|
|
1234
|
+
async create(serverId, options) {
|
|
1235
|
+
return this.http.post(`/servers/${serverId}/categories`, options);
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Edit an existing channel category.
|
|
1239
|
+
*
|
|
1240
|
+
* @example
|
|
1241
|
+
* await client.categories.edit('cat-id', { name: 'Renamed' })
|
|
1242
|
+
*/
|
|
1243
|
+
async edit(categoryId, options) {
|
|
1244
|
+
return this.http.patch(`/categories/${categoryId}`, options);
|
|
1245
|
+
}
|
|
1246
|
+
/**
|
|
1247
|
+
* Delete a channel category.
|
|
1248
|
+
*
|
|
1249
|
+
* @example
|
|
1250
|
+
* await client.categories.delete('cat-id')
|
|
1251
|
+
*/
|
|
1252
|
+
async delete(categoryId) {
|
|
1253
|
+
await this.http.delete(`/categories/${categoryId}`);
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
|
|
1257
|
+
// src/api/automod.ts
|
|
1258
|
+
var AutoModAPI = class {
|
|
1259
|
+
constructor(http) {
|
|
1260
|
+
this.http = http;
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* List all automod rules for a server.
|
|
1264
|
+
*
|
|
1265
|
+
* @example
|
|
1266
|
+
* const rules = await client.automod.list('server-id')
|
|
1267
|
+
*/
|
|
1268
|
+
async list(serverId) {
|
|
1269
|
+
return this.http.get(`/servers/${serverId}/automod`);
|
|
1270
|
+
}
|
|
1271
|
+
/**
|
|
1272
|
+
* Create a new automod rule.
|
|
1273
|
+
*
|
|
1274
|
+
* @example
|
|
1275
|
+
* await client.automod.create('server-id', { type: 'BLOCKED_WORD', value: 'badword' })
|
|
1276
|
+
* await client.automod.create('server-id', { type: 'BLOCKED_LINK', value: 'example.com' })
|
|
1277
|
+
*/
|
|
1278
|
+
async create(serverId, options) {
|
|
1279
|
+
return this.http.post(`/servers/${serverId}/automod`, options);
|
|
1280
|
+
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Enable / disable a rule or update the blocked value.
|
|
1283
|
+
*
|
|
1284
|
+
* @example
|
|
1285
|
+
* await client.automod.edit('rule-id', { enabled: false })
|
|
1286
|
+
*/
|
|
1287
|
+
async edit(ruleId, options) {
|
|
1288
|
+
return this.http.patch(`/automod/${ruleId}`, options);
|
|
1289
|
+
}
|
|
1290
|
+
/**
|
|
1291
|
+
* Delete an automod rule.
|
|
1292
|
+
*
|
|
1293
|
+
* @example
|
|
1294
|
+
* await client.automod.delete('rule-id')
|
|
1295
|
+
*/
|
|
1296
|
+
async delete(ruleId) {
|
|
1297
|
+
await this.http.delete(`/automod/${ruleId}`);
|
|
1298
|
+
}
|
|
1299
|
+
};
|
|
1300
|
+
|
|
1301
|
+
// src/api/users.ts
|
|
1302
|
+
var UsersAPI = class {
|
|
1303
|
+
constructor(http) {
|
|
1304
|
+
this.http = http;
|
|
1305
|
+
}
|
|
1306
|
+
/**
|
|
1307
|
+
* Fetch a user's public profile by ID.
|
|
1308
|
+
*
|
|
1309
|
+
* @example
|
|
1310
|
+
* const user = await client.users.fetch('user-id')
|
|
1311
|
+
* console.log(user.displayName)
|
|
1312
|
+
*/
|
|
1313
|
+
async fetch(userId) {
|
|
1314
|
+
return this.http.get(`/users/${userId}`);
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
1317
|
+
|
|
992
1318
|
// src/structures/NovaInteraction.ts
|
|
993
1319
|
var InteractionOptions = class {
|
|
994
1320
|
constructor(data) {
|
|
@@ -1313,6 +1639,63 @@ var NovaMessage = class _NovaMessage {
|
|
|
1313
1639
|
const raw = await this._messages.fetchOne(this.id);
|
|
1314
1640
|
return new _NovaMessage(raw, this._messages, this._reactions);
|
|
1315
1641
|
}
|
|
1642
|
+
/**
|
|
1643
|
+
* Forward this message's content (and embed if present) to another channel.
|
|
1644
|
+
* Creates a new message in the target channel.
|
|
1645
|
+
*
|
|
1646
|
+
* @example
|
|
1647
|
+
* const forwarded = await msg.forward('target-channel-id')
|
|
1648
|
+
* console.log('Forwarded to:', forwarded.channelId)
|
|
1649
|
+
*/
|
|
1650
|
+
async forward(channelId) {
|
|
1651
|
+
const opts = {
|
|
1652
|
+
content: this.content || void 0,
|
|
1653
|
+
...this.embed ? { embed: this.embed } : {}
|
|
1654
|
+
};
|
|
1655
|
+
const raw = await this._messages.send(channelId, opts);
|
|
1656
|
+
return new _NovaMessage(raw, this._messages, this._reactions);
|
|
1657
|
+
}
|
|
1658
|
+
/**
|
|
1659
|
+
* Fetch detailed reaction data for a specific emoji on this message.
|
|
1660
|
+
* Returns the users who reacted with that emoji.
|
|
1661
|
+
*
|
|
1662
|
+
* @example
|
|
1663
|
+
* const reactors = await msg.fetchReactionDetails('👍')
|
|
1664
|
+
* for (const r of reactors) console.log(r.username, 'reacted 👍')
|
|
1665
|
+
*/
|
|
1666
|
+
fetchReactionDetails(emoji) {
|
|
1667
|
+
return this._reactions.fetchEmoji(this.id, emoji);
|
|
1668
|
+
}
|
|
1669
|
+
/**
|
|
1670
|
+
* Remove **all** reactions from this message.
|
|
1671
|
+
* Requires the `messages.manage` scope.
|
|
1672
|
+
*
|
|
1673
|
+
* @example
|
|
1674
|
+
* await msg.clearAllReactions()
|
|
1675
|
+
*/
|
|
1676
|
+
clearAllReactions() {
|
|
1677
|
+
return this._reactions.removeAll(this.id);
|
|
1678
|
+
}
|
|
1679
|
+
/**
|
|
1680
|
+
* Remove all reactions for a specific emoji from this message.
|
|
1681
|
+
* Requires the `messages.manage` scope.
|
|
1682
|
+
*
|
|
1683
|
+
* @example
|
|
1684
|
+
* await msg.clearReactionsFor('👍')
|
|
1685
|
+
*/
|
|
1686
|
+
clearReactionsFor(emoji) {
|
|
1687
|
+
return this._reactions.removeEmoji(this.id, emoji);
|
|
1688
|
+
}
|
|
1689
|
+
/**
|
|
1690
|
+
* Fetch a breakdown of all reactions on this message, grouped by emoji.
|
|
1691
|
+
*
|
|
1692
|
+
* @example
|
|
1693
|
+
* const details = await msg.fetchAllReactions()
|
|
1694
|
+
* for (const d of details) console.log(`${d.emoji} — ${d.count}`)
|
|
1695
|
+
*/
|
|
1696
|
+
fetchAllReactions() {
|
|
1697
|
+
return this._reactions.fetch(this.id);
|
|
1698
|
+
}
|
|
1316
1699
|
/**
|
|
1317
1700
|
* Get a URL to this message (deep link).
|
|
1318
1701
|
*/
|
|
@@ -1344,7 +1727,7 @@ var NovaMessage = class _NovaMessage {
|
|
|
1344
1727
|
|
|
1345
1728
|
// src/structures/NovaChannel.ts
|
|
1346
1729
|
var NovaChannel = class _NovaChannel {
|
|
1347
|
-
constructor(raw, channels, messages) {
|
|
1730
|
+
constructor(raw, channels, messages, webhooks, forum) {
|
|
1348
1731
|
this.id = raw.id;
|
|
1349
1732
|
this.name = raw.name;
|
|
1350
1733
|
this.type = raw.type;
|
|
@@ -1355,6 +1738,8 @@ var NovaChannel = class _NovaChannel {
|
|
|
1355
1738
|
this.createdAt = new Date(raw.createdAt);
|
|
1356
1739
|
this._channels = channels;
|
|
1357
1740
|
this._messages = messages;
|
|
1741
|
+
this._webhooks = webhooks;
|
|
1742
|
+
this._forum = forum;
|
|
1358
1743
|
}
|
|
1359
1744
|
// ─── Type guards ────────────────────────────────────────────────────────────
|
|
1360
1745
|
/** `true` for text channels. */
|
|
@@ -1431,7 +1816,7 @@ var NovaChannel = class _NovaChannel {
|
|
|
1431
1816
|
*/
|
|
1432
1817
|
async edit(options) {
|
|
1433
1818
|
const raw = await this._channels.edit(this.id, options);
|
|
1434
|
-
return new _NovaChannel(raw, this._channels, this._messages);
|
|
1819
|
+
return new _NovaChannel(raw, this._channels, this._messages, this._webhooks, this._forum);
|
|
1435
1820
|
}
|
|
1436
1821
|
/**
|
|
1437
1822
|
* Delete this channel.
|
|
@@ -1443,6 +1828,67 @@ var NovaChannel = class _NovaChannel {
|
|
|
1443
1828
|
delete() {
|
|
1444
1829
|
return this._channels.delete(this.id);
|
|
1445
1830
|
}
|
|
1831
|
+
/**
|
|
1832
|
+
* Bulk-delete multiple messages in this channel (max 100 at once).
|
|
1833
|
+
* Requires the `messages.manage` scope.
|
|
1834
|
+
*
|
|
1835
|
+
* @example
|
|
1836
|
+
* const ids = messages.map(m => m.id)
|
|
1837
|
+
* const result = await channel.bulkDelete(ids)
|
|
1838
|
+
* console.log(`Deleted ${result.deleted} messages`)
|
|
1839
|
+
*/
|
|
1840
|
+
bulkDelete(messageIds) {
|
|
1841
|
+
return this._channels.bulkDelete(this.id, messageIds);
|
|
1842
|
+
}
|
|
1843
|
+
// ─── Webhooks ────────────────────────────────────────────────────────────────
|
|
1844
|
+
/**
|
|
1845
|
+
* Create a webhook in this channel.
|
|
1846
|
+
* Requires the `webhooks.manage` scope.
|
|
1847
|
+
*
|
|
1848
|
+
* @example
|
|
1849
|
+
* const wh = await channel.createWebhook({ name: 'Notifications' })
|
|
1850
|
+
* console.log('Token:', wh.token)
|
|
1851
|
+
*/
|
|
1852
|
+
createWebhook(options) {
|
|
1853
|
+
if (!this._webhooks) throw new Error("[NovaChannel] WebhooksAPI not available \u2014 ensure you obtained this channel from client.fetchChannel()");
|
|
1854
|
+
return this._webhooks.create(this.id, options);
|
|
1855
|
+
}
|
|
1856
|
+
/**
|
|
1857
|
+
* Fetch all webhooks in this channel.
|
|
1858
|
+
* Requires the `webhooks.manage` scope.
|
|
1859
|
+
*
|
|
1860
|
+
* @example
|
|
1861
|
+
* const webhooks = await channel.fetchWebhooks()
|
|
1862
|
+
*/
|
|
1863
|
+
fetchWebhooks() {
|
|
1864
|
+
if (!this._webhooks) throw new Error("[NovaChannel] WebhooksAPI not available \u2014 ensure you obtained this channel from client.fetchChannel()");
|
|
1865
|
+
return this._webhooks.list(this.id);
|
|
1866
|
+
}
|
|
1867
|
+
// ─── Forum ───────────────────────────────────────────────────────────────────
|
|
1868
|
+
/**
|
|
1869
|
+
* Create a new post in this FORUM channel.
|
|
1870
|
+
* Requires the `channels.write` scope.
|
|
1871
|
+
*
|
|
1872
|
+
* @example
|
|
1873
|
+
* const post = await channel.createForumPost({
|
|
1874
|
+
* title: 'Announcement',
|
|
1875
|
+
* content: 'Welcome everyone!',
|
|
1876
|
+
* })
|
|
1877
|
+
*/
|
|
1878
|
+
createForumPost(options) {
|
|
1879
|
+
if (!this._forum) throw new Error("[NovaChannel] ForumAPI not available \u2014 ensure you obtained this channel from client.fetchChannel()");
|
|
1880
|
+
return this._forum.create(this.id, options);
|
|
1881
|
+
}
|
|
1882
|
+
/**
|
|
1883
|
+
* Fetch posts from this FORUM channel.
|
|
1884
|
+
*
|
|
1885
|
+
* @example
|
|
1886
|
+
* const posts = await channel.fetchForumPosts({ limit: 20 })
|
|
1887
|
+
*/
|
|
1888
|
+
fetchForumPosts(options) {
|
|
1889
|
+
if (!this._forum) throw new Error("[NovaChannel] ForumAPI not available \u2014 ensure you obtained this channel from client.fetchChannel()");
|
|
1890
|
+
return this._forum.list(this.id, options);
|
|
1891
|
+
}
|
|
1446
1892
|
// ─── Serialisation ──────────────────────────────────────────────────────────
|
|
1447
1893
|
/**
|
|
1448
1894
|
* Returns the channel as a mention-style string: `#name`.
|
|
@@ -1560,29 +2006,106 @@ var NovaMember = class {
|
|
|
1560
2006
|
removeRole(roleId) {
|
|
1561
2007
|
return this._members.removeRole(this.serverId, this.userId, roleId);
|
|
1562
2008
|
}
|
|
1563
|
-
// ───
|
|
2009
|
+
// ─── Moderation ─────────────────────────────────────────────────────────────
|
|
1564
2010
|
/**
|
|
1565
|
-
*
|
|
2011
|
+
* Issue a warning to this member.
|
|
2012
|
+
* Requires the `members.moderate` scope.
|
|
2013
|
+
*
|
|
2014
|
+
* @example
|
|
2015
|
+
* await member.warn('Excessive spamming')
|
|
1566
2016
|
*/
|
|
1567
|
-
|
|
1568
|
-
return
|
|
2017
|
+
warn(reason) {
|
|
2018
|
+
return this._members.warn(this.serverId, this.userId, reason);
|
|
1569
2019
|
}
|
|
1570
|
-
/**
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
2020
|
+
/**
|
|
2021
|
+
* Fetch all warnings for this member in this server.
|
|
2022
|
+
* Requires the `members.moderate` scope.
|
|
2023
|
+
*
|
|
2024
|
+
* @example
|
|
2025
|
+
* const warnings = await member.fetchWarnings()
|
|
2026
|
+
* console.log(`${warnings.length} warnings on record`)
|
|
2027
|
+
*/
|
|
2028
|
+
fetchWarnings() {
|
|
2029
|
+
return this._members.fetchWarnings(this.serverId, { userId: this.userId });
|
|
2030
|
+
}
|
|
2031
|
+
// ─── XP & Levels ────────────────────────────────────────────────────────────
|
|
2032
|
+
/**
|
|
2033
|
+
* Get this member's current XP and level.
|
|
2034
|
+
* Requires the `members.read` scope.
|
|
2035
|
+
*
|
|
2036
|
+
* @example
|
|
2037
|
+
* const xp = await member.getXP()
|
|
2038
|
+
* console.log(`Level ${xp.level} — ${xp.xp} XP`)
|
|
2039
|
+
*/
|
|
2040
|
+
getXP() {
|
|
2041
|
+
return this._members.getXP(this.serverId, this.userId);
|
|
2042
|
+
}
|
|
2043
|
+
/**
|
|
2044
|
+
* Set this member's XP to a specific value.
|
|
2045
|
+
* Requires the `members.manage` scope.
|
|
2046
|
+
*
|
|
2047
|
+
* @example
|
|
2048
|
+
* await member.setXP(500)
|
|
2049
|
+
*/
|
|
2050
|
+
setXP(xp) {
|
|
2051
|
+
return this._members.setXP(this.serverId, this.userId, { xp });
|
|
2052
|
+
}
|
|
2053
|
+
/**
|
|
2054
|
+
* Add (or subtract) XP to this member's current total.
|
|
2055
|
+
* Requires the `members.manage` scope.
|
|
2056
|
+
*
|
|
2057
|
+
* @example
|
|
2058
|
+
* await member.addXP(100) // reward 100 XP
|
|
2059
|
+
* await member.addXP(-50) // deduct 50 XP
|
|
2060
|
+
*/
|
|
2061
|
+
addXP(amount) {
|
|
2062
|
+
return this._members.setXP(this.serverId, this.userId, { add: amount });
|
|
2063
|
+
}
|
|
2064
|
+
/**
|
|
2065
|
+
* Delete all warnings on record for this member in this server.
|
|
2066
|
+
* Requires the `members.moderate` scope.
|
|
2067
|
+
*
|
|
2068
|
+
* @example
|
|
2069
|
+
* await member.clearWarnings()
|
|
2070
|
+
*/
|
|
2071
|
+
async clearWarnings() {
|
|
2072
|
+
const warnings = await this.fetchWarnings();
|
|
2073
|
+
await Promise.all(warnings.map((w) => this._members.removeWarning(w.id)));
|
|
2074
|
+
return warnings.length;
|
|
2075
|
+
}
|
|
2076
|
+
/**
|
|
2077
|
+
* Reset this member's XP back to zero.
|
|
2078
|
+
* Requires the `members.manage` scope.
|
|
2079
|
+
*
|
|
2080
|
+
* @example
|
|
2081
|
+
* await member.resetXP()
|
|
2082
|
+
*/
|
|
2083
|
+
resetXP() {
|
|
2084
|
+
return this._members.setXP(this.serverId, this.userId, { xp: 0 });
|
|
2085
|
+
}
|
|
2086
|
+
// ─── Serialisation ──────────────────────────────────────────────────────────
|
|
2087
|
+
/**
|
|
2088
|
+
* Returns a mention-style string: `@displayName`.
|
|
2089
|
+
*/
|
|
2090
|
+
toString() {
|
|
2091
|
+
return `@${this.displayName}`;
|
|
2092
|
+
}
|
|
2093
|
+
/** Returns the raw member data. */
|
|
2094
|
+
toJSON() {
|
|
2095
|
+
return {
|
|
2096
|
+
role: this.role,
|
|
2097
|
+
joinedAt: this.joinedAt.toISOString(),
|
|
2098
|
+
user: {
|
|
2099
|
+
id: this.userId,
|
|
2100
|
+
username: this.username,
|
|
2101
|
+
displayName: this.displayName,
|
|
2102
|
+
avatar: this.avatar,
|
|
2103
|
+
status: this.status,
|
|
2104
|
+
isBot: this.isBot
|
|
2105
|
+
}
|
|
2106
|
+
};
|
|
2107
|
+
}
|
|
2108
|
+
};
|
|
1586
2109
|
|
|
1587
2110
|
// src/structures/NovaRole.ts
|
|
1588
2111
|
var NovaRole = class {
|
|
@@ -1659,7 +2182,7 @@ var NovaRole = class {
|
|
|
1659
2182
|
|
|
1660
2183
|
// src/structures/NovaServer.ts
|
|
1661
2184
|
var NovaServerWrapper = class {
|
|
1662
|
-
constructor(raw, servers, channels, members, invites, roles, messages) {
|
|
2185
|
+
constructor(raw, servers, channels, members, invites, roles, messages, categories, events, automod, auditLog) {
|
|
1663
2186
|
this.id = raw.id;
|
|
1664
2187
|
this.name = raw.name;
|
|
1665
2188
|
this.icon = raw.icon;
|
|
@@ -1673,6 +2196,10 @@ var NovaServerWrapper = class {
|
|
|
1673
2196
|
this._invites = invites;
|
|
1674
2197
|
this._roles = roles;
|
|
1675
2198
|
this._messages = messages;
|
|
2199
|
+
this._categories = categories;
|
|
2200
|
+
this._events = events;
|
|
2201
|
+
this._automod = automod;
|
|
2202
|
+
this._auditLog = auditLog;
|
|
1676
2203
|
}
|
|
1677
2204
|
// ─── Server management ────────────────────────────────────────────────────
|
|
1678
2205
|
/**
|
|
@@ -1751,14 +2278,25 @@ var NovaServerWrapper = class {
|
|
|
1751
2278
|
}
|
|
1752
2279
|
// ─── Roles ────────────────────────────────────────────────────────────────
|
|
1753
2280
|
/**
|
|
1754
|
-
*
|
|
2281
|
+
* List all custom roles in this server, sorted by position.
|
|
1755
2282
|
*
|
|
1756
2283
|
* @example
|
|
1757
2284
|
* const roles = await server.fetchRoles()
|
|
2285
|
+
* const admins = roles.filter(r => r.name === 'Admin')
|
|
1758
2286
|
*/
|
|
1759
2287
|
fetchRoles() {
|
|
1760
2288
|
return this._roles.list(this.id);
|
|
1761
2289
|
}
|
|
2290
|
+
/**
|
|
2291
|
+
* Create a new custom role in this server.
|
|
2292
|
+
* Requires the `roles.manage` scope.
|
|
2293
|
+
*
|
|
2294
|
+
* @example
|
|
2295
|
+
* const role = await server.createRole({ name: 'Supporter', color: '#00d4ff', hoist: true })
|
|
2296
|
+
*/
|
|
2297
|
+
createRole(options) {
|
|
2298
|
+
return this._roles.create(this.id, options);
|
|
2299
|
+
}
|
|
1762
2300
|
// ─── Invites ──────────────────────────────────────────────────────────────
|
|
1763
2301
|
/**
|
|
1764
2302
|
* Fetch all active invites for this server.
|
|
@@ -1789,6 +2327,133 @@ var NovaServerWrapper = class {
|
|
|
1789
2327
|
send(channelId, content) {
|
|
1790
2328
|
return this._messages.send(channelId, { content });
|
|
1791
2329
|
}
|
|
2330
|
+
// ─── Categories ───────────────────────────────────────────────────────────
|
|
2331
|
+
/**
|
|
2332
|
+
* Fetch all channel categories in this server.
|
|
2333
|
+
*
|
|
2334
|
+
* @example
|
|
2335
|
+
* const cats = await server.fetchCategories()
|
|
2336
|
+
* const general = cats.find(c => c.name === 'General')
|
|
2337
|
+
*/
|
|
2338
|
+
fetchCategories() {
|
|
2339
|
+
if (!this._categories) throw new Error("[NovaServerWrapper] CategoriesAPI not available");
|
|
2340
|
+
return this._categories.list(this.id);
|
|
2341
|
+
}
|
|
2342
|
+
/**
|
|
2343
|
+
* Create a new channel category in this server.
|
|
2344
|
+
* Requires the `channels.manage` scope.
|
|
2345
|
+
*
|
|
2346
|
+
* @example
|
|
2347
|
+
* const cat = await server.createCategory({ name: 'Bot Channels' })
|
|
2348
|
+
*/
|
|
2349
|
+
createCategory(options) {
|
|
2350
|
+
if (!this._categories) throw new Error("[NovaServerWrapper] CategoriesAPI not available");
|
|
2351
|
+
return this._categories.create(this.id, options);
|
|
2352
|
+
}
|
|
2353
|
+
// ─── Events ───────────────────────────────────────────────────────────────
|
|
2354
|
+
/**
|
|
2355
|
+
* Fetch server events. Pass `{ upcoming: true }` to only return future events.
|
|
2356
|
+
*
|
|
2357
|
+
* @example
|
|
2358
|
+
* const events = await server.fetchEvents({ upcoming: true })
|
|
2359
|
+
*/
|
|
2360
|
+
fetchEvents(options = {}) {
|
|
2361
|
+
if (!this._events) throw new Error("[NovaServerWrapper] EventsAPI not available");
|
|
2362
|
+
return this._events.list(this.id, options);
|
|
2363
|
+
}
|
|
2364
|
+
/**
|
|
2365
|
+
* Create a new server event.
|
|
2366
|
+
* Requires the `server.manage` scope.
|
|
2367
|
+
*
|
|
2368
|
+
* @example
|
|
2369
|
+
* const event = await server.createEvent({
|
|
2370
|
+
* title: 'Game Night',
|
|
2371
|
+
* startAt: new Date('2025-09-01T20:00:00Z').toISOString(),
|
|
2372
|
+
* })
|
|
2373
|
+
*/
|
|
2374
|
+
createEvent(options) {
|
|
2375
|
+
if (!this._events) throw new Error("[NovaServerWrapper] EventsAPI not available");
|
|
2376
|
+
return this._events.create(this.id, options);
|
|
2377
|
+
}
|
|
2378
|
+
// ─── Stats & Audit Log ────────────────────────────────────────────────────
|
|
2379
|
+
/**
|
|
2380
|
+
* Fetch server statistics (member count, message count, etc.).
|
|
2381
|
+
*
|
|
2382
|
+
* @example
|
|
2383
|
+
* const stats = await server.fetchStats()
|
|
2384
|
+
* console.log(`Online: ${stats.onlineCount} / ${stats.memberCount}`)
|
|
2385
|
+
*/
|
|
2386
|
+
fetchStats() {
|
|
2387
|
+
return this._servers.getStats(this.id);
|
|
2388
|
+
}
|
|
2389
|
+
/**
|
|
2390
|
+
* Fetch the audit log for this server.
|
|
2391
|
+
* Requires the `audit-log.read` scope.
|
|
2392
|
+
*
|
|
2393
|
+
* @example
|
|
2394
|
+
* const log = await server.fetchAuditLog({ action: 'member.banned', limit: 20 })
|
|
2395
|
+
*/
|
|
2396
|
+
fetchAuditLog(options = {}) {
|
|
2397
|
+
if (!this._auditLog) throw new Error("[NovaServerWrapper] AuditLogAPI not available");
|
|
2398
|
+
return this._auditLog.fetch(this.id, options);
|
|
2399
|
+
}
|
|
2400
|
+
// ─── Warnings & Leaderboard ───────────────────────────────────────────────
|
|
2401
|
+
/**
|
|
2402
|
+
* Issue a warning to a member in this server.
|
|
2403
|
+
* Requires the `members.moderate` scope.
|
|
2404
|
+
*
|
|
2405
|
+
* @example
|
|
2406
|
+
* await server.warn('user-id', 'Insulting other members')
|
|
2407
|
+
*/
|
|
2408
|
+
warn(userId, reason) {
|
|
2409
|
+
return this._members.warn(this.id, userId, reason);
|
|
2410
|
+
}
|
|
2411
|
+
/**
|
|
2412
|
+
* Fetch warnings for this server, optionally filtered by user.
|
|
2413
|
+
* Requires the `members.moderate` scope.
|
|
2414
|
+
*
|
|
2415
|
+
* @example
|
|
2416
|
+
* const all = await server.fetchWarnings()
|
|
2417
|
+
* const userWarns = await server.fetchWarnings({ userId: 'user-id' })
|
|
2418
|
+
*/
|
|
2419
|
+
fetchWarnings(options = {}) {
|
|
2420
|
+
return this._members.fetchWarnings(this.id, options);
|
|
2421
|
+
}
|
|
2422
|
+
/**
|
|
2423
|
+
* Fetch the XP leaderboard for this server.
|
|
2424
|
+
* Requires the `members.read` scope.
|
|
2425
|
+
*
|
|
2426
|
+
* @example
|
|
2427
|
+
* const top = await server.fetchLeaderboard(10)
|
|
2428
|
+
* top.forEach(e => console.log(`#${e.rank} ${e.user.displayName} — ${e.xp} XP`))
|
|
2429
|
+
*/
|
|
2430
|
+
fetchLeaderboard(limit = 10) {
|
|
2431
|
+
return this._members.leaderboard(this.id, limit);
|
|
2432
|
+
}
|
|
2433
|
+
// ─── AutoMod ─────────────────────────────────────────────────────────────
|
|
2434
|
+
/**
|
|
2435
|
+
* Fetch all AutoMod rules for this server.
|
|
2436
|
+
* Requires the `server.manage` scope.
|
|
2437
|
+
*
|
|
2438
|
+
* @example
|
|
2439
|
+
* const rules = await server.fetchAutoModRules()
|
|
2440
|
+
* const active = rules.filter(r => r.enabled)
|
|
2441
|
+
*/
|
|
2442
|
+
fetchAutoModRules() {
|
|
2443
|
+
if (!this._automod) throw new Error("[NovaServerWrapper] AutoModAPI not available");
|
|
2444
|
+
return this._automod.list(this.id);
|
|
2445
|
+
}
|
|
2446
|
+
/**
|
|
2447
|
+
* Create a new AutoMod rule for this server.
|
|
2448
|
+
* Requires the `server.manage` scope.
|
|
2449
|
+
*
|
|
2450
|
+
* @example
|
|
2451
|
+
* await server.createAutoModRule({ type: 'BLOCKED_WORD', value: 'badword' })
|
|
2452
|
+
*/
|
|
2453
|
+
createAutoModRule(options) {
|
|
2454
|
+
if (!this._automod) throw new Error("[NovaServerWrapper] AutoModAPI not available");
|
|
2455
|
+
return this._automod.create(this.id, options);
|
|
2456
|
+
}
|
|
1792
2457
|
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
1793
2458
|
toJSON() {
|
|
1794
2459
|
return {
|
|
@@ -1916,6 +2581,265 @@ var NovaWebhook = class {
|
|
|
1916
2581
|
}
|
|
1917
2582
|
};
|
|
1918
2583
|
|
|
2584
|
+
// src/structures/NovaForumPost.ts
|
|
2585
|
+
var NovaForumPost = class _NovaForumPost {
|
|
2586
|
+
constructor(raw, forum) {
|
|
2587
|
+
this.raw = raw;
|
|
2588
|
+
this.forum = forum;
|
|
2589
|
+
this.id = raw.id;
|
|
2590
|
+
this.channelId = raw.channelId;
|
|
2591
|
+
this.authorId = raw.authorId;
|
|
2592
|
+
this.title = raw.title;
|
|
2593
|
+
this.body = raw.body;
|
|
2594
|
+
this.tags = raw.tags;
|
|
2595
|
+
this.pinned = raw.pinned;
|
|
2596
|
+
this.closed = raw.closed;
|
|
2597
|
+
this.upvoteCount = raw.upvoteCount;
|
|
2598
|
+
this.replyCount = raw.replyCount;
|
|
2599
|
+
this.author = raw.author;
|
|
2600
|
+
this.createdAt = raw.createdAt;
|
|
2601
|
+
this.updatedAt = raw.updatedAt;
|
|
2602
|
+
}
|
|
2603
|
+
/**
|
|
2604
|
+
* Edit this forum post.
|
|
2605
|
+
*
|
|
2606
|
+
* @example
|
|
2607
|
+
* await post.edit({ title: 'Updated title' })
|
|
2608
|
+
*/
|
|
2609
|
+
async edit(options) {
|
|
2610
|
+
const updated = await this.forum.edit(this.id, options);
|
|
2611
|
+
return new _NovaForumPost(updated, this.forum);
|
|
2612
|
+
}
|
|
2613
|
+
/**
|
|
2614
|
+
* Close the forum post (no new replies).
|
|
2615
|
+
*
|
|
2616
|
+
* @example
|
|
2617
|
+
* await post.close()
|
|
2618
|
+
*/
|
|
2619
|
+
async close() {
|
|
2620
|
+
return this.edit({ closed: true });
|
|
2621
|
+
}
|
|
2622
|
+
/**
|
|
2623
|
+
* Re-open a closed forum post.
|
|
2624
|
+
*
|
|
2625
|
+
* @example
|
|
2626
|
+
* await post.open()
|
|
2627
|
+
*/
|
|
2628
|
+
async open() {
|
|
2629
|
+
return this.edit({ closed: false });
|
|
2630
|
+
}
|
|
2631
|
+
/**
|
|
2632
|
+
* Pin this forum post in the channel.
|
|
2633
|
+
*
|
|
2634
|
+
* @example
|
|
2635
|
+
* await post.pin()
|
|
2636
|
+
*/
|
|
2637
|
+
async pin() {
|
|
2638
|
+
return this.edit({ pinned: true });
|
|
2639
|
+
}
|
|
2640
|
+
/**
|
|
2641
|
+
* Unpin this forum post.
|
|
2642
|
+
*
|
|
2643
|
+
* @example
|
|
2644
|
+
* await post.unpin()
|
|
2645
|
+
*/
|
|
2646
|
+
async unpin() {
|
|
2647
|
+
return this.edit({ pinned: false });
|
|
2648
|
+
}
|
|
2649
|
+
/**
|
|
2650
|
+
* Delete this forum post.
|
|
2651
|
+
*
|
|
2652
|
+
* @example
|
|
2653
|
+
* await post.delete()
|
|
2654
|
+
*/
|
|
2655
|
+
async delete() {
|
|
2656
|
+
await this.forum.delete(this.id);
|
|
2657
|
+
}
|
|
2658
|
+
/** Whether this post was created by the currently connected bot. */
|
|
2659
|
+
get isOwnPost() {
|
|
2660
|
+
return this.authorId === this.author.id;
|
|
2661
|
+
}
|
|
2662
|
+
/** Returns `true` if the post has been closed. */
|
|
2663
|
+
get isClosed() {
|
|
2664
|
+
return this.closed;
|
|
2665
|
+
}
|
|
2666
|
+
/** Returns `true` if the post is pinned. */
|
|
2667
|
+
get isPinned() {
|
|
2668
|
+
return this.pinned;
|
|
2669
|
+
}
|
|
2670
|
+
toJSON() {
|
|
2671
|
+
return { ...this.raw };
|
|
2672
|
+
}
|
|
2673
|
+
};
|
|
2674
|
+
|
|
2675
|
+
// src/structures/NovaServerEvent.ts
|
|
2676
|
+
var NovaServerEvent = class _NovaServerEvent {
|
|
2677
|
+
constructor(raw, events) {
|
|
2678
|
+
this.raw = raw;
|
|
2679
|
+
this.events = events;
|
|
2680
|
+
this.id = raw.id;
|
|
2681
|
+
this.serverId = raw.serverId;
|
|
2682
|
+
this.channelId = raw.channelId;
|
|
2683
|
+
this.createdById = raw.createdById;
|
|
2684
|
+
this.title = raw.title;
|
|
2685
|
+
this.description = raw.description;
|
|
2686
|
+
this.imageUrl = raw.imageUrl;
|
|
2687
|
+
this.location = raw.location;
|
|
2688
|
+
this.startAt = raw.startAt;
|
|
2689
|
+
this.endAt = raw.endAt;
|
|
2690
|
+
this.attendeeCount = raw.attendeeCount;
|
|
2691
|
+
this.createdAt = raw.createdAt;
|
|
2692
|
+
}
|
|
2693
|
+
/**
|
|
2694
|
+
* Edit this event.
|
|
2695
|
+
*
|
|
2696
|
+
* @example
|
|
2697
|
+
* await event.edit({ title: 'New Title' })
|
|
2698
|
+
*/
|
|
2699
|
+
async edit(options) {
|
|
2700
|
+
const updated = await this.events.edit(this.id, options);
|
|
2701
|
+
return new _NovaServerEvent(updated, this.events);
|
|
2702
|
+
}
|
|
2703
|
+
/**
|
|
2704
|
+
* Fetch full event details including the attendee list.
|
|
2705
|
+
*
|
|
2706
|
+
* @example
|
|
2707
|
+
* const full = await event.fetchAttendees()
|
|
2708
|
+
* console.log(`${full.attendees.length} attendees`)
|
|
2709
|
+
*/
|
|
2710
|
+
async fetchAttendees() {
|
|
2711
|
+
const data = await this.events.fetch(this.id);
|
|
2712
|
+
return {
|
|
2713
|
+
event: new _NovaServerEvent(data, this.events),
|
|
2714
|
+
attendees: data.attendees
|
|
2715
|
+
};
|
|
2716
|
+
}
|
|
2717
|
+
/**
|
|
2718
|
+
* Delete this event.
|
|
2719
|
+
*
|
|
2720
|
+
* @example
|
|
2721
|
+
* await event.delete()
|
|
2722
|
+
*/
|
|
2723
|
+
async delete() {
|
|
2724
|
+
await this.events.delete(this.id);
|
|
2725
|
+
}
|
|
2726
|
+
/**
|
|
2727
|
+
* Whether the event has already started.
|
|
2728
|
+
*/
|
|
2729
|
+
get hasStarted() {
|
|
2730
|
+
return new Date(this.startAt) <= /* @__PURE__ */ new Date();
|
|
2731
|
+
}
|
|
2732
|
+
/**
|
|
2733
|
+
* Whether the event has ended.
|
|
2734
|
+
* Returns `false` if no `endAt` is set.
|
|
2735
|
+
*/
|
|
2736
|
+
get hasEnded() {
|
|
2737
|
+
return this.endAt ? new Date(this.endAt) <= /* @__PURE__ */ new Date() : false;
|
|
2738
|
+
}
|
|
2739
|
+
/**
|
|
2740
|
+
* Whether this is an upcoming event that has not yet started.
|
|
2741
|
+
*/
|
|
2742
|
+
get isUpcoming() {
|
|
2743
|
+
return new Date(this.startAt) > /* @__PURE__ */ new Date();
|
|
2744
|
+
}
|
|
2745
|
+
toJSON() {
|
|
2746
|
+
return { ...this.raw };
|
|
2747
|
+
}
|
|
2748
|
+
};
|
|
2749
|
+
|
|
2750
|
+
// src/structures/NovaBan.ts
|
|
2751
|
+
var NovaBan = class {
|
|
2752
|
+
constructor(raw, serverId, members) {
|
|
2753
|
+
this.userId = raw.userId;
|
|
2754
|
+
this.username = raw.username;
|
|
2755
|
+
this.displayName = raw.displayName;
|
|
2756
|
+
this.avatar = raw.avatar;
|
|
2757
|
+
this.reason = raw.reason;
|
|
2758
|
+
this.bannedAt = raw.bannedAt;
|
|
2759
|
+
this.moderatorId = raw.moderatorId;
|
|
2760
|
+
this.serverId = serverId;
|
|
2761
|
+
this._members = members;
|
|
2762
|
+
}
|
|
2763
|
+
/**
|
|
2764
|
+
* Revoke this ban, allowing the user to rejoin the server.
|
|
2765
|
+
* Requires the `members.ban` scope.
|
|
2766
|
+
*
|
|
2767
|
+
* @example
|
|
2768
|
+
* await ban.unban()
|
|
2769
|
+
*/
|
|
2770
|
+
unban() {
|
|
2771
|
+
return this._members.unban(this.serverId, this.userId);
|
|
2772
|
+
}
|
|
2773
|
+
/**
|
|
2774
|
+
* When the ban was issued as a `Date` object.
|
|
2775
|
+
*/
|
|
2776
|
+
get bannedAtDate() {
|
|
2777
|
+
return new Date(this.bannedAt);
|
|
2778
|
+
}
|
|
2779
|
+
/**
|
|
2780
|
+
* Human-readable label: `"displayName (username)"`.
|
|
2781
|
+
*/
|
|
2782
|
+
get label() {
|
|
2783
|
+
return `${this.displayName} (${this.username})`;
|
|
2784
|
+
}
|
|
2785
|
+
toJSON() {
|
|
2786
|
+
return {
|
|
2787
|
+
userId: this.userId,
|
|
2788
|
+
username: this.username,
|
|
2789
|
+
displayName: this.displayName,
|
|
2790
|
+
avatar: this.avatar,
|
|
2791
|
+
reason: this.reason,
|
|
2792
|
+
bannedAt: this.bannedAt,
|
|
2793
|
+
moderatorId: this.moderatorId
|
|
2794
|
+
};
|
|
2795
|
+
}
|
|
2796
|
+
};
|
|
2797
|
+
|
|
2798
|
+
// src/structures/NovaWarning.ts
|
|
2799
|
+
var NovaWarning = class {
|
|
2800
|
+
constructor(raw, members) {
|
|
2801
|
+
this.id = raw.id;
|
|
2802
|
+
this.serverId = raw.serverId;
|
|
2803
|
+
this.userId = raw.userId;
|
|
2804
|
+
this.moderatorId = raw.moderatorId;
|
|
2805
|
+
this.reason = raw.reason;
|
|
2806
|
+
this.createdAt = raw.createdAt;
|
|
2807
|
+
this._members = members;
|
|
2808
|
+
}
|
|
2809
|
+
/**
|
|
2810
|
+
* Delete (expunge) this warning from the record.
|
|
2811
|
+
* Requires the `members.moderate` scope.
|
|
2812
|
+
*
|
|
2813
|
+
* @example
|
|
2814
|
+
* await warning.delete()
|
|
2815
|
+
*/
|
|
2816
|
+
delete() {
|
|
2817
|
+
return this._members.removeWarning(this.id);
|
|
2818
|
+
}
|
|
2819
|
+
/**
|
|
2820
|
+
* When the warning was issued as a `Date` object.
|
|
2821
|
+
*/
|
|
2822
|
+
get issuedAt() {
|
|
2823
|
+
return new Date(this.createdAt);
|
|
2824
|
+
}
|
|
2825
|
+
/**
|
|
2826
|
+
* How long ago the warning was issued, in milliseconds.
|
|
2827
|
+
*/
|
|
2828
|
+
get ageMs() {
|
|
2829
|
+
return Date.now() - this.issuedAt.getTime();
|
|
2830
|
+
}
|
|
2831
|
+
toJSON() {
|
|
2832
|
+
return {
|
|
2833
|
+
id: this.id,
|
|
2834
|
+
serverId: this.serverId,
|
|
2835
|
+
userId: this.userId,
|
|
2836
|
+
moderatorId: this.moderatorId,
|
|
2837
|
+
reason: this.reason,
|
|
2838
|
+
createdAt: this.createdAt
|
|
2839
|
+
};
|
|
2840
|
+
}
|
|
2841
|
+
};
|
|
2842
|
+
|
|
1919
2843
|
// src/client.ts
|
|
1920
2844
|
var NovaClient = class extends EventEmitter {
|
|
1921
2845
|
constructor(options) {
|
|
@@ -1953,6 +2877,11 @@ var NovaClient = class extends EventEmitter {
|
|
|
1953
2877
|
this.invites = new InvitesAPI(this.http);
|
|
1954
2878
|
this.webhooks = new WebhooksAPI(this.http);
|
|
1955
2879
|
this.auditLog = new AuditLogAPI(this.http);
|
|
2880
|
+
this.forum = new ForumAPI(this.http);
|
|
2881
|
+
this.events = new EventsAPI(this.http);
|
|
2882
|
+
this.categories = new CategoriesAPI(this.http);
|
|
2883
|
+
this.automod = new AutoModAPI(this.http);
|
|
2884
|
+
this.users = new UsersAPI(this.http);
|
|
1956
2885
|
this.on("error", () => {
|
|
1957
2886
|
});
|
|
1958
2887
|
const cleanup = () => this.disconnect();
|
|
@@ -2164,16 +3093,53 @@ var NovaClient = class extends EventEmitter {
|
|
|
2164
3093
|
case "server.updated":
|
|
2165
3094
|
this.emit("serverUpdate", event.data);
|
|
2166
3095
|
break;
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
3096
|
+
case "invite.created":
|
|
3097
|
+
this.emit("inviteCreate", event.data);
|
|
3098
|
+
break;
|
|
3099
|
+
case "invite.deleted":
|
|
3100
|
+
this.emit("inviteDelete", event.data);
|
|
3101
|
+
break;
|
|
3102
|
+
case "forum.post.created": {
|
|
3103
|
+
const rawPost = event.data;
|
|
3104
|
+
this.emit("forumPostCreate", new NovaForumPost(rawPost, this.forum));
|
|
3105
|
+
break;
|
|
3106
|
+
}
|
|
3107
|
+
case "event.created": {
|
|
3108
|
+
const rawEvent = event.data;
|
|
3109
|
+
this.emit("eventCreate", new NovaServerEvent(rawEvent, this.events));
|
|
3110
|
+
break;
|
|
3111
|
+
}
|
|
3112
|
+
case "event.updated":
|
|
3113
|
+
this.emit("eventUpdate", event.data);
|
|
3114
|
+
break;
|
|
3115
|
+
case "event.deleted":
|
|
3116
|
+
this.emit("eventDelete", event.data);
|
|
3117
|
+
break;
|
|
3118
|
+
case "category.created":
|
|
3119
|
+
this.emit("categoryCreate", event.data);
|
|
3120
|
+
break;
|
|
3121
|
+
case "category.updated":
|
|
3122
|
+
this.emit("categoryUpdate", event.data);
|
|
3123
|
+
break;
|
|
3124
|
+
case "category.deleted":
|
|
3125
|
+
this.emit("categoryDelete", event.data);
|
|
3126
|
+
break;
|
|
3127
|
+
case "member.warned":
|
|
3128
|
+
this.emit("memberWarned", event.data);
|
|
3129
|
+
break;
|
|
3130
|
+
case "messages.bulk_deleted":
|
|
3131
|
+
this.emit("messagesBulkDelete", event.data);
|
|
3132
|
+
break;
|
|
3133
|
+
}
|
|
3134
|
+
});
|
|
3135
|
+
this.socket.on("bot:error", (err) => {
|
|
3136
|
+
this.emit("error", err);
|
|
3137
|
+
});
|
|
3138
|
+
this.socket.on("disconnect", (reason) => {
|
|
3139
|
+
this.emit("disconnect", reason);
|
|
3140
|
+
});
|
|
3141
|
+
});
|
|
3142
|
+
}
|
|
2177
3143
|
/**
|
|
2178
3144
|
* Register a recurring task that fires at a set interval.
|
|
2179
3145
|
* All cron tasks are automatically cancelled on `disconnect()`.
|
|
@@ -2230,7 +3196,7 @@ var NovaClient = class extends EventEmitter {
|
|
|
2230
3196
|
*/
|
|
2231
3197
|
async fetchChannel(channelId) {
|
|
2232
3198
|
const raw = await this.channels.fetch(channelId);
|
|
2233
|
-
return new NovaChannel(raw, this.channels, this.messages);
|
|
3199
|
+
return new NovaChannel(raw, this.channels, this.messages, this.webhooks, this.forum);
|
|
2234
3200
|
}
|
|
2235
3201
|
/**
|
|
2236
3202
|
* Fetch all channels in a server and return them as `NovaChannel` wrappers.
|
|
@@ -2241,7 +3207,7 @@ var NovaClient = class extends EventEmitter {
|
|
|
2241
3207
|
*/
|
|
2242
3208
|
async fetchChannels(serverId) {
|
|
2243
3209
|
const list = await this.channels.list(serverId);
|
|
2244
|
-
return list.map((raw) => new NovaChannel(raw, this.channels, this.messages));
|
|
3210
|
+
return list.map((raw) => new NovaChannel(raw, this.channels, this.messages, this.webhooks, this.forum));
|
|
2245
3211
|
}
|
|
2246
3212
|
/**
|
|
2247
3213
|
* Fetch all members in a server and return them as `NovaMember` wrappers.
|
|
@@ -2266,6 +3232,36 @@ var NovaClient = class extends EventEmitter {
|
|
|
2266
3232
|
const raw = await this.members.fetch(serverId, userId);
|
|
2267
3233
|
return new NovaMember(raw, serverId, this.members);
|
|
2268
3234
|
}
|
|
3235
|
+
/**
|
|
3236
|
+
* Fetch all bans in a server and return them as `NovaBan` wrappers.
|
|
3237
|
+
* Each `NovaBan` has an `.unban()` convenience method.
|
|
3238
|
+
* Requires the `members.ban` scope.
|
|
3239
|
+
*
|
|
3240
|
+
* @example
|
|
3241
|
+
* const bans = await client.fetchBans('server-id')
|
|
3242
|
+
* for (const ban of bans) {
|
|
3243
|
+
* if (ban.reason === 'test') await ban.unban()
|
|
3244
|
+
* }
|
|
3245
|
+
*/
|
|
3246
|
+
async fetchBans(serverId) {
|
|
3247
|
+
const list = await this.members.listBans(serverId);
|
|
3248
|
+
return list.map((raw) => new NovaBan(raw, serverId, this.members));
|
|
3249
|
+
}
|
|
3250
|
+
/**
|
|
3251
|
+
* Fetch warnings for a member (or all members) in a server and return them
|
|
3252
|
+
* as `NovaWarning` wrappers. Each wrapper has a `.delete()` convenience method.
|
|
3253
|
+
* Requires the `members.moderate` scope.
|
|
3254
|
+
*
|
|
3255
|
+
* @example
|
|
3256
|
+
* const warnings = await client.fetchMemberWarnings('server-id', 'user-id')
|
|
3257
|
+
* for (const w of warnings) {
|
|
3258
|
+
* if (w.ageMs > 30 * 24 * 60 * 60_000) await w.delete() // older than 30 days
|
|
3259
|
+
* }
|
|
3260
|
+
*/
|
|
3261
|
+
async fetchMemberWarnings(serverId, userId) {
|
|
3262
|
+
const list = await this.members.fetchWarnings(serverId, userId ? { userId } : {});
|
|
3263
|
+
return list.map((raw) => new NovaWarning(raw, this.members));
|
|
3264
|
+
}
|
|
2269
3265
|
/**
|
|
2270
3266
|
* Fetch a single server by ID and return it as a `NovaServerWrapper`.
|
|
2271
3267
|
*
|
|
@@ -2275,7 +3271,7 @@ var NovaClient = class extends EventEmitter {
|
|
|
2275
3271
|
*/
|
|
2276
3272
|
async fetchServer(serverId) {
|
|
2277
3273
|
const raw = await this.servers.fetch(serverId);
|
|
2278
|
-
return new NovaServerWrapper(raw, this.servers, this.channels, this.members, this.invites, this.roles, this.messages);
|
|
3274
|
+
return new NovaServerWrapper(raw, this.servers, this.channels, this.members, this.invites, this.roles, this.messages, this.categories, this.events, this.automod, this.auditLog);
|
|
2279
3275
|
}
|
|
2280
3276
|
/**
|
|
2281
3277
|
* Fetch all servers the bot is in and return them as `NovaServerWrapper` objects.
|
|
@@ -2286,7 +3282,7 @@ var NovaClient = class extends EventEmitter {
|
|
|
2286
3282
|
*/
|
|
2287
3283
|
async fetchServers() {
|
|
2288
3284
|
const list = await this.servers.list();
|
|
2289
|
-
return list.map((raw) => new NovaServerWrapper(raw, this.servers, this.channels, this.members, this.invites, this.roles, this.messages));
|
|
3285
|
+
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));
|
|
2290
3286
|
}
|
|
2291
3287
|
/**
|
|
2292
3288
|
* Fetch all custom roles in a server and return them as `NovaRole` wrappers.
|
|
@@ -2342,6 +3338,116 @@ var NovaClient = class extends EventEmitter {
|
|
|
2342
3338
|
const raw = await this.webhooks.fetch(webhookId);
|
|
2343
3339
|
return new NovaWebhook(raw, this.webhooks);
|
|
2344
3340
|
}
|
|
3341
|
+
/**
|
|
3342
|
+
* Fetch forum posts from a FORUM channel and return them as `NovaForumPost` wrappers.
|
|
3343
|
+
*
|
|
3344
|
+
* @example
|
|
3345
|
+
* const posts = await client.fetchForumPosts('channel-id', { limit: 20 })
|
|
3346
|
+
* const open = posts.filter(p => !p.isClosed)
|
|
3347
|
+
*/
|
|
3348
|
+
async fetchForumPosts(channelId, options) {
|
|
3349
|
+
const list = await this.forum.list(channelId, options);
|
|
3350
|
+
return list.map((raw) => new NovaForumPost(raw, this.forum));
|
|
3351
|
+
}
|
|
3352
|
+
/**
|
|
3353
|
+
* Fetch server events and return them as `NovaServerEvent` wrappers.
|
|
3354
|
+
*
|
|
3355
|
+
* @example
|
|
3356
|
+
* const upcoming = await client.fetchEvents('server-id', { upcoming: true })
|
|
3357
|
+
* for (const event of upcoming) console.log(event.title)
|
|
3358
|
+
*/
|
|
3359
|
+
async fetchEvents(serverId, options) {
|
|
3360
|
+
const list = await this.events.list(serverId, options);
|
|
3361
|
+
return list.map((raw) => new NovaServerEvent(raw, this.events));
|
|
3362
|
+
}
|
|
3363
|
+
/**
|
|
3364
|
+
* Fetch a single server event by ID and return it as a `NovaServerEvent` wrapper.
|
|
3365
|
+
*
|
|
3366
|
+
* @example
|
|
3367
|
+
* const event = await client.fetchEvent('event-id')
|
|
3368
|
+
* if (event.isUpcoming) await event.edit({ title: 'Updated!' })
|
|
3369
|
+
*/
|
|
3370
|
+
async fetchEvent(eventId) {
|
|
3371
|
+
const raw = await this.events.fetch(eventId);
|
|
3372
|
+
return new NovaServerEvent(raw, this.events);
|
|
3373
|
+
}
|
|
3374
|
+
/**
|
|
3375
|
+
* Fetch a user's public profile.
|
|
3376
|
+
*
|
|
3377
|
+
* @example
|
|
3378
|
+
* const user = await client.fetchUserProfile('user-id')
|
|
3379
|
+
* console.log(user.bio)
|
|
3380
|
+
*/
|
|
3381
|
+
fetchUserProfile(userId) {
|
|
3382
|
+
return this.users.fetch(userId);
|
|
3383
|
+
}
|
|
3384
|
+
/**
|
|
3385
|
+
* Fetch the XP leaderboard for a server.
|
|
3386
|
+
*
|
|
3387
|
+
* @example
|
|
3388
|
+
* const top = await client.fetchLeaderboard('server-id', 10)
|
|
3389
|
+
* top.forEach(entry => console.log(`#${entry.rank} ${entry.user.username} — ${entry.xp} XP`))
|
|
3390
|
+
*/
|
|
3391
|
+
fetchLeaderboard(serverId, limit = 10) {
|
|
3392
|
+
return this.members.leaderboard(serverId, limit);
|
|
3393
|
+
}
|
|
3394
|
+
/**
|
|
3395
|
+
* Fetch warnings in a server, optionally filtered by user.
|
|
3396
|
+
*
|
|
3397
|
+
* @example
|
|
3398
|
+
* const warnings = await client.fetchWarnings('server-id', { userId: 'user-id' })
|
|
3399
|
+
*/
|
|
3400
|
+
fetchWarnings(serverId, options) {
|
|
3401
|
+
return this.members.fetchWarnings(serverId, options);
|
|
3402
|
+
}
|
|
3403
|
+
/**
|
|
3404
|
+
* Fetch all automod rules for a server.
|
|
3405
|
+
*
|
|
3406
|
+
* @example
|
|
3407
|
+
* const rules = await client.fetchAutoModRules('server-id')
|
|
3408
|
+
*/
|
|
3409
|
+
fetchAutoModRules(serverId) {
|
|
3410
|
+
return this.automod.list(serverId);
|
|
3411
|
+
}
|
|
3412
|
+
/**
|
|
3413
|
+
* Fetch all channel categories for a server.
|
|
3414
|
+
*
|
|
3415
|
+
* @example
|
|
3416
|
+
* const cats = await client.fetchCategories('server-id')
|
|
3417
|
+
*/
|
|
3418
|
+
fetchCategories(serverId) {
|
|
3419
|
+
return this.categories.list(serverId);
|
|
3420
|
+
}
|
|
3421
|
+
/**
|
|
3422
|
+
* Fetch soundboard clips for a server.
|
|
3423
|
+
*
|
|
3424
|
+
* @example
|
|
3425
|
+
* const clips = await client.fetchSoundboard('server-id')
|
|
3426
|
+
*/
|
|
3427
|
+
fetchSoundboard(serverId) {
|
|
3428
|
+
return this.servers.listSoundboard(serverId);
|
|
3429
|
+
}
|
|
3430
|
+
/**
|
|
3431
|
+
* Fetch statistics for a server (member count, message count, etc.).
|
|
3432
|
+
*
|
|
3433
|
+
* @example
|
|
3434
|
+
* const stats = await client.fetchServerStats('server-id')
|
|
3435
|
+
* console.log(`${stats.onlineCount}/${stats.memberCount} online`)
|
|
3436
|
+
*/
|
|
3437
|
+
fetchServerStats(serverId) {
|
|
3438
|
+
return this.servers.getStats(serverId);
|
|
3439
|
+
}
|
|
3440
|
+
/**
|
|
3441
|
+
* Bulk delete up to 100 messages in a channel.
|
|
3442
|
+
* Requires the `messages.manage` scope.
|
|
3443
|
+
*
|
|
3444
|
+
* @example
|
|
3445
|
+
* const result = await client.bulkDelete('channel-id', messageIds)
|
|
3446
|
+
* console.log(`Deleted ${result.deleted} messages`)
|
|
3447
|
+
*/
|
|
3448
|
+
bulkDelete(channelId, messageIds) {
|
|
3449
|
+
return this.channels.bulkDelete(channelId, messageIds);
|
|
3450
|
+
}
|
|
2345
3451
|
// ─── Event fence ──────────────────────────────────────────────────────────────
|
|
2346
3452
|
/**
|
|
2347
3453
|
* Wait for a specific event to be emitted, optionally filtered.
|
|
@@ -2371,6 +3477,58 @@ var NovaClient = class extends EventEmitter {
|
|
|
2371
3477
|
this.on(event, handler);
|
|
2372
3478
|
});
|
|
2373
3479
|
}
|
|
3480
|
+
/**
|
|
3481
|
+
* Wait for the next message matching an optional filter.
|
|
3482
|
+
* Short for `waitFor('messageCreate', filter, timeoutMs)`.
|
|
3483
|
+
*
|
|
3484
|
+
* @example
|
|
3485
|
+
* // Wait for any message in a specific channel
|
|
3486
|
+
* const msg = await client.waitForMessage(
|
|
3487
|
+
* m => m.channelId === channelId && !m.isFromBot(),
|
|
3488
|
+
* 30_000,
|
|
3489
|
+
* )
|
|
3490
|
+
* console.log(msg.content)
|
|
3491
|
+
*/
|
|
3492
|
+
waitForMessage(filter, timeoutMs = 3e4) {
|
|
3493
|
+
return this.waitFor("messageCreate", filter, timeoutMs);
|
|
3494
|
+
}
|
|
3495
|
+
/**
|
|
3496
|
+
* Wait for the next button-click interaction matching an optional filter.
|
|
3497
|
+
* Short for `waitFor('interactionCreate', i => i.isButton() && filter(i), timeoutMs)`.
|
|
3498
|
+
*
|
|
3499
|
+
* @example
|
|
3500
|
+
* // Wait for any button on a specific message
|
|
3501
|
+
* const click = await client.waitForButton(
|
|
3502
|
+
* i => i.triggerMsgId === message.id,
|
|
3503
|
+
* 60_000,
|
|
3504
|
+
* )
|
|
3505
|
+
* await click.reply('Button clicked!')
|
|
3506
|
+
*/
|
|
3507
|
+
waitForButton(filter, timeoutMs = 3e4) {
|
|
3508
|
+
return this.waitFor(
|
|
3509
|
+
"interactionCreate",
|
|
3510
|
+
(i) => i.isButton() && (!filter || filter(i)),
|
|
3511
|
+
timeoutMs
|
|
3512
|
+
);
|
|
3513
|
+
}
|
|
3514
|
+
/**
|
|
3515
|
+
* Wait for the next select-menu interaction matching an optional filter.
|
|
3516
|
+
* Short for `waitFor('interactionCreate', i => i.isSelectMenu() && filter(i), timeoutMs)`.
|
|
3517
|
+
*
|
|
3518
|
+
* @example
|
|
3519
|
+
* const pick = await client.waitForSelectMenu(
|
|
3520
|
+
* i => i.customId === 'colour_pick',
|
|
3521
|
+
* 60_000,
|
|
3522
|
+
* )
|
|
3523
|
+
* await pick.reply(`You picked: ${pick.values.join(', ')}`)
|
|
3524
|
+
*/
|
|
3525
|
+
waitForSelectMenu(filter, timeoutMs = 3e4) {
|
|
3526
|
+
return this.waitFor(
|
|
3527
|
+
"interactionCreate",
|
|
3528
|
+
(i) => i.isSelectMenu() && (!filter || filter(i)),
|
|
3529
|
+
timeoutMs
|
|
3530
|
+
);
|
|
3531
|
+
}
|
|
2374
3532
|
/**
|
|
2375
3533
|
* Disconnect from the gateway and clean up.
|
|
2376
3534
|
*/
|
|
@@ -3738,46 +4896,1375 @@ function countdown(target) {
|
|
|
3738
4896
|
seconds: Math.floor(total % MINUTE / SECOND)
|
|
3739
4897
|
};
|
|
3740
4898
|
}
|
|
4899
|
+
|
|
4900
|
+
// src/structures/NovaCategory.ts
|
|
4901
|
+
var NovaCategory = class _NovaCategory {
|
|
4902
|
+
constructor(raw, categories) {
|
|
4903
|
+
this.id = raw.id;
|
|
4904
|
+
this.name = raw.name;
|
|
4905
|
+
this.position = raw.position;
|
|
4906
|
+
this.serverId = raw.serverId;
|
|
4907
|
+
this.createdAt = raw.createdAt;
|
|
4908
|
+
this._categories = categories;
|
|
4909
|
+
}
|
|
4910
|
+
// ─── Convenience methods ──────────────────────────────────────────────────
|
|
4911
|
+
/**
|
|
4912
|
+
* Edit this category's name and/or position.
|
|
4913
|
+
* Requires the `channels.manage` scope.
|
|
4914
|
+
*
|
|
4915
|
+
* @example
|
|
4916
|
+
* await cat.edit({ name: 'Lounges', position: 0 })
|
|
4917
|
+
*/
|
|
4918
|
+
async edit(options) {
|
|
4919
|
+
const updated = await this._categories.edit(this.id, options);
|
|
4920
|
+
return new _NovaCategory(updated, this._categories);
|
|
4921
|
+
}
|
|
4922
|
+
/**
|
|
4923
|
+
* Rename this category.
|
|
4924
|
+
* Requires the `channels.manage` scope.
|
|
4925
|
+
*
|
|
4926
|
+
* @example
|
|
4927
|
+
* await cat.rename('Lounges')
|
|
4928
|
+
*/
|
|
4929
|
+
rename(name) {
|
|
4930
|
+
return this.edit({ name });
|
|
4931
|
+
}
|
|
4932
|
+
/**
|
|
4933
|
+
* Move this category to a specific position.
|
|
4934
|
+
* Requires the `channels.manage` scope.
|
|
4935
|
+
*
|
|
4936
|
+
* @example
|
|
4937
|
+
* await cat.setPosition(0)
|
|
4938
|
+
*/
|
|
4939
|
+
setPosition(position) {
|
|
4940
|
+
return this.edit({ position });
|
|
4941
|
+
}
|
|
4942
|
+
/**
|
|
4943
|
+
* Delete this category.
|
|
4944
|
+
* Requires the `channels.manage` scope.
|
|
4945
|
+
*
|
|
4946
|
+
* @example
|
|
4947
|
+
* await cat.delete()
|
|
4948
|
+
*/
|
|
4949
|
+
async delete() {
|
|
4950
|
+
await this._categories.delete(this.id);
|
|
4951
|
+
}
|
|
4952
|
+
// ─── Serialisation ─────────────────────────────────────────────────────────
|
|
4953
|
+
toJSON() {
|
|
4954
|
+
return {
|
|
4955
|
+
id: this.id,
|
|
4956
|
+
name: this.name,
|
|
4957
|
+
position: this.position,
|
|
4958
|
+
serverId: this.serverId,
|
|
4959
|
+
createdAt: this.createdAt
|
|
4960
|
+
};
|
|
4961
|
+
}
|
|
4962
|
+
toString() {
|
|
4963
|
+
return `NovaCategory(${this.id}): ${this.name}`;
|
|
4964
|
+
}
|
|
4965
|
+
};
|
|
4966
|
+
|
|
4967
|
+
// src/builders/ForumPostBuilder.ts
|
|
4968
|
+
var ForumPostBuilder = class {
|
|
4969
|
+
constructor() {
|
|
4970
|
+
this._title = "";
|
|
4971
|
+
this._body = "";
|
|
4972
|
+
this._tags = [];
|
|
4973
|
+
}
|
|
4974
|
+
/**
|
|
4975
|
+
* Set the post title.
|
|
4976
|
+
*/
|
|
4977
|
+
setTitle(title) {
|
|
4978
|
+
this._title = title;
|
|
4979
|
+
return this;
|
|
4980
|
+
}
|
|
4981
|
+
/**
|
|
4982
|
+
* Set the post body / content.
|
|
4983
|
+
*/
|
|
4984
|
+
setBody(body) {
|
|
4985
|
+
this._body = body;
|
|
4986
|
+
return this;
|
|
4987
|
+
}
|
|
4988
|
+
/**
|
|
4989
|
+
* Add a tag to the post.
|
|
4990
|
+
* Tags are used to categorise forum posts.
|
|
4991
|
+
*/
|
|
4992
|
+
addTag(tag) {
|
|
4993
|
+
this._tags.push(tag);
|
|
4994
|
+
return this;
|
|
4995
|
+
}
|
|
4996
|
+
/**
|
|
4997
|
+
* Set all tags at once, replacing any previously added tags.
|
|
4998
|
+
*/
|
|
4999
|
+
setTags(tags) {
|
|
5000
|
+
this._tags = [...tags];
|
|
5001
|
+
return this;
|
|
5002
|
+
}
|
|
5003
|
+
/**
|
|
5004
|
+
* Validate and return the final `CreateForumPostOptions` object.
|
|
5005
|
+
*
|
|
5006
|
+
* @throws {Error} if title or body is missing.
|
|
5007
|
+
*/
|
|
5008
|
+
build() {
|
|
5009
|
+
if (!this._title.trim()) throw new Error("[ForumPostBuilder] title is required");
|
|
5010
|
+
if (!this._body.trim()) throw new Error("[ForumPostBuilder] body is required");
|
|
5011
|
+
return {
|
|
5012
|
+
title: this._title.trim(),
|
|
5013
|
+
body: this._body.trim(),
|
|
5014
|
+
tags: this._tags
|
|
5015
|
+
};
|
|
5016
|
+
}
|
|
5017
|
+
};
|
|
5018
|
+
|
|
5019
|
+
// src/builders/EventBuilder.ts
|
|
5020
|
+
var EventBuilder = class {
|
|
5021
|
+
constructor() {
|
|
5022
|
+
this._title = "";
|
|
5023
|
+
}
|
|
5024
|
+
/** Set the event title (required). */
|
|
5025
|
+
setTitle(title) {
|
|
5026
|
+
this._title = title;
|
|
5027
|
+
return this;
|
|
5028
|
+
}
|
|
5029
|
+
/** Set the event description. */
|
|
5030
|
+
setDescription(description) {
|
|
5031
|
+
this._description = description;
|
|
5032
|
+
return this;
|
|
5033
|
+
}
|
|
5034
|
+
/** Set a banner/cover image URL for the event. */
|
|
5035
|
+
setImage(url) {
|
|
5036
|
+
this._imageUrl = url;
|
|
5037
|
+
return this;
|
|
5038
|
+
}
|
|
5039
|
+
/** Set the physical or virtual location for the event. */
|
|
5040
|
+
setLocation(location) {
|
|
5041
|
+
this._location = location;
|
|
5042
|
+
return this;
|
|
5043
|
+
}
|
|
5044
|
+
/** Set when the event starts. Accepts a Date or ISO string. */
|
|
5045
|
+
setStartAt(date) {
|
|
5046
|
+
this._startAt = date instanceof Date ? date.toISOString() : date;
|
|
5047
|
+
return this;
|
|
5048
|
+
}
|
|
5049
|
+
/** Set when the event ends. Accepts a Date or ISO string. */
|
|
5050
|
+
setEndAt(date) {
|
|
5051
|
+
this._endAt = date instanceof Date ? date.toISOString() : date;
|
|
5052
|
+
return this;
|
|
5053
|
+
}
|
|
5054
|
+
/** Link the event to a specific channel. */
|
|
5055
|
+
setChannel(channelId) {
|
|
5056
|
+
this._channelId = channelId;
|
|
5057
|
+
return this;
|
|
5058
|
+
}
|
|
5059
|
+
/**
|
|
5060
|
+
* Validate and return the final `CreateEventOptions` object.
|
|
5061
|
+
*
|
|
5062
|
+
* @throws {Error} if title or startAt is missing.
|
|
5063
|
+
*/
|
|
5064
|
+
build() {
|
|
5065
|
+
if (!this._title.trim()) throw new Error("[EventBuilder] title is required");
|
|
5066
|
+
if (!this._startAt) throw new Error("[EventBuilder] startAt is required \u2014 call setStartAt()");
|
|
5067
|
+
return {
|
|
5068
|
+
title: this._title.trim(),
|
|
5069
|
+
...this._description !== void 0 ? { description: this._description } : {},
|
|
5070
|
+
...this._imageUrl !== void 0 ? { imageUrl: this._imageUrl } : {},
|
|
5071
|
+
...this._location !== void 0 ? { location: this._location } : {},
|
|
5072
|
+
startAt: this._startAt,
|
|
5073
|
+
...this._endAt !== void 0 ? { endAt: this._endAt } : {},
|
|
5074
|
+
...this._channelId !== void 0 ? { channelId: this._channelId } : {}
|
|
5075
|
+
};
|
|
5076
|
+
}
|
|
5077
|
+
};
|
|
5078
|
+
|
|
5079
|
+
// src/builders/RoleBuilder.ts
|
|
5080
|
+
var RoleBuilder = class {
|
|
5081
|
+
constructor() {
|
|
5082
|
+
this._name = "";
|
|
5083
|
+
}
|
|
5084
|
+
/** Set the role's display name (required). */
|
|
5085
|
+
setName(name) {
|
|
5086
|
+
this._name = name;
|
|
5087
|
+
return this;
|
|
5088
|
+
}
|
|
5089
|
+
/**
|
|
5090
|
+
* Set the role's accent colour.
|
|
5091
|
+
* @param color Hex string — e.g. `'#5865F2'` or `'5865F2'`
|
|
5092
|
+
*/
|
|
5093
|
+
setColor(color) {
|
|
5094
|
+
this._color = color.startsWith("#") ? color : `#${color}`;
|
|
5095
|
+
return this;
|
|
5096
|
+
}
|
|
5097
|
+
/**
|
|
5098
|
+
* Whether this role is shown separately in the member sidebar.
|
|
5099
|
+
* Default: `true` when called without arguments.
|
|
5100
|
+
*/
|
|
5101
|
+
setHoist(hoist = true) {
|
|
5102
|
+
this._hoist = hoist;
|
|
5103
|
+
return this;
|
|
5104
|
+
}
|
|
5105
|
+
/** Set a specific permission key to true or false. */
|
|
5106
|
+
addPermission(key, value = true) {
|
|
5107
|
+
if (!this._permissions) this._permissions = {};
|
|
5108
|
+
this._permissions[key] = value;
|
|
5109
|
+
return this;
|
|
5110
|
+
}
|
|
5111
|
+
/** Replace all permission overrides at once. */
|
|
5112
|
+
setPermissions(permissions) {
|
|
5113
|
+
this._permissions = { ...permissions };
|
|
5114
|
+
return this;
|
|
5115
|
+
}
|
|
5116
|
+
// ─── Common permission shorthands ─────────────────────────────────────────
|
|
5117
|
+
/** Grant the `sendMessages` permission. */
|
|
5118
|
+
allowSendMessages() {
|
|
5119
|
+
return this.addPermission("sendMessages");
|
|
5120
|
+
}
|
|
5121
|
+
/** Deny the `sendMessages` permission. */
|
|
5122
|
+
denySendMessages() {
|
|
5123
|
+
return this.addPermission("sendMessages", false);
|
|
5124
|
+
}
|
|
5125
|
+
/** Grant the `addReactions` permission. */
|
|
5126
|
+
allowAddReactions() {
|
|
5127
|
+
return this.addPermission("addReactions");
|
|
5128
|
+
}
|
|
5129
|
+
/** Grant the `manageMessages` permission (delete/pin messages). */
|
|
5130
|
+
allowManageMessages() {
|
|
5131
|
+
return this.addPermission("manageMessages");
|
|
5132
|
+
}
|
|
5133
|
+
/** Grant the `manageChannels` permission. */
|
|
5134
|
+
allowManageChannels() {
|
|
5135
|
+
return this.addPermission("manageChannels");
|
|
5136
|
+
}
|
|
5137
|
+
/** Grant the `kickMembers` permission. */
|
|
5138
|
+
allowKickMembers() {
|
|
5139
|
+
return this.addPermission("kickMembers");
|
|
5140
|
+
}
|
|
5141
|
+
/** Grant the `banMembers` permission. */
|
|
5142
|
+
allowBanMembers() {
|
|
5143
|
+
return this.addPermission("banMembers");
|
|
5144
|
+
}
|
|
5145
|
+
/** Grant the `manageServer` permission. */
|
|
5146
|
+
allowManageServer() {
|
|
5147
|
+
return this.addPermission("manageServer");
|
|
5148
|
+
}
|
|
5149
|
+
/** Grant the `manageRoles` permission. */
|
|
5150
|
+
allowManageRoles() {
|
|
5151
|
+
return this.addPermission("manageRoles");
|
|
5152
|
+
}
|
|
5153
|
+
/**
|
|
5154
|
+
* Validate and build the `RoleCreateOptions` object.
|
|
5155
|
+
*
|
|
5156
|
+
* @throws if name is not set.
|
|
5157
|
+
*/
|
|
5158
|
+
build() {
|
|
5159
|
+
if (!this._name.trim()) throw new Error("[RoleBuilder] name is required \u2014 call .setName()");
|
|
5160
|
+
return {
|
|
5161
|
+
name: this._name.trim(),
|
|
5162
|
+
...this._color !== void 0 ? { color: this._color } : {},
|
|
5163
|
+
...this._hoist !== void 0 ? { hoist: this._hoist } : {},
|
|
5164
|
+
...this._permissions !== void 0 ? { permissions: this._permissions } : {}
|
|
5165
|
+
};
|
|
5166
|
+
}
|
|
5167
|
+
};
|
|
5168
|
+
|
|
5169
|
+
// src/builders/ChannelBuilder.ts
|
|
5170
|
+
var ChannelBuilder = class {
|
|
5171
|
+
constructor() {
|
|
5172
|
+
this._name = "";
|
|
5173
|
+
this._type = "TEXT";
|
|
5174
|
+
}
|
|
5175
|
+
/** Set the channel name (required). No # prefix needed. */
|
|
5176
|
+
setName(name) {
|
|
5177
|
+
this._name = name;
|
|
5178
|
+
return this;
|
|
5179
|
+
}
|
|
5180
|
+
/**
|
|
5181
|
+
* Set the channel type.
|
|
5182
|
+
* @default 'TEXT'
|
|
5183
|
+
*/
|
|
5184
|
+
setType(type) {
|
|
5185
|
+
this._type = type;
|
|
5186
|
+
return this;
|
|
5187
|
+
}
|
|
5188
|
+
// ─── Type shorthands ──────────────────────────────────────────────────────
|
|
5189
|
+
/** Create a TEXT channel. */
|
|
5190
|
+
asText() {
|
|
5191
|
+
return this.setType("TEXT");
|
|
5192
|
+
}
|
|
5193
|
+
/** Create a VOICE channel. */
|
|
5194
|
+
asVoice() {
|
|
5195
|
+
return this.setType("VOICE");
|
|
5196
|
+
}
|
|
5197
|
+
/** Create an ANNOUNCEMENT channel. */
|
|
5198
|
+
asAnnouncement() {
|
|
5199
|
+
return this.setType("ANNOUNCEMENT");
|
|
5200
|
+
}
|
|
5201
|
+
/** Create a FORUM channel. */
|
|
5202
|
+
asForum() {
|
|
5203
|
+
return this.setType("FORUM");
|
|
5204
|
+
}
|
|
5205
|
+
/** Create a STAGE channel. */
|
|
5206
|
+
asStage() {
|
|
5207
|
+
return this.setType("STAGE");
|
|
5208
|
+
}
|
|
5209
|
+
// ─── Properties ───────────────────────────────────────────────────────────
|
|
5210
|
+
/** Set the channel topic / description. */
|
|
5211
|
+
setTopic(topic) {
|
|
5212
|
+
this._topic = topic;
|
|
5213
|
+
return this;
|
|
5214
|
+
}
|
|
5215
|
+
/** Set the channel's sort position. */
|
|
5216
|
+
setPosition(position) {
|
|
5217
|
+
this._position = position;
|
|
5218
|
+
return this;
|
|
5219
|
+
}
|
|
5220
|
+
/** Place the channel inside an existing category by its ID. */
|
|
5221
|
+
setCategory(categoryId) {
|
|
5222
|
+
this._categoryId = categoryId;
|
|
5223
|
+
return this;
|
|
5224
|
+
}
|
|
5225
|
+
/**
|
|
5226
|
+
* Set a slow-mode interval in seconds.
|
|
5227
|
+
* Members must wait this long between messages.
|
|
5228
|
+
* Pass `0` to disable.
|
|
5229
|
+
*/
|
|
5230
|
+
setSlowMode(seconds) {
|
|
5231
|
+
this._slowMode = seconds;
|
|
5232
|
+
return this;
|
|
5233
|
+
}
|
|
5234
|
+
/**
|
|
5235
|
+
* Mark the channel as private.
|
|
5236
|
+
* Private channels are only visible to members with explicit access.
|
|
5237
|
+
*/
|
|
5238
|
+
setPrivate(isPrivate = true) {
|
|
5239
|
+
this._isPrivate = isPrivate;
|
|
5240
|
+
return this;
|
|
5241
|
+
}
|
|
5242
|
+
/**
|
|
5243
|
+
* Validate and build the `CreateChannelOptions` object.
|
|
5244
|
+
*
|
|
5245
|
+
* @throws if name is not set.
|
|
5246
|
+
*/
|
|
5247
|
+
build() {
|
|
5248
|
+
if (!this._name.trim()) throw new Error("[ChannelBuilder] name is required \u2014 call .setName()");
|
|
5249
|
+
return {
|
|
5250
|
+
name: this._name.trim(),
|
|
5251
|
+
type: this._type,
|
|
5252
|
+
...this._topic !== void 0 ? { topic: this._topic } : {},
|
|
5253
|
+
...this._position !== void 0 ? { position: this._position } : {},
|
|
5254
|
+
...this._categoryId !== void 0 ? { categoryId: this._categoryId } : {},
|
|
5255
|
+
...this._slowMode !== void 0 ? { slowMode: this._slowMode } : {},
|
|
5256
|
+
...this._isPrivate !== void 0 ? { isPrivate: this._isPrivate } : {}
|
|
5257
|
+
};
|
|
5258
|
+
}
|
|
5259
|
+
};
|
|
5260
|
+
|
|
5261
|
+
// src/builders/InviteBuilder.ts
|
|
5262
|
+
var InviteBuilder = class {
|
|
5263
|
+
/**
|
|
5264
|
+
* Set the maximum number of times this invite can be used.
|
|
5265
|
+
* Pass `null` for unlimited uses.
|
|
5266
|
+
*/
|
|
5267
|
+
setMaxUses(maxUses) {
|
|
5268
|
+
this._maxUses = maxUses;
|
|
5269
|
+
return this;
|
|
5270
|
+
}
|
|
5271
|
+
/**
|
|
5272
|
+
* Set an absolute expiry date/time for this invite.
|
|
5273
|
+
* Pass a `Date`, an ISO string, or `null` to never expire.
|
|
5274
|
+
*/
|
|
5275
|
+
setExpiresAt(date) {
|
|
5276
|
+
if (date === null) {
|
|
5277
|
+
this._expiresAt = null;
|
|
5278
|
+
} else {
|
|
5279
|
+
this._expiresAt = date instanceof Date ? date.toISOString() : date;
|
|
5280
|
+
}
|
|
5281
|
+
return this;
|
|
5282
|
+
}
|
|
5283
|
+
/**
|
|
5284
|
+
* Set expiry as a duration from now.
|
|
5285
|
+
* @param ms Duration in milliseconds.
|
|
5286
|
+
*
|
|
5287
|
+
* @example
|
|
5288
|
+
* builder.expiresIn(7 * 24 * 60 * 60 * 1000) // 7 days
|
|
5289
|
+
*/
|
|
5290
|
+
expiresIn(ms) {
|
|
5291
|
+
this._expiresAt = new Date(Date.now() + ms).toISOString();
|
|
5292
|
+
return this;
|
|
5293
|
+
}
|
|
5294
|
+
/**
|
|
5295
|
+
* Create a one-time use invite (`maxUses = 1`).
|
|
5296
|
+
*/
|
|
5297
|
+
setOneTimeUse() {
|
|
5298
|
+
return this.setMaxUses(1);
|
|
5299
|
+
}
|
|
5300
|
+
/**
|
|
5301
|
+
* Create a permanent invite with no expiry or usage limit.
|
|
5302
|
+
*/
|
|
5303
|
+
setPermanent() {
|
|
5304
|
+
this._maxUses = null;
|
|
5305
|
+
this._expiresAt = null;
|
|
5306
|
+
return this;
|
|
5307
|
+
}
|
|
5308
|
+
/**
|
|
5309
|
+
* Build the `InviteCreateOptions` object.
|
|
5310
|
+
*/
|
|
5311
|
+
build() {
|
|
5312
|
+
return {
|
|
5313
|
+
...this._maxUses !== void 0 ? { maxUses: this._maxUses } : {},
|
|
5314
|
+
...this._expiresAt !== void 0 ? { expiresAt: this._expiresAt } : {}
|
|
5315
|
+
};
|
|
5316
|
+
}
|
|
5317
|
+
};
|
|
5318
|
+
|
|
5319
|
+
// src/builders/AutoModRuleBuilder.ts
|
|
5320
|
+
var AutoModRuleBuilder = class {
|
|
5321
|
+
constructor() {
|
|
5322
|
+
this._value = "";
|
|
5323
|
+
this._enabled = true;
|
|
5324
|
+
}
|
|
5325
|
+
/** Set the rule type explicitly. */
|
|
5326
|
+
setType(type) {
|
|
5327
|
+
this._type = type;
|
|
5328
|
+
return this;
|
|
5329
|
+
}
|
|
5330
|
+
/**
|
|
5331
|
+
* Set the blocked value (word or domain).
|
|
5332
|
+
*/
|
|
5333
|
+
setValue(value) {
|
|
5334
|
+
this._value = value;
|
|
5335
|
+
return this;
|
|
5336
|
+
}
|
|
5337
|
+
/**
|
|
5338
|
+
* Shorthand: set type to BLOCKED_WORD and optionally set the value.
|
|
5339
|
+
*
|
|
5340
|
+
* @example
|
|
5341
|
+
* builder.blockWord('badword')
|
|
5342
|
+
*/
|
|
5343
|
+
blockWord(word) {
|
|
5344
|
+
this._type = "BLOCKED_WORD";
|
|
5345
|
+
if (word !== void 0) this._value = word;
|
|
5346
|
+
return this;
|
|
5347
|
+
}
|
|
5348
|
+
/**
|
|
5349
|
+
* Shorthand: set type to BLOCKED_LINK and optionally set the domain.
|
|
5350
|
+
*
|
|
5351
|
+
* @example
|
|
5352
|
+
* builder.blockLink('spam.com')
|
|
5353
|
+
*/
|
|
5354
|
+
blockLink(domain) {
|
|
5355
|
+
this._type = "BLOCKED_LINK";
|
|
5356
|
+
if (domain !== void 0) this._value = domain;
|
|
5357
|
+
return this;
|
|
5358
|
+
}
|
|
5359
|
+
/**
|
|
5360
|
+
* Whether the rule is active immediately.
|
|
5361
|
+
* @default true
|
|
5362
|
+
*/
|
|
5363
|
+
setEnabled(enabled) {
|
|
5364
|
+
this._enabled = enabled;
|
|
5365
|
+
return this;
|
|
5366
|
+
}
|
|
5367
|
+
/**
|
|
5368
|
+
* Validate and build the `CreateAutoModRuleOptions` object.
|
|
5369
|
+
*
|
|
5370
|
+
* @throws if type or value is not set.
|
|
5371
|
+
*/
|
|
5372
|
+
build() {
|
|
5373
|
+
if (!this._type) {
|
|
5374
|
+
throw new Error("[AutoModRuleBuilder] type is required \u2014 call .setType(), .blockWord(), or .blockLink()");
|
|
5375
|
+
}
|
|
5376
|
+
if (!this._value.trim()) {
|
|
5377
|
+
throw new Error("[AutoModRuleBuilder] value is required \u2014 call .setValue(), .blockWord(word), or .blockLink(domain)");
|
|
5378
|
+
}
|
|
5379
|
+
return {
|
|
5380
|
+
type: this._type,
|
|
5381
|
+
value: this._value.trim(),
|
|
5382
|
+
enabled: this._enabled
|
|
5383
|
+
};
|
|
5384
|
+
}
|
|
5385
|
+
};
|
|
5386
|
+
|
|
5387
|
+
// src/builders/ContextMenuCommandBuilder.ts
|
|
5388
|
+
var ContextMenuCommandBuilder = class {
|
|
5389
|
+
constructor() {
|
|
5390
|
+
this._name = "";
|
|
5391
|
+
this._target = "MESSAGE";
|
|
5392
|
+
this._guildId = null;
|
|
5393
|
+
}
|
|
5394
|
+
/**
|
|
5395
|
+
* Display name of the command (shown in the context menu).
|
|
5396
|
+
* Max 32 characters. May contain spaces.
|
|
5397
|
+
*
|
|
5398
|
+
* @example
|
|
5399
|
+
* builder.setName('Report Message')
|
|
5400
|
+
*/
|
|
5401
|
+
setName(name) {
|
|
5402
|
+
this._name = name.trim();
|
|
5403
|
+
return this;
|
|
5404
|
+
}
|
|
5405
|
+
/**
|
|
5406
|
+
* Set whether the command targets messages or users.
|
|
5407
|
+
*
|
|
5408
|
+
* @example
|
|
5409
|
+
* builder.setTarget('USER')
|
|
5410
|
+
*/
|
|
5411
|
+
setTarget(target) {
|
|
5412
|
+
this._target = target;
|
|
5413
|
+
return this;
|
|
5414
|
+
}
|
|
5415
|
+
/**
|
|
5416
|
+
* Shorthand for `setTarget('MESSAGE')`.
|
|
5417
|
+
*
|
|
5418
|
+
* @example
|
|
5419
|
+
* builder.forMessages()
|
|
5420
|
+
*/
|
|
5421
|
+
forMessages() {
|
|
5422
|
+
return this.setTarget("MESSAGE");
|
|
5423
|
+
}
|
|
5424
|
+
/**
|
|
5425
|
+
* Shorthand for `setTarget('USER')`.
|
|
5426
|
+
*
|
|
5427
|
+
* @example
|
|
5428
|
+
* builder.forUsers()
|
|
5429
|
+
*/
|
|
5430
|
+
forUsers() {
|
|
5431
|
+
return this.setTarget("USER");
|
|
5432
|
+
}
|
|
5433
|
+
/**
|
|
5434
|
+
* Register this command only for a specific server (guild).
|
|
5435
|
+
* Omit (or pass `null`) to make the command available globally.
|
|
5436
|
+
*
|
|
5437
|
+
* @example
|
|
5438
|
+
* builder.setGuildId('server-id')
|
|
5439
|
+
*/
|
|
5440
|
+
setGuildId(guildId) {
|
|
5441
|
+
this._guildId = guildId;
|
|
5442
|
+
return this;
|
|
5443
|
+
}
|
|
5444
|
+
/**
|
|
5445
|
+
* Validate and return the final command definition object.
|
|
5446
|
+
* Throws if `name` has not been set.
|
|
5447
|
+
*/
|
|
5448
|
+
build() {
|
|
5449
|
+
if (!this._name) {
|
|
5450
|
+
throw new Error("[ContextMenuCommandBuilder] Command name is required");
|
|
5451
|
+
}
|
|
5452
|
+
return {
|
|
5453
|
+
name: this._name,
|
|
5454
|
+
target: this._target,
|
|
5455
|
+
guildId: this._guildId
|
|
5456
|
+
};
|
|
5457
|
+
}
|
|
5458
|
+
/** Alias for `build()`. */
|
|
5459
|
+
toJSON() {
|
|
5460
|
+
return this.build();
|
|
5461
|
+
}
|
|
5462
|
+
};
|
|
5463
|
+
|
|
5464
|
+
// src/utils/MessageCollector.ts
|
|
5465
|
+
var MessageCollector = class {
|
|
5466
|
+
constructor(emitter, options = {}) {
|
|
5467
|
+
this._emitter = emitter;
|
|
5468
|
+
this._options = {
|
|
5469
|
+
count: options.count ?? 1,
|
|
5470
|
+
timeout: options.timeout ?? 6e4,
|
|
5471
|
+
filter: options.filter ?? (() => true)
|
|
5472
|
+
};
|
|
5473
|
+
}
|
|
5474
|
+
/**
|
|
5475
|
+
* Start collecting messages.
|
|
5476
|
+
* Returns a promise that resolves with the collected `NovaMessage` array.
|
|
5477
|
+
*/
|
|
5478
|
+
run() {
|
|
5479
|
+
return new Promise((resolve) => {
|
|
5480
|
+
const collected = [];
|
|
5481
|
+
const cleanup = () => {
|
|
5482
|
+
clearTimeout(timer);
|
|
5483
|
+
this._emitter.off("messageCreate", handler);
|
|
5484
|
+
};
|
|
5485
|
+
const handler = (msg) => {
|
|
5486
|
+
if (!this._options.filter(msg)) return;
|
|
5487
|
+
collected.push(msg);
|
|
5488
|
+
if (collected.length >= this._options.count) {
|
|
5489
|
+
cleanup();
|
|
5490
|
+
resolve([...collected]);
|
|
5491
|
+
}
|
|
5492
|
+
};
|
|
5493
|
+
const timer = setTimeout(() => {
|
|
5494
|
+
this._emitter.off("messageCreate", handler);
|
|
5495
|
+
resolve([...collected]);
|
|
5496
|
+
}, this._options.timeout);
|
|
5497
|
+
this._emitter.on("messageCreate", handler);
|
|
5498
|
+
});
|
|
5499
|
+
}
|
|
5500
|
+
};
|
|
5501
|
+
|
|
5502
|
+
// src/utils/InteractionCollector.ts
|
|
5503
|
+
var InteractionCollector = class {
|
|
5504
|
+
constructor(emitter, options = {}) {
|
|
5505
|
+
this._emitter = emitter;
|
|
5506
|
+
this._options = {
|
|
5507
|
+
count: options.count ?? 1,
|
|
5508
|
+
timeout: options.timeout ?? 6e4,
|
|
5509
|
+
filter: options.filter ?? (() => true),
|
|
5510
|
+
messageId: options.messageId
|
|
5511
|
+
};
|
|
5512
|
+
}
|
|
5513
|
+
/**
|
|
5514
|
+
* Start collecting interactions.
|
|
5515
|
+
* Returns a promise that resolves with the collected `NovaInteraction` array.
|
|
5516
|
+
*/
|
|
5517
|
+
run() {
|
|
5518
|
+
return new Promise((resolve) => {
|
|
5519
|
+
const collected = [];
|
|
5520
|
+
const cleanup = () => {
|
|
5521
|
+
clearTimeout(timer);
|
|
5522
|
+
this._emitter.off("interactionCreate", handler);
|
|
5523
|
+
};
|
|
5524
|
+
const handler = (interaction) => {
|
|
5525
|
+
if (this._options.messageId && interaction.triggerMsgId !== this._options.messageId) return;
|
|
5526
|
+
if (!this._options.filter(interaction)) return;
|
|
5527
|
+
collected.push(interaction);
|
|
5528
|
+
if (collected.length >= this._options.count) {
|
|
5529
|
+
cleanup();
|
|
5530
|
+
resolve([...collected]);
|
|
5531
|
+
}
|
|
5532
|
+
};
|
|
5533
|
+
const timer = setTimeout(() => {
|
|
5534
|
+
this._emitter.off("interactionCreate", handler);
|
|
5535
|
+
resolve([...collected]);
|
|
5536
|
+
}, this._options.timeout);
|
|
5537
|
+
this._emitter.on("interactionCreate", handler);
|
|
5538
|
+
});
|
|
5539
|
+
}
|
|
5540
|
+
};
|
|
5541
|
+
|
|
5542
|
+
// src/utils/MentionParser.ts
|
|
5543
|
+
var MentionParser = class {
|
|
5544
|
+
// ─── Extraction ────────────────────────────────────────────────────────────
|
|
5545
|
+
/**
|
|
5546
|
+
* Extract all user IDs mentioned in the content (`<@userId>`).
|
|
5547
|
+
*
|
|
5548
|
+
* @example
|
|
5549
|
+
* MentionParser.userIds('Hello <@abc> and <@xyz>!') // ['abc', 'xyz']
|
|
5550
|
+
*/
|
|
5551
|
+
static userIds(content) {
|
|
5552
|
+
return [...content.matchAll(/<@([a-zA-Z0-9_-]+)>/g)].map((m) => m[1]);
|
|
5553
|
+
}
|
|
5554
|
+
/**
|
|
5555
|
+
* Extract all channel IDs mentioned in the content (`<#channelId>`).
|
|
5556
|
+
*
|
|
5557
|
+
* @example
|
|
5558
|
+
* MentionParser.channelIds('Go to <#general>!') // ['general']
|
|
5559
|
+
*/
|
|
5560
|
+
static channelIds(content) {
|
|
5561
|
+
return [...content.matchAll(/<#([a-zA-Z0-9_-]+)>/g)].map((m) => m[1]);
|
|
5562
|
+
}
|
|
5563
|
+
/**
|
|
5564
|
+
* Extract all role IDs mentioned in the content (`<&roleId>`).
|
|
5565
|
+
*
|
|
5566
|
+
* @example
|
|
5567
|
+
* MentionParser.roleIds('Paging <&mods>!') // ['mods']
|
|
5568
|
+
*/
|
|
5569
|
+
static roleIds(content) {
|
|
5570
|
+
return [...content.matchAll(/<&([a-zA-Z0-9_-]+)>/g)].map((m) => m[1]);
|
|
5571
|
+
}
|
|
5572
|
+
/**
|
|
5573
|
+
* Extract all custom emoji IDs mentioned in the content (`<:name:id>`).
|
|
5574
|
+
*
|
|
5575
|
+
* @example
|
|
5576
|
+
* MentionParser.emojiIds('Love it <:nova:12345>!')
|
|
5577
|
+
* // [{ name: 'nova', id: '12345' }]
|
|
5578
|
+
*/
|
|
5579
|
+
static emojiIds(content) {
|
|
5580
|
+
return [...content.matchAll(/<:([a-zA-Z0-9_]+):([a-zA-Z0-9_-]+)>/g)].map((m) => ({
|
|
5581
|
+
name: m[1],
|
|
5582
|
+
id: m[2]
|
|
5583
|
+
}));
|
|
5584
|
+
}
|
|
5585
|
+
// ─── Format ─────────────────────────────────────────────────────────────────
|
|
5586
|
+
/**
|
|
5587
|
+
* Format a user ID into a mention string `<@userId>`.
|
|
5588
|
+
*
|
|
5589
|
+
* @example
|
|
5590
|
+
* MentionParser.user('abc123') // '<@abc123>'
|
|
5591
|
+
*/
|
|
5592
|
+
static user(userId) {
|
|
5593
|
+
return `<@${userId}>`;
|
|
5594
|
+
}
|
|
5595
|
+
/**
|
|
5596
|
+
* Format a channel ID into a mention string `<#channelId>`.
|
|
5597
|
+
*
|
|
5598
|
+
* @example
|
|
5599
|
+
* MentionParser.channel('general') // '<#general>'
|
|
5600
|
+
*/
|
|
5601
|
+
static channel(channelId) {
|
|
5602
|
+
return `<#${channelId}>`;
|
|
5603
|
+
}
|
|
5604
|
+
/**
|
|
5605
|
+
* Format a role ID into a mention string `<&roleId>`.
|
|
5606
|
+
*
|
|
5607
|
+
* @example
|
|
5608
|
+
* MentionParser.role('mods') // '<&mods>'
|
|
5609
|
+
*/
|
|
5610
|
+
static role(roleId) {
|
|
5611
|
+
return `<&${roleId}>`;
|
|
5612
|
+
}
|
|
5613
|
+
/**
|
|
5614
|
+
* Format a custom emoji mention `<:name:id>`.
|
|
5615
|
+
*
|
|
5616
|
+
* @example
|
|
5617
|
+
* MentionParser.emoji('nova', '12345') // '<:nova:12345>'
|
|
5618
|
+
*/
|
|
5619
|
+
static emoji(name, id) {
|
|
5620
|
+
return `<:${name}:${id}>`;
|
|
5621
|
+
}
|
|
5622
|
+
// ─── Clean ──────────────────────────────────────────────────────────────────
|
|
5623
|
+
/**
|
|
5624
|
+
* Remove all mention tokens from `content` and return the cleaned string.
|
|
5625
|
+
*
|
|
5626
|
+
* @example
|
|
5627
|
+
* MentionParser.stripMentions('Hello <@u1> in <#c1>!')
|
|
5628
|
+
* // 'Hello in !'
|
|
5629
|
+
*/
|
|
5630
|
+
static stripMentions(content) {
|
|
5631
|
+
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, "");
|
|
5632
|
+
}
|
|
5633
|
+
/**
|
|
5634
|
+
* Returns `true` if `content` mentions at least one user.
|
|
5635
|
+
* Optionally checks for a specific `userId`.
|
|
5636
|
+
*
|
|
5637
|
+
* @example
|
|
5638
|
+
* MentionParser.hasMention('Hi <@u1>') // true
|
|
5639
|
+
* MentionParser.hasMention('Hi <@u1>', 'u1') // true
|
|
5640
|
+
* MentionParser.hasMention('Hi <@u1>', 'u2') // false
|
|
5641
|
+
*/
|
|
5642
|
+
static hasMention(content, userId) {
|
|
5643
|
+
if (userId) return content.includes(`<@${userId}>`);
|
|
5644
|
+
return /<@[a-zA-Z0-9_-]+>/.test(content);
|
|
5645
|
+
}
|
|
5646
|
+
/**
|
|
5647
|
+
* Returns `true` if `content` mentions a specific bot by its ID.
|
|
5648
|
+
*
|
|
5649
|
+
* @example
|
|
5650
|
+
* if (MentionParser.mentionsBotId(message.content, client.user.id)) {
|
|
5651
|
+
* await message.reply('You called?')
|
|
5652
|
+
* }
|
|
5653
|
+
*/
|
|
5654
|
+
static mentionsBotId(content, botId) {
|
|
5655
|
+
return content.includes(`<@${botId}>`);
|
|
5656
|
+
}
|
|
5657
|
+
};
|
|
5658
|
+
|
|
5659
|
+
// src/utils/EmbedPaginator.ts
|
|
5660
|
+
var EmbedPaginator = class {
|
|
5661
|
+
constructor(emitter, messages, interactions, options) {
|
|
5662
|
+
this.emitter = emitter;
|
|
5663
|
+
this.messages = messages;
|
|
5664
|
+
this.interactions = interactions;
|
|
5665
|
+
this.options = options;
|
|
5666
|
+
this.page = 0;
|
|
5667
|
+
const size = Math.max(1, options.pageSize ?? 10);
|
|
5668
|
+
const pages = [];
|
|
5669
|
+
for (let i = 0; i < options.items.length; i += size) {
|
|
5670
|
+
pages.push(options.items.slice(i, i + size));
|
|
5671
|
+
}
|
|
5672
|
+
this.pages = pages;
|
|
5673
|
+
}
|
|
5674
|
+
/** Total number of pages. */
|
|
5675
|
+
get totalPages() {
|
|
5676
|
+
return this.pages.length;
|
|
5677
|
+
}
|
|
5678
|
+
/**
|
|
5679
|
+
* Send the first page to the configured channel.
|
|
5680
|
+
* If there are multiple pages, attaches ◀ ▶ navigation buttons and
|
|
5681
|
+
* listens for clicks for up to `options.timeout` ms.
|
|
5682
|
+
*/
|
|
5683
|
+
async send() {
|
|
5684
|
+
if (this.pages.length === 0) {
|
|
5685
|
+
await this.messages.send(this.options.channelId, { content: "*Nothing to display.*" });
|
|
5686
|
+
return;
|
|
5687
|
+
}
|
|
5688
|
+
const msg = await this.messages.send(this.options.channelId, this._buildPayload());
|
|
5689
|
+
if (this.pages.length <= 1) return;
|
|
5690
|
+
const timeout = this.options.timeout ?? 6e4;
|
|
5691
|
+
const deadline = Date.now() + timeout;
|
|
5692
|
+
while (true) {
|
|
5693
|
+
const remaining = deadline - Date.now();
|
|
5694
|
+
if (remaining <= 0) break;
|
|
5695
|
+
const click = await this._awaitClick(msg.id, remaining);
|
|
5696
|
+
if (!click) break;
|
|
5697
|
+
if (click.customId === "_pg_prev" && this.page > 0) this.page--;
|
|
5698
|
+
else if (click.customId === "_pg_next" && this.page < this.pages.length - 1) this.page++;
|
|
5699
|
+
this.interactions.respond(click.id, { content: "\u200B", ephemeral: true }).catch(() => void 0);
|
|
5700
|
+
await this.messages.edit(msg.id, this._buildPayload());
|
|
5701
|
+
}
|
|
5702
|
+
}
|
|
5703
|
+
// ── Internal helpers ────────────────────────────────────────────────────────
|
|
5704
|
+
_awaitClick(msgId, timeoutMs) {
|
|
5705
|
+
return new Promise((resolve) => {
|
|
5706
|
+
const timer = setTimeout(() => {
|
|
5707
|
+
this.emitter.off("interactionCreate", handler);
|
|
5708
|
+
resolve(null);
|
|
5709
|
+
}, timeoutMs);
|
|
5710
|
+
const handler = (i) => {
|
|
5711
|
+
const isOurs = i.triggerMsgId === msgId && (i.customId === "_pg_prev" || i.customId === "_pg_next");
|
|
5712
|
+
const isAllowed = !this.options.userId || i.userId === this.options.userId;
|
|
5713
|
+
if (isOurs && isAllowed) {
|
|
5714
|
+
clearTimeout(timer);
|
|
5715
|
+
this.emitter.off("interactionCreate", handler);
|
|
5716
|
+
resolve({ id: i.id, customId: i.customId });
|
|
5717
|
+
}
|
|
5718
|
+
};
|
|
5719
|
+
this.emitter.on("interactionCreate", handler);
|
|
5720
|
+
});
|
|
5721
|
+
}
|
|
5722
|
+
_buildPayload() {
|
|
5723
|
+
const page = this.pages[this.page];
|
|
5724
|
+
const embed = this.options.renderPage(page, this.page, this.pages.length);
|
|
5725
|
+
if (this.options.showPageCounter !== false && this.pages.length > 1) {
|
|
5726
|
+
const counter = `Page ${this.page + 1} / ${this.pages.length}`;
|
|
5727
|
+
embed.footer = embed.footer ? `${embed.footer} \u2022 ${counter}` : counter;
|
|
5728
|
+
}
|
|
5729
|
+
const row = new ActionRowBuilder().addComponent(
|
|
5730
|
+
new ButtonBuilder().setCustomId("_pg_prev").setLabel("\u25C0").setStyle("secondary").setDisabled(this.page === 0)
|
|
5731
|
+
).addComponent(
|
|
5732
|
+
new ButtonBuilder().setCustomId("_pg_next").setLabel("\u25B6").setStyle("secondary").setDisabled(this.page === this.pages.length - 1)
|
|
5733
|
+
);
|
|
5734
|
+
return { embed, components: row.toJSON() };
|
|
5735
|
+
}
|
|
5736
|
+
};
|
|
5737
|
+
|
|
5738
|
+
// src/utils/ConfirmationDialog.ts
|
|
5739
|
+
var ConfirmationDialog = class {
|
|
5740
|
+
constructor(emitter, messages, interactions) {
|
|
5741
|
+
this.emitter = emitter;
|
|
5742
|
+
this.messages = messages;
|
|
5743
|
+
this.interactions = interactions;
|
|
5744
|
+
}
|
|
5745
|
+
/**
|
|
5746
|
+
* Send the confirmation prompt and await the user's choice.
|
|
5747
|
+
* Returns `true` for confirm, `false` for cancel or timeout.
|
|
5748
|
+
*/
|
|
5749
|
+
async ask(options) {
|
|
5750
|
+
const content = options.content ?? "Are you sure?";
|
|
5751
|
+
const confirmLabel = options.confirmLabel ?? "Confirm";
|
|
5752
|
+
const cancelLabel = options.cancelLabel ?? "Cancel";
|
|
5753
|
+
const timeout = options.timeout ?? 3e4;
|
|
5754
|
+
const row = new ActionRowBuilder().addComponent(
|
|
5755
|
+
new ButtonBuilder().setCustomId("_confirm_yes").setLabel(confirmLabel).setStyle("success")
|
|
5756
|
+
).addComponent(
|
|
5757
|
+
new ButtonBuilder().setCustomId("_confirm_no").setLabel(cancelLabel).setStyle("danger")
|
|
5758
|
+
);
|
|
5759
|
+
const msg = await this.messages.send(options.channelId, {
|
|
5760
|
+
content,
|
|
5761
|
+
components: row.toJSON()
|
|
5762
|
+
});
|
|
5763
|
+
const click = await this._awaitClick(msg.id, options.userId, timeout);
|
|
5764
|
+
const disabledRow = new ActionRowBuilder().addComponent(
|
|
5765
|
+
new ButtonBuilder().setCustomId("_confirm_yes").setLabel(confirmLabel).setStyle("success").setDisabled(true)
|
|
5766
|
+
).addComponent(
|
|
5767
|
+
new ButtonBuilder().setCustomId("_confirm_no").setLabel(cancelLabel).setStyle("danger").setDisabled(true)
|
|
5768
|
+
);
|
|
5769
|
+
await this.messages.edit(msg.id, { content, components: disabledRow.toJSON() }).catch(() => void 0);
|
|
5770
|
+
if (click) {
|
|
5771
|
+
this.interactions.respond(click.id, { content: "\u200B", ephemeral: true }).catch(() => void 0);
|
|
5772
|
+
return click.customId === "_confirm_yes";
|
|
5773
|
+
}
|
|
5774
|
+
return false;
|
|
5775
|
+
}
|
|
5776
|
+
_awaitClick(msgId, userId, timeoutMs) {
|
|
5777
|
+
return new Promise((resolve) => {
|
|
5778
|
+
const timer = setTimeout(() => {
|
|
5779
|
+
this.emitter.off("interactionCreate", handler);
|
|
5780
|
+
resolve(null);
|
|
5781
|
+
}, timeoutMs);
|
|
5782
|
+
const handler = (i) => {
|
|
5783
|
+
const isOurs = i.triggerMsgId === msgId && (i.customId === "_confirm_yes" || i.customId === "_confirm_no");
|
|
5784
|
+
const isAllowed = !userId || i.userId === userId;
|
|
5785
|
+
if (isOurs && isAllowed && typeof i.customId === "string") {
|
|
5786
|
+
clearTimeout(timer);
|
|
5787
|
+
this.emitter.off("interactionCreate", handler);
|
|
5788
|
+
resolve({ id: i.id, customId: i.customId });
|
|
5789
|
+
}
|
|
5790
|
+
};
|
|
5791
|
+
this.emitter.on("interactionCreate", handler);
|
|
5792
|
+
});
|
|
5793
|
+
}
|
|
5794
|
+
};
|
|
5795
|
+
|
|
5796
|
+
// src/utils/ArgumentParser.ts
|
|
5797
|
+
var ParsedArguments = class {
|
|
5798
|
+
constructor(positional, flags) {
|
|
5799
|
+
this.positional = positional;
|
|
5800
|
+
this._flags = flags;
|
|
5801
|
+
}
|
|
5802
|
+
/**
|
|
5803
|
+
* Whether a named flag is present (regardless of value).
|
|
5804
|
+
*
|
|
5805
|
+
* @example
|
|
5806
|
+
* args.has('notify') // true for --notify or --notify true
|
|
5807
|
+
*/
|
|
5808
|
+
has(flag) {
|
|
5809
|
+
return this._flags.has(flag);
|
|
5810
|
+
}
|
|
5811
|
+
/**
|
|
5812
|
+
* Get the string value of a named flag, or `null` if absent.
|
|
5813
|
+
*
|
|
5814
|
+
* @example
|
|
5815
|
+
* args.get('reason') // 'rule violation'
|
|
5816
|
+
*/
|
|
5817
|
+
get(flag) {
|
|
5818
|
+
return this._flags.get(flag) ?? null;
|
|
5819
|
+
}
|
|
5820
|
+
/**
|
|
5821
|
+
* Get the string value of a named flag, throwing if missing.
|
|
5822
|
+
*
|
|
5823
|
+
* @example
|
|
5824
|
+
* const reason = args.require('reason') // throws if --reason not supplied
|
|
5825
|
+
*/
|
|
5826
|
+
require(flag) {
|
|
5827
|
+
const v = this._flags.get(flag);
|
|
5828
|
+
if (v == null) throw new Error(`[ArgumentParser] Required flag "--${flag}" is missing`);
|
|
5829
|
+
return v;
|
|
5830
|
+
}
|
|
5831
|
+
/**
|
|
5832
|
+
* Parse the flag value as an integer, or `null` if absent / not a number.
|
|
5833
|
+
*
|
|
5834
|
+
* @example
|
|
5835
|
+
* args.getInt('days') // 7 from '--days 7'
|
|
5836
|
+
*/
|
|
5837
|
+
getInt(flag) {
|
|
5838
|
+
const v = this._flags.get(flag);
|
|
5839
|
+
if (v == null) return null;
|
|
5840
|
+
const n = parseInt(v, 10);
|
|
5841
|
+
return isNaN(n) ? null : n;
|
|
5842
|
+
}
|
|
5843
|
+
/**
|
|
5844
|
+
* Parse the flag value as a float, or `null` if absent / not a number.
|
|
5845
|
+
*/
|
|
5846
|
+
getFloat(flag) {
|
|
5847
|
+
const v = this._flags.get(flag);
|
|
5848
|
+
if (v == null) return null;
|
|
5849
|
+
const n = parseFloat(v);
|
|
5850
|
+
return isNaN(n) ? null : n;
|
|
5851
|
+
}
|
|
5852
|
+
/**
|
|
5853
|
+
* Get a positional argument by index (0-based), or `null` if absent.
|
|
5854
|
+
*
|
|
5855
|
+
* @example
|
|
5856
|
+
* args.at(0) // first positional token
|
|
5857
|
+
*/
|
|
5858
|
+
at(index) {
|
|
5859
|
+
return this.positional[index] ?? null;
|
|
5860
|
+
}
|
|
5861
|
+
/**
|
|
5862
|
+
* Get positional argument at `index` as an integer, or `null`.
|
|
5863
|
+
*
|
|
5864
|
+
* @example
|
|
5865
|
+
* args.getInt(1) // 7 when positional is ['nick123', '7']
|
|
5866
|
+
*/
|
|
5867
|
+
getIntAt(index) {
|
|
5868
|
+
const v = this.positional[index];
|
|
5869
|
+
if (v == null) return null;
|
|
5870
|
+
const n = parseInt(v, 10);
|
|
5871
|
+
return isNaN(n) ? null : n;
|
|
5872
|
+
}
|
|
5873
|
+
/**
|
|
5874
|
+
* All flag names that were parsed.
|
|
5875
|
+
*
|
|
5876
|
+
* @example
|
|
5877
|
+
* args.flags() // ['reason', 'notify']
|
|
5878
|
+
*/
|
|
5879
|
+
flags() {
|
|
5880
|
+
return [...this._flags.keys()];
|
|
5881
|
+
}
|
|
5882
|
+
toJSON() {
|
|
5883
|
+
return {
|
|
5884
|
+
positional: this.positional,
|
|
5885
|
+
flags: Object.fromEntries(this._flags)
|
|
5886
|
+
};
|
|
5887
|
+
}
|
|
5888
|
+
};
|
|
5889
|
+
var ArgumentParser = class _ArgumentParser {
|
|
5890
|
+
/**
|
|
5891
|
+
* Parse a raw string of arguments.
|
|
5892
|
+
* Returns a `ParsedArguments` instance.
|
|
5893
|
+
*/
|
|
5894
|
+
static parse(input) {
|
|
5895
|
+
const positional = [];
|
|
5896
|
+
const flags = /* @__PURE__ */ new Map();
|
|
5897
|
+
const tokens = _ArgumentParser._tokenise(input);
|
|
5898
|
+
let i = 0;
|
|
5899
|
+
while (i < tokens.length) {
|
|
5900
|
+
const token = tokens[i];
|
|
5901
|
+
if (token.startsWith("--")) {
|
|
5902
|
+
const key = token.slice(2);
|
|
5903
|
+
const next = tokens[i + 1];
|
|
5904
|
+
if (next && !next.startsWith("-")) {
|
|
5905
|
+
flags.set(key, next);
|
|
5906
|
+
i += 2;
|
|
5907
|
+
} else {
|
|
5908
|
+
flags.set(key, "true");
|
|
5909
|
+
i++;
|
|
5910
|
+
}
|
|
5911
|
+
} else if (token.startsWith("-") && token.length === 2) {
|
|
5912
|
+
flags.set(token.slice(1), "true");
|
|
5913
|
+
i++;
|
|
5914
|
+
} else {
|
|
5915
|
+
positional.push(token);
|
|
5916
|
+
i++;
|
|
5917
|
+
}
|
|
5918
|
+
}
|
|
5919
|
+
return new ParsedArguments(positional, flags);
|
|
5920
|
+
}
|
|
5921
|
+
/**
|
|
5922
|
+
* Strip a user mention like `<@123456>` from the start of the input string
|
|
5923
|
+
* and return the remainder trimmed. Returns the original string unchanged if
|
|
5924
|
+
* no leading mention is found.
|
|
5925
|
+
*
|
|
5926
|
+
* @example
|
|
5927
|
+
* ArgumentParser.stripMention('<@98765> hello world') // 'hello world'
|
|
5928
|
+
*/
|
|
5929
|
+
static stripMention(input) {
|
|
5930
|
+
return input.replace(/^<@!?\d+>\s*/, "").trim();
|
|
5931
|
+
}
|
|
5932
|
+
/**
|
|
5933
|
+
* Split `input` into tokens. Quoted strings are treated as single tokens.
|
|
5934
|
+
*/
|
|
5935
|
+
static _tokenise(input) {
|
|
5936
|
+
const tokens = [];
|
|
5937
|
+
let current = "";
|
|
5938
|
+
let inDouble = false;
|
|
5939
|
+
let inSingle = false;
|
|
5940
|
+
for (let i = 0; i < input.length; i++) {
|
|
5941
|
+
const ch = input[i];
|
|
5942
|
+
if (ch === '"' && !inSingle) {
|
|
5943
|
+
inDouble = !inDouble;
|
|
5944
|
+
continue;
|
|
5945
|
+
}
|
|
5946
|
+
if (ch === "'" && !inDouble) {
|
|
5947
|
+
inSingle = !inSingle;
|
|
5948
|
+
continue;
|
|
5949
|
+
}
|
|
5950
|
+
if (ch === " " && !inDouble && !inSingle) {
|
|
5951
|
+
if (current.length > 0) {
|
|
5952
|
+
tokens.push(current);
|
|
5953
|
+
current = "";
|
|
5954
|
+
}
|
|
5955
|
+
continue;
|
|
5956
|
+
}
|
|
5957
|
+
current += ch;
|
|
5958
|
+
}
|
|
5959
|
+
if (current.length > 0) tokens.push(current);
|
|
5960
|
+
return tokens;
|
|
5961
|
+
}
|
|
5962
|
+
};
|
|
5963
|
+
|
|
5964
|
+
// src/utils/CacheMap.ts
|
|
5965
|
+
var CacheMap = class {
|
|
5966
|
+
constructor() {
|
|
5967
|
+
this._store = /* @__PURE__ */ new Map();
|
|
5968
|
+
}
|
|
5969
|
+
/**
|
|
5970
|
+
* Store a value.
|
|
5971
|
+
*
|
|
5972
|
+
* @param key - Cache key.
|
|
5973
|
+
* @param value - Value to store.
|
|
5974
|
+
* @param ttlMs - Optional time-to-live in milliseconds. Omit for permanent storage.
|
|
5975
|
+
*/
|
|
5976
|
+
set(key, value, ttlMs) {
|
|
5977
|
+
this._store.set(key, {
|
|
5978
|
+
value,
|
|
5979
|
+
expiresAt: ttlMs != null ? Date.now() + ttlMs : null
|
|
5980
|
+
});
|
|
5981
|
+
return this;
|
|
5982
|
+
}
|
|
5983
|
+
/**
|
|
5984
|
+
* Retrieve a value, or `undefined` if absent or expired.
|
|
5985
|
+
* Expired entries are automatically deleted on access.
|
|
5986
|
+
*/
|
|
5987
|
+
get(key) {
|
|
5988
|
+
const entry = this._store.get(key);
|
|
5989
|
+
if (!entry) return void 0;
|
|
5990
|
+
if (entry.expiresAt !== null && Date.now() >= entry.expiresAt) {
|
|
5991
|
+
this._store.delete(key);
|
|
5992
|
+
return void 0;
|
|
5993
|
+
}
|
|
5994
|
+
return entry.value;
|
|
5995
|
+
}
|
|
5996
|
+
/**
|
|
5997
|
+
* Check if a live (non-expired) entry exists for this key.
|
|
5998
|
+
*/
|
|
5999
|
+
has(key) {
|
|
6000
|
+
return this.get(key) !== void 0;
|
|
6001
|
+
}
|
|
6002
|
+
/**
|
|
6003
|
+
* Delete an entry.
|
|
6004
|
+
* Returns `true` if the entry existed (even if it was expired).
|
|
6005
|
+
*/
|
|
6006
|
+
delete(key) {
|
|
6007
|
+
return this._store.delete(key);
|
|
6008
|
+
}
|
|
6009
|
+
/** Remove all entries. */
|
|
6010
|
+
clear() {
|
|
6011
|
+
this._store.clear();
|
|
6012
|
+
}
|
|
6013
|
+
/**
|
|
6014
|
+
* Number of **live** (non-expired) entries.
|
|
6015
|
+
* This prunes expired entries as a side-effect.
|
|
6016
|
+
*/
|
|
6017
|
+
get size() {
|
|
6018
|
+
this.prune();
|
|
6019
|
+
return this._store.size;
|
|
6020
|
+
}
|
|
6021
|
+
/**
|
|
6022
|
+
* Remove all expired entries immediately.
|
|
6023
|
+
* Useful to call occasionally to free memory.
|
|
6024
|
+
*
|
|
6025
|
+
* @example
|
|
6026
|
+
* // Prune every 10 minutes
|
|
6027
|
+
* setInterval(() => cache.prune(), 10 * 60_000)
|
|
6028
|
+
*/
|
|
6029
|
+
prune() {
|
|
6030
|
+
const now = Date.now();
|
|
6031
|
+
let removed = 0;
|
|
6032
|
+
for (const [key, entry] of this._store) {
|
|
6033
|
+
if (entry.expiresAt !== null && now >= entry.expiresAt) {
|
|
6034
|
+
this._store.delete(key);
|
|
6035
|
+
removed++;
|
|
6036
|
+
}
|
|
6037
|
+
}
|
|
6038
|
+
return removed;
|
|
6039
|
+
}
|
|
6040
|
+
/**
|
|
6041
|
+
* Return all **live** keys.
|
|
6042
|
+
*/
|
|
6043
|
+
keys() {
|
|
6044
|
+
this.prune();
|
|
6045
|
+
return [...this._store.keys()];
|
|
6046
|
+
}
|
|
6047
|
+
/**
|
|
6048
|
+
* Return all **live** values.
|
|
6049
|
+
*/
|
|
6050
|
+
values() {
|
|
6051
|
+
this.prune();
|
|
6052
|
+
return [...this._store.values()].map((e) => e.value);
|
|
6053
|
+
}
|
|
6054
|
+
/**
|
|
6055
|
+
* Return all **live** `[key, value]` pairs.
|
|
6056
|
+
*/
|
|
6057
|
+
entries() {
|
|
6058
|
+
this.prune();
|
|
6059
|
+
return [...this._store.entries()].map(([k, e]) => [k, e.value]);
|
|
6060
|
+
}
|
|
6061
|
+
/**
|
|
6062
|
+
* Get a value or compute & cache it if missing.
|
|
6063
|
+
* Very useful for "cache-aside" data loading.
|
|
6064
|
+
*
|
|
6065
|
+
* @param key - Cache key.
|
|
6066
|
+
* @param loader - Async function that produces the value when the cache is cold.
|
|
6067
|
+
* @param ttlMs - Optional TTL to apply to the newly cached value.
|
|
6068
|
+
*
|
|
6069
|
+
* @example
|
|
6070
|
+
* const profile = await cache.getOrSet(userId, () => client.fetchUserProfile(userId), 60_000)
|
|
6071
|
+
*/
|
|
6072
|
+
async getOrSet(key, loader, ttlMs) {
|
|
6073
|
+
const existing = this.get(key);
|
|
6074
|
+
if (existing !== void 0) return existing;
|
|
6075
|
+
const value = await loader();
|
|
6076
|
+
this.set(key, value, ttlMs);
|
|
6077
|
+
return value;
|
|
6078
|
+
}
|
|
6079
|
+
/**
|
|
6080
|
+
* Return the number of milliseconds until a key expires.
|
|
6081
|
+
* Returns `null` if the key has no TTL, `0` if it is already expired.
|
|
6082
|
+
*/
|
|
6083
|
+
ttl(key) {
|
|
6084
|
+
const entry = this._store.get(key);
|
|
6085
|
+
if (!entry) return null;
|
|
6086
|
+
if (entry.expiresAt === null) return null;
|
|
6087
|
+
return Math.max(0, entry.expiresAt - Date.now());
|
|
6088
|
+
}
|
|
6089
|
+
[Symbol.iterator]() {
|
|
6090
|
+
return this.entries()[Symbol.iterator]();
|
|
6091
|
+
}
|
|
6092
|
+
};
|
|
6093
|
+
|
|
6094
|
+
// src/utils/EventScheduler.ts
|
|
6095
|
+
var _idCounter = 0;
|
|
6096
|
+
function nextId() {
|
|
6097
|
+
return `task_${++_idCounter}`;
|
|
6098
|
+
}
|
|
6099
|
+
var EventScheduler = class {
|
|
6100
|
+
constructor() {
|
|
6101
|
+
this._tasks = /* @__PURE__ */ new Map();
|
|
6102
|
+
}
|
|
6103
|
+
/**
|
|
6104
|
+
* Schedule a one-shot task to run after `delayMs` milliseconds.
|
|
6105
|
+
* Returns a task ID that can be passed to `cancel()`.
|
|
6106
|
+
*
|
|
6107
|
+
* @example
|
|
6108
|
+
* const id = scheduler.schedule(5_000, () => console.log('5 seconds later'))
|
|
6109
|
+
* // Change your mind:
|
|
6110
|
+
* scheduler.cancel(id)
|
|
6111
|
+
*/
|
|
6112
|
+
schedule(delayMs, fn) {
|
|
6113
|
+
const id = nextId();
|
|
6114
|
+
const handle = setTimeout(() => {
|
|
6115
|
+
this._tasks.delete(id);
|
|
6116
|
+
this._run(fn, id);
|
|
6117
|
+
}, delayMs);
|
|
6118
|
+
this._tasks.set(id, { id, handle, type: "once" });
|
|
6119
|
+
return id;
|
|
6120
|
+
}
|
|
6121
|
+
/**
|
|
6122
|
+
* Schedule a one-shot task to run at a specific `Date`.
|
|
6123
|
+
* If the date is in the past, the callback fires on the next event-loop tick.
|
|
6124
|
+
*
|
|
6125
|
+
* @example
|
|
6126
|
+
* const nextMidnight = new Date()
|
|
6127
|
+
* nextMidnight.setHours(24, 0, 0, 0)
|
|
6128
|
+
* scheduler.scheduleAt(nextMidnight, () => console.log('Midnight!'))
|
|
6129
|
+
*/
|
|
6130
|
+
scheduleAt(date, fn) {
|
|
6131
|
+
const delay = Math.max(0, date.getTime() - Date.now());
|
|
6132
|
+
return this.schedule(delay, fn);
|
|
6133
|
+
}
|
|
6134
|
+
/**
|
|
6135
|
+
* Schedule a recurring task that fires every `intervalMs` milliseconds.
|
|
6136
|
+
* Returns a task ID that can be passed to `cancel()`.
|
|
6137
|
+
*
|
|
6138
|
+
* @example
|
|
6139
|
+
* // Check incoming messages every minute
|
|
6140
|
+
* const id = scheduler.repeat(60_000, () => checkForAnnouncements())
|
|
6141
|
+
*/
|
|
6142
|
+
repeat(intervalMs, fn) {
|
|
6143
|
+
const id = nextId();
|
|
6144
|
+
const handle = setInterval(() => this._run(fn, id), intervalMs);
|
|
6145
|
+
this._tasks.set(id, { id, handle, type: "repeat" });
|
|
6146
|
+
return id;
|
|
6147
|
+
}
|
|
6148
|
+
/**
|
|
6149
|
+
* Cancel a previously scheduled task by its ID.
|
|
6150
|
+
* Returns `true` if the task existed, `false` if it had already fired or
|
|
6151
|
+
* was not found.
|
|
6152
|
+
*
|
|
6153
|
+
* @example
|
|
6154
|
+
* const id = scheduler.schedule(30_000, doSomething)
|
|
6155
|
+
* // …maybe cancel it:
|
|
6156
|
+
* scheduler.cancel(id)
|
|
6157
|
+
*/
|
|
6158
|
+
cancel(id) {
|
|
6159
|
+
const task = this._tasks.get(id);
|
|
6160
|
+
if (!task) return false;
|
|
6161
|
+
if (task.type === "once") clearTimeout(task.handle);
|
|
6162
|
+
else clearInterval(task.handle);
|
|
6163
|
+
this._tasks.delete(id);
|
|
6164
|
+
return true;
|
|
6165
|
+
}
|
|
6166
|
+
/**
|
|
6167
|
+
* Cancel all pending tasks.
|
|
6168
|
+
* Useful during graceful shutdown.
|
|
6169
|
+
*
|
|
6170
|
+
* @example
|
|
6171
|
+
* client.once('disconnect', () => scheduler.cancelAll())
|
|
6172
|
+
*/
|
|
6173
|
+
cancelAll() {
|
|
6174
|
+
for (const id of this._tasks.keys()) this.cancel(id);
|
|
6175
|
+
}
|
|
6176
|
+
/**
|
|
6177
|
+
* Number of currently active (pending or recurring) tasks.
|
|
6178
|
+
*/
|
|
6179
|
+
get pendingCount() {
|
|
6180
|
+
return this._tasks.size;
|
|
6181
|
+
}
|
|
6182
|
+
/**
|
|
6183
|
+
* IDs of all currently registered tasks.
|
|
6184
|
+
*/
|
|
6185
|
+
taskIds() {
|
|
6186
|
+
return [...this._tasks.keys()];
|
|
6187
|
+
}
|
|
6188
|
+
_run(fn, id) {
|
|
6189
|
+
try {
|
|
6190
|
+
const result = fn();
|
|
6191
|
+
if (result && typeof result.catch === "function") {
|
|
6192
|
+
;
|
|
6193
|
+
result.catch((e) => {
|
|
6194
|
+
console.error(`[EventScheduler] Task ${id} threw:`, e);
|
|
6195
|
+
});
|
|
6196
|
+
}
|
|
6197
|
+
} catch (e) {
|
|
6198
|
+
console.error(`[EventScheduler] Task ${id} threw:`, e);
|
|
6199
|
+
}
|
|
6200
|
+
}
|
|
6201
|
+
};
|
|
3741
6202
|
export {
|
|
3742
6203
|
ActionRowBuilder,
|
|
6204
|
+
ArgumentParser,
|
|
3743
6205
|
AuditLogAPI,
|
|
6206
|
+
AutoModAPI,
|
|
6207
|
+
AutoModRuleBuilder,
|
|
3744
6208
|
ButtonBuilder,
|
|
6209
|
+
CacheMap,
|
|
6210
|
+
CategoriesAPI,
|
|
6211
|
+
ChannelBuilder,
|
|
3745
6212
|
ChannelsAPI,
|
|
3746
6213
|
Collection,
|
|
3747
6214
|
CommandsAPI,
|
|
6215
|
+
ConfirmationDialog,
|
|
6216
|
+
ContextMenuCommandBuilder,
|
|
3748
6217
|
Cooldown,
|
|
3749
6218
|
CooldownManager,
|
|
3750
6219
|
EmbedBuilder,
|
|
6220
|
+
EmbedPaginator,
|
|
6221
|
+
EventBuilder,
|
|
6222
|
+
EventScheduler,
|
|
6223
|
+
EventsAPI,
|
|
6224
|
+
ForumAPI,
|
|
6225
|
+
ForumPostBuilder,
|
|
3751
6226
|
HttpClient,
|
|
6227
|
+
InteractionCollector,
|
|
3752
6228
|
InteractionOptions,
|
|
3753
6229
|
InteractionsAPI,
|
|
6230
|
+
InviteBuilder,
|
|
3754
6231
|
InvitesAPI,
|
|
3755
6232
|
Logger,
|
|
3756
6233
|
MembersAPI,
|
|
6234
|
+
MentionParser,
|
|
3757
6235
|
MessageBuilder,
|
|
6236
|
+
MessageCollector,
|
|
3758
6237
|
MessagesAPI,
|
|
3759
6238
|
ModalBuilder,
|
|
6239
|
+
NovaBan,
|
|
6240
|
+
NovaCategory,
|
|
3760
6241
|
NovaChannel,
|
|
3761
6242
|
NovaClient,
|
|
6243
|
+
NovaForumPost,
|
|
3762
6244
|
NovaInteraction,
|
|
3763
6245
|
NovaInvite,
|
|
3764
6246
|
NovaMember,
|
|
3765
6247
|
NovaMessage,
|
|
3766
6248
|
NovaRole,
|
|
6249
|
+
NovaServerEvent,
|
|
3767
6250
|
NovaServerWrapper,
|
|
6251
|
+
NovaWarning,
|
|
3768
6252
|
NovaWebhook,
|
|
3769
6253
|
Paginator,
|
|
6254
|
+
ParsedArguments,
|
|
3770
6255
|
Permissions,
|
|
3771
6256
|
PermissionsAPI,
|
|
3772
6257
|
PermissionsBitfield,
|
|
3773
6258
|
PollBuilder,
|
|
3774
6259
|
ReactionsAPI,
|
|
6260
|
+
RoleBuilder,
|
|
3775
6261
|
RolesAPI,
|
|
3776
6262
|
SelectMenuBuilder,
|
|
3777
6263
|
ServersAPI,
|
|
3778
6264
|
SlashCommandBuilder,
|
|
3779
6265
|
SlashCommandOptionBuilder,
|
|
3780
6266
|
TextInputBuilder,
|
|
6267
|
+
UsersAPI,
|
|
3781
6268
|
WebhooksAPI,
|
|
3782
6269
|
countdown,
|
|
3783
6270
|
formatDuration,
|