Skip to content

Commit 803700a

Browse files
authored
Merge pull request #61 from DjDeveloperr/slash
Add Sub Commands and Sub Command Group handlers and decorators
2 parents 55ddc64 + 9c46287 commit 803700a

File tree

11 files changed

+265
-84
lines changed

11 files changed

+265
-84
lines changed

src/gateway/handlers/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ export interface ClientEvents extends EventTypes {
330330
*/
331331
webhooksUpdate: (guild: Guild, channel: GuildTextChannel) => void
332332
/**
333-
* A Slash Command was triggered
333+
* An Interaction was created
334334
*/
335335
interactionCreate: (interaction: Interaction) => void
336336
}

src/models/client.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { Extension } from './extensions.ts'
1515
import { SlashClient } from './slashClient.ts'
1616
import { Interaction } from '../structures/slash.ts'
1717
import { SlashModule } from './slashModule.ts'
18+
import type { ShardManager } from './shard.ts'
1819

1920
/** OS related properties sent with Gateway Identify */
2021
export interface ClientProperties {
@@ -93,6 +94,8 @@ export class Client extends EventEmitter {
9394
_decoratedSlash?: Array<{
9495
name: string
9596
guild?: string
97+
parent?: string
98+
group?: string
9699
handler: (interaction: Interaction) => any
97100
}>
98101

@@ -110,6 +113,11 @@ export class Client extends EventEmitter {
110113
...args: Parameters<ClientEvents[K]>
111114
): boolean => this._untypedEmit(event, ...args)
112115

116+
/** Shard on which this Client is */
117+
shard: number = 0
118+
/** Shard Manager of this Client if Sharded */
119+
shardManager?: ShardManager
120+
113121
constructor(options: ClientOptions = {}) {
114122
super()
115123
this.token = options.token
@@ -231,6 +239,47 @@ export function slash(name?: string, guild?: string) {
231239
}
232240
}
233241

242+
export function subslash(parent: string, name?: string, guild?: string) {
243+
return function (client: Client | SlashModule, prop: string) {
244+
if (client._decoratedSlash === undefined) client._decoratedSlash = []
245+
const item = (client as { [name: string]: any })[prop]
246+
if (typeof item !== 'function') {
247+
item.parent = parent
248+
client._decoratedSlash.push(item)
249+
} else
250+
client._decoratedSlash.push({
251+
parent,
252+
name: name ?? prop,
253+
guild,
254+
handler: item
255+
})
256+
}
257+
}
258+
259+
export function groupslash(
260+
parent: string,
261+
group: string,
262+
name?: string,
263+
guild?: string
264+
) {
265+
return function (client: Client | SlashModule, prop: string) {
266+
if (client._decoratedSlash === undefined) client._decoratedSlash = []
267+
const item = (client as { [name: string]: any })[prop]
268+
if (typeof item !== 'function') {
269+
item.parent = parent
270+
item.group = group
271+
client._decoratedSlash.push(item)
272+
} else
273+
client._decoratedSlash.push({
274+
group,
275+
parent,
276+
name: name ?? prop,
277+
guild,
278+
handler: item
279+
})
280+
}
281+
}
282+
234283
export function slashModule() {
235284
return function (client: Client, prop: string) {
236285
if (client._decoratedSlashModules === undefined)

src/models/command.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -503,12 +503,13 @@ export const parseCommand = (
503503
client: CommandClient,
504504
msg: Message,
505505
prefix: string
506-
): ParsedCommand => {
506+
): ParsedCommand | undefined => {
507507
let content = msg.content.slice(prefix.length)
508508
if (client.spacesAfterPrefix === true) content = content.trim()
509509
const args = parse(content)._.map((e) => e.toString())
510510

511-
const name = args.shift() as string
511+
const name = args.shift()
512+
if (name === undefined) return
512513
const argString = content.slice(name.length).trim()
513514

514515
return {

src/models/commandClient.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ export class CommandClient extends Client implements CommandClientOptions {
187187
prefix = usedPrefix
188188

189189
const parsed = parseCommand(this, msg, prefix)
190+
if (parsed === undefined) return
190191
const command = this.commands.fetch(parsed)
191192

192193
if (command === undefined) return

src/models/shard.ts

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,69 @@
1-
// TODO: write code
1+
import { Collection } from '../utils/collection.ts'
2+
import { Client, ClientOptions } from './client.ts'
3+
import EventEmitter from 'https://deno.land/[email protected]/node/events.ts'
4+
import { RESTManager } from './rest.ts'
5+
// import { GATEWAY_BOT } from '../types/endpoint.ts'
6+
// import { GatewayBotPayload } from '../types/gatewayBot.ts'
7+
8+
// TODO(DjDeveloperr)
9+
// I'm kinda confused; will continue on this later once
10+
// Deno namespace in Web Woker is stable!
11+
export interface ShardManagerOptions {
12+
client: Client | typeof Client
13+
token?: string
14+
intents?: number[]
15+
options?: ClientOptions
16+
shards: number
17+
}
18+
19+
export interface ShardManagerInitOptions {
20+
file: string
21+
token?: string
22+
intents?: number[]
23+
options?: ClientOptions
24+
shards?: number
25+
}
26+
27+
export class ShardManager extends EventEmitter {
28+
workers: Collection<string, Worker> = new Collection()
29+
token: string
30+
intents: number[]
31+
shardCount: number
32+
private readonly __client: Client
33+
34+
get rest(): RESTManager {
35+
return this.__client.rest
36+
}
37+
38+
constructor(options: ShardManagerOptions) {
39+
super()
40+
this.__client =
41+
options.client instanceof Client
42+
? options.client
43+
: // eslint-disable-next-line new-cap
44+
new options.client(options.options)
45+
46+
if (this.__client.token === undefined || options.token === undefined)
47+
throw new Error('Token should be provided when constructing ShardManager')
48+
if (this.__client.intents === undefined || options.intents === undefined)
49+
throw new Error(
50+
'Intents should be provided when constructing ShardManager'
51+
)
52+
53+
this.token = this.__client.token ?? options.token
54+
this.intents = this.__client.intents ?? options.intents
55+
this.shardCount = options.shards
56+
}
57+
58+
// static async init(): Promise<ShardManager> {}
59+
60+
// async start(): Promise<ShardManager> {
61+
// const info = ((await this.rest.get(
62+
// GATEWAY_BOT()
63+
// )) as unknown) as GatewayBotPayload
64+
65+
// const totalShards = this.__shardCount ?? info.shards
66+
67+
// return this
68+
// }
69+
}

src/models/slashClient.ts

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export class SlashCommand {
3434
this.applicationID = data.application_id
3535
this.name = data.name
3636
this.description = data.description
37-
this.options = data.options
37+
this.options = data.options ?? []
3838
}
3939

4040
async delete(): Promise<void> {
@@ -158,6 +158,8 @@ export type SlashCommandHandlerCallback = (interaction: Interaction) => any
158158
export interface SlashCommandHandler {
159159
name: string
160160
guild?: string
161+
parent?: string
162+
group?: string
161163
handler: SlashCommandHandlerCallback
162164
}
163165

@@ -182,38 +184,45 @@ export class SlashClient {
182184
}
183185

184186
this.client.on('interactionCreate', (interaction) =>
185-
this.process(interaction)
187+
this._process(interaction)
186188
)
187189
}
188190

189191
/** Adds a new Slash Command Handler */
190-
handle(
191-
name: string,
192-
handler: SlashCommandHandlerCallback,
193-
guild?: string
194-
): SlashClient {
195-
this.handlers.push({
196-
name,
197-
guild,
198-
handler
199-
})
192+
handle(handler: SlashCommandHandler): SlashClient {
193+
this.handlers.push(handler)
200194
return this
201195
}
202196

197+
private _getCommand(i: Interaction): SlashCommandHandler | undefined {
198+
return this.handlers.find((e) => {
199+
const hasGroupOrParent = e.group !== undefined || e.parent !== undefined
200+
const groupMatched =
201+
e.group !== undefined && e.parent !== undefined
202+
? i.options
203+
.find((o) => o.name === e.group)
204+
?.options?.find((o) => o.name === e.name) !== undefined
205+
: true
206+
const subMatched =
207+
e.group === undefined && e.parent !== undefined
208+
? i.options.find((o) => o.name === e.name) !== undefined
209+
: true
210+
const nameMatched1 = e.name === i.name
211+
const parentMatched = hasGroupOrParent ? e.parent === i.name : true
212+
const nameMatched = hasGroupOrParent ? parentMatched : nameMatched1
213+
214+
const matched = groupMatched && subMatched && nameMatched
215+
return matched
216+
})
217+
}
218+
203219
/** Process an incoming Slash Command (interaction) */
204-
private process(interaction: Interaction): void {
220+
private _process(interaction: Interaction): void {
205221
if (!this.enabled) return
206222

207223
if (interaction.type !== InteractionType.APPLICATION_COMMAND) return
208224

209-
let cmd
210-
211-
if (interaction.guild !== undefined)
212-
cmd =
213-
this.handlers.find(
214-
(e) => e.guild !== undefined && e.name === interaction.name
215-
) ?? this.handlers.find((e) => e.name === interaction.name)
216-
else cmd = this.handlers.find((e) => e.name === interaction.name)
225+
const cmd = this._getCommand(interaction)
217226

218227
if (cmd === undefined) return
219228

src/structures/slash.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { MessageOption } from '../types/channel.ts'
33
import { INTERACTION_CALLBACK, WEBHOOK_MESSAGE } from '../types/endpoint.ts'
44
import {
55
InteractionData,
6+
InteractionOption,
67
InteractionPayload,
78
InteractionResponsePayload,
89
InteractionResponseType
@@ -76,10 +77,15 @@ export class Interaction {
7677
return this.data.name
7778
}
7879

80+
get options(): InteractionOption[] {
81+
return this.data.options ?? []
82+
}
83+
7984
option<T = any>(name: string): T {
80-
return this.data.options.find((e) => e.name === name)?.value
85+
return this.options.find((e) => e.name === name)?.value
8186
}
8287

88+
/** Respond to an Interaction */
8389
async respond(data: InteractionResponse): Promise<Interaction> {
8490
const payload: InteractionResponsePayload = {
8591
type: data.type ?? InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
@@ -105,6 +111,7 @@ export class Interaction {
105111
return this
106112
}
107113

114+
/** Edit the original Interaction response */
108115
async editResponse(data: {
109116
content?: string
110117
embeds?: Embed[]
@@ -121,6 +128,7 @@ export class Interaction {
121128
return this
122129
}
123130

131+
/** Delete the original Interaction Response */
124132
async deleteResponse(): Promise<Interaction> {
125133
const url = WEBHOOK_MESSAGE(
126134
this.client.user?.id as string,
@@ -135,6 +143,7 @@ export class Interaction {
135143
return `https://discord.com/api/v8/webhooks/${this.client.user?.id}/${this.token}`
136144
}
137145

146+
/** Send a followup message */
138147
async send(
139148
text?: string | AllWebhookMessageOptions,
140149
option?: AllWebhookMessageOptions
@@ -195,6 +204,7 @@ export class Interaction {
195204
return res
196205
}
197206

207+
/** Edit a Followup message */
198208
async editMessage(
199209
msg: Message | string,
200210
data: {

0 commit comments

Comments
 (0)