Skip to content

Instantly share code, notes, and snippets.

@DV8FromTheWorld
Last active July 31, 2021 01:40
Show Gist options
  • Save DV8FromTheWorld/531712ade36fb0525938c3522fdaeb51 to your computer and use it in GitHub Desktop.
Save DV8FromTheWorld/531712ade36fb0525938c3522fdaeb51 to your computer and use it in GitHub Desktop.
JDA - Automated Event Firing
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