11import type { CommandKit } from '../../CommandKit' ;
22import {
33 Awaitable ,
4+ ChatInputCommandInteraction ,
45 Collection ,
56 ContextMenuCommandBuilder ,
7+ Events ,
68 Interaction ,
79 Locale ,
810 Message ,
11+ PartialMessage ,
912 SlashCommandBuilder ,
1013} from 'discord.js' ;
11- import { Context } from '../commands/Context' ;
14+ import {
15+ CommandExecutionMode ,
16+ Context ,
17+ MiddlewareContext ,
18+ } from '../commands/Context' ;
1219import { toFileURL } from '../../utils/resolve-file-url' ;
1320import { TranslatableCommandOptions } from '../i18n/Translation' ;
1421import { MessageCommandParser } from '../commands/MessageCommandParser' ;
1522import { CommandKitErrorCodes , isErrorType } from '../../utils/error-codes' ;
1623import { ParsedCommand , ParsedMiddleware } from '../router' ;
1724import { CommandRegistrar } from '../register/CommandRegistrar' ;
25+ import { GenericFunction } from '../../context/async-context' ;
26+ import { Logger } from '../../logger/Logger' ;
1827
1928interface AppCommand {
2029 command : SlashCommandBuilder | Record < string , any > ;
@@ -73,6 +82,11 @@ export class AppCommandHandler {
7382 private loadedCommands = new Collection < string , LoadedCommand > ( ) ;
7483 private loadedMiddlewares = new Collection < string , LoadedMiddleware > ( ) ;
7584 public readonly registrar : CommandRegistrar ;
85+ private onInteraction : GenericFunction < [ Interaction ] > | null = null ;
86+ private onMessageCreate : GenericFunction < [ Message ] > | null = null ;
87+ private onMessageUpdate : GenericFunction <
88+ [ Message | PartialMessage , Message | PartialMessage ]
89+ > | null = null ;
7690
7791 public constructor ( public readonly commandkit : CommandKit ) {
7892 this . registrar = new CommandRegistrar ( this . commandkit ) ;
@@ -83,6 +97,154 @@ export class AppCommandHandler {
8397 return loaded ;
8498 }
8599
100+ public registerCommandHandler ( ) {
101+ this . onInteraction ??= async ( interaction : Interaction ) => {
102+ const success = await this . commandkit . plugins . execute (
103+ async ( ctx , plugin ) => {
104+ return plugin . onBeforeInteraction ( ctx , interaction ) ;
105+ } ,
106+ ) ;
107+
108+ // plugin will handle the interaction
109+ if ( success ) return ;
110+
111+ const isCommandLike =
112+ interaction . isCommand ( ) ||
113+ interaction . isAutocomplete ( ) ||
114+ interaction . isUserContextMenuCommand ( ) ||
115+ interaction . isMessageContextMenuCommand ( ) ;
116+
117+ if ( ! isCommandLike ) return ;
118+
119+ const command = await this . prepareCommandRun ( interaction ) ;
120+
121+ if ( ! command ) return ;
122+
123+ return this . runCommand ( command , interaction ) ;
124+ } ;
125+
126+ this . onMessageCreate ??= async ( message : Message ) => {
127+ const success = await this . commandkit . plugins . execute (
128+ async ( ctx , plugin ) => {
129+ return plugin . onBeforeMessageCommand ( ctx , message ) ;
130+ } ,
131+ ) ;
132+
133+ // plugin will handle the message
134+ if ( success ) return ;
135+ if ( message . author . bot ) return ;
136+
137+ const command = await this . prepareCommandRun ( message ) ;
138+
139+ if ( ! command ) return ;
140+
141+ return this . runCommand ( command , message ) ;
142+ } ;
143+
144+ this . onMessageUpdate ??= async (
145+ oldMessage : Message | PartialMessage ,
146+ newMessage : Message | PartialMessage ,
147+ ) => {
148+ const success = await this . commandkit . plugins . execute (
149+ async ( ctx , plugin ) => {
150+ return plugin . onBeforeMessageUpdateCommand (
151+ ctx ,
152+ oldMessage ,
153+ newMessage ,
154+ ) ;
155+ } ,
156+ ) ;
157+
158+ // plugin will handle the message
159+ if ( success ) return ;
160+ if ( oldMessage . partial || newMessage . partial ) return ;
161+ if ( oldMessage . author . bot ) return ;
162+
163+ const command = await this . prepareCommandRun ( newMessage ) ;
164+
165+ if ( ! command ) return ;
166+
167+ return this . runCommand ( command , newMessage ) ;
168+ } ;
169+
170+ this . commandkit . client . on ( Events . InteractionCreate , this . onInteraction ) ;
171+ this . commandkit . client . on ( Events . MessageCreate , this . onMessageCreate ) ;
172+ this . commandkit . client . on ( Events . MessageUpdate , this . onMessageUpdate ) ;
173+ }
174+
175+ public getExecutionMode ( source : Interaction | Message ) : CommandExecutionMode {
176+ if ( source instanceof Message ) return CommandExecutionMode . Message ;
177+ if ( source . isChatInputCommand ( ) ) return CommandExecutionMode . SlashCommand ;
178+ if ( source . isAutocomplete ( ) ) {
179+ return CommandExecutionMode . Autocomplete ;
180+ }
181+ if ( source . isMessageContextMenuCommand ( ) ) {
182+ return CommandExecutionMode . MessageContextMenu ;
183+ }
184+ if ( source . isUserContextMenuCommand ( ) ) {
185+ return CommandExecutionMode . UserContextMenu ;
186+ }
187+
188+ return null as never ;
189+ }
190+
191+ public async runCommand (
192+ command : PreparedAppCommandExecution ,
193+ source : Interaction | Message ,
194+ ) {
195+ if (
196+ source instanceof Message &&
197+ ( source . editedTimestamp || source . partial )
198+ ) {
199+ // TODO: handle message edit
200+ return ;
201+ }
202+
203+ const executionMode = this . getExecutionMode ( source ) ;
204+
205+ const ctx = new MiddlewareContext ( this . commandkit , {
206+ executionMode,
207+ interaction : ! ( source instanceof Message )
208+ ? ( source as ChatInputCommandInteraction )
209+ : ( null as never ) ,
210+ message : source instanceof Message ? source : ( null as never ) ,
211+ forwarded : false ,
212+ } ) ;
213+
214+ for ( const middleware of command . middlewares ) {
215+ await middleware . data . beforeExecute ( ctx ) ;
216+ }
217+
218+ const fn = command . command . data [ executionMode ] ;
219+
220+ if ( ! fn ) {
221+ Logger . warn (
222+ `Command ${ command . command . command . name } has no handler for ${ executionMode } ` ,
223+ ) ;
224+ }
225+
226+ if ( fn ) {
227+ try {
228+ const executeCommand = async ( ) => fn ( ctx . clone ( ) ) ;
229+ const res = await this . commandkit . plugins . execute (
230+ async ( ctx , plugin ) => {
231+ return plugin . executeCommand ( ctx , source , command , executeCommand ) ;
232+ } ,
233+ ) ;
234+
235+ if ( ! res ) {
236+ await executeCommand ( ) ;
237+ }
238+ } catch ( e ) {
239+ Logger . error ( e ) ;
240+ }
241+ }
242+
243+ for ( const middleware of command . middlewares ) {
244+ await middleware . data . afterExecute ( ctx ) ;
245+ }
246+ }
247+
86248 public async prepareCommandRun (
87249 source : Interaction | Message ,
88250 ) : Promise < PreparedAppCommandExecution | null > {
0 commit comments