Last active
July 31, 2021 01:40
-
-
Save DV8FromTheWorld/531712ade36fb0525938c3522fdaeb51 to your computer and use it in GitHub Desktop.
JDA - Automated Event Firing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.dv8tion.jda.test; | |
//implementation "org.javassist:javassist:3.28.0-GA" | |
import javassist.util.proxy.MethodHandler; | |
import javassist.util.proxy.ProxyFactory; | |
import net.dv8tion.jda.api.JDA; | |
import net.dv8tion.jda.api.entities.*; | |
import net.dv8tion.jda.api.events.GenericEvent; | |
import net.dv8tion.jda.api.hooks.EventListener; | |
import net.dv8tion.jda.api.hooks.ListenerAdapter; | |
import net.dv8tion.jda.api.requests.Request; | |
import net.dv8tion.jda.internal.JDAImpl; | |
import net.dv8tion.jda.internal.requests.RestActionImpl; | |
import net.dv8tion.jda.internal.requests.Route; | |
import net.dv8tion.jda.internal.utils.config.AuthorizationConfig; | |
//org.reflections:reflections:0.9.12 | |
import org.reflections.Reflections; | |
import org.reflections.scanners.SubTypesScanner; | |
import org.reflections.util.ClasspathHelper; | |
import org.reflections.util.ConfigurationBuilder; | |
import java.lang.reflect.*; | |
import java.util.*; | |
import java.util.stream.Collectors; | |
public class EventTest { | |
static final Map<Class<?>, Object> MOCKED_PARAMETERS = new HashMap<>(); | |
static { | |
MOCKED_PARAMETERS.put(JDA.class, makeFakeJDAImpl()); | |
MOCKED_PARAMETERS.put(Request.class, makeFakeRequest()); | |
MOCKED_PARAMETERS.put(MessageReaction.class, makeFakeMessageReaction()); | |
} | |
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException | |
{ | |
//Create the ListenerAdapter proxy to capture invocation information | |
EventListener adapter = makeAdapterProxy(ListenerAdapter.class); | |
//Setup to find all event classes | |
Reflections myReflections = new Reflections(new ConfigurationBuilder() | |
.setUrls(ClasspathHelper.forPackage("net.dv8tion.jda")) | |
.setScanners(new SubTypesScanner())); | |
//Filter down to classes that extend GenericEvent and make sure they're sorted by package.className for consistency between tests. | |
List<? extends Class<? extends GenericEvent>> eventClasses = myReflections | |
.getSubTypesOf(GenericEvent.class) | |
.stream() | |
.filter(clazz -> !Modifier.isAbstract(clazz.getModifiers())) | |
.sorted(Comparator.comparing(Class::getName)) | |
.collect(Collectors.toList()); | |
//Build actual event instances for each of the Events | |
List<GenericEvent> eventInstances = new ArrayList<>(); | |
for (Class<? extends GenericEvent> eventClass : eventClasses) | |
{ | |
try { | |
GenericEvent buildEvent = buildEvent(eventClass); | |
eventInstances.add(buildEvent); | |
} | |
catch (InvocationTargetException e) { | |
//Get the error message from the underlying exception | |
Throwable targetException = e.getTargetException(); | |
String exceptionMessage = targetException.toString(); | |
//If it was a further wrapped exception caused by the Proxy Invoke handler method throwing an error that | |
// the original interface hadn't defined in the 'throws', then further unwrap to ge the error message | |
if (targetException instanceof UndeclaredThrowableException) { | |
exceptionMessage = ((UndeclaredThrowableException) targetException).getUndeclaredThrowable().toString(); | |
} | |
System.out.println(eventClass.getSimpleName() + " - " + exceptionMessage); | |
System.out.println(); | |
} | |
} | |
eventInstances.forEach(event -> { | |
System.out.println(event.getClass().getSimpleName()); | |
adapter.onEvent(event); | |
System.out.println(); | |
}); | |
} | |
static GenericEvent buildEvent(Class<? extends GenericEvent> eventClazz) throws IllegalAccessException, InvocationTargetException, InstantiationException { | |
Constructor<?> defaultConstructor = eventClazz.getConstructors()[0]; | |
Object[] constructorParameterValues = Arrays.stream(defaultConstructor.getParameterTypes()) | |
.map(EventTest::getDefaultValueForClass) | |
.toArray(); | |
try { | |
return (GenericEvent) defaultConstructor.newInstance(constructorParameterValues); | |
} | |
catch (InstantiationException e) { | |
throw new RuntimeException("Cannot instantiate Event class: " + eventClazz.getSimpleName()); | |
} | |
} | |
static Object getDefaultValueForClass(Class clazz) { | |
Object val = MOCKED_PARAMETERS.get(clazz); | |
if (val != null) { | |
return val; | |
} | |
if (clazz.isInterface()) { | |
return makeInterfaceProxy(clazz); | |
} | |
if (clazz.isEnum()) { | |
//noinspection unchecked | |
return EnumSet.allOf(clazz).stream().findFirst().get(); | |
} | |
if (clazz.isPrimitive()) { | |
return Array.get(Array.newInstance(clazz, 1), 0); | |
} | |
return null; | |
} | |
static Object makeInterfaceProxy(Class<?> ...clazzes) { | |
InvocationHandler handler = (proxy, method, args) -> | |
{ | |
if (method.getName().equals("toString")) { | |
String className = Arrays.stream(clazzes) | |
.map(Class::getSimpleName) | |
.collect(Collectors.joining(" / ")); | |
return "Proxy: (" + className + ")"; | |
} | |
return getDefaultValueForClass(method.getReturnType()); | |
}; | |
return Proxy.newProxyInstance( | |
clazzes[0].getClassLoader(), | |
clazzes, | |
handler | |
); | |
} | |
static EventListener makeAdapterProxy(Class<? extends EventListener>eventListenerClazz) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException | |
{ | |
ProxyFactory proxyFactory = new ProxyFactory(); | |
proxyFactory.setSuperclass(eventListenerClazz); | |
MethodHandler mi = (self, m, proceed, args1) -> | |
{ | |
System.out.println("Executed: " + m.getName()); | |
return proceed.invoke(self, args1); // execute the original method. | |
}; | |
return (EventListener) proxyFactory.create(new Class[0], new Object[0], mi); | |
} | |
static Request<Void> makeFakeRequest() { | |
return new Request<>( | |
new RestActionImpl<>(makeFakeJDAImpl(), Route.Misc.TRACK.compile()), | |
null, | |
null, | |
null, | |
false, | |
null, | |
null, | |
1L, | |
false, | |
Route.Misc.TRACK.compile(), | |
null | |
); | |
} | |
static MessageReaction makeFakeMessageReaction() { | |
return new MessageReaction( | |
(MessageChannel) makeInterfaceProxy(TextChannel.class, PrivateChannel.class), | |
MessageReaction.ReactionEmote.fromCustom((Emote) makeInterfaceProxy(Emote.class)), | |
1L, | |
false, | |
1 | |
); | |
} | |
static JDAImpl makeFakeJDAImpl() { | |
return new JDAImpl(new AuthorizationConfig("fake token")) { | |
@Override | |
public SelfUser getSelfUser() { | |
return (SelfUser) makeInterfaceProxy(SelfUser.class); | |
} | |
}; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment