Skip to content

Commit a495ea0

Browse files
authored
Add caching for the Discord models and Dynamic Event Handlers (#70)
Caching and annotation processing
1 parent 4cc1967 commit a495ea0

File tree

128 files changed

+1766
-239
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

128 files changed

+1766
-239
lines changed

.github/ISSUE_TEMPLATE/feature_request.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,17 @@ assignees: ''
1212
<!-- Description of what you want to be able to do -->
1313

1414
## So that...
15-
<-- Description of why you want this -->
15+
<!-- Description of why you want this -->
1616

1717
## Context
18-
<-- Add context and details on this request -->
18+
<!-- Add context and details on this request -->
1919

2020
## Acceptance Criteria
2121

2222
<!--
2323
EXAMPLE:
2424
25-
As a discord moderator I wan to...
25+
As a discord moderator I want to...
2626
be able to send messages to a text channel
2727
So that...
2828
I can send informative messages to our users.

build.gradle

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ dependencies {
3535
testImplementation "io.gatehill.imposter:imposter-server:3.38.2"
3636
testImplementation "io.gatehill.imposter:config-dynamic:3.38.2"
3737
testImplementation "io.gatehill.imposter:mock-openapi:3.38.2"
38+
39+
testImplementation 'org.mockito:mockito-core:5.11.0'
3840
}
3941

4042
spotless {
@@ -86,7 +88,7 @@ test {
8688
sourceSets {
8789
test {
8890
java {
89-
srcDirs 'src/test/integration'
91+
srcDirs 'src/test/integration', 'src/test/unit'
9092
}
9193
}
9294
}

src/main/java/com/javadiscord/example/ExampleListener.java

Lines changed: 0 additions & 29 deletions
This file was deleted.

src/main/java/com/javadiscord/example/ExampleSlashCommand.java

Lines changed: 0 additions & 10 deletions
This file was deleted.

src/main/java/com/javadiscord/jdi/core/Discord.java

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
11
package com.javadiscord.jdi.core;
22

3+
import java.io.File;
4+
import java.lang.reflect.Constructor;
35
import java.net.URI;
46
import java.net.http.HttpClient;
57
import java.net.http.HttpRequest;
68
import java.net.http.HttpResponse;
9+
import java.util.ArrayList;
710
import java.util.List;
811
import java.util.concurrent.Executor;
912
import java.util.concurrent.Executors;
1013

14+
import com.javadiscord.jdi.core.annotations.EventListener;
15+
import com.javadiscord.jdi.core.cache.Cache;
16+
import com.javadiscord.jdi.core.cache.CacheType;
17+
import com.javadiscord.jdi.core.processor.ClassFileUtil;
18+
import com.javadiscord.jdi.core.processor.EventListenerValidator;
19+
import com.javadiscord.jdi.internal.api.DiscordRequest;
1120
import com.javadiscord.jdi.internal.api.DiscordRequestDispatcher;
21+
import com.javadiscord.jdi.internal.api.DiscordResponseFuture;
1222
import com.javadiscord.jdi.internal.gateway.*;
1323
import com.javadiscord.jdi.internal.gateway.identify.IdentifyRequest;
1424

@@ -18,18 +28,20 @@
1828

1929
public class Discord {
2030
private static final Logger LOGGER = LogManager.getLogger();
21-
private static final Executor EXECUTOR = Executors.newSingleThreadExecutor();
31+
private static final Executor EXECUTOR = Executors.newCachedThreadPool();
2232
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
2333
private static final String WEBSITE = "https://javadiscord.com/";
2434
private static final String BASE_URL = System.getProperty("DISCORD_BASE_URL") != null
2535
? System.getProperty("DISCORD_BASE_URL")
2636
: "https://discord.com/api";
2737
private final String botToken;
2838
private final IdentifyRequest identifyRequest;
29-
3039
private final DiscordRequestDispatcher discordRequestDispatcher;
3140
private final Gateway gateway;
3241
private final GatewaySetting gatewaySetting;
42+
private final Cache cache;
43+
private final List<Object> eventListeners = new ArrayList<>();
44+
private final EventListenerValidator eventListenerValidator = new EventListenerValidator();
3345

3446
public Discord(String botToken) {
3547
this(
@@ -72,14 +84,29 @@ public Discord(String botToken, List<GatewayIntent> intents) {
7284
}
7385

7486
public Discord(String botToken, IdentifyRequest identifyRequest) {
87+
this(botToken, identifyRequest, CacheType.FULL);
88+
}
89+
90+
public Discord(String botToken, IdentifyRequest identifyRequest, CacheType cacheType) {
7591
this.botToken = botToken;
7692
this.discordRequestDispatcher = new DiscordRequestDispatcher(botToken);
7793
this.gateway = getGatewayURL(botToken);
7894
this.gatewaySetting = new GatewaySetting().setEncoding(GatewayEncoding.JSON).setApiVersion(10);
7995
this.identifyRequest = identifyRequest;
96+
this.cache = new Cache(cacheType);
8097
}
8198

8299
public void start() {
100+
try {
101+
loadListeners();
102+
if (eventListeners.isEmpty()) {
103+
LOGGER.warn("No event listeners have been registered");
104+
}
105+
} catch (Exception e) {
106+
LOGGER.error("Failed to load listeners", e);
107+
throw new RuntimeException(e);
108+
}
109+
83110
WebSocketManager webSocketManager = new WebSocketManager(
84111
new GatewaySetting().setApiVersion(10).setEncoding(GatewayEncoding.JSON),
85112
identifyRequest,
@@ -98,6 +125,47 @@ public void startWithoutGatewayEvents() {
98125
EXECUTOR.execute(discordRequestDispatcher);
99126
}
100127

128+
private void loadListeners() throws Exception {
129+
List<File> classes = ClassFileUtil.getClassesInClassPath();
130+
for (File classFile : classes) {
131+
Class<?> clazz = Class.forName(ClassFileUtil.getClassName(classFile));
132+
if (clazz.isAnnotationPresent(EventListener.class)) {
133+
if (validateListener(clazz)) {
134+
registerListener(clazz);
135+
} else {
136+
LOGGER.error("{} failed validation", clazz.getName());
137+
}
138+
}
139+
}
140+
}
141+
142+
private void registerListener(Class<?> clazz) {
143+
try {
144+
LOGGER.info("Registered listener {}", clazz.getName());
145+
eventListeners.add(getZeroArgConstructor(clazz).newInstance());
146+
} catch (Exception e) {
147+
LOGGER.error("Failed to create {} instance", clazz.getName(), e);
148+
}
149+
}
150+
151+
boolean validateListener(Class<?> clazz) {
152+
return eventListenerValidator.validate(clazz);
153+
}
154+
155+
public DiscordResponseFuture sendRequest(DiscordRequest request) {
156+
return discordRequestDispatcher.queue(request);
157+
}
158+
159+
static Constructor<?> getZeroArgConstructor(Class<?> clazz) {
160+
Constructor<?>[] constructors = clazz.getConstructors();
161+
for (Constructor<?> constructor : constructors) {
162+
if (constructor.getParameterCount() == 0) {
163+
return constructor;
164+
}
165+
}
166+
throw new RuntimeException("No zero arg constructor found for " + clazz.getName());
167+
}
168+
101169
private static Gateway getGatewayURL(String authentication) {
102170
try (HttpClient httpClient = HttpClient.newBuilder().build()) {
103171
HttpRequest request = HttpRequest.newBuilder()
@@ -120,4 +188,12 @@ public static String getBaseUrl() {
120188
public DiscordRequestDispatcher getDiscordRequestDispatcher() {
121189
return discordRequestDispatcher;
122190
}
191+
192+
public Cache getCache() {
193+
return cache;
194+
}
195+
196+
public List<Object> getEventListeners() {
197+
return eventListeners;
198+
}
123199
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.javadiscord.jdi.core.annotations;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
@Retention(RetentionPolicy.RUNTIME)
9+
@Target({ ElementType.METHOD })
10+
public @interface AutoModerationRuleCreate {}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.javadiscord.jdi.core.annotations;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
@Retention(RetentionPolicy.RUNTIME)
9+
@Target({ ElementType.METHOD })
10+
public @interface AutoModerationRuleDelete {}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.javadiscord.jdi.core.annotations;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
@Retention(RetentionPolicy.RUNTIME)
9+
@Target({ ElementType.METHOD })
10+
public @interface AutoModerationRuleExecution {}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.javadiscord.jdi.core.annotations;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
@Retention(RetentionPolicy.RUNTIME)
9+
@Target({ ElementType.METHOD })
10+
public @interface AutoModerationRuleUpdate {}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.javadiscord.jdi.core.annotations;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
@Retention(RetentionPolicy.RUNTIME)
9+
@Target({ ElementType.METHOD })
10+
public @interface ChannelDelete {}

0 commit comments

Comments
 (0)