@buape/carbon 0.1.0 → 0.1.2

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.
Files changed (61) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-dev.log +168 -5
  3. package/.turbo/turbo-docs.log +3 -1
  4. package/CHANGELOG.md +14 -0
  5. package/dist/package.json +2 -2
  6. package/dist/src/abstracts/BaseCommand.d.ts +1 -2
  7. package/dist/src/abstracts/BaseCommand.d.ts.map +1 -1
  8. package/dist/src/abstracts/BaseCommand.js.map +1 -1
  9. package/dist/src/abstracts/BaseInteraction.d.ts +2 -0
  10. package/dist/src/abstracts/BaseInteraction.d.ts.map +1 -1
  11. package/dist/src/abstracts/BaseInteraction.js +8 -0
  12. package/dist/src/abstracts/BaseInteraction.js.map +1 -1
  13. package/dist/src/classes/Client.d.ts +12 -0
  14. package/dist/src/classes/Client.d.ts.map +1 -1
  15. package/dist/src/classes/Client.js +45 -46
  16. package/dist/src/classes/Client.js.map +1 -1
  17. package/dist/src/classes/Command.d.ts +1 -3
  18. package/dist/src/classes/Command.d.ts.map +1 -1
  19. package/dist/src/classes/Command.js +1 -1
  20. package/dist/src/classes/Command.js.map +1 -1
  21. package/dist/src/internals/AutocompleteInteraction.d.ts +1 -1
  22. package/dist/src/internals/AutocompleteInteraction.d.ts.map +1 -1
  23. package/dist/src/internals/AutocompleteInteraction.js +1 -1
  24. package/dist/src/internals/AutocompleteInteraction.js.map +1 -1
  25. package/dist/src/internals/CommandHandler.js +1 -1
  26. package/dist/src/internals/CommandHandler.js.map +1 -1
  27. package/dist/src/internals/OptionsHandler.d.ts +1 -1
  28. package/dist/src/internals/OptionsHandler.d.ts.map +1 -1
  29. package/dist/src/internals/OptionsHandler.js +1 -1
  30. package/dist/src/internals/OptionsHandler.js.map +1 -1
  31. package/dist/src/structures/Guild.d.ts +5 -0
  32. package/dist/src/structures/Guild.d.ts.map +1 -1
  33. package/dist/src/structures/Guild.js +8 -0
  34. package/dist/src/structures/Guild.js.map +1 -1
  35. package/dist/src/structures/GuildMember.d.ts +109 -0
  36. package/dist/src/structures/GuildMember.d.ts.map +1 -0
  37. package/dist/src/structures/GuildMember.js +186 -0
  38. package/dist/src/structures/GuildMember.js.map +1 -0
  39. package/dist/tsconfig.tsbuildinfo +1 -1
  40. package/docs/classes/AnySelectMenuInteraction.mdx +14 -0
  41. package/docs/classes/AutocompleteInteraction.mdx +14 -0
  42. package/docs/classes/BaseComponentInteraction.mdx +14 -0
  43. package/docs/classes/BaseInteraction.mdx +10 -0
  44. package/docs/classes/ButtonInteraction.mdx +14 -0
  45. package/docs/classes/ChannelSelectMenuInteraction.mdx +14 -0
  46. package/docs/classes/CommandInteraction.mdx +14 -0
  47. package/docs/classes/Guild.mdx +18 -0
  48. package/docs/classes/MentionableSelectMenuInteraction.mdx +14 -0
  49. package/docs/classes/RoleSelectMenuInteraction.mdx +14 -0
  50. package/docs/classes/StringSelectMenuInteraction.mdx +14 -0
  51. package/docs/classes/UserSelectMenuInteraction.mdx +14 -0
  52. package/package.json +2 -2
  53. package/src/abstracts/BaseCommand.ts +6 -2
  54. package/src/abstracts/BaseInteraction.ts +7 -0
  55. package/src/classes/Client.ts +64 -59
  56. package/src/classes/Command.ts +5 -3
  57. package/src/internals/AutocompleteInteraction.ts +5 -5
  58. package/src/internals/CommandHandler.ts +1 -1
  59. package/src/internals/OptionsHandler.ts +2 -1
  60. package/src/structures/Guild.ts +12 -0
  61. package/src/structures/GuildMember.ts +231 -0
