diff --git a/build.gradle.kts b/build.gradle.kts index b29b431..a8402b9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,6 +3,7 @@ plugins { id("java") id("com.github.johnrengelman.shadow") version "7.1.0" id("maven-publish") + id("org.springframework.boot") version("2.7.8") } group = "com.alttd" @@ -34,12 +35,16 @@ tasks { withType { manifest { - attributes["Main-Class"] = "${rootProject.group}.${project.name}" +// attributes["Main-Class"] = "BOOT-INF/classes/${rootProject.group}.${project.name}" + attributes["Main-Class"] = "org.springframework.boot.loader.JarLauncher" } } shadowJar { archiveFileName.set(rootProject.name + ".jar") + manifest { + attributes["Main-Class"] = "org.springframework.boot.loader.JarLauncher" + } } build { @@ -70,4 +75,7 @@ dependencies { compileOnly("org.projectlombok:lombok:1.18.30") annotationProcessor("org.projectlombok:lombok:1.18.24") implementation("com.alttd:AltitudeLogs:1.0") + implementation("org.springframework.boot:spring-boot-starter-web:3.2.1") + implementation("org.springframework.boot:spring-boot-starter-validation:3.2.1") + implementation("com.google.code.gson:gson:2.8.9") } \ No newline at end of file diff --git a/src/main/java/com/alttd/AltitudeBot.java b/src/main/java/com/alttd/AltitudeBot.java index cd6751a..7817fe0 100644 --- a/src/main/java/com/alttd/AltitudeBot.java +++ b/src/main/java/com/alttd/AltitudeBot.java @@ -7,28 +7,44 @@ import com.alttd.database.Database; import com.alttd.database.DatabaseTables; import com.alttd.listeners.JDAListener; import com.alttd.util.Logger; +import lombok.Getter; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.requests.GatewayIntent; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; import java.io.File; +import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Path; +@SpringBootApplication public class AltitudeBot { private JDA jda; + @Getter private static AltitudeBot instance; - - public static AltitudeBot getInstance() { - return instance; - } + private static String path; public static void main(String[] args) { + if (args.length == 0) { //TODO change scripts so it works with this + System.out.println("Please give the location for the configs as an arg"); + return; + } + path = args[0]; instance = new AltitudeBot(); - instance.start(); + instance.start(args); +// SpringApplication.run(AltitudeBot.class, args); + new SpringApplicationBuilder(AltitudeBot.class) + .web(WebApplicationType.SERVLET) // .REACTIVE, .SERVLET + .run(args); } - private void start() { + private void start(String[] args) { +// Logger.altitudeLogs.info("Starting spring application"); +// SpringApplication.run(AltitudeBot.class, args); Logger.altitudeLogs.info("Starting bot..."); initConfigs(); jda = JDABuilder.createDefault(SettingsConfig.TOKEN, @@ -52,11 +68,11 @@ public class AltitudeBot { // } catch (IllegalArgumentException e) { // Logger.exception(e); // } - initListeners(); + initListeners(args); } - private void initListeners() { - jda.addEventListener(new JDAListener(jda)); + private void initListeners(String[] args) { + jda.addEventListener(new JDAListener(jda, args)); } private void initConfigs() { @@ -67,7 +83,13 @@ public class AltitudeBot { public String getDataFolder() { try { - return new File(AltitudeBot.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getPath(); +// return new File(AltitudeBot.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getPath(); + File file = Path.of(new URI("file://"+ path)).toFile(); + if (!file.exists() || !file.canWrite() || !file.isDirectory()) { + System.out.println("Directory does not exist or can't be written to: " + file.getPath()); + return null; + } + return file.getPath(); } catch (URISyntaxException e) { Logger.altitudeLogs.error("Unable to retrieve config directory"); e.printStackTrace(); diff --git a/src/main/java/com/alttd/commandManager/commands/CommandAuction.java b/src/main/java/com/alttd/commandManager/commands/CommandAuction.java index 99784d6..db2cb73 100644 --- a/src/main/java/com/alttd/commandManager/commands/CommandAuction.java +++ b/src/main/java/com/alttd/commandManager/commands/CommandAuction.java @@ -142,8 +142,7 @@ public class CommandAuction extends DiscordCommand { private void addScreenshot(Message.Attachment screenshot, Message message) { String dataFolder = AltitudeBot.getInstance().getDataFolder(); - Path parent = Path.of(dataFolder).getParent(); - Path path = Path.of(parent.toString() + UUID.randomUUID() + "." + screenshot.getFileExtension()); + Path path = Path.of(dataFolder + UUID.randomUUID() + "." + screenshot.getFileExtension()); screenshot.getProxy().downloadToFile(path.toFile()).whenComplete((file, throwable) -> message.editMessageAttachments(AttachedFile.fromData(file)).queue(done -> file.delete(), failed -> { Util.handleFailure(failed); diff --git a/src/main/java/com/alttd/communication/contact/ContactEndpoint.java b/src/main/java/com/alttd/communication/contact/ContactEndpoint.java new file mode 100644 index 0000000..d9c45fc --- /dev/null +++ b/src/main/java/com/alttd/communication/contact/ContactEndpoint.java @@ -0,0 +1,52 @@ +package com.alttd.communication.contact; + +import com.alttd.AltitudeBot; +import com.alttd.communication.formData.ContactFormData; +import jakarta.validation.Valid; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.concurrent.CompletableFuture; + +@CrossOrigin(origins = "*") +@RestController +@RequestMapping("/api/contact") +public class ContactEndpoint { + + private static final Logger logger = LoggerFactory.getLogger(ContactEndpoint.class); + + + @PostMapping("/submitContactForm") + public CompletableFuture> sendFormToDiscord(@Valid @RequestBody ContactFormData formData) { + logger.debug("Sending form to Discord: " + formData); + MessageEmbed messageEmbed = formData.toMessageEmbed(); + Guild guild = AltitudeBot.getInstance().getJDA().getGuildById(514920774923059209L); + if (guild == null) { + logger.error("Unable to retrieve staff guild"); + return CompletableFuture.completedFuture(ResponseEntity.internalServerError().body("Failed to submit form to Discord")); + } + TextChannel channel = guild.getChannelById(TextChannel.class, 514922567883292673L); + if (channel == null) { + logger.error("Unable to retrieve contact form channel"); + return CompletableFuture.completedFuture(ResponseEntity.internalServerError().body("Failed to submit form to Discord")); + } + + return CompletableFuture.supplyAsync(() -> { + try { + Message complete = channel.sendMessageEmbeds(messageEmbed).complete(); + if (complete != null) + return ResponseEntity.ok(""); + } catch (Exception exception) { + logger.error("Failed to send message to Discord", exception); + } + return ResponseEntity.internalServerError().body("Failed to submit form to Discord"); + }); + } + +} diff --git a/src/main/java/com/alttd/communication/formData/ContactFormData.java b/src/main/java/com/alttd/communication/formData/ContactFormData.java new file mode 100644 index 0000000..016d7bc --- /dev/null +++ b/src/main/java/com/alttd/communication/formData/ContactFormData.java @@ -0,0 +1,51 @@ +package com.alttd.communication.formData; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Pattern; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.MessageEmbed; +import org.hibernate.validator.constraints.Length; + +import java.awt.*; + +public class ContactFormData extends Form { + + public ContactFormData(String username, String email, String question) { + this.username = username; + this.email = email; + this.question = question; + } + + @NotEmpty(message = "You have to provide a username") + @Length(min = 3, max = 16, message = "Usernames have to be between 3 and 16 characters") + @Pattern(regexp = "[a-zA-Z-0-9_]{3,16}", message = "Your username has to be a valid Minecraft username") + public final String username; + + @NotEmpty(message = "You have to provide an e-mail address") + @Email(regexp = "(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)])", + message = "This is not a valid e-mail address") + public final String email; + + @Length(min = 11, max = 2000, message = "Your question should have between 10 and 2000 characters") + public final String question; + + @Override + public String toString() { + return "ContactFormData{" + + "username='" + username + '\'' + + ", email='" + email + '\'' + + ", question='" + question + '\'' + + '}'; + } + + @Override + public MessageEmbed toMessageEmbed() { + return new EmbedBuilder() + .addField("Username", username, false) + .addField("email", email, false) + .appendDescription(question) + .setColor(Color.GREEN) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/alttd/communication/formData/Form.java b/src/main/java/com/alttd/communication/formData/Form.java new file mode 100644 index 0000000..8089cbf --- /dev/null +++ b/src/main/java/com/alttd/communication/formData/Form.java @@ -0,0 +1,11 @@ +package com.alttd.communication.formData; + +import net.dv8tion.jda.api.entities.MessageEmbed; + +public abstract class Form { + + @Override + public abstract String toString(); + + public abstract MessageEmbed toMessageEmbed(); +} diff --git a/src/main/java/com/alttd/config/AbstractConfig.java b/src/main/java/com/alttd/config/AbstractConfig.java index 62e4011..004ec66 100644 --- a/src/main/java/com/alttd/config/AbstractConfig.java +++ b/src/main/java/com/alttd/config/AbstractConfig.java @@ -27,7 +27,7 @@ public abstract class AbstractConfig { private ConfigurationNode config; protected AbstractConfig(String filename) { - init(new File(new File(AltitudeBot.getInstance().getDataFolder()).getParentFile(), filename), filename); + init(new File(new File(AltitudeBot.getInstance().getDataFolder()), filename), filename); } private void init(File file, String filename) { diff --git a/src/main/java/com/alttd/database/DatabaseTables.java b/src/main/java/com/alttd/database/DatabaseTables.java index 167bbce..bdfa4da 100644 --- a/src/main/java/com/alttd/database/DatabaseTables.java +++ b/src/main/java/com/alttd/database/DatabaseTables.java @@ -176,6 +176,21 @@ public class DatabaseTables { } } + private void createSettingsTable() { + String sql = "CREATE TABLE IF NOT EXISTS settings(" + + "name VARCHAR(32) NOT NULL, " + + "value VARCHAR(64) NOT NULL, " + + "type VARCHAR(16) NOT NULL, " + + "PRIMARY KEY (name)" + + ")"; + try { + connection.prepareStatement(sql).executeUpdate(); + } catch (SQLException e) { + Logger.altitudeLogs.error(e); + Logger.altitudeLogs.error("Unable to create auction settings table, shutting down..."); + } + } + public static void createTables(Connection connection) { if (instance == null) instance = new DatabaseTables(connection); diff --git a/src/main/java/com/alttd/database/queries/settings/QueriesSettings.java b/src/main/java/com/alttd/database/queries/settings/QueriesSettings.java new file mode 100644 index 0000000..209e197 --- /dev/null +++ b/src/main/java/com/alttd/database/queries/settings/QueriesSettings.java @@ -0,0 +1,4 @@ +package com.alttd.database.queries.settings; + +public class QueriesSettings { +} diff --git a/src/main/java/com/alttd/database/queries/settings/Setting.java b/src/main/java/com/alttd/database/queries/settings/Setting.java new file mode 100644 index 0000000..f58d286 --- /dev/null +++ b/src/main/java/com/alttd/database/queries/settings/Setting.java @@ -0,0 +1,59 @@ +package com.alttd.database.queries.settings; + +import com.alttd.database.Database; + +import java.sql.*; +import java.util.List; +import java.util.stream.Collectors; + +public class Setting { + + private final List allowedClasses = List.of(Boolean.class, Integer.class, Long.class, Float.class, Double.class, String.class); + + public void insertSetting(String key, T value, String type) throws SQLException, IllegalArgumentException { + if (!allowedClasses.contains(type)) { + throw new IllegalArgumentException(String.format("Invalid type, the only allowed types are: %s", allowedClasses.stream().map(Class::getSimpleName).collect(Collectors.joining(", ")))); + } + String query = "INSERT INTO settings (name, value, type) VALUES (?, ?, ?)"; + + try (Connection connection = DriverManager.getConnection("your_connection_string"); + PreparedStatement preparedStatement = connection.prepareStatement(query)) { + + preparedStatement.setString(1, key); + preparedStatement.setString(2, value.toString()); + preparedStatement.setString(3, type); + preparedStatement.executeUpdate(); + } + } + + public T getSetting(String key, Class type) throws SQLException, IllegalArgumentException { + if (!allowedClasses.contains(type)) { + throw new IllegalArgumentException(String.format("Invalid type, the only allowed types are: %s", allowedClasses.stream().map(Class::getSimpleName).collect(Collectors.joining(", ")))); + } + String query = "SELECT value, type FROM settings WHERE name = ?"; + + try (Connection connection = Database.getDatabase().getConnection(); + PreparedStatement preparedStatement = connection.prepareStatement(query)) { + + preparedStatement.setString(1, key); + ResultSet resultSet = preparedStatement.executeQuery(); + if (resultSet.next()) { + String dbType = resultSet.getString("type"); + if (!dbType.equals(type.getSimpleName())) { + throw new IllegalArgumentException(String.format("%s is of type %s not %s", key, dbType, type.getSimpleName())); + } + String value = resultSet.getString("value"); + if (type.equals(Integer.class)) { + return type.cast(Integer.parseInt(value)); + } else if (type.equals(Boolean.class)) { + return type.cast(Boolean.parseBoolean(value)); + } // and so on for other types + else { + return type.cast(value); + } + } + } + + throw new SQLException("Key not found in settings"); + } +} \ No newline at end of file diff --git a/src/main/java/com/alttd/listeners/JDAListener.java b/src/main/java/com/alttd/listeners/JDAListener.java index 917861e..2b342db 100644 --- a/src/main/java/com/alttd/listeners/JDAListener.java +++ b/src/main/java/com/alttd/listeners/JDAListener.java @@ -1,5 +1,6 @@ package com.alttd.listeners; +import com.alttd.AltitudeBot; import com.alttd.buttonManager.ButtonManager; import com.alttd.commandManager.CommandManager; import com.alttd.contextMenuManager.ContextMenuManager; @@ -19,6 +20,7 @@ import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.GenericSelectMenuInteractionEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import org.jetbrains.annotations.NotNull; +import org.springframework.boot.SpringApplication; import java.util.Timer; import java.util.concurrent.TimeUnit; @@ -26,9 +28,11 @@ import java.util.concurrent.TimeUnit; public class JDAListener extends ListenerAdapter { private final JDA jda; + private final String[] args; - public JDAListener(JDA jda) { + public JDAListener(JDA jda, String[] args) { this.jda = jda; + this.args = args; } @Override @@ -46,6 +50,8 @@ public class JDAListener extends ListenerAdapter { new Timer().scheduleAtFixedRate(new PollTimerTask(jda, Logger.altitudeLogs), TimeUnit.MINUTES.toMillis(1), TimeUnit.MINUTES.toMillis(5)); startSchedulers(); // RequestManager.init(); +// Logger.altitudeLogs.info("Starting spring application"); +// SpringApplication.run(AltitudeBot.class, args); } private void startSchedulers() { diff --git a/src/main/java/com/alttd/util/Logger.java b/src/main/java/com/alttd/util/Logger.java index 7571fc7..5153973 100644 --- a/src/main/java/com/alttd/util/Logger.java +++ b/src/main/java/com/alttd/util/Logger.java @@ -13,7 +13,7 @@ public class Logger { Logger.altitudeLogs = new AltitudeLogs().setTimeFormat("[HH:mm:ss] "); try { Logger.altitudeLogs - .setLogPath(new File(AltitudeBot.getInstance().getDataFolder()).getParent() + File.separator + "logs") + .setLogPath(new File(AltitudeBot.getInstance().getDataFolder()) + File.separator + "logs") .setLogName("debug.log", LogLevel.DEBUG) .setLogName("info.log", LogLevel.INFO) .setLogName("warning.log", LogLevel.WARNING) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..cafbf31 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,2 @@ +server: + port: 8001 \ No newline at end of file