@pikokr/command.ts 3.0.0-dev.a94f324 → 3.0.0-dev.ca01eb5
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/.prettierrc +2 -1
- package/README.md +2 -2
- package/dist/builtinModules/BuiltinCommandConverters.d.ts +9 -1
- package/dist/builtinModules/BuiltinCommandConverters.js +53 -0
- package/dist/builtinModules/BuiltinSlashCommandConverters.d.ts +9 -0
- package/dist/builtinModules/BuiltinSlashCommandConverters.js +32 -0
- package/dist/builtinModules/CommandHandler.d.ts +4 -2
- package/dist/builtinModules/CommandHandler.js +152 -53
- package/dist/builtinModules/index.d.ts +1 -0
- package/dist/builtinModules/index.js +1 -0
- package/dist/command/ArgumentConverter.d.ts +7 -1
- package/dist/command/ArgumentConverter.js +11 -1
- package/dist/command/Command.d.ts +8 -2
- package/dist/command/Command.js +7 -1
- package/dist/command/cooldown/adapter.d.ts +10 -0
- package/dist/command/cooldown/adapter.js +30 -0
- package/dist/command/cooldown/decorator.d.ts +2 -0
- package/dist/command/cooldown/decorator.js +72 -0
- package/dist/command/cooldown/error.d.ts +4 -0
- package/dist/command/cooldown/error.js +10 -0
- package/dist/command/cooldown/index.d.ts +5 -0
- package/dist/command/cooldown/index.js +17 -0
- package/dist/command/cooldown/type.d.ts +8 -0
- package/dist/command/cooldown/type.js +12 -0
- package/dist/command/decorator.d.ts +9 -1
- package/dist/command/decorator.js +70 -12
- package/dist/command/index.d.ts +2 -0
- package/dist/command/index.js +2 -0
- package/dist/command/utils.d.ts +2 -0
- package/dist/command/utils.js +29 -0
- package/dist/constants.d.ts +6 -0
- package/dist/constants.js +7 -1
- package/dist/error/ArgumentConverterNotFound.d.ts +7 -1
- package/dist/error/ArgumentConverterNotFound.js +9 -1
- package/dist/error/ArgumentNotProvided.d.ts +3 -1
- package/dist/error/ArgumentNotProvided.js +2 -1
- package/dist/error/CommandCheckFailed.d.ts +13 -0
- package/dist/error/CommandCheckFailed.js +19 -0
- package/dist/error/PermissionRequired.d.ts +10 -0
- package/dist/error/PermissionRequired.js +18 -0
- package/dist/error/index.d.ts +2 -0
- package/dist/error/index.js +2 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/slashCommand/SlashCommand.d.ts +19 -0
- package/dist/slashCommand/SlashCommand.js +21 -0
- package/dist/slashCommand/decorator.d.ts +10 -0
- package/dist/slashCommand/decorator.js +37 -0
- package/dist/slashCommand/index.d.ts +2 -0
- package/dist/slashCommand/index.js +14 -0
- package/dist/structures/CommandClient.d.ts +14 -3
- package/dist/structures/CommandClient.js +27 -12
- package/dist/structures/Module.d.ts +5 -1
- package/dist/structures/Module.js +9 -0
- package/dist/structures/Registry.d.ts +16 -6
- package/dist/structures/Registry.js +130 -40
- package/dist/typings.d.ts +18 -0
- package/dist/typings.js +2 -0
- package/package.json +4 -1
- package/src/builtinModules/BuiltinCommandConverters.ts +42 -1
- package/src/builtinModules/BuiltinSlashCommandConverters.ts +18 -0
- package/src/builtinModules/CommandHandler.ts +155 -63
- package/src/builtinModules/index.ts +1 -0
- package/src/command/ArgumentConverter.ts +10 -6
- package/src/command/Command.ts +12 -1
- package/src/command/cooldown/adapter.ts +18 -0
- package/src/command/cooldown/decorator.ts +62 -0
- package/src/command/cooldown/error.ts +5 -0
- package/src/command/cooldown/index.ts +5 -0
- package/src/command/cooldown/type.ts +8 -0
- package/src/command/decorator.ts +102 -36
- package/src/command/index.ts +2 -0
- package/src/command/utils.ts +29 -0
- package/src/constants.ts +12 -0
- package/src/error/ArgumentConverterNotFound.ts +7 -1
- package/src/error/ArgumentNotProvided.ts +2 -1
- package/src/error/CommandCheckFailed.ts +15 -0
- package/src/error/PermissionRequired.ts +13 -0
- package/src/error/index.ts +2 -0
- package/src/index.ts +3 -0
- package/src/slashCommand/SlashCommand.ts +29 -0
- package/src/slashCommand/decorator.ts +58 -0
- package/src/slashCommand/index.ts +2 -0
- package/src/structures/CommandClient.ts +31 -19
- package/src/structures/Module.ts +15 -2
- package/src/structures/Registry.ts +96 -29
- package/src/typings.ts +19 -0
- package/test/index.ts +4 -0
- package/test/modules/test.ts +32 -5
- package/pagesconfig.json +0 -13
package/src/command/Command.ts
CHANGED
|
@@ -1,20 +1,31 @@
|
|
|
1
1
|
import { Module } from '../structures'
|
|
2
|
-
import { Message } from 'discord.js'
|
|
2
|
+
import { CommandInteraction, Message } from 'discord.js'
|
|
3
|
+
import { KCommandChecks } from '../constants'
|
|
3
4
|
|
|
4
5
|
export type Argument = {
|
|
5
6
|
optional: boolean
|
|
6
7
|
type: any
|
|
8
|
+
rest: boolean
|
|
7
9
|
}
|
|
8
10
|
|
|
11
|
+
export type CheckFunction = (msg: Message) => boolean | Promise<boolean>
|
|
12
|
+
export type SlashCheckFunction = (i: CommandInteraction) => boolean | Promise<boolean>
|
|
13
|
+
|
|
9
14
|
export class Command {
|
|
10
15
|
execute(module: Module, args: any[]) {
|
|
11
16
|
return this.run.apply(module, args)
|
|
12
17
|
}
|
|
13
18
|
|
|
19
|
+
get checks(): CheckFunction[] {
|
|
20
|
+
return Reflect.getMetadata(KCommandChecks, this.module, this.key) || []
|
|
21
|
+
}
|
|
22
|
+
|
|
14
23
|
constructor(
|
|
15
24
|
private run: Function,
|
|
16
25
|
public argTypes: Argument[],
|
|
17
26
|
public name: string,
|
|
18
27
|
public aliases: string[] | ((msg: Message) => string[]),
|
|
28
|
+
public module: Module,
|
|
29
|
+
public key: symbol | string,
|
|
19
30
|
) {}
|
|
20
31
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Collection, Guild, GuildChannel, GuildMember, Message, Role, TextChannel, ThreadChannel, User } from 'discord.js'
|
|
2
|
+
|
|
3
|
+
export interface CoolDownAdapter {
|
|
4
|
+
get(id: string): Promise<number | undefined>
|
|
5
|
+
set(id: string, value: number): Promise<void>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class DefaultCoolDownAdapter implements CoolDownAdapter {
|
|
9
|
+
map = new Collection<string, number>()
|
|
10
|
+
async get(id: string): Promise<number | undefined> {
|
|
11
|
+
return this.map.get(id)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async set(id: string, value: number) {
|
|
15
|
+
this.map.set(id, value)
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createCheckDecorator } from '../utils'
|
|
2
|
+
import { CoolDownType } from './type'
|
|
3
|
+
import { DMChannel, GuildMember, TextChannel } from 'discord.js'
|
|
4
|
+
import { CoolDownError } from './error'
|
|
5
|
+
|
|
6
|
+
export const coolDown = (type: CoolDownType, seconds: number) =>
|
|
7
|
+
createCheckDecorator(
|
|
8
|
+
async (msg) => {
|
|
9
|
+
const a = msg.data.cts.coolDownAdapter
|
|
10
|
+
const getKey = (): string => {
|
|
11
|
+
switch (type) {
|
|
12
|
+
case CoolDownType.USER:
|
|
13
|
+
return msg.author.id
|
|
14
|
+
case CoolDownType.GUILD:
|
|
15
|
+
return (msg.guild || msg.author).id
|
|
16
|
+
case CoolDownType.CHANNEL:
|
|
17
|
+
return msg.channel.id
|
|
18
|
+
case CoolDownType.MEMBER:
|
|
19
|
+
return `${msg.guild?.id}.${msg.author.id}`
|
|
20
|
+
case CoolDownType.ROLE:
|
|
21
|
+
return (msg.channel instanceof DMChannel ? msg.channel : msg.member!.roles.highest).id
|
|
22
|
+
case CoolDownType.CATEGORY:
|
|
23
|
+
return ((msg.channel as TextChannel).parent || msg.channel).id
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const key = getKey()
|
|
27
|
+
const data = await a.get(key)
|
|
28
|
+
const now = Date.now()
|
|
29
|
+
if (!data || !(now - data < seconds * 1000)) {
|
|
30
|
+
await a.set(key, now)
|
|
31
|
+
return true
|
|
32
|
+
}
|
|
33
|
+
throw new CoolDownError(new Date(data + seconds * 1000))
|
|
34
|
+
},
|
|
35
|
+
async (i) => {
|
|
36
|
+
const a = i.data.cts.coolDownAdapter
|
|
37
|
+
const getKey = (): string => {
|
|
38
|
+
switch (type) {
|
|
39
|
+
case CoolDownType.USER:
|
|
40
|
+
return i.user.id
|
|
41
|
+
case CoolDownType.GUILD:
|
|
42
|
+
return (i.guild || i.user).id
|
|
43
|
+
case CoolDownType.CHANNEL:
|
|
44
|
+
return i.channel!.id
|
|
45
|
+
case CoolDownType.MEMBER:
|
|
46
|
+
return `${i.guild?.id}.${i.user.id}`
|
|
47
|
+
case CoolDownType.ROLE:
|
|
48
|
+
return (i.channel instanceof DMChannel ? i.channel : (i.member as GuildMember)!.roles.highest).id
|
|
49
|
+
case CoolDownType.CATEGORY:
|
|
50
|
+
return ((i.channel as TextChannel).parent || i.channel!).id
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const key = getKey()
|
|
54
|
+
const data = await a.get(key)
|
|
55
|
+
const now = Date.now()
|
|
56
|
+
if (!data || !(now - data < seconds * 1000)) {
|
|
57
|
+
await a.set(key, now)
|
|
58
|
+
return true
|
|
59
|
+
}
|
|
60
|
+
throw new CoolDownError(new Date(data + seconds * 1000))
|
|
61
|
+
},
|
|
62
|
+
)
|
package/src/command/decorator.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import { KArgumentConverters, KCommands, KOptionals } from '../constants'
|
|
1
|
+
import { KArgumentConverters, KCommands, KOptionals, KRest, KSlashArgumentConverters } from '../constants'
|
|
2
2
|
import { Command } from './Command'
|
|
3
3
|
import { checkTarget } from '../utils'
|
|
4
|
-
import { ArgumentConverter } from './ArgumentConverter'
|
|
4
|
+
import { ArgumentConverter, SlashArgumentConverter } from './ArgumentConverter'
|
|
5
|
+
import { Module } from '../structures'
|
|
6
|
+
import { createCheckDecorator } from './utils'
|
|
7
|
+
import { GuildMember, Message, PermissionResolvable, Permissions, TextChannel } from 'discord.js'
|
|
8
|
+
import { ClientPermissionRequired, UserPermissionRequired } from '../error'
|
|
5
9
|
|
|
6
10
|
type CommandOptions = {
|
|
7
11
|
name: string
|
|
8
|
-
aliases: string[]
|
|
12
|
+
aliases: string[] | ((msg: Message) => string[])
|
|
9
13
|
}
|
|
10
14
|
|
|
11
15
|
export const command = (options: Partial<CommandOptions> = {}) => {
|
|
@@ -18,23 +22,23 @@ export const command = (options: Partial<CommandOptions> = {}) => {
|
|
|
18
22
|
|
|
19
23
|
let properties: Command[] = Reflect.getMetadata(KCommands, target)
|
|
20
24
|
|
|
21
|
-
const params: any[] = Reflect.getMetadata(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
propertyKey,
|
|
25
|
-
)
|
|
25
|
+
const params: any[] = Reflect.getMetadata('design:paramtypes', target, propertyKey)
|
|
26
|
+
|
|
27
|
+
const optionals: number = Reflect.getMetadata(KOptionals, target, propertyKey) || -1
|
|
26
28
|
|
|
27
|
-
const
|
|
28
|
-
Reflect.getMetadata(KOptionals, target, propertyKey) || []
|
|
29
|
+
const rest = Reflect.getMetadata(KRest, target, propertyKey) || -1
|
|
29
30
|
|
|
30
31
|
const command = new Command(
|
|
31
32
|
Reflect.get(target, propertyKey),
|
|
32
33
|
params.map((x, i) => ({
|
|
33
34
|
type: x,
|
|
34
|
-
optional: optionals
|
|
35
|
+
optional: optionals === -1 ? false : optionals <= i,
|
|
36
|
+
rest: rest === -1 ? false : rest === i,
|
|
35
37
|
})),
|
|
36
38
|
options.name || propertyKey,
|
|
37
39
|
options.aliases || [],
|
|
40
|
+
target as Module,
|
|
41
|
+
propertyKey,
|
|
38
42
|
)
|
|
39
43
|
|
|
40
44
|
if (properties) {
|
|
@@ -54,16 +58,9 @@ export const argumentConverter = (type: object, requireParameter = true) => {
|
|
|
54
58
|
) => {
|
|
55
59
|
checkTarget(target)
|
|
56
60
|
|
|
57
|
-
let properties: ArgumentConverter[] = Reflect.getMetadata(
|
|
58
|
-
KArgumentConverters,
|
|
59
|
-
target,
|
|
60
|
-
)
|
|
61
|
+
let properties: ArgumentConverter[] = Reflect.getMetadata(KArgumentConverters, target)
|
|
61
62
|
|
|
62
|
-
const converter = new ArgumentConverter(
|
|
63
|
-
type,
|
|
64
|
-
Reflect.get(target, propertyKey),
|
|
65
|
-
!requireParameter,
|
|
66
|
-
)
|
|
63
|
+
const converter = new ArgumentConverter(type, Reflect.get(target, propertyKey), !requireParameter)
|
|
67
64
|
|
|
68
65
|
if (properties) {
|
|
69
66
|
properties.push(converter)
|
|
@@ -74,23 +71,92 @@ export const argumentConverter = (type: object, requireParameter = true) => {
|
|
|
74
71
|
}
|
|
75
72
|
}
|
|
76
73
|
|
|
77
|
-
export const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
74
|
+
export const slashArgumentConverter = (type: object) => {
|
|
75
|
+
return (
|
|
76
|
+
target: Object,
|
|
77
|
+
propertyKey: string,
|
|
78
|
+
// descriptor: TypedPropertyDescriptor<any>,
|
|
79
|
+
) => {
|
|
80
|
+
checkTarget(target)
|
|
83
81
|
|
|
84
|
-
|
|
85
|
-
KOptionals,
|
|
86
|
-
target,
|
|
87
|
-
propertyKey,
|
|
88
|
-
)
|
|
82
|
+
let properties: SlashArgumentConverter[] = Reflect.getMetadata(KSlashArgumentConverters, target)
|
|
89
83
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
84
|
+
const converter = new SlashArgumentConverter(type, Reflect.get(target, propertyKey))
|
|
85
|
+
|
|
86
|
+
if (properties) {
|
|
87
|
+
properties.push(converter)
|
|
88
|
+
} else {
|
|
89
|
+
properties = [converter]
|
|
90
|
+
Reflect.defineMetadata(KSlashArgumentConverters, properties, target)
|
|
91
|
+
}
|
|
95
92
|
}
|
|
96
93
|
}
|
|
94
|
+
|
|
95
|
+
export const optional: ParameterDecorator = (target, propertyKey, parameterIndex) => {
|
|
96
|
+
checkTarget(target)
|
|
97
|
+
|
|
98
|
+
Reflect.defineMetadata(KOptionals, parameterIndex, target, propertyKey)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const rest: ParameterDecorator = (target, propertyKey, parameterIndex) => {
|
|
102
|
+
checkTarget(target)
|
|
103
|
+
|
|
104
|
+
const params: any[] = Reflect.getMetadata('design:paramtypes', target, propertyKey)
|
|
105
|
+
|
|
106
|
+
if (params.length - 1 !== parameterIndex) throw new Error('Rest decorator must be used at last argument.')
|
|
107
|
+
|
|
108
|
+
if (params[parameterIndex] !== String) throw new Error('Rest argument type must be "String"')
|
|
109
|
+
|
|
110
|
+
Reflect.defineMetadata(KRest, parameterIndex, target, propertyKey)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export const ownerOnly = createCheckDecorator(
|
|
114
|
+
(msg) => msg.data.cts.owners.includes(msg.author.id),
|
|
115
|
+
(i) => i.data.cts.owners.includes(i.user.id),
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
export const guildOnly = createCheckDecorator(
|
|
119
|
+
(msg) => !!msg.guild,
|
|
120
|
+
(i) => !!i.guildId,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
export const dmOnly = createCheckDecorator(
|
|
124
|
+
(msg) => !msg.guild,
|
|
125
|
+
(i) => !i.guildId,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
export const requireUserPermissions = (permission: PermissionResolvable) =>
|
|
129
|
+
createCheckDecorator(
|
|
130
|
+
(msg) => {
|
|
131
|
+
if (!msg.guild || !msg.member) throw new Error('This command must be used in guild.')
|
|
132
|
+
if (msg.member.permissionsIn(msg.channel as TextChannel).has(permission)) {
|
|
133
|
+
return true
|
|
134
|
+
}
|
|
135
|
+
throw new UserPermissionRequired(msg.member, new Permissions(permission))
|
|
136
|
+
},
|
|
137
|
+
(i) => {
|
|
138
|
+
if (!i.guild || !i.member) throw new Error('This command must be used in serer.')
|
|
139
|
+
if (!(i.member instanceof GuildMember) || i.member.permissionsIn(i.channel as TextChannel).has(permission)) {
|
|
140
|
+
return true
|
|
141
|
+
}
|
|
142
|
+
throw new UserPermissionRequired(i.member, new Permissions(permission))
|
|
143
|
+
},
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
export const requireClientPermissions = (permission: PermissionResolvable) =>
|
|
147
|
+
createCheckDecorator(
|
|
148
|
+
(msg) => {
|
|
149
|
+
if (!msg.guild) throw new Error('This command must be used in guild.')
|
|
150
|
+
if (msg.guild.me!.permissionsIn(msg.channel as TextChannel).has(permission)) {
|
|
151
|
+
return true
|
|
152
|
+
}
|
|
153
|
+
throw new ClientPermissionRequired(new Permissions(permission))
|
|
154
|
+
},
|
|
155
|
+
(i) => {
|
|
156
|
+
if (!i.guild) throw new Error('This command must be used in guild.')
|
|
157
|
+
if (i.guild.me!.permissionsIn(i.channel as TextChannel).has(permission)) {
|
|
158
|
+
return true
|
|
159
|
+
}
|
|
160
|
+
throw new ClientPermissionRequired(new Permissions(permission))
|
|
161
|
+
},
|
|
162
|
+
)
|
package/src/command/index.ts
CHANGED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { CommandInteraction, Message } from 'discord.js'
|
|
2
|
+
import type { CheckFunction, SlashCheckFunction } from './Command'
|
|
3
|
+
import { KCommandChecks, KSlashCommandChecks } from '../constants'
|
|
4
|
+
|
|
5
|
+
export const createCheckDecorator = (
|
|
6
|
+
execute: ((msg: Message) => boolean | Promise<boolean>) | null,
|
|
7
|
+
slashExecute?: (i: CommandInteraction) => boolean | Promise<boolean>,
|
|
8
|
+
): MethodDecorator => {
|
|
9
|
+
return (target, propertyKey) => {
|
|
10
|
+
if (execute) {
|
|
11
|
+
let properties: CheckFunction[] = Reflect.getMetadata(KCommandChecks, target, propertyKey)
|
|
12
|
+
if (properties) {
|
|
13
|
+
properties.push(execute)
|
|
14
|
+
} else {
|
|
15
|
+
properties = [execute]
|
|
16
|
+
Reflect.defineMetadata(KCommandChecks, properties, target, propertyKey)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (slashExecute) {
|
|
20
|
+
let properties: SlashCheckFunction[] = Reflect.getMetadata(KSlashCommandChecks, target, propertyKey)
|
|
21
|
+
if (properties) {
|
|
22
|
+
properties.push(slashExecute)
|
|
23
|
+
} else {
|
|
24
|
+
properties = [slashExecute]
|
|
25
|
+
Reflect.defineMetadata(KSlashCommandChecks, properties, target, propertyKey)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
package/src/constants.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export const KCommands = Symbol('Command.TS Commands')
|
|
2
2
|
|
|
3
|
+
export const KSlashCommands = Symbol('Command.TS Slash Commands')
|
|
4
|
+
|
|
5
|
+
export const KSlashCommandOptions = Symbol('Command.TS Slash Command Options')
|
|
6
|
+
|
|
3
7
|
export const KListeners = Symbol('Command.TS Listeners')
|
|
4
8
|
|
|
5
9
|
export const KModulePath = Symbol('Command.TS Module Path')
|
|
@@ -10,4 +14,12 @@ export const KBuiltInModule = Symbol('Command.TS Built-In Module')
|
|
|
10
14
|
|
|
11
15
|
export const KOptionals = Symbol('Command.TS Optional Parameters')
|
|
12
16
|
|
|
17
|
+
export const KRest = Symbol('Command.TS Rest Parameter')
|
|
18
|
+
|
|
13
19
|
export const KArgumentConverters = Symbol('Command.TS Argument Converter')
|
|
20
|
+
|
|
21
|
+
export const KSlashArgumentConverters = Symbol('Command.TS Slash Argument Converter')
|
|
22
|
+
|
|
23
|
+
export const KCommandChecks = Symbol('Command.TS Command Checks')
|
|
24
|
+
|
|
25
|
+
export const KSlashCommandChecks = Symbol('Command.TS Slash Command Checks')
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import { Message } from 'discord.js'
|
|
1
|
+
import { CommandInteraction, Message } from 'discord.js'
|
|
2
2
|
import type { Argument } from '../command'
|
|
3
|
+
import type { SlashArgument } from '../slashCommand'
|
|
3
4
|
|
|
4
5
|
export class ArgumentConverterNotFound extends Error {
|
|
5
6
|
constructor(public type: Argument, public msg: Message) {
|
|
6
7
|
super(`Argument converter ${type.type.name} not found.`)
|
|
7
8
|
}
|
|
8
9
|
}
|
|
10
|
+
export class SlashArgumentConverterNotFound extends Error {
|
|
11
|
+
constructor(public type: SlashArgument, public interaction: CommandInteraction) {
|
|
12
|
+
super(`Argument converter ${type.type.name} not found.`)
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Message } from 'discord.js'
|
|
2
|
+
import { Command } from '../command'
|
|
2
3
|
|
|
3
4
|
export class ArgumentNotProvided extends Error {
|
|
4
|
-
constructor(public index: number, public msg: Message) {
|
|
5
|
+
constructor(public index: number, public command: Command, public msg: Message) {
|
|
5
6
|
super(`Required argument #${index} not provided.`)
|
|
6
7
|
}
|
|
7
8
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { CommandInteraction, Message } from 'discord.js'
|
|
2
|
+
import { Command } from '../command'
|
|
3
|
+
import { SlashCommand } from '../slashCommand'
|
|
4
|
+
|
|
5
|
+
export class CommandCheckFailed extends Error {
|
|
6
|
+
constructor(public msg: Message, public command: Command) {
|
|
7
|
+
super()
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class SlashCommandCheckFailed extends Error {
|
|
12
|
+
constructor(public msg: CommandInteraction, public command: SlashCommand) {
|
|
13
|
+
super()
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { GuildMember, Permissions } from 'discord.js'
|
|
2
|
+
|
|
3
|
+
export class UserPermissionRequired extends Error {
|
|
4
|
+
constructor(public user: GuildMember, public permissions: Permissions) {
|
|
5
|
+
super()
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class ClientPermissionRequired extends Error {
|
|
10
|
+
constructor(public permissions: Permissions) {
|
|
11
|
+
super()
|
|
12
|
+
}
|
|
13
|
+
}
|
package/src/error/index.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import 'reflect-metadata'
|
|
2
|
+
import './typings'
|
|
2
3
|
|
|
3
4
|
export * from './interface'
|
|
4
5
|
export * from './structures'
|
|
5
6
|
export * from './error'
|
|
6
7
|
export * from './command'
|
|
7
8
|
export * from './listener'
|
|
9
|
+
export * from './builtinModules/BuiltInModule'
|
|
10
|
+
export * from './slashCommand'
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { SlashCommandBuilder } from '@discordjs/builders'
|
|
2
|
+
import { Module } from '../structures'
|
|
3
|
+
import { Snowflake } from 'discord.js'
|
|
4
|
+
import { KSlashCommandChecks } from '../constants'
|
|
5
|
+
import { SlashCheckFunction } from '../command'
|
|
6
|
+
|
|
7
|
+
export type SlashArgument = {
|
|
8
|
+
type: any
|
|
9
|
+
name?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class SlashCommand {
|
|
13
|
+
get checks(): SlashCheckFunction[] {
|
|
14
|
+
return Reflect.getMetadata(KSlashCommandChecks, this.module, this.key) || []
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
execute(module: Module, args: any[]) {
|
|
18
|
+
return this.run.apply(module, args)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
constructor(
|
|
22
|
+
public commandBuilder: SlashCommandBuilder | Omit<SlashCommandBuilder, 'addSubcommand' | 'addSubcommandGroup'>,
|
|
23
|
+
private run: Function,
|
|
24
|
+
public module: Module,
|
|
25
|
+
public params: SlashArgument[],
|
|
26
|
+
public guild: Snowflake | Snowflake[] | undefined,
|
|
27
|
+
private key: string | symbol,
|
|
28
|
+
) {}
|
|
29
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Collection, Snowflake } from 'discord.js'
|
|
2
|
+
import { checkTarget } from '../utils'
|
|
3
|
+
import { KSlashCommandOptions, KSlashCommands } from '../constants'
|
|
4
|
+
import { Module } from '../structures'
|
|
5
|
+
import { SlashCommand } from './SlashCommand'
|
|
6
|
+
import { SlashCommandBuilder } from '@discordjs/builders'
|
|
7
|
+
|
|
8
|
+
type SlashOptions = {
|
|
9
|
+
guild: Snowflake | Snowflake[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const slashCommand = (opt: Partial<SlashOptions> & { command: SlashCommandBuilder | Omit<SlashCommandBuilder, 'addSubcommand' | 'addSubcommandGroup'> }) => {
|
|
13
|
+
return (
|
|
14
|
+
target: Object,
|
|
15
|
+
propertyKey: string,
|
|
16
|
+
// descriptor: TypedPropertyDescriptor<any>,
|
|
17
|
+
) => {
|
|
18
|
+
checkTarget(target)
|
|
19
|
+
|
|
20
|
+
let properties: SlashCommand[] = Reflect.getMetadata(KSlashCommands, target)
|
|
21
|
+
|
|
22
|
+
const params: any[] = Reflect.getMetadata('design:paramtypes', target, propertyKey)
|
|
23
|
+
|
|
24
|
+
const options: Collection<number, string> = Reflect.getMetadata(KSlashCommandOptions, target, propertyKey) || new Collection<number, string>()
|
|
25
|
+
|
|
26
|
+
const command = new SlashCommand(
|
|
27
|
+
opt.command,
|
|
28
|
+
Reflect.get(target, propertyKey),
|
|
29
|
+
target as Module,
|
|
30
|
+
params.map((x, i) => ({
|
|
31
|
+
type: x,
|
|
32
|
+
name: options.get(i),
|
|
33
|
+
})),
|
|
34
|
+
opt.guild,
|
|
35
|
+
propertyKey,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
if (properties) {
|
|
39
|
+
properties.push(command)
|
|
40
|
+
} else {
|
|
41
|
+
properties = [command]
|
|
42
|
+
Reflect.defineMetadata(KSlashCommands, properties, target)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const option = (key: string): ParameterDecorator => (target, propertyKey, parameterIndex) => {
|
|
48
|
+
checkTarget(target)
|
|
49
|
+
|
|
50
|
+
let properties: Collection<number, string> = Reflect.getMetadata(KSlashCommandOptions, target, propertyKey)
|
|
51
|
+
|
|
52
|
+
if (!properties) {
|
|
53
|
+
properties = new Collection<number, string>()
|
|
54
|
+
Reflect.defineMetadata(KSlashCommandOptions, properties, target, propertyKey)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
properties.set(parameterIndex, key)
|
|
58
|
+
}
|
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
import _ from 'lodash'
|
|
2
2
|
import { Registry } from './Registry'
|
|
3
|
-
import { Client,
|
|
4
|
-
import { BuiltinCommandConverters, CommandHandler } from '../builtinModules'
|
|
3
|
+
import { Client, Snowflake, User } from 'discord.js'
|
|
4
|
+
import { BuiltinCommandConverters, BuiltinSlashCommandConverters, CommandHandler } from '../builtinModules'
|
|
5
|
+
import { CoolDownAdapter, DefaultCoolDownAdapter } from '../command'
|
|
6
|
+
import { REST } from '@discordjs/rest'
|
|
5
7
|
|
|
6
8
|
export interface CommandOptions {
|
|
7
|
-
prefix:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
prefix: string | ((msg: any) => string | Promise<string | string[]> | string[]) | string[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface SlashCommandOptions {
|
|
13
|
+
guild?: Snowflake | Snowflake[]
|
|
14
|
+
autoSync: boolean
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
export interface CommandClientOptions {
|
|
14
18
|
command: CommandOptions
|
|
15
|
-
owners: 'auto' |
|
|
19
|
+
owners: 'auto' | Snowflake[]
|
|
20
|
+
slashCommands: SlashCommandOptions
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
export interface CommandClientOptionsParam {
|
|
19
24
|
command: Partial<CommandOptions>
|
|
20
25
|
owners: 'auto' | string[]
|
|
26
|
+
slashCommands: Partial<SlashCommandOptions>
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
export class CommandClient {
|
|
@@ -25,10 +31,15 @@ export class CommandClient {
|
|
|
25
31
|
owners: string[] = []
|
|
26
32
|
registry = new Registry(this)
|
|
27
33
|
client: Client
|
|
34
|
+
coolDownAdapter: CoolDownAdapter
|
|
35
|
+
rest = new REST({
|
|
36
|
+
version: '9',
|
|
37
|
+
})
|
|
28
38
|
|
|
29
39
|
private _isReady = false
|
|
30
40
|
|
|
31
|
-
private fetchOwners(): string[] {
|
|
41
|
+
private async fetchOwners(): Promise<string[]> {
|
|
42
|
+
await this.client.application?.fetch()
|
|
32
43
|
const o = this.client.application?.owner
|
|
33
44
|
if (!o) return []
|
|
34
45
|
if (o instanceof User) return [o.id]
|
|
@@ -37,29 +48,30 @@ export class CommandClient {
|
|
|
37
48
|
|
|
38
49
|
async ready() {
|
|
39
50
|
if (this._isReady) return
|
|
51
|
+
this.rest.setToken(this.client.token!)
|
|
40
52
|
this._isReady = true
|
|
41
53
|
if (this.options.owners === 'auto') {
|
|
42
|
-
const owners = this.fetchOwners()
|
|
54
|
+
const owners = await this.fetchOwners()
|
|
43
55
|
this.owners.push(...owners)
|
|
44
56
|
}
|
|
57
|
+
await this.registry.syncCommands()
|
|
45
58
|
}
|
|
46
59
|
|
|
47
|
-
constructor({
|
|
48
|
-
client,
|
|
49
|
-
...options
|
|
50
|
-
}: Partial<CommandClientOptionsParam> & { client: Client }) {
|
|
60
|
+
constructor({ client, coolDownAdapter, ...options }: Partial<CommandClientOptionsParam> & { client: Client; coolDownAdapter?: CoolDownAdapter }) {
|
|
51
61
|
this.client = client
|
|
52
|
-
this.
|
|
53
|
-
|
|
54
|
-
CommandClientOptions
|
|
55
|
-
>(options, {
|
|
62
|
+
this.coolDownAdapter = coolDownAdapter || new DefaultCoolDownAdapter()
|
|
63
|
+
this.options = _.merge<Partial<CommandClientOptionsParam>, CommandClientOptions>(options, {
|
|
56
64
|
command: {
|
|
57
65
|
prefix: '!',
|
|
58
66
|
},
|
|
59
67
|
owners: 'auto',
|
|
68
|
+
slashCommands: {
|
|
69
|
+
autoSync: true,
|
|
70
|
+
},
|
|
60
71
|
})
|
|
61
|
-
this.client.once('ready', this.ready)
|
|
72
|
+
this.client.once('ready', () => this.ready())
|
|
62
73
|
this.registry.registerModule(new CommandHandler(this.registry))
|
|
63
|
-
this.registry.registerModule(new BuiltinCommandConverters())
|
|
74
|
+
this.registry.registerModule(new BuiltinCommandConverters(this))
|
|
75
|
+
this.registry.registerModule(new BuiltinSlashCommandConverters(this))
|
|
64
76
|
}
|
|
65
77
|
}
|
package/src/structures/Module.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { KArgumentConverters, KCommands, KListeners } from '../constants'
|
|
1
|
+
import { KArgumentConverters, KCommands, KListeners, KModulePath, KSlashArgumentConverters, KSlashCommands } from '../constants'
|
|
2
2
|
import type { Command } from '../command'
|
|
3
3
|
import { Listener } from '../listener'
|
|
4
|
-
import { ArgumentConverter } from '../command'
|
|
4
|
+
import { ArgumentConverter, SlashArgumentConverter } from '../command'
|
|
5
|
+
import { SlashCommand } from '../slashCommand'
|
|
5
6
|
|
|
6
7
|
export abstract class Module {
|
|
7
8
|
get commands(): Command[] {
|
|
@@ -16,6 +17,18 @@ export abstract class Module {
|
|
|
16
17
|
return Reflect.getMetadata(KArgumentConverters, this) || []
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
get slashArgumentConverters(): SlashArgumentConverter[] {
|
|
21
|
+
return Reflect.getMetadata(KSlashArgumentConverters, this) || []
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get slashCommands(): SlashCommand[] {
|
|
25
|
+
return Reflect.getMetadata(KSlashCommands, this) || []
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get path(): string | undefined {
|
|
29
|
+
return Reflect.getMetadata(KModulePath, this)
|
|
30
|
+
}
|
|
31
|
+
|
|
19
32
|
load() {}
|
|
20
33
|
unload() {}
|
|
21
34
|
beforeReload() {}
|