Skip to content
This repository was archived by the owner on May 22, 2022. It is now read-only.

Commit a1f24f6

Browse files
committed
feat: application commands hint provider in client options, and set name from base command options
1 parent 66d5b92 commit a1f24f6

File tree

3 files changed

+69
-7
lines changed

3 files changed

+69
-7
lines changed

src/lib/ApplicationCommand.ts

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ApplicationCommandData, ApplicationCommandPermissionData, ChatInputApplicationCommandData, MessageApplicationCommandData, UserApplicationCommandData } from 'discord.js'
22
import type { Command } from '@sapphire/framework'
3+
import { container } from '@sapphire/framework'
34

45
const types = [ 'chatInput', 'message', 'user' ] as const
56

@@ -10,11 +11,22 @@ export class ApplicationCommand {
1011
public messageApplicationIdHints: Set<string> | null = null
1112
public userApplicationIdHints: Set<string> | null = null
1213

13-
public constructor( options: ApplicationCommandOptions ) {
14+
private constructor( options: ApplicationCommandOptions ) {
1415
this.command = options.command
1516

17+
if ( this.command.options.chatInputApplicationOptions ) {
18+
this.command.options.chatInputApplicationOptions.description ??= this.command.description
19+
}
20+
1621
for ( const type of types ) {
17-
const idHints = this.command.options[ `${ type }ApplicationOptions` ]?.idHints
22+
// Set some defaults
23+
const options = this.command.options[ `${ type }ApplicationOptions` ]
24+
if ( !options ) continue
25+
26+
options.name ??= this.command.name
27+
28+
// Store id hints
29+
const { idHints } = options
1830
if ( idHints && idHints.length > 0 ) {
1931
const set = new Set<string>()
2032
idHints.forEach( id => set.add( id ) )
@@ -23,6 +35,33 @@ export class ApplicationCommand {
2335
}
2436
}
2537

38+
public static async create( options: ApplicationCommandOptions ): Promise<ApplicationCommand> {
39+
const instance = new ApplicationCommand( options )
40+
await instance.fillHints()
41+
return instance
42+
}
43+
44+
protected async fillHints(): Promise<void> {
45+
const clientOptions = container.client.options
46+
const hintProvider = clientOptions.applicationCommandsHintProvider
47+
if ( !hintProvider ) return
48+
49+
for ( const type of types ) {
50+
const hints = await hintProvider( this.command.name, type )
51+
if ( !hints ) continue
52+
const options = this.command.options[ `${ type }ApplicationOptions` ]
53+
if ( !options ) continue
54+
if ( hints.guildIds ) {
55+
options.guildIds ??= []
56+
options.guildIds.push( ...hints.guildIds )
57+
}
58+
if ( hints.idHints ) {
59+
options.idHints ??= []
60+
options.idHints.push( ...hints.idHints )
61+
}
62+
}
63+
}
64+
2665
public get allGuilds(): string[] | null {
2766
const chatInputHints = this.chatInputApplicationGuilds
2867
const messageHints = this.messageApplicationGuilds
@@ -68,6 +107,15 @@ export class ApplicationCommand {
68107
public get chatInputApplicationData(): ChatInputApplicationCommandData | null {
69108
const options = this.command.options.chatInputApplicationOptions
70109
if ( !options ) return null
110+
if ( !options.description ) {
111+
container.logger.error( 'Missing required property "description"' )
112+
return null
113+
}
114+
if ( !options.name ) {
115+
container.logger.error( 'Missing required property "name"' )
116+
return null
117+
}
118+
71119
return {
72120
defaultPermission: options.defaultPermission ?? true,
73121
description: options.description,
@@ -80,6 +128,10 @@ export class ApplicationCommand {
80128
public get messageApplicationData(): MessageApplicationCommandData | null {
81129
const options = this.command.options.messageApplicationOptions
82130
if ( !options ) return null
131+
if ( !options.name ) {
132+
container.logger.error( 'Missing required property "name"' )
133+
return null
134+
}
83135
return {
84136
defaultPermission: options.defaultPermission ?? true,
85137
name: options.name,
@@ -90,6 +142,10 @@ export class ApplicationCommand {
90142
public get userApplicationData(): UserApplicationCommandData | null {
91143
const options = this.command.options.userApplicationOptions
92144
if ( !options ) return null
145+
if ( !options.name ) {
146+
container.logger.error( 'Missing required property "name"' )
147+
return null
148+
}
93149
return {
94150
defaultPermission: options.defaultPermission ?? true,
95151
name: options.name,

src/listeners/Ready.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export class UserEvent extends Listener {
1414
public async run(): Promise<void> {
1515
this.container.applicationCommands = new Map()
1616

17-
const [ applicationCommandsList, guildIds ] = this.getLists()
17+
const [ applicationCommandsList, guildIds ] = await this.getLists()
1818

1919
const [ globalCommandList, guildCommandList ] = this.filterCommandList( applicationCommandsList )
2020

@@ -37,14 +37,14 @@ export class UserEvent extends Listener {
3737
return [ globalList, guildList ]
3838
}
3939

40-
protected getLists(): [ ApplicationCommand[], Set<string> ] {
40+
protected async getLists(): Promise<[ ApplicationCommand[], Set<string> ]> {
4141
const list: ApplicationCommand[] = []
4242
const guilds = new Set<string>()
4343
const commands = this.container.stores.get( 'commands' )
4444

4545
for ( const [ , command ] of commands ) {
4646
if ( !command.options.chatInputApplicationOptions && !command.options.messageApplicationOptions && !command.options.userApplicationOptions ) continue
47-
const applicationCommand = new ApplicationCommand( {
47+
const applicationCommand = await ApplicationCommand.create( {
4848
command
4949
} )
5050
list.push( applicationCommand )

src/main.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
import type { ApplicationCommandPermissionData, BaseApplicationCommandData, ChatInputApplicationCommandData, CommandInteraction, MessageApplicationCommandData, MessageInteraction, UserApplicationCommandData, UserContextMenuInteraction } from 'discord.js'
22
import type { ApplicationCommand } from './lib'
3+
import type { Awaitable } from '@sapphire/framework'
34

45
export * from './lib'
56

6-
type NoType<T> = Omit<T, 'type'>
7+
type ApplicationCommandOptionsFilter<T extends BaseApplicationCommandData> = Omit<T, 'type' | 'name' | 'description'>
8+
& Partial<Pick<T, T extends ChatInputApplicationCommandData ? 'name' | 'description' : 'name'>>
79

810
interface ApplicationCommandAdditionalData {
911
guildIds?: string[]
1012
idHints?: string[]
1113
}
1214

13-
type ApplicationCommandOptions<T extends BaseApplicationCommandData> = NoType<T> & ApplicationCommandAdditionalData & {
15+
type ApplicationCommandOptions<T extends BaseApplicationCommandData> = ApplicationCommandOptionsFilter<T> & ApplicationCommandAdditionalData & {
1416
permissions?: ApplicationCommandPermissionData[]
1517
}
1618

1719
declare module '@sapphire/framework' {
20+
interface SapphireClientOptions {
21+
applicationCommandsHintProvider?: ( name: string, type: 'chatInput' | 'message' | 'user' ) => Awaitable<ApplicationCommandAdditionalData | undefined | null>
22+
}
23+
1824
class Command {
1925
public chatInputApplicationRun?( interaction: CommandInteraction ): void | Promise<void>
2026
public messageApplicationRun?( interaction: MessageInteraction ): void | Promise<void>

0 commit comments

Comments
 (0)