1
1
package com .javadiscord .jdi .core ;
2
2
3
+ import java .lang .annotation .Annotation ;
3
4
import java .lang .reflect .Constructor ;
4
- import java .lang .reflect .InvocationTargetException ;
5
+ import java .lang .reflect .Method ;
5
6
import java .lang .reflect .Parameter ;
6
7
import java .net .URI ;
7
8
import java .net .http .HttpClient ;
8
9
import java .net .http .HttpRequest ;
9
10
import java .net .http .HttpResponse ;
10
11
import java .util .ArrayList ;
12
+ import java .util .HashMap ;
11
13
import java .util .List ;
14
+ import java .util .Map ;
12
15
import java .util .concurrent .ExecutorService ;
13
16
import java .util .concurrent .Executors ;
14
17
import java .util .concurrent .TimeUnit ;
15
18
19
+ import com .javadiscord .jdi .core .api .builders .command .CommandBuilder ;
20
+ import com .javadiscord .jdi .core .api .builders .command .CommandOption ;
21
+ import com .javadiscord .jdi .core .api .builders .command .CommandOptionType ;
22
+ import com .javadiscord .jdi .core .interaction .InteractionEventHandler ;
23
+ import com .javadiscord .jdi .core .models .ready .ReadyEvent ;
16
24
import com .javadiscord .jdi .internal .api .DiscordRequest ;
17
25
import com .javadiscord .jdi .internal .api .DiscordRequestDispatcher ;
18
26
import com .javadiscord .jdi .internal .api .DiscordResponseFuture ;
27
+ import com .javadiscord .jdi .internal .api .application_commands .CreateCommandRequest ;
19
28
import com .javadiscord .jdi .internal .cache .Cache ;
20
29
import com .javadiscord .jdi .internal .cache .CacheType ;
21
30
import com .javadiscord .jdi .internal .gateway .*;
@@ -43,10 +52,13 @@ public class Discord {
43
52
private final GatewaySetting gatewaySetting ;
44
53
private final Cache cache ;
45
54
private final List <Object > annotatedEventListeners = new ArrayList <>();
55
+ private final Map <String , Object > loadedSlashCommands = new HashMap <>();
46
56
private final List <EventListener > eventListeners = new ArrayList <>();
57
+ private final List <CommandBuilder > createInteractionRequests = new ArrayList <>();
47
58
48
59
private WebSocketManager webSocketManager ;
49
- private Object listenerLoader ;
60
+ private long applicationId ;
61
+ private boolean started = false ;
50
62
51
63
public Discord (String botToken ) {
52
64
this (
@@ -101,11 +113,66 @@ public Discord(String botToken, IdentifyRequest identifyRequest, Cache cache) {
101
113
this .identifyRequest = identifyRequest ;
102
114
this .cache = cache ;
103
115
if (annotationLibPresent ()) {
104
- LOGGER .info ("Annotation lib is present, loading annotations listeners... " );
116
+ LOGGER .info ("Annotation lib is present" );
105
117
loadAnnotations ();
118
+ loadSlashCommands ();
119
+ registerLoadedAnnotationsWithDiscord ();
106
120
}
107
121
}
108
122
123
+ private void registerLoadedAnnotationsWithDiscord () {
124
+ LOGGER .info ("Registering slash commands with Discord" );
125
+ loadedSlashCommands .forEach ((commandName , slashCommandClassInstance ) -> {
126
+ try {
127
+ Class <?> slashCommandClassInstanceClass = slashCommandClassInstance .getClass ();
128
+ Method method = (Method ) slashCommandClassInstanceClass
129
+ .getMethod ("method" )
130
+ .invoke (slashCommandClassInstance );
131
+
132
+ Annotation [] annotations = method .getAnnotations ();
133
+ for (Annotation annotation : annotations ) {
134
+ if (annotation .annotationType ().getName ().equals ("com.javadiscord.jdi.core.annotations.SlashCommand" )) {
135
+ Method nameMethod = annotation .annotationType ().getMethod ("name" );
136
+ String name = (String ) nameMethod .invoke (annotation );
137
+
138
+ Method descriptionMethod = annotation .annotationType ().getMethod ("description" );
139
+ String description = (String ) descriptionMethod .invoke (annotation );
140
+
141
+ Method optionsMethod = annotation .annotationType ().getMethod ("options" );
142
+ Object [] options = (Object []) optionsMethod .invoke (annotation );
143
+
144
+ CommandBuilder builder = new CommandBuilder (name , description );
145
+
146
+ for (Object option : options ) {
147
+ Method optionNameMethod = option .getClass ().getMethod ("name" );
148
+ String optionName = (String ) optionNameMethod .invoke (option );
149
+
150
+ Method optionDescriptionMethod = option .getClass ().getMethod ("description" );
151
+ String optionDescription = (String ) optionDescriptionMethod .invoke (option );
152
+
153
+ Method optionTypeMethod = option .getClass ().getMethod ("type" );
154
+ Enum <?> optionType = (Enum <?>) optionTypeMethod .invoke (option );
155
+ String optionTypeValue = optionType .name ();
156
+
157
+ Method optionRequiredMethod = option .getClass ().getMethod ("required" );
158
+ boolean optionRequired = (boolean ) optionRequiredMethod .invoke (option );
159
+
160
+ builder .addOption (new CommandOption (
161
+ optionName ,
162
+ optionDescription ,
163
+ CommandOptionType .fromName (optionTypeValue ),
164
+ optionRequired ));
165
+ }
166
+
167
+ createInteractionRequests .add (builder );
168
+ }
169
+ }
170
+ } catch (Exception e ) {
171
+ LOGGER .error ("Error registering slash command with Discord" , e );
172
+ }
173
+ });
174
+ }
175
+
109
176
private boolean annotationLibPresent () {
110
177
try {
111
178
Class .forName ("com.javadiscord.jdi.core.processor.ListenerLoader" );
@@ -116,28 +183,45 @@ private boolean annotationLibPresent() {
116
183
}
117
184
118
185
private void loadAnnotations () {
186
+ LOGGER .info ("Loading EventListeners" );
119
187
try {
120
188
Class <?> clazz = Class .forName ("com.javadiscord.jdi.core.processor.ListenerLoader" );
121
189
for (Constructor <?> constructor : clazz .getConstructors ()) {
122
190
if (constructor .getParameterCount () == 1 ) {
123
191
Parameter parameters = constructor .getParameters ()[0 ];
124
192
if (parameters .getType ().equals (List .class )) {
125
- listenerLoader = constructor .newInstance (annotatedEventListeners );
193
+ constructor .newInstance (annotatedEventListeners );
126
194
return ;
127
195
}
128
196
}
129
197
}
130
- } catch (
131
- ClassNotFoundException
132
- | InstantiationException
133
- | IllegalAccessException
134
- | InvocationTargetException ignore
135
- ) {
198
+ } catch (Exception | Error e ) {
136
199
/* Ignore */
137
200
}
138
201
}
139
202
203
+ private void loadSlashCommands () {
204
+ LOGGER .info ("Loading SlashCommands" );
205
+ try {
206
+ Class <?> clazz = Class .forName ("com.javadiscord.jdi.core.processor.SlashCommandLoader" );
207
+ for (Constructor <?> constructor : clazz .getConstructors ()) {
208
+ if (constructor .getParameterCount () == 1 ) {
209
+ Parameter parameters = constructor .getParameters ()[0 ];
210
+ if (parameters .getType ().equals (Map .class )) {
211
+ eventListeners .add (new InteractionEventHandler (constructor .newInstance (loadedSlashCommands )));
212
+ return ;
213
+ }
214
+ return ;
215
+ }
216
+ }
217
+ } catch (Exception | Error e ) {
218
+ LOGGER .error ("Failed to load SlashCommands" , e );
219
+ }
220
+ }
221
+
140
222
public void start () {
223
+ started = true ;
224
+
141
225
this .webSocketManager =
142
226
new WebSocketManager (
143
227
new GatewaySetting ().setApiVersion (10 ).setEncoding (GatewayEncoding .JSON ),
@@ -154,11 +238,11 @@ public void start() {
154
238
connectionMediator .addObserver (new GatewayEventListenerAnnotations (this ));
155
239
connectionMediator .addObserver (new GatewayEventListener (this ));
156
240
webSocketManagerProxy .start (connectionMediator );
157
-
158
- EXECUTOR .execute (discordRequestDispatcher );
159
241
}
160
242
161
243
public void stop () {
244
+ started = false ;
245
+
162
246
if (this .webSocketManager != null ) {
163
247
this .webSocketManager .stop ();
164
248
}
@@ -219,6 +303,32 @@ private static Gateway getGatewayURL(String authentication) {
219
303
}
220
304
}
221
305
306
+ public void registerSlashCommand (
307
+ String name ,
308
+ String description ,
309
+ CommandOption ... options
310
+ ) {
311
+ CommandBuilder builder =
312
+ new CommandBuilder (
313
+ name ,
314
+ description
315
+ );
316
+ for (CommandOption option : options ) {
317
+ builder .addOption (option );
318
+ }
319
+ builder .applicationId (applicationId );
320
+ createInteractionRequests .add (builder );
321
+ }
322
+
323
+ public void registerSlashCommand (CommandBuilder builder ) {
324
+ builder .applicationId (applicationId );
325
+ createInteractionRequests .add (builder );
326
+ }
327
+
328
+ public void deleteSlashCommand (long id ) {
329
+
330
+ }
331
+
222
332
public DiscordRequestDispatcher getDiscordRequestDispatcher () {
223
333
return discordRequestDispatcher ;
224
334
}
@@ -231,7 +341,43 @@ public List<Object> getAnnotatedEventListeners() {
231
341
return annotatedEventListeners ;
232
342
}
233
343
344
+ public boolean started () {
345
+ return started ;
346
+ }
347
+
234
348
public List <EventListener > getEventListeners () {
235
349
return eventListeners ;
236
350
}
351
+
352
+ public long getApplicationId () {
353
+ return applicationId ;
354
+ }
355
+
356
+ void handleReadyEvent (ReadyEvent event ) {
357
+ applicationId = event .application ().id ();
358
+
359
+ EXECUTOR .execute (discordRequestDispatcher );
360
+
361
+ for (CommandBuilder builder : createInteractionRequests ) {
362
+ builder .applicationId (applicationId );
363
+ CreateCommandRequest request = builder .build ();
364
+ DiscordResponseFuture future = sendRequest (request );
365
+ future .onSuccess (res -> {
366
+ if (res .status () >= 200 && res .status () < 300 ) {
367
+ LOGGER .info ("Registered slash command {} with discord" , request .name ());
368
+ } else {
369
+ LOGGER .error (
370
+ "Failed to register slash command {} with discord\n {}" , request .name (),
371
+ res .body ()
372
+ );
373
+ }
374
+ });
375
+ future .onError (
376
+ err -> LOGGER
377
+ .error ("Failed to register slash command {} with discord" , request .name (), err )
378
+ );
379
+ }
380
+
381
+ createInteractionRequests .clear ();
382
+ }
237
383
}
0 commit comments