novaapp-sdk 1.3.3 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +817 -17
- package/dist/index.d.ts +817 -17
- package/dist/index.js +783 -5
- package/dist/index.mjs +775 -5
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -253,16 +253,33 @@ var MembersAPI = class {
|
|
|
253
253
|
}
|
|
254
254
|
/**
|
|
255
255
|
* Fetch members of a server the bot is in.
|
|
256
|
+
* Supports filtering by role and searching by username/displayName.
|
|
256
257
|
*
|
|
257
258
|
* @example
|
|
258
259
|
* const members = await client.members.list('server-id', { limit: 50 })
|
|
260
|
+
* const admins = await client.members.list('server-id', { role: 'ADMIN' })
|
|
261
|
+
* const found = await client.members.list('server-id', { search: 'john' })
|
|
259
262
|
*/
|
|
260
263
|
list(serverId, options = {}) {
|
|
261
264
|
const params = new URLSearchParams();
|
|
262
265
|
if (options.limit) params.set("limit", String(options.limit));
|
|
266
|
+
if (options.role) params.set("role", options.role);
|
|
267
|
+
if (options.search) params.set("search", options.search);
|
|
263
268
|
const qs = params.toString();
|
|
264
269
|
return this.http.get(`/servers/${serverId}/members${qs ? `?${qs}` : ""}`);
|
|
265
270
|
}
|
|
271
|
+
/**
|
|
272
|
+
* Fetch a single member from a server by their user ID.
|
|
273
|
+
* Returns the member's custom role if assigned.
|
|
274
|
+
* Requires the `members.read` scope.
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* const member = await client.members.fetch('server-id', 'user-id')
|
|
278
|
+
* console.log(member.customRole?.name ?? 'No custom role')
|
|
279
|
+
*/
|
|
280
|
+
fetch(serverId, userId) {
|
|
281
|
+
return this.http.get(`/servers/${serverId}/members/${userId}`);
|
|
282
|
+
}
|
|
266
283
|
/**
|
|
267
284
|
* Kick a member from a server.
|
|
268
285
|
* Bots cannot kick owners or admins (403 will be thrown).
|
|
@@ -355,6 +372,27 @@ var ServersAPI = class {
|
|
|
355
372
|
list() {
|
|
356
373
|
return this.http.get("/servers");
|
|
357
374
|
}
|
|
375
|
+
/**
|
|
376
|
+
* Fetch a single server by ID.
|
|
377
|
+
* The bot must be a member of the server.
|
|
378
|
+
*
|
|
379
|
+
* @example
|
|
380
|
+
* const server = await client.servers.fetch('server-id')
|
|
381
|
+
* console.log(`${server.name} — ${server.memberCount} members`)
|
|
382
|
+
*/
|
|
383
|
+
fetch(serverId) {
|
|
384
|
+
return this.http.get(`/servers/${serverId}`);
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Update a server's name, description, or icon.
|
|
388
|
+
* Requires the `server.manage` scope.
|
|
389
|
+
*
|
|
390
|
+
* @example
|
|
391
|
+
* await client.servers.update('server-id', { name: 'Better Name', description: 'A great place!' })
|
|
392
|
+
*/
|
|
393
|
+
update(serverId, options) {
|
|
394
|
+
return this.http.patch(`/servers/${serverId}`, options);
|
|
395
|
+
}
|
|
358
396
|
/**
|
|
359
397
|
* Fetch all roles in a server.
|
|
360
398
|
* Results are sorted by position (lowest first).
|
|
@@ -404,6 +442,26 @@ var InteractionsAPI = class {
|
|
|
404
442
|
options
|
|
405
443
|
);
|
|
406
444
|
}
|
|
445
|
+
/**
|
|
446
|
+
* Respond to an autocomplete interaction with a list of suggested choices.
|
|
447
|
+
* The gateway fires `AUTOCOMPLETE` interactions when a user is typing in
|
|
448
|
+
* an option that has `autocomplete: true`. Call this within ~3 seconds.
|
|
449
|
+
*
|
|
450
|
+
* @example
|
|
451
|
+
* client.autocomplete('search', async (interaction) => {
|
|
452
|
+
* const query = interaction.autocomplete?.value ?? ''
|
|
453
|
+
* const results = await db.search(query)
|
|
454
|
+
* await client.interactions.autocomplete(interaction.id,
|
|
455
|
+
* results.map(r => ({ name: r.title, value: r.id }))
|
|
456
|
+
* )
|
|
457
|
+
* })
|
|
458
|
+
*/
|
|
459
|
+
autocomplete(interactionId, choices) {
|
|
460
|
+
return this.http.post(
|
|
461
|
+
`/interactions/${interactionId}/autocomplete`,
|
|
462
|
+
{ choices }
|
|
463
|
+
);
|
|
464
|
+
}
|
|
407
465
|
/**
|
|
408
466
|
* Poll for pending (unacknowledged) interactions.
|
|
409
467
|
* Useful when not using the WebSocket gateway.
|
|
@@ -698,6 +756,239 @@ var ReactionsAPI = class {
|
|
|
698
756
|
}
|
|
699
757
|
};
|
|
700
758
|
|
|
759
|
+
// src/api/roles.ts
|
|
760
|
+
var RolesAPI = class {
|
|
761
|
+
constructor(http) {
|
|
762
|
+
this.http = http;
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* List all custom roles in a server, sorted by position.
|
|
766
|
+
*
|
|
767
|
+
* @example
|
|
768
|
+
* const roles = await client.roles.list('server-id')
|
|
769
|
+
* const adminRole = roles.find(r => r.name === 'Admin')
|
|
770
|
+
*/
|
|
771
|
+
list(serverId) {
|
|
772
|
+
return this.http.get(`/servers/${serverId}/roles`);
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Create a new custom role in a server.
|
|
776
|
+
* Requires the `roles.manage` scope.
|
|
777
|
+
*
|
|
778
|
+
* @example
|
|
779
|
+
* const role = await client.roles.create('server-id', {
|
|
780
|
+
* name: 'Verified',
|
|
781
|
+
* color: '#00d4ff',
|
|
782
|
+
* hoist: true,
|
|
783
|
+
* permissions: { sendMessages: true, addReactions: true },
|
|
784
|
+
* })
|
|
785
|
+
*/
|
|
786
|
+
create(serverId, options) {
|
|
787
|
+
return this.http.post(`/servers/${serverId}/roles`, options);
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Edit an existing role.
|
|
791
|
+
* Requires the `roles.manage` scope.
|
|
792
|
+
*
|
|
793
|
+
* @example
|
|
794
|
+
* await client.roles.edit('role-id', { color: '#ff0000', name: 'Danger' })
|
|
795
|
+
*/
|
|
796
|
+
edit(roleId, options) {
|
|
797
|
+
return this.http.patch(`/roles/${roleId}`, options);
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Delete a custom role from a server.
|
|
801
|
+
* Any member currently assigned this role will have it removed automatically.
|
|
802
|
+
* Requires the `roles.manage` scope.
|
|
803
|
+
*
|
|
804
|
+
* @example
|
|
805
|
+
* await client.roles.delete('role-id')
|
|
806
|
+
*/
|
|
807
|
+
delete(roleId) {
|
|
808
|
+
return this.http.delete(`/roles/${roleId}`);
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Assign a custom role to a server member.
|
|
812
|
+
* Requires the `members.roles` scope.
|
|
813
|
+
*
|
|
814
|
+
* @example
|
|
815
|
+
* await client.roles.assign('server-id', 'user-id', 'role-id')
|
|
816
|
+
*/
|
|
817
|
+
assign(serverId, userId, roleId) {
|
|
818
|
+
return this.http.post(`/servers/${serverId}/members/${userId}/roles/${roleId}`);
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Remove a custom role from a server member.
|
|
822
|
+
* Requires the `members.roles` scope.
|
|
823
|
+
*
|
|
824
|
+
* @example
|
|
825
|
+
* await client.roles.remove('server-id', 'user-id', 'role-id')
|
|
826
|
+
*/
|
|
827
|
+
remove(serverId, userId, roleId) {
|
|
828
|
+
return this.http.delete(`/servers/${serverId}/members/${userId}/roles/${roleId}`);
|
|
829
|
+
}
|
|
830
|
+
};
|
|
831
|
+
|
|
832
|
+
// src/api/invites.ts
|
|
833
|
+
var InvitesAPI = class {
|
|
834
|
+
constructor(http) {
|
|
835
|
+
this.http = http;
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* List all active invites for a server.
|
|
839
|
+
*
|
|
840
|
+
* @example
|
|
841
|
+
* const invites = await client.invites.list('server-id')
|
|
842
|
+
* console.log(`${invites.length} active invites`)
|
|
843
|
+
*/
|
|
844
|
+
list(serverId) {
|
|
845
|
+
return this.http.get(`/servers/${serverId}/invites`);
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Create a new invite for a server.
|
|
849
|
+
* Requires the `invites.manage` scope.
|
|
850
|
+
*
|
|
851
|
+
* @example
|
|
852
|
+
* // A one-time invite that expires in 24 hours
|
|
853
|
+
* const invite = await client.invites.create('server-id', {
|
|
854
|
+
* maxUses: 1,
|
|
855
|
+
* expiresAt: new Date(Date.now() + 86_400_000).toISOString(),
|
|
856
|
+
* })
|
|
857
|
+
* console.log(`https://novachatapp.com/invite/${invite.code}`)
|
|
858
|
+
*/
|
|
859
|
+
create(serverId, options = {}) {
|
|
860
|
+
return this.http.post(`/servers/${serverId}/invites`, options);
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Fetch a single invite by its code.
|
|
864
|
+
*
|
|
865
|
+
* @example
|
|
866
|
+
* const invite = await client.invites.fetch('abc123')
|
|
867
|
+
* console.log(`${invite.uses} / ${invite.maxUses ?? '∞'} uses`)
|
|
868
|
+
*/
|
|
869
|
+
fetch(code) {
|
|
870
|
+
return this.http.get(`/invites/${code}`);
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Revoke (delete) an invite.
|
|
874
|
+
* Requires the `invites.manage` scope.
|
|
875
|
+
*
|
|
876
|
+
* @example
|
|
877
|
+
* await client.invites.revoke('abc123')
|
|
878
|
+
*/
|
|
879
|
+
revoke(code) {
|
|
880
|
+
return this.http.delete(`/invites/${code}`);
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
|
|
884
|
+
// src/api/webhooks.ts
|
|
885
|
+
var WebhooksAPI = class {
|
|
886
|
+
constructor(http) {
|
|
887
|
+
this.http = http;
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* List all webhooks in a channel.
|
|
891
|
+
* Requires the `webhooks.manage` scope.
|
|
892
|
+
*
|
|
893
|
+
* @example
|
|
894
|
+
* const webhooks = await client.webhooks.list('channel-id')
|
|
895
|
+
*/
|
|
896
|
+
list(channelId) {
|
|
897
|
+
return this.http.get(`/channels/${channelId}/webhooks`);
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Create a webhook in a channel.
|
|
901
|
+
* Requires the `webhooks.manage` scope.
|
|
902
|
+
*
|
|
903
|
+
* @example
|
|
904
|
+
* const webhook = await client.webhooks.create('channel-id', { name: 'Announcements' })
|
|
905
|
+
* console.log('Token:', webhook.token)
|
|
906
|
+
*/
|
|
907
|
+
create(channelId, options) {
|
|
908
|
+
return this.http.post(`/channels/${channelId}/webhooks`, options);
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Fetch a webhook by its ID.
|
|
912
|
+
* Requires the `webhooks.manage` scope.
|
|
913
|
+
*
|
|
914
|
+
* @example
|
|
915
|
+
* const webhook = await client.webhooks.fetch('webhook-id')
|
|
916
|
+
*/
|
|
917
|
+
fetch(webhookId) {
|
|
918
|
+
return this.http.get(`/webhooks/${webhookId}`);
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Edit a webhook's name and/or target channel.
|
|
922
|
+
* Requires the `webhooks.manage` scope.
|
|
923
|
+
*
|
|
924
|
+
* @example
|
|
925
|
+
* await client.webhooks.edit('webhook-id', { name: 'New Name', channelId: 'other-channel' })
|
|
926
|
+
*/
|
|
927
|
+
edit(webhookId, options) {
|
|
928
|
+
return this.http.patch(`/webhooks/${webhookId}`, options);
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Delete a webhook.
|
|
932
|
+
* Requires the `webhooks.manage` scope.
|
|
933
|
+
*
|
|
934
|
+
* @example
|
|
935
|
+
* await client.webhooks.delete('webhook-id')
|
|
936
|
+
*/
|
|
937
|
+
delete(webhookId) {
|
|
938
|
+
return this.http.delete(`/webhooks/${webhookId}`);
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Execute a webhook — post a message to the webhook's channel.
|
|
942
|
+
* Does not require any extra scope (uses the webhook data already owned by
|
|
943
|
+
* this bot application).
|
|
944
|
+
*
|
|
945
|
+
* @example
|
|
946
|
+
* await client.webhooks.execute('webhook-id', {
|
|
947
|
+
* content: '🚀 Deployment successful!',
|
|
948
|
+
* username: 'Deploy Bot',
|
|
949
|
+
* })
|
|
950
|
+
*/
|
|
951
|
+
execute(webhookId, options) {
|
|
952
|
+
return this.http.post(`/webhooks/${webhookId}/execute`, options);
|
|
953
|
+
}
|
|
954
|
+
};
|
|
955
|
+
|
|
956
|
+
// src/api/auditlog.ts
|
|
957
|
+
var AuditLogAPI = class {
|
|
958
|
+
constructor(http) {
|
|
959
|
+
this.http = http;
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* Fetch the audit log for a server.
|
|
963
|
+
* Requires the `audit-log.read` scope.
|
|
964
|
+
*
|
|
965
|
+
* You can filter by action type, actor user ID, and paginate backwards
|
|
966
|
+
* using the `before` cursor (an ISO timestamp).
|
|
967
|
+
*
|
|
968
|
+
* @example
|
|
969
|
+
* // Most recent 50 entries
|
|
970
|
+
* const entries = await client.auditLog.fetch('server-id')
|
|
971
|
+
*
|
|
972
|
+
* // Only kick and ban actions
|
|
973
|
+
* const modActions = await client.auditLog.fetch('server-id', {
|
|
974
|
+
* action: 'member.banned',
|
|
975
|
+
* limit: 20,
|
|
976
|
+
* })
|
|
977
|
+
*
|
|
978
|
+
* // All actions performed by a specific user
|
|
979
|
+
* const byUser = await client.auditLog.fetch('server-id', { userId: 'user-id' })
|
|
980
|
+
*/
|
|
981
|
+
fetch(serverId, options = {}) {
|
|
982
|
+
const params = new URLSearchParams();
|
|
983
|
+
if (options.limit) params.set("limit", String(options.limit));
|
|
984
|
+
if (options.action) params.set("action", options.action);
|
|
985
|
+
if (options.userId) params.set("userId", options.userId);
|
|
986
|
+
if (options.before) params.set("before", options.before);
|
|
987
|
+
const qs = params.toString();
|
|
988
|
+
return this.http.get(`/servers/${serverId}/audit-logs${qs ? `?${qs}` : ""}`);
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
|
|
701
992
|
// src/structures/NovaInteraction.ts
|
|
702
993
|
var InteractionOptions = class {
|
|
703
994
|
constructor(data) {
|
|
@@ -761,6 +1052,20 @@ var NovaInteraction = class _NovaInteraction {
|
|
|
761
1052
|
this.createdAt = _raw.createdAt;
|
|
762
1053
|
this.options = new InteractionOptions(_raw.data);
|
|
763
1054
|
}
|
|
1055
|
+
/**
|
|
1056
|
+
* For `AUTOCOMPLETE` interactions — the option being completed.
|
|
1057
|
+
* Contains `name` (option name) and `value` (partial text typed so far).
|
|
1058
|
+
* `null` for all other interaction types.
|
|
1059
|
+
*
|
|
1060
|
+
* @example
|
|
1061
|
+
* client.autocomplete('search', async (interaction) => {
|
|
1062
|
+
* const query = interaction.autocomplete?.value ?? ''
|
|
1063
|
+
* await interaction.respondAutocomplete([{ name: query, value: query }])
|
|
1064
|
+
* })
|
|
1065
|
+
*/
|
|
1066
|
+
get autocomplete() {
|
|
1067
|
+
return this._raw.autocomplete ?? null;
|
|
1068
|
+
}
|
|
764
1069
|
// ── Type guards ─────────────────────────────────────────────────────────────
|
|
765
1070
|
/** `true` when triggered by a `/slash` command. */
|
|
766
1071
|
isSlashCommand() {
|
|
@@ -790,6 +1095,21 @@ var NovaInteraction = class _NovaInteraction {
|
|
|
790
1095
|
isAutocomplete() {
|
|
791
1096
|
return this.type === "AUTOCOMPLETE";
|
|
792
1097
|
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Send autocomplete suggestions back to the user.
|
|
1100
|
+
* Call this inside a `client.autocomplete()` handler.
|
|
1101
|
+
* Up to 25 choices are shown; each needs a `name` (displayed) and `value` (submitted).
|
|
1102
|
+
*
|
|
1103
|
+
* @example
|
|
1104
|
+
* client.autocomplete('color', async (interaction) => {
|
|
1105
|
+
* const query = interaction.autocomplete?.value ?? ''
|
|
1106
|
+
* const colors = ['red', 'green', 'blue'].filter(c => c.startsWith(query))
|
|
1107
|
+
* await interaction.respondAutocomplete(colors.map(c => ({ name: c, value: c })))
|
|
1108
|
+
* })
|
|
1109
|
+
*/
|
|
1110
|
+
respondAutocomplete(choices) {
|
|
1111
|
+
return this._api.autocomplete(this.id, choices);
|
|
1112
|
+
}
|
|
793
1113
|
/** `true` when triggered via a context-menu command. */
|
|
794
1114
|
isContextMenu() {
|
|
795
1115
|
return this.type === "CONTEXT_MENU";
|
|
@@ -1264,6 +1584,338 @@ var NovaMember = class {
|
|
|
1264
1584
|
}
|
|
1265
1585
|
};
|
|
1266
1586
|
|
|
1587
|
+
// src/structures/NovaRole.ts
|
|
1588
|
+
var NovaRole = class {
|
|
1589
|
+
constructor(raw, roles) {
|
|
1590
|
+
this.id = raw.id;
|
|
1591
|
+
this.name = raw.name;
|
|
1592
|
+
this.color = raw.color;
|
|
1593
|
+
this.position = raw.position;
|
|
1594
|
+
this.serverId = raw.serverId;
|
|
1595
|
+
this.hoist = raw.hoist;
|
|
1596
|
+
this.createdAt = new Date(raw.createdAt);
|
|
1597
|
+
this._roles = roles;
|
|
1598
|
+
}
|
|
1599
|
+
// ─── Convenience methods ──────────────────────────────────────────────────
|
|
1600
|
+
/**
|
|
1601
|
+
* Edit this role's name, colour, hoist flag, or permissions.
|
|
1602
|
+
*
|
|
1603
|
+
* @example
|
|
1604
|
+
* await role.edit({ color: '#ff0000', name: 'Red Team' })
|
|
1605
|
+
*/
|
|
1606
|
+
edit(options) {
|
|
1607
|
+
return this._roles.edit(this.id, options);
|
|
1608
|
+
}
|
|
1609
|
+
/**
|
|
1610
|
+
* Delete this role from the server.
|
|
1611
|
+
* Any members currently assigned this role will have it removed.
|
|
1612
|
+
*
|
|
1613
|
+
* @example
|
|
1614
|
+
* await role.delete()
|
|
1615
|
+
*/
|
|
1616
|
+
delete() {
|
|
1617
|
+
return this._roles.delete(this.id);
|
|
1618
|
+
}
|
|
1619
|
+
/**
|
|
1620
|
+
* Assign this role to a server member.
|
|
1621
|
+
*
|
|
1622
|
+
* @example
|
|
1623
|
+
* await role.assign('user-id')
|
|
1624
|
+
*/
|
|
1625
|
+
assign(userId) {
|
|
1626
|
+
return this._roles.assign(this.serverId, userId, this.id);
|
|
1627
|
+
}
|
|
1628
|
+
/**
|
|
1629
|
+
* Remove this role from a server member.
|
|
1630
|
+
*
|
|
1631
|
+
* @example
|
|
1632
|
+
* await role.remove('user-id')
|
|
1633
|
+
*/
|
|
1634
|
+
remove(userId) {
|
|
1635
|
+
return this._roles.remove(this.serverId, userId, this.id);
|
|
1636
|
+
}
|
|
1637
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
1638
|
+
/** Returns `true` if the role has a custom hex colour set. */
|
|
1639
|
+
hasColor() {
|
|
1640
|
+
return this.color !== null;
|
|
1641
|
+
}
|
|
1642
|
+
/** Returns the CSS-safe hex colour string. Falls back to `'#99aab5'` (grey). */
|
|
1643
|
+
toHex() {
|
|
1644
|
+
return this.color ?? "#99aab5";
|
|
1645
|
+
}
|
|
1646
|
+
toJSON() {
|
|
1647
|
+
return {
|
|
1648
|
+
id: this.id,
|
|
1649
|
+
name: this.name,
|
|
1650
|
+
color: this.color,
|
|
1651
|
+
position: this.position,
|
|
1652
|
+
serverId: this.serverId,
|
|
1653
|
+
hoist: this.hoist,
|
|
1654
|
+
permissions: {},
|
|
1655
|
+
createdAt: this.createdAt.toISOString()
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
};
|
|
1659
|
+
|
|
1660
|
+
// src/structures/NovaServer.ts
|
|
1661
|
+
var NovaServerWrapper = class {
|
|
1662
|
+
constructor(raw, servers, channels, members, invites, roles, messages) {
|
|
1663
|
+
this.id = raw.id;
|
|
1664
|
+
this.name = raw.name;
|
|
1665
|
+
this.icon = raw.icon;
|
|
1666
|
+
this.description = raw.description;
|
|
1667
|
+
this.memberCount = raw.memberCount;
|
|
1668
|
+
this.channelCount = raw.channelCount;
|
|
1669
|
+
this.joinedAt = new Date(raw.joinedAt);
|
|
1670
|
+
this._servers = servers;
|
|
1671
|
+
this._channels = channels;
|
|
1672
|
+
this._members = members;
|
|
1673
|
+
this._invites = invites;
|
|
1674
|
+
this._roles = roles;
|
|
1675
|
+
this._messages = messages;
|
|
1676
|
+
}
|
|
1677
|
+
// ─── Server management ────────────────────────────────────────────────────
|
|
1678
|
+
/**
|
|
1679
|
+
* Update this server's name, description, or icon.
|
|
1680
|
+
* Requires the `server.manage` scope.
|
|
1681
|
+
*
|
|
1682
|
+
* @example
|
|
1683
|
+
* await server.update({ name: 'New Name' })
|
|
1684
|
+
*/
|
|
1685
|
+
update(options) {
|
|
1686
|
+
return this._servers.update(this.id, options);
|
|
1687
|
+
}
|
|
1688
|
+
// ─── Channels ─────────────────────────────────────────────────────────────
|
|
1689
|
+
/**
|
|
1690
|
+
* Fetch all channels in this server.
|
|
1691
|
+
*
|
|
1692
|
+
* @example
|
|
1693
|
+
* const channels = await server.fetchChannels()
|
|
1694
|
+
* const text = channels.filter(c => c.type === 'TEXT')
|
|
1695
|
+
*/
|
|
1696
|
+
fetchChannels() {
|
|
1697
|
+
return this._channels.list(this.id);
|
|
1698
|
+
}
|
|
1699
|
+
/**
|
|
1700
|
+
* Create a new channel in this server.
|
|
1701
|
+
* Requires the `channels.manage` scope.
|
|
1702
|
+
*
|
|
1703
|
+
* @example
|
|
1704
|
+
* const ch = await server.createChannel({ name: 'bot-logs', type: 'TEXT' })
|
|
1705
|
+
*/
|
|
1706
|
+
createChannel(options) {
|
|
1707
|
+
return this._channels.create(this.id, options);
|
|
1708
|
+
}
|
|
1709
|
+
// ─── Members ─────────────────────────────────────────────────────────────
|
|
1710
|
+
/**
|
|
1711
|
+
* Fetch members in this server.
|
|
1712
|
+
*
|
|
1713
|
+
* @example
|
|
1714
|
+
* const members = await server.fetchMembers({ limit: 100 })
|
|
1715
|
+
*/
|
|
1716
|
+
fetchMembers(options = {}) {
|
|
1717
|
+
return this._members.list(this.id, options);
|
|
1718
|
+
}
|
|
1719
|
+
/**
|
|
1720
|
+
* Fetch a single member by user ID.
|
|
1721
|
+
*
|
|
1722
|
+
* @example
|
|
1723
|
+
* const member = await server.fetchMember('user-id')
|
|
1724
|
+
*/
|
|
1725
|
+
fetchMember(userId) {
|
|
1726
|
+
return this._members.fetch(this.id, userId);
|
|
1727
|
+
}
|
|
1728
|
+
/**
|
|
1729
|
+
* Kick a member from this server.
|
|
1730
|
+
*
|
|
1731
|
+
* @example
|
|
1732
|
+
* await server.kick('user-id')
|
|
1733
|
+
*/
|
|
1734
|
+
kick(userId) {
|
|
1735
|
+
return this._members.kick(this.id, userId);
|
|
1736
|
+
}
|
|
1737
|
+
/**
|
|
1738
|
+
* Ban a member from this server.
|
|
1739
|
+
*
|
|
1740
|
+
* @example
|
|
1741
|
+
* await server.ban('user-id', 'Spamming')
|
|
1742
|
+
*/
|
|
1743
|
+
ban(userId, reason) {
|
|
1744
|
+
return this._members.ban(this.id, userId, reason);
|
|
1745
|
+
}
|
|
1746
|
+
/**
|
|
1747
|
+
* Fetch the ban list for this server.
|
|
1748
|
+
*/
|
|
1749
|
+
fetchBans() {
|
|
1750
|
+
return this._members.listBans(this.id);
|
|
1751
|
+
}
|
|
1752
|
+
// ─── Roles ────────────────────────────────────────────────────────────────
|
|
1753
|
+
/**
|
|
1754
|
+
* Fetch all custom roles in this server.
|
|
1755
|
+
*
|
|
1756
|
+
* @example
|
|
1757
|
+
* const roles = await server.fetchRoles()
|
|
1758
|
+
*/
|
|
1759
|
+
fetchRoles() {
|
|
1760
|
+
return this._roles.list(this.id);
|
|
1761
|
+
}
|
|
1762
|
+
// ─── Invites ──────────────────────────────────────────────────────────────
|
|
1763
|
+
/**
|
|
1764
|
+
* Fetch all active invites for this server.
|
|
1765
|
+
*
|
|
1766
|
+
* @example
|
|
1767
|
+
* const invites = await server.fetchInvites()
|
|
1768
|
+
*/
|
|
1769
|
+
fetchInvites() {
|
|
1770
|
+
return this._invites.list(this.id);
|
|
1771
|
+
}
|
|
1772
|
+
/**
|
|
1773
|
+
* Create an invite for this server.
|
|
1774
|
+
*
|
|
1775
|
+
* @example
|
|
1776
|
+
* const invite = await server.createInvite({ maxUses: 10 })
|
|
1777
|
+
* console.log(`https://novachatapp.com/invite/${invite.code}`)
|
|
1778
|
+
*/
|
|
1779
|
+
createInvite(options = {}) {
|
|
1780
|
+
return this._invites.create(this.id, options);
|
|
1781
|
+
}
|
|
1782
|
+
// ─── Messaging shortcut ───────────────────────────────────────────────────
|
|
1783
|
+
/**
|
|
1784
|
+
* Send a message to a channel in this server by channel ID.
|
|
1785
|
+
*
|
|
1786
|
+
* @example
|
|
1787
|
+
* await server.send('channel-id', 'Hello, server!')
|
|
1788
|
+
*/
|
|
1789
|
+
send(channelId, content) {
|
|
1790
|
+
return this._messages.send(channelId, { content });
|
|
1791
|
+
}
|
|
1792
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
1793
|
+
toJSON() {
|
|
1794
|
+
return {
|
|
1795
|
+
id: this.id,
|
|
1796
|
+
name: this.name,
|
|
1797
|
+
icon: this.icon,
|
|
1798
|
+
description: this.description,
|
|
1799
|
+
memberCount: this.memberCount,
|
|
1800
|
+
channelCount: this.channelCount,
|
|
1801
|
+
joinedAt: this.joinedAt.toISOString()
|
|
1802
|
+
};
|
|
1803
|
+
}
|
|
1804
|
+
};
|
|
1805
|
+
|
|
1806
|
+
// src/structures/NovaInvite.ts
|
|
1807
|
+
var NovaInvite = class {
|
|
1808
|
+
constructor(raw, invites) {
|
|
1809
|
+
this.code = raw.code;
|
|
1810
|
+
this.serverId = raw.serverId;
|
|
1811
|
+
this.creatorId = raw.creatorId;
|
|
1812
|
+
this.uses = raw.uses;
|
|
1813
|
+
this.maxUses = raw.maxUses;
|
|
1814
|
+
this.expiresAt = raw.expiresAt ? new Date(raw.expiresAt) : null;
|
|
1815
|
+
this.createdAt = new Date(raw.createdAt);
|
|
1816
|
+
this._invites = invites;
|
|
1817
|
+
}
|
|
1818
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
1819
|
+
/** The full URL users can follow to join the server. */
|
|
1820
|
+
get url() {
|
|
1821
|
+
return `https://novachatapp.com/invite/${this.code}`;
|
|
1822
|
+
}
|
|
1823
|
+
/**
|
|
1824
|
+
* Returns `true` if the invite has passed its expiry time.
|
|
1825
|
+
* Always `false` for invites with no expiry.
|
|
1826
|
+
*/
|
|
1827
|
+
isExpired() {
|
|
1828
|
+
if (!this.expiresAt) return false;
|
|
1829
|
+
return this.expiresAt < /* @__PURE__ */ new Date();
|
|
1830
|
+
}
|
|
1831
|
+
/**
|
|
1832
|
+
* Returns `true` if the invite has reached its maximum use count.
|
|
1833
|
+
* Always `false` for unlimited invites.
|
|
1834
|
+
*/
|
|
1835
|
+
isExhausted() {
|
|
1836
|
+
if (this.maxUses === null) return false;
|
|
1837
|
+
return this.uses >= this.maxUses;
|
|
1838
|
+
}
|
|
1839
|
+
/** Whether this invite is still usable (not expired and not exhausted). */
|
|
1840
|
+
isValid() {
|
|
1841
|
+
return !this.isExpired() && !this.isExhausted();
|
|
1842
|
+
}
|
|
1843
|
+
/**
|
|
1844
|
+
* Revoke (delete) this invite, making the code unusable.
|
|
1845
|
+
* Requires the `invites.manage` scope.
|
|
1846
|
+
*
|
|
1847
|
+
* @example
|
|
1848
|
+
* await invite.revoke()
|
|
1849
|
+
*/
|
|
1850
|
+
revoke() {
|
|
1851
|
+
return this._invites.revoke(this.code);
|
|
1852
|
+
}
|
|
1853
|
+
toJSON() {
|
|
1854
|
+
return {
|
|
1855
|
+
code: this.code,
|
|
1856
|
+
serverId: this.serverId,
|
|
1857
|
+
creatorId: this.creatorId,
|
|
1858
|
+
uses: this.uses,
|
|
1859
|
+
maxUses: this.maxUses,
|
|
1860
|
+
expiresAt: this.expiresAt?.toISOString() ?? null,
|
|
1861
|
+
createdAt: this.createdAt.toISOString()
|
|
1862
|
+
};
|
|
1863
|
+
}
|
|
1864
|
+
};
|
|
1865
|
+
|
|
1866
|
+
// src/structures/NovaWebhook.ts
|
|
1867
|
+
var NovaWebhook = class {
|
|
1868
|
+
constructor(raw, webhooks) {
|
|
1869
|
+
this.id = raw.id;
|
|
1870
|
+
this.serverId = raw.serverId;
|
|
1871
|
+
this.channelId = raw.channelId;
|
|
1872
|
+
this.name = raw.name;
|
|
1873
|
+
this.token = raw.token;
|
|
1874
|
+
this.createdAt = new Date(raw.createdAt);
|
|
1875
|
+
this._webhooks = webhooks;
|
|
1876
|
+
}
|
|
1877
|
+
// ─── Convenience methods ──────────────────────────────────────────────────
|
|
1878
|
+
/**
|
|
1879
|
+
* Edit this webhook's name or target channel.
|
|
1880
|
+
* Requires the `webhooks.manage` scope.
|
|
1881
|
+
*
|
|
1882
|
+
* @example
|
|
1883
|
+
* await webhook.edit({ name: 'New Name', channelId: 'other-channel-id' })
|
|
1884
|
+
*/
|
|
1885
|
+
edit(options) {
|
|
1886
|
+
return this._webhooks.edit(this.id, options);
|
|
1887
|
+
}
|
|
1888
|
+
/**
|
|
1889
|
+
* Delete this webhook.
|
|
1890
|
+
* Requires the `webhooks.manage` scope.
|
|
1891
|
+
*
|
|
1892
|
+
* @example
|
|
1893
|
+
* await webhook.delete()
|
|
1894
|
+
*/
|
|
1895
|
+
delete() {
|
|
1896
|
+
return this._webhooks.delete(this.id);
|
|
1897
|
+
}
|
|
1898
|
+
/**
|
|
1899
|
+
* Post a message via this webhook.
|
|
1900
|
+
*
|
|
1901
|
+
* @example
|
|
1902
|
+
* await webhook.execute({ content: '❌ Tests failed on branch main', username: 'CI' })
|
|
1903
|
+
*/
|
|
1904
|
+
execute(options) {
|
|
1905
|
+
return this._webhooks.execute(this.id, options);
|
|
1906
|
+
}
|
|
1907
|
+
toJSON() {
|
|
1908
|
+
return {
|
|
1909
|
+
id: this.id,
|
|
1910
|
+
serverId: this.serverId,
|
|
1911
|
+
channelId: this.channelId,
|
|
1912
|
+
name: this.name,
|
|
1913
|
+
token: this.token,
|
|
1914
|
+
createdAt: this.createdAt.toISOString()
|
|
1915
|
+
};
|
|
1916
|
+
}
|
|
1917
|
+
};
|
|
1918
|
+
|
|
1267
1919
|
// src/client.ts
|
|
1268
1920
|
var NovaClient = class extends EventEmitter {
|
|
1269
1921
|
constructor(options) {
|
|
@@ -1272,11 +1924,12 @@ var NovaClient = class extends EventEmitter {
|
|
|
1272
1924
|
this.botUser = null;
|
|
1273
1925
|
this.socket = null;
|
|
1274
1926
|
this._cronTimers = [];
|
|
1275
|
-
// ── Command / component routing
|
|
1927
|
+
// ── Command / component routing ────────────────────────────────────────────
|
|
1276
1928
|
this._commandHandlers = /* @__PURE__ */ new Map();
|
|
1277
1929
|
this._buttonHandlers = /* @__PURE__ */ new Map();
|
|
1278
1930
|
this._selectHandlers = /* @__PURE__ */ new Map();
|
|
1279
1931
|
this._modalHandlers = /* @__PURE__ */ new Map();
|
|
1932
|
+
this._autocompleteHandlers = /* @__PURE__ */ new Map();
|
|
1280
1933
|
if (!options.token) {
|
|
1281
1934
|
throw new Error("[nova-bot-sdk] A bot token is required.");
|
|
1282
1935
|
}
|
|
@@ -1296,6 +1949,10 @@ var NovaClient = class extends EventEmitter {
|
|
|
1296
1949
|
this.permissions = new PermissionsAPI(this.http);
|
|
1297
1950
|
this.channels = new ChannelsAPI(this.http);
|
|
1298
1951
|
this.reactions = new ReactionsAPI(this.http);
|
|
1952
|
+
this.roles = new RolesAPI(this.http);
|
|
1953
|
+
this.invites = new InvitesAPI(this.http);
|
|
1954
|
+
this.webhooks = new WebhooksAPI(this.http);
|
|
1955
|
+
this.auditLog = new AuditLogAPI(this.http);
|
|
1299
1956
|
this.on("error", () => {
|
|
1300
1957
|
});
|
|
1301
1958
|
const cleanup = () => this.disconnect();
|
|
@@ -1361,6 +2018,26 @@ var NovaClient = class extends EventEmitter {
|
|
|
1361
2018
|
this._modalHandlers.set(customId, handler);
|
|
1362
2019
|
return this;
|
|
1363
2020
|
}
|
|
2021
|
+
/**
|
|
2022
|
+
* Register a handler for an autocomplete interaction by command name.
|
|
2023
|
+
* Called when the user is typing in an option with `autocomplete: true`.
|
|
2024
|
+
* The handler receives the interaction with `interaction.autocomplete.value`
|
|
2025
|
+
* containing the partial text, and should call `interaction.respondAutocomplete()`
|
|
2026
|
+
* to send completion suggestions.
|
|
2027
|
+
*
|
|
2028
|
+
* @example
|
|
2029
|
+
* client.autocomplete('search', async (interaction) => {
|
|
2030
|
+
* const query = interaction.autocomplete?.value ?? ''
|
|
2031
|
+
* const results = await myDB.search(query)
|
|
2032
|
+
* await interaction.respondAutocomplete(
|
|
2033
|
+
* results.map(r => ({ name: r.label, value: r.id }))
|
|
2034
|
+
* )
|
|
2035
|
+
* })
|
|
2036
|
+
*/
|
|
2037
|
+
autocomplete(commandName, handler) {
|
|
2038
|
+
this._autocompleteHandlers.set(commandName, handler);
|
|
2039
|
+
return this;
|
|
2040
|
+
}
|
|
1364
2041
|
/**
|
|
1365
2042
|
* Connect to the Nova WebSocket gateway.
|
|
1366
2043
|
* Resolves when the `ready` event is received.
|
|
@@ -1412,6 +2089,8 @@ var NovaClient = class extends EventEmitter {
|
|
|
1412
2089
|
run(this._selectHandlers.get(interaction.customId));
|
|
1413
2090
|
} else if (interaction.isModalSubmit() && interaction.customId) {
|
|
1414
2091
|
run(this._modalHandlers.get(interaction.customId));
|
|
2092
|
+
} else if (interaction.isAutocomplete() && interaction.commandName) {
|
|
2093
|
+
run(this._autocompleteHandlers.get(interaction.commandName));
|
|
1415
2094
|
}
|
|
1416
2095
|
});
|
|
1417
2096
|
this.socket.on("bot:event", (event) => {
|
|
@@ -1476,6 +2155,15 @@ var NovaClient = class extends EventEmitter {
|
|
|
1476
2155
|
case "user.updated_profile":
|
|
1477
2156
|
this.emit("memberUpdate", event.data);
|
|
1478
2157
|
break;
|
|
2158
|
+
case "user.role_added":
|
|
2159
|
+
this.emit("memberRoleAdded", event.data);
|
|
2160
|
+
break;
|
|
2161
|
+
case "user.role_removed":
|
|
2162
|
+
this.emit("memberRoleRemoved", event.data);
|
|
2163
|
+
break;
|
|
2164
|
+
case "server.updated":
|
|
2165
|
+
this.emit("serverUpdate", event.data);
|
|
2166
|
+
break;
|
|
1479
2167
|
}
|
|
1480
2168
|
});
|
|
1481
2169
|
this.socket.on("bot:error", (err) => {
|
|
@@ -1568,18 +2256,92 @@ var NovaClient = class extends EventEmitter {
|
|
|
1568
2256
|
}
|
|
1569
2257
|
/**
|
|
1570
2258
|
* Fetch a single member from a server and return them as a `NovaMember` wrapper.
|
|
1571
|
-
*
|
|
2259
|
+
* Uses the dedicated single-member endpoint (more efficient than listing all members).
|
|
1572
2260
|
*
|
|
1573
2261
|
* @example
|
|
1574
2262
|
* const member = await client.fetchMember('server-id', 'user-id')
|
|
1575
2263
|
* await member.dm('Welcome!')
|
|
1576
2264
|
*/
|
|
1577
2265
|
async fetchMember(serverId, userId) {
|
|
1578
|
-
const
|
|
1579
|
-
const raw = list.find((m) => m.user.id === userId);
|
|
1580
|
-
if (!raw) throw new Error(`[nova-bot-sdk] Member ${userId} not found in server ${serverId}`);
|
|
2266
|
+
const raw = await this.members.fetch(serverId, userId);
|
|
1581
2267
|
return new NovaMember(raw, serverId, this.members);
|
|
1582
2268
|
}
|
|
2269
|
+
/**
|
|
2270
|
+
* Fetch a single server by ID and return it as a `NovaServerWrapper`.
|
|
2271
|
+
*
|
|
2272
|
+
* @example
|
|
2273
|
+
* const server = await client.fetchServer('server-id')
|
|
2274
|
+
* console.log(`${server.name} — ${server.memberCount} members`)
|
|
2275
|
+
*/
|
|
2276
|
+
async fetchServer(serverId) {
|
|
2277
|
+
const raw = await this.servers.fetch(serverId);
|
|
2278
|
+
return new NovaServerWrapper(raw, this.servers, this.channels, this.members, this.invites, this.roles, this.messages);
|
|
2279
|
+
}
|
|
2280
|
+
/**
|
|
2281
|
+
* Fetch all servers the bot is in and return them as `NovaServerWrapper` objects.
|
|
2282
|
+
*
|
|
2283
|
+
* @example
|
|
2284
|
+
* const servers = await client.fetchServers()
|
|
2285
|
+
* for (const s of servers) console.log(s.name)
|
|
2286
|
+
*/
|
|
2287
|
+
async fetchServers() {
|
|
2288
|
+
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));
|
|
2290
|
+
}
|
|
2291
|
+
/**
|
|
2292
|
+
* Fetch all custom roles in a server and return them as `NovaRole` wrappers.
|
|
2293
|
+
*
|
|
2294
|
+
* @example
|
|
2295
|
+
* const roles = await client.fetchRoles('server-id')
|
|
2296
|
+
* const mod = roles.find(r => r.name === 'Moderator')
|
|
2297
|
+
*/
|
|
2298
|
+
async fetchRoles(serverId) {
|
|
2299
|
+
const list = await this.roles.list(serverId);
|
|
2300
|
+
return list.map((raw) => new NovaRole(raw, this.roles));
|
|
2301
|
+
}
|
|
2302
|
+
/**
|
|
2303
|
+
* Fetch all active invites for a server and return them as `NovaInvite` wrappers.
|
|
2304
|
+
*
|
|
2305
|
+
* @example
|
|
2306
|
+
* const invites = await client.fetchInvites('server-id')
|
|
2307
|
+
* const active = invites.filter(i => i.isValid())
|
|
2308
|
+
*/
|
|
2309
|
+
async fetchInvites(serverId) {
|
|
2310
|
+
const list = await this.invites.list(serverId);
|
|
2311
|
+
return list.map((raw) => new NovaInvite(raw, this.invites));
|
|
2312
|
+
}
|
|
2313
|
+
/**
|
|
2314
|
+
* Fetch a single invite by code and return it as a `NovaInvite` wrapper.
|
|
2315
|
+
*
|
|
2316
|
+
* @example
|
|
2317
|
+
* const invite = await client.fetchInvite('abc123')
|
|
2318
|
+
* if (invite.isExpired()) await invite.revoke()
|
|
2319
|
+
*/
|
|
2320
|
+
async fetchInvite(code) {
|
|
2321
|
+
const raw = await this.invites.fetch(code);
|
|
2322
|
+
return new NovaInvite(raw, this.invites);
|
|
2323
|
+
}
|
|
2324
|
+
/**
|
|
2325
|
+
* Fetch all webhooks in a channel and return them as `NovaWebhook` wrappers.
|
|
2326
|
+
*
|
|
2327
|
+
* @example
|
|
2328
|
+
* const webhooks = await client.fetchWebhooks('channel-id')
|
|
2329
|
+
*/
|
|
2330
|
+
async fetchWebhooks(channelId) {
|
|
2331
|
+
const list = await this.webhooks.list(channelId);
|
|
2332
|
+
return list.map((raw) => new NovaWebhook(raw, this.webhooks));
|
|
2333
|
+
}
|
|
2334
|
+
/**
|
|
2335
|
+
* Fetch a single webhook by ID and return it as a `NovaWebhook` wrapper.
|
|
2336
|
+
*
|
|
2337
|
+
* @example
|
|
2338
|
+
* const webhook = await client.fetchWebhook('webhook-id')
|
|
2339
|
+
* await webhook.execute({ content: 'Build passed! ✅' })
|
|
2340
|
+
*/
|
|
2341
|
+
async fetchWebhook(webhookId) {
|
|
2342
|
+
const raw = await this.webhooks.fetch(webhookId);
|
|
2343
|
+
return new NovaWebhook(raw, this.webhooks);
|
|
2344
|
+
}
|
|
1583
2345
|
// ─── Event fence ──────────────────────────────────────────────────────────────
|
|
1584
2346
|
/**
|
|
1585
2347
|
* Wait for a specific event to be emitted, optionally filtered.
|
|
@@ -2978,6 +3740,7 @@ function countdown(target) {
|
|
|
2978
3740
|
}
|
|
2979
3741
|
export {
|
|
2980
3742
|
ActionRowBuilder,
|
|
3743
|
+
AuditLogAPI,
|
|
2981
3744
|
ButtonBuilder,
|
|
2982
3745
|
ChannelsAPI,
|
|
2983
3746
|
Collection,
|
|
@@ -2988,6 +3751,7 @@ export {
|
|
|
2988
3751
|
HttpClient,
|
|
2989
3752
|
InteractionOptions,
|
|
2990
3753
|
InteractionsAPI,
|
|
3754
|
+
InvitesAPI,
|
|
2991
3755
|
Logger,
|
|
2992
3756
|
MembersAPI,
|
|
2993
3757
|
MessageBuilder,
|
|
@@ -2996,19 +3760,25 @@ export {
|
|
|
2996
3760
|
NovaChannel,
|
|
2997
3761
|
NovaClient,
|
|
2998
3762
|
NovaInteraction,
|
|
3763
|
+
NovaInvite,
|
|
2999
3764
|
NovaMember,
|
|
3000
3765
|
NovaMessage,
|
|
3766
|
+
NovaRole,
|
|
3767
|
+
NovaServerWrapper,
|
|
3768
|
+
NovaWebhook,
|
|
3001
3769
|
Paginator,
|
|
3002
3770
|
Permissions,
|
|
3003
3771
|
PermissionsAPI,
|
|
3004
3772
|
PermissionsBitfield,
|
|
3005
3773
|
PollBuilder,
|
|
3006
3774
|
ReactionsAPI,
|
|
3775
|
+
RolesAPI,
|
|
3007
3776
|
SelectMenuBuilder,
|
|
3008
3777
|
ServersAPI,
|
|
3009
3778
|
SlashCommandBuilder,
|
|
3010
3779
|
SlashCommandOptionBuilder,
|
|
3011
3780
|
TextInputBuilder,
|
|
3781
|
+
WebhooksAPI,
|
|
3012
3782
|
countdown,
|
|
3013
3783
|
formatDuration,
|
|
3014
3784
|
formatRelative,
|