@@ -2,8 +2,11 @@ import {
2
2
  ApplicationCommandType,
3
3
  type RESTPostAPIApplicationCommandsJSONBody
4
4
  } from "discord-api-types/v10"
5
- import { ApplicationIntegrationType, InteractionContextType } from "../index.js"
6
- import type { BaseComponent } from "./BaseComponent.js"
5
+ import {
6
+ ApplicationIntegrationType,
7
+ type BaseComponent,
8
+ InteractionContextType
9
+ } from "../index.js"
7
10
 
8
11
  /**
9
12
  * Represents the base data of a command that the user creates
@@ -52,6 +55,7 @@ export abstract class BaseCommand {
52
55
  * You mount these here so the handler can access them
53
56
  */
54
57
  components?: BaseComponent[] = []
58
+
55
59
  /**
56
60
  * All the paginators that the command is able to use.
57
61
  * You mount these here so the handler can access them
@@ -9,6 +9,7 @@ import type { Client } from "../classes/Client.js"
9
9
  import type { Row } from "../classes/Row.js"
10
10
  import { channelFactory } from "../factories/channelFactory.js"
11
11
  import { Guild } from "../structures/Guild.js"
12
+ import { GuildMember } from "../structures/GuildMember.js"
12
13
  import { Message } from "../structures/Message.js"
13
14
  import { User } from "../structures/User.js"
14
15
  import { Base } from "./Base.js"
@@ -109,6 +110,12 @@ export abstract class BaseInteraction<T extends APIInteraction> extends Base {
109
110
  return channelFactory(this.client, this.rawData.channel as APIChannel)
110
111
  }
111
112
 
113
+ get member() {
114
+ if (!this.rawData.member) return null
115
+ if (!this.guild) return null
116
+ return new GuildMember(this.client, this.rawData.member, this.guild)
117
+ }
118
+
112
119
  /**
113
120
  * Reply to an interaction.
114
121
  * If the interaction is deferred, this will edit the original response.
@@ -58,6 +58,11 @@ export type ClientOptions = {
58
58
  * ```
59
59
  */
60
60
  mode: ClientMode
61
+ /**
62
+ * The route to use for interactions on your server.
63
+ * @default "/interaction"
64
+ */
65
+ interactionRoute?: string
61
66
  /**
62
67
  * The options used to initialize the request client, if you want to customize it.
63
68
  */
@@ -150,71 +155,71 @@ export class Client {
150
155
  return Response.redirect(this.options.redirectUrl, 302)
151
156
  throw new StatusError(404)
152
157
  })
153
- this.router.post("/interaction", async (req, ctx?: ExecutionContext) => {
154
- const isValid = await this.validateInteraction(req)
155
- if (!isValid) {
156
- return new Response("Invalid request signature", { status: 401 })
158
+ this.router.post(
159
+ this.options.interactionRoute || "/interaction",
160
+ async (req, ctx?: ExecutionContext) => {
161
+ return await this.handle(req, ctx)
157
162
  }
163
+ )
164
+ }
158
165
 
159
- const rawInteraction = (await req.json()) as unknown as APIInteraction
160
- if (rawInteraction.type === InteractionType.Ping) {
161
- return json({
162
- type: InteractionResponseType.Pong
163
- })
164
- }
166
+ /**
167
+ * If you want use a custom handler for HTTP requests instead of Carbon's router, you can use this method.
168
+ * @param req The request to handle
169
+ * @param ctx Cloudflare Workers only. The execution context of the request, provided in the fetch handler from CF.
170
+ * @returns A response to send back to the client.
171
+ */
172
+ public async handle(req: IRequestStrict, ctx?: ExecutionContext) {
173
+ const isValid = await this.validateInteraction(req)
174
+ if (!isValid) {
175
+ return new Response("Invalid request signature", { status: 401 })
176
+ }
165
177
 
166
- if (rawInteraction.type === InteractionType.ApplicationCommand) {
167
- if (ctx?.waitUntil) {
168
- ctx.waitUntil(
169
- (async () => {
170
- await this.commandHandler.handleCommandInteraction(rawInteraction)
171
- })()
172
- )
173
- } else {
174
- await this.commandHandler.handleCommandInteraction(rawInteraction)
175
- }
176
- }
177
- if (
178
- rawInteraction.type === InteractionType.ApplicationCommandAutocomplete
179
- ) {
180
- if (ctx?.waitUntil) {
181
- ctx.waitUntil(
182
- (async () => {
183
- await this.commandHandler.handleAutocompleteInteraction(
184
- rawInteraction
185
- )
186
- })()
187
- )
188
- } else {
189
- await this.commandHandler.handleAutocompleteInteraction(
190
- rawInteraction
191
- )
192
- }
178
+ const rawInteraction = (await req.json()) as unknown as APIInteraction
179
+ if (rawInteraction.type === InteractionType.Ping) {
180
+ return json({
181
+ type: InteractionResponseType.Pong
182
+ })
183
+ }
184
+
185
+ if (rawInteraction.type === InteractionType.ApplicationCommand) {
186
+ if (ctx?.waitUntil) {
187
+ ctx.waitUntil(
188
+ (async () => {
189
+ await this.commandHandler.handleCommandInteraction(rawInteraction)
190
+ })()
191
+ )
192
+ } else {
193
+ await this.commandHandler.handleCommandInteraction(rawInteraction)
193
194
  }
194
- if (rawInteraction.type === InteractionType.ApplicationCommand) {
195
- if (ctx?.waitUntil) {
196
- ctx.waitUntil(
197
- (async () => {
198
- await this.commandHandler.handleCommandInteraction(rawInteraction)
199
- })()
200
- )
201
- } else {
202
- await this.commandHandler.handleCommandInteraction(rawInteraction)
203
- }
195
+ }
196
+ if (
197
+ rawInteraction.type === InteractionType.ApplicationCommandAutocomplete
198
+ ) {
199
+ if (ctx?.waitUntil) {
200
+ ctx.waitUntil(
201
+ (async () => {
202
+ await this.commandHandler.handleAutocompleteInteraction(
203
+ rawInteraction
204
+ )
205
+ })()
206
+ )
207
+ } else {
208
+ await this.commandHandler.handleAutocompleteInteraction(rawInteraction)
204
209
  }
205
- if (rawInteraction.type === InteractionType.MessageComponent) {
206
- if (ctx?.waitUntil) {
207
- ctx.waitUntil(
208
- (async () => {
209
- await this.componentHandler.handleInteraction(rawInteraction)
210
- })()
211
- )
212
- } else {
213
- await this.componentHandler.handleInteraction(rawInteraction)
214
- }
210
+ }
211
+ if (rawInteraction.type === InteractionType.MessageComponent) {
212
+ if (ctx?.waitUntil) {
213
+ ctx.waitUntil(
214
+ (async () => {
215
+ await this.componentHandler.handleInteraction(rawInteraction)
216
+ })()
217
+ )
218
+ } else {
219
+ await this.componentHandler.handleInteraction(rawInteraction)
215
220
  }
216
- return new Response(null, { status: 202 })
217
- })
221
+ }
222
+ return new Response(null, { status: 202 })
218
223
  }
219
224
 
220
225
  /**
@@ -2,9 +2,11 @@ import {
2
2
  type APIApplicationCommandBasicOption,
3
3
  ApplicationCommandType
4
4
  } from "discord-api-types/v10"
5
- import { BaseCommand } from "../abstracts/BaseCommand.js"
6
- import type { CommandInteraction } from "../internals/CommandInteraction.js"
7
- import type { AutocompleteInteraction } from "../internals/AutocompleteInteraction.js"
5
+ import {
6
+ type AutocompleteInteraction,
7
+ BaseCommand,
8
+ type CommandInteraction
9
+ } from "../index.js"
8
10
 
9
11
  export type CommandOptions = APIApplicationCommandBasicOption[]
10
12
 
@@ -1,14 +1,14 @@
1
- import type { BaseCommand } from "../abstracts/BaseCommand.js"
2
- import { BaseInteraction } from "../abstracts/BaseInteraction.js"
3
1
  import {
2
+ type APIApplicationCommandAutocompleteInteraction,
4
3
  type APIApplicationCommandInteractionDataBasicOption,
5
4
  ApplicationCommandOptionType,
6
5
  ApplicationCommandType,
6
+ InteractionResponseType,
7
7
  InteractionType,
8
- type APIApplicationCommandAutocompleteInteraction,
9
- Routes,
10
- InteractionResponseType
8
+ Routes
11
9
  } from "discord-api-types/v10"
10
+ import type { BaseCommand } from "../abstracts/BaseCommand.js"
11
+ import { BaseInteraction } from "../abstracts/BaseInteraction.js"
12
12
  import type { Client } from "../classes/Client.js"
13
13
  import { Command } from "../classes/Command.js"
14
14
  import { OptionsHandler } from "./OptionsHandler.js"
@@ -9,8 +9,8 @@ import { Base } from "../abstracts/Base.js"
9
9
  import { Command } from "../classes/Command.js"
10
10
  import { CommandWithSubcommandGroups } from "../classes/CommandWithSubcommandGroups.js"
11
11
  import { CommandWithSubcommands } from "../classes/CommandWithSubcommands.js"
12
- import { CommandInteraction } from "./CommandInteraction.js"
13
12
  import { AutocompleteInteraction } from "./AutocompleteInteraction.js"
13
+ import { CommandInteraction } from "./CommandInteraction.js"
14
14
 
15
15
  export class CommandHandler extends Base {
16
16
  private getCommand(
@@ -57,7 +57,8 @@ export class OptionsHandler extends Base {
57
57
  const num = this.raw.find(
58
58
  (x) => x.name === key && x.type === ApplicationCommandOptionType.Integer
59
59
  )?.value
60
- if (!num || !Number.isSafeInteger(num)) return undefined
60
+ if (!num || typeof num !== "number" || !Number.isSafeInteger(num))
61
+ return undefined
61
62
  return num
62
63
  }
63
64
 
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  type APIChannel,
3
3
  type APIGuild,
4
+ type APIGuildMember,
4
5
  type APIRole,
5
6
  type RESTPostAPIGuildRoleJSONBody,
6
7
  Routes
@@ -8,6 +9,7 @@ import {
8
9
  import { Base } from "../abstracts/Base.js"
9
10
  import type { Client } from "../classes/Client.js"
10
11
  import { channelFactory } from "../factories/channelFactory.js"
12
+ import { GuildMember } from "./GuildMember.js"
11
13
  import { Role } from "./Role.js"
12
14
 
13
15
  export class Guild extends Base {
@@ -103,6 +105,16 @@ export class Guild extends Base {
103
105
  return new Role(this.client, role)
104
106
  }
105
107
 
108
+ /**
109
+ * Get a member in the guild by ID
110
+ */
111
+ async fetchMember(memberId: string) {
112
+ const member = (await this.client.rest.get(
113
+ Routes.guildMember(this.id, memberId)
114
+ )) as APIGuildMember
115
+ return new GuildMember(this.client, member, this)
116
+ }
117
+
106
118
  /**
107
119
  * Get the URL of the guild's icon
108
120
  */
@@ -0,0 +1,231 @@
1
+ import type { APIGuildMember, GuildMemberFlags } from "discord-api-types/v10"
2
+ import { Base } from "../abstracts/Base.js"
3
+ import type { Client } from "../classes/Client.js"
4
+ import type { Guild } from "./Guild.js"
5
+ import { Role } from "./Role.js"
6
+ import { User } from "./User.js"
7
+
8
+ export class GuildMember extends Base {
9
+ /**
10
+ * The guild-specific nickname of the member.
11
+ */
12
+ nickname?: string | null
13
+ /**
14
+ * The guild-specific avatar hash of the member.
15
+ * You can use {@link GuildMember.avatarUrl} to get the URL of the avatar.
16
+ */
17
+ avatar?: string | null
18
+ /**
19
+ * Is this member muted in Voice Channels?
20
+ */
21
+ mute?: boolean | null
22
+ /**
23
+ * Is this member deafened in Voice Channels?
24
+ */
25
+ deaf?: boolean | null
26
+ /**
27
+ * The date since this member boosted the guild, if applicable.
28
+ */
29
+ premiumSince?: string | null
30
+ /**
31
+ * The flags of the member.
32
+ * @see https://discord.com/developers/docs/resources/guild#guild-member-object-guild-member-flags
33
+ */
34
+ flags?: GuildMemberFlags | null
35
+ /**
36
+ * The roles of the member
37
+ */
38
+ roles?: Role[] | null
39
+ /**
40
+ * The joined date of the member
41
+ */
42
+ joinedAt?: string | null
43
+ /**
44
+ * The date when the member's communication privileges (timeout) will be reinstated
45
+ */
46
+ communicationDisabledUntil?: string | null
47
+ /**
48
+ * Is this member yet to pass the guild's Membership Screening requirements?
49
+ */
50
+ pending?: boolean | null
51
+ /**
52
+ * The guild object of the member
53
+ */
54
+ guild: Guild
55
+ /**
56
+ * The user object of the member
57
+ */
58
+ user?: User | null
59
+
60
+ private rawData: APIGuildMember | null = null
61
+
62
+ constructor(client: Client, rawData: APIGuildMember, guild: Guild) {
63
+ super(client)
64
+ this.rawData = rawData
65
+ this.guild = guild
66
+ this.setData(rawData)
67
+ }
68
+
69
+ private setData(data: typeof this.rawData) {
70
+ if (!data) throw new Error("Cannot set data without having data... smh")
71
+ this.rawData = data
72
+ this.nickname = data.nick
73
+ this.avatar = data.avatar
74
+ this.mute = data.mute
75
+ this.deaf = data.deaf
76
+ this.premiumSince = data.premium_since
77
+ this.flags = data.flags
78
+ this.roles = data.roles?.map((roleId) => new Role(this.client, roleId))
79
+ this.joinedAt = data.joined_at
80
+ this.communicationDisabledUntil = data.communication_disabled_until
81
+ this.pending = data.pending
82
+ this.user = data.user ? new User(this.client, data.user) : null
83
+ }
84
+
85
+ /**
86
+ * Set the nickname of the member
87
+ */
88
+ async setNickname(nickname: string | null): Promise<void> {
89
+ await this.client.rest.patch(
90
+ `/guilds/${this.guild?.id}/members/${this.user?.id}`,
91
+ {
92
+ body: {
93
+ nick: nickname
94
+ }
95
+ }
96
+ )
97
+ this.nickname = nickname
98
+ }
99
+
100
+ /**
101
+ * Add a role to the member
102
+ */
103
+ async addRole(roleId: string): Promise<void> {
104
+ await this.client.rest.put(
105
+ `/guilds/${this.guild?.id}/members/${this.user?.id}/roles/${roleId}`,
106
+ {}
107
+ )
108
+ this.roles?.push(new Role(this.client, roleId))
109
+ }
110
+
111
+ /**
112
+ * Remove a role from the member
113
+ */
114
+ async removeRole(roleId: string): Promise<void> {
115
+ await this.client.rest.delete(
116
+ `/guilds/${this.guild?.id}/members/${this.user?.id}/roles/${roleId}`
117
+ )
118
+ this.roles = this.roles?.filter((role) => role.id !== roleId)
119
+ }
120
+
121
+ /**
122
+ * Kick the member from the guild
123
+ */
124
+ async kick(): Promise<void> {
125
+ await this.client.rest.delete(
126
+ `/guilds/${this.guild?.id}/members/${this.user?.id}`
127
+ )
128
+ }
129
+
130
+ /**
131
+ * Ban the member from the guild
132
+ */
133
+ async ban(
134
+ options: { reason?: string; deleteMessageDays?: number } = {}
135
+ ): Promise<void> {
136
+ await this.client.rest.put(
137
+ `/guilds/${this.guild?.id}/bans/${this.user?.id}`,
138
+ {
139
+ body: {
140
+ reason: options.reason,
141
+ delete_message_days: options.deleteMessageDays
142
+ }
143
+ }
144
+ )
145
+ }
146
+
147
+ /**
148
+ * Mute a member in voice channels
149
+ */
150
+ async muteMember(): Promise<void> {
151
+ await this.client.rest.patch(
152
+ `/guilds/${this.guild?.id}/members/${this.user?.id}`,
153
+ {
154
+ body: {
155
+ mute: true
156
+ }
157
+ }
158
+ )
159
+ this.mute = true
160
+ }
161
+
162
+ /**
163
+ * Unmute a member in voice channels
164
+ */
165
+ async unmuteMember(): Promise<void> {
166
+ await this.client.rest.patch(
167
+ `/guilds/${this.guild?.id}/members/${this.user?.id}`,
168
+ {
169
+ body: {
170
+ mute: false
171
+ }
172
+ }
173
+ )
174
+ this.mute = false
175
+ }
176
+
177
+ /**
178
+ * Deafen a member in voice channels
179
+ */
180
+ async deafenMember(): Promise<void> {
181
+ await this.client.rest.patch(
182
+ `/guilds/${this.guild?.id}/members/${this.user?.id}`,
183
+ {
184
+ body: {
185
+ deaf: true
186
+ }
187
+ }
188
+ )
189
+ this.deaf = true
190
+ }
191
+
192
+ /**
193
+ * Undeafen a member in voice channels
194
+ */
195
+ async undeafenMember(): Promise<void> {
196
+ await this.client.rest.patch(
197
+ `/guilds/${this.guild?.id}/members/${this.user?.id}`,
198
+ {
199
+ body: {
200
+ deaf: false
201
+ }
202
+ }
203
+ )
204
+ this.deaf = false
205
+ }
206
+
207
+ /**
208
+ * Set or remove a timeout for a member in the guild
209
+ */
210
+ async timeoutMember(communicationDisabledUntil: string): Promise<void> {
211
+ await this.client.rest.patch(
212
+ `/guilds/${this.guild?.id}/members/${this.user?.id}`,
213
+ {
214
+ body: {
215
+ communication_disabled_until: communicationDisabledUntil
216
+ }
217
+ }
218
+ )
219
+ this.communicationDisabledUntil = communicationDisabledUntil
220
+ }
221
+
222
+ /**
223
+ * Get the URL of the member's guild-specific avatar
224
+ */
225
+ get avatarUrl(): string | null {
226
+ if (!this.user) return null
227
+ return this.avatar
228
+ ? `https://cdn.discordapp.com/guilds/${this.guild.id}/users/${this.user?.id}/${this.avatar}.png`
229
+ : null
230
+ }
231
+ }