Skip to content

Mega PR; Slash Commands, Rate limits, Code Clean up and more! #148

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 59 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
ec0bbd6
Added support for slash command loading into `:annotations` module
surajkumar May 26, 2024
f9f633d
chore: Removed unused retry handler in WebSocketHandler
surajkumar May 26, 2024
842e934
Added slash command request classes to `:api` module
surajkumar May 26, 2024
e2445d5
Added logic into `:core` to register slash commands with discord and …
surajkumar May 26, 2024
8e43b2a
chore: Remove add to cache for an interaction
surajkumar May 26, 2024
c95f899
Added ExampleSlashCommand
surajkumar May 26, 2024
5dd7dd4
Spotless...
surajkumar May 26, 2024
8b4c05a
Merge branch 'main' into slash-commands
surajkumar May 26, 2024
dd10bfd
Removed @EventListener on ExampleSlashCommand
surajkumar May 26, 2024
04dbec3
Added SlashCommandEvent object for slash commands
surajkumar May 26, 2024
30ff380
Fix invoking handler
surajkumar May 26, 2024
9904af9
Add helper methods to ApplicationCommandOption
surajkumar May 26, 2024
f6f3628
Fix choices in SlashCommand parsing
surajkumar May 26, 2024
4d32ce6
* Added defer/reply for slash command
surajkumar May 27, 2024
373e85c
* Embed builder
surajkumar May 27, 2024
1ee5617
Fix and features
surajkumar May 27, 2024
770d25c
Use JavaTimeModule module with all OBJECT_MAPPER references
surajkumar May 27, 2024
02492ce
Port over lj-discord-bot into examples
surajkumar May 27, 2024
c1acab7
Added @Inject and @Component
surajkumar May 28, 2024
011f09a
Added @Inject and @Component for JShell
surajkumar May 28, 2024
ba04843
Finish doing lj-bot example
surajkumar May 28, 2024
f98b3b7
Added docker command to lj-example
surajkumar May 28, 2024
ba722ba
Fix example
surajkumar May 28, 2024
4ecd69e
Fix StickerUpdate
surajkumar May 28, 2024
9288c33
Header :D
surajkumar May 28, 2024
7b2b6c8
Fix bug with IntegrationUpdate
surajkumar May 28, 2024
28263e5
Moved log4j2.xml into core
surajkumar May 29, 2024
c44f316
chore: cleanup and fixes
surajkumar May 29, 2024
93b8b92
chore: readability refactoring
surajkumar May 29, 2024
84c101e
chore: readability refactoring
surajkumar May 29, 2024
e241d3e
Merge branch 'main' into slash-commands
surajkumar May 29, 2024
f4a3dfa
chore: fix
surajkumar May 29, 2024
10c0457
chore: spotless
surajkumar May 29, 2024
439c8dc
Add Qodana GHA and fixes
surajkumar May 29, 2024
b124a2c
Fixes
surajkumar May 29, 2024
c21832c
Better compile time reflection and refactoring
surajkumar May 29, 2024
fe0240a
Refactoring
surajkumar May 29, 2024
deb8ac8
Refactoring
surajkumar May 29, 2024
b880043
Refactoring
surajkumar May 29, 2024
1870954
Rate limit
surajkumar May 29, 2024
3769887
Fix Tests
surajkumar May 29, 2024
47f0ca3
Remove Qodana
surajkumar May 29, 2024
e61be1d
Busy-wait fix
surajkumar May 29, 2024
b60f6ed
Sonar fixes
surajkumar May 29, 2024
d59cadc
Sonar fixes
surajkumar May 29, 2024
50a8c3c
Sonar fixes
surajkumar May 29, 2024
4f7968c
Clean up
surajkumar May 29, 2024
239b686
Removed Error in catch
surajkumar May 29, 2024
7e26ed5
Reduce Cognitive complexity
surajkumar May 29, 2024
d4548dc
Sonar
surajkumar May 29, 2024
c038ef7
Sonar
surajkumar May 29, 2024
4e96c08
Delete command request
surajkumar May 29, 2024
d5f6404
Sonar
surajkumar May 29, 2024
7e1161c
Sonar
surajkumar May 29, 2024
3d340bd
Sonar
surajkumar May 30, 2024
d24e183
Added custom exceptions (Sonar)
surajkumar May 30, 2024
66ca27a
Sonar
surajkumar May 30, 2024
dcf8c96
Sonar
surajkumar May 30, 2024
8c11532
Sonar security fix
surajkumar May 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.javadiscord.jdi.core;

public enum CommandOptionType {
SUB_COMMAND(1),
SUB_COMMAND_GROUP(2),
STRING(3),
INTEGER(4),
BOOLEAN(5),
USER(6),
CHANNEL(7),
ROLE(8),
MENTIONABLE(9),
NUMBER(10),
ATTACHMENT(11),
;

private final int value;

CommandOptionType(int value) {
this.value = value;
}

public int getValue() {
return value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.javadiscord.jdi.core.annotations;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import com.javadiscord.jdi.core.CommandOptionType;

@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface CommandOption {
String name();

String description();

CommandOptionType type();

CommandOptionChoice[] choices() default {};

boolean required() default true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.javadiscord.jdi.core.annotations;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface CommandOptionChoice {
String name();

String value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.javadiscord.jdi.core.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Component {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.javadiscord.jdi.core.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Inject {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.javadiscord.jdi.core.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface SlashCommand {
String name();

String description();

CommandOption[] options() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.javadiscord.jdi.internal.exceptions;

public class ComponentInjectionException extends RuntimeException {

public ComponentInjectionException() {
super();
}

public ComponentInjectionException(String message) {
super(message);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.javadiscord.jdi.internal.exceptions;

public class NoZeroArgConstructorException extends RuntimeException {

public NoZeroArgConstructorException() {
super();
}

public NoZeroArgConstructorException(String message) {
super(message);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.javadiscord.jdi.internal.exceptions;

public class ValidationException extends RuntimeException {

public ValidationException() {
super();
}

public ValidationException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -1,63 +1,75 @@
package com.javadiscord.jdi.core.processor;
package com.javadiscord.jdi.internal.processor;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javassist.bytecode.ClassFile;

public class ClassFileUtil {
private static final List<File> classesInPath = new ArrayList<>();
private static boolean loadedParentJar = false;

private ClassFileUtil() {}
private ClassFileUtil() {
throw new UnsupportedOperationException("Utility class");
}

public static List<File> getClassesInClassPath() {
List<File> classes = new ArrayList<>();
String classpath = System.getProperty("java.class.path");
String[] classpathEntries = classpath.split(File.pathSeparator);
for (String entry : classpathEntries) {
File file = new File(entry);
try {
classes.addAll(getClasses(file));
} catch (IOException ignore) {
/* Ignore */
if (classesInPath.isEmpty()) {
String classpath = System.getProperty("java.class.path");
String[] classpathEntries = classpath.split(File.pathSeparator);

for (String entry : classpathEntries) {
File file = new File(entry);
try {
classesInPath.addAll(getClasses(file));
} catch (IOException ignore) {
/* Ignore */
}
}
}
return classes;
return classesInPath;
}

public static String getClassName(File file) throws IOException {
String className = null;
try (
FileInputStream fis = new FileInputStream(file);
DataInputStream dis = new DataInputStream(fis)
) {
if (isJarFile(file)) {
try (ZipInputStream zip = new ZipInputStream(fis)) {
ZipEntry entry;
while ((entry = zip.getNextEntry()) != null) {
if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
className = extractClassName(zip);
break;
}
}
}
return getClassNameFromJar(fis);
} else {
className = extractClassName(dis);
return extractClassName(dis);
}
}
}

private static String getClassNameFromJar(FileInputStream fis) throws IOException {
try (ZipInputStream zip = ZipSecurity.createSecureInputStream(new ZipInputStream(fis))) {
ZipEntry entry;
while ((entry = zip.getNextEntry()) != null) {
if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
return extractClassName(zip);
}
}
}
return className;
return null;
}

private static List<File> getClasses(File file) throws IOException {
List<File> classFiles = new ArrayList<>();
if (file.isDirectory()) {
classFiles.addAll(getClassesFromDirectory(file));
} else if (isJarFile(file)) {
} else if (isJarFile(file) && !loadedParentJar) {
loadedParentJar = true;
classFiles.addAll(getClassesFromJar(file));
} else if (file.getName().endsWith(".class")) {
classFiles.add(file);
Expand All @@ -80,7 +92,7 @@ private static List<File> getClassesFromJar(File jarFile) throws IOException {
List<File> classFiles = new ArrayList<>();
try (
FileInputStream fis = new FileInputStream(jarFile);
ZipInputStream zip = new ZipInputStream(fis)
ZipInputStream zip = ZipSecurity.createSecureInputStream(new ZipInputStream(fis))
) {
ZipEntry entry;
while ((entry = zip.getNextEntry()) != null) {
Expand Down Expand Up @@ -109,7 +121,7 @@ private static File extractClassFileFromJar(
ZipInputStream zip,
String entryName
) throws IOException {
File tempFile = File.createTempFile(entryName.replace('/', '_'), ".class");
File tempFile = safeTempFile(entryName).toFile();
tempFile.deleteOnExit();
try (FileOutputStream fos = new FileOutputStream(tempFile)) {
byte[] buffer = new byte[1024];
Expand All @@ -120,4 +132,10 @@ private static File extractClassFileFromJar(
}
return tempFile;
}

private static Path safeTempFile(String entryName) throws IOException {
String sanitizedEntryName = entryName.replace('/', '_');
Path secureTempDir = Paths.get(System.getProperty("java.io.tmpdir"));
return Files.createTempFile(secureTempDir, sanitizedEntryName, ".class");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.javadiscord.jdi.internal.processor;

import java.lang.reflect.Method;

public record SlashCommandClassMethod(Class<?> clazz, Method method) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.javadiscord.jdi.internal.processor;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class ZipSecurity {

public static ZipInputStream createSecureInputStream(InputStream stream) {
return new SecureZipInputStream(stream);
}

private static class SecureZipInputStream extends ZipInputStream {

public SecureZipInputStream(InputStream in) {
super(in);
}

@Override
public ZipEntry getNextEntry() throws IOException {
ZipEntry entry = super.getNextEntry();
if (entry == null) {
return null;
}
String entryName = entry.getName();
if (!entryName.trim().isEmpty()) {
if (isAbsolutePath(entryName)) {
throw new SecurityException(
"Encountered zip file with absolute path: " + entryName
);
}
if (containsPathTraversal(entryName)) {
throw new SecurityException(
"Path contains traversal to sensitive locations: " + entryName
);
}
}
return entry;
}

private boolean containsPathTraversal(String entryName) {
if (entryName.contains("../") || entryName.contains("..\\")) {
try {
if (isPathOutsideCurrentDirectory(entryName)) {
return true;
}
} catch (IOException ignore) {
/* Ignore */
}
}
return false;
}

private boolean isPathOutsideCurrentDirectory(String entryName) throws IOException {
File currentDirectory = new File("").getCanonicalFile();
File untrustedFile = new File(currentDirectory, entryName);
Path untrustedPath = untrustedFile.getCanonicalFile().toPath();
return !untrustedPath.startsWith(currentDirectory.toPath());
}

private boolean isAbsolutePath(String entryName) {
return entryName.startsWith("/");
}
}
}
Loading
Loading