From 093571d312d772f2c5be9346f2d915b20e4c5f9d Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Sat, 30 Mar 2024 16:05:04 +0100 Subject: [PATCH 1/3] Created the start of what's needed to auto upload messages to Kanboard Added event listener for tags being added, still missing the tag id Added code to forward mesage to kanboard, missing the correct link and title/body Error handling that only outputs in console, stil needs to notify the user who adds the tags --- .../java/com/alttd/config/SettingsConfig.java | 2 + .../java/com/alttd/listeners/JDAListener.java | 3 +- .../java/com/alttd/listeners/TagAdded.java | 70 +++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/alttd/listeners/TagAdded.java diff --git a/src/main/java/com/alttd/config/SettingsConfig.java b/src/main/java/com/alttd/config/SettingsConfig.java index cc788e0..7736298 100644 --- a/src/main/java/com/alttd/config/SettingsConfig.java +++ b/src/main/java/com/alttd/config/SettingsConfig.java @@ -16,10 +16,12 @@ public class SettingsConfig extends AbstractConfig { // SETTINGS public static String TOKEN = "token"; + public static String KANBOARD_TOKEN = "kanboard-token"; public static boolean DEBUG = false; private void loadSettings() { TOKEN = settingsConfig.getString("settings.token", TOKEN); + KANBOARD_TOKEN = settingsConfig.getString("settings.kanboard-token", KANBOARD_TOKEN); DEBUG = settingsConfig.getBoolean("settings.debug", DEBUG); } diff --git a/src/main/java/com/alttd/listeners/JDAListener.java b/src/main/java/com/alttd/listeners/JDAListener.java index 917861e..ede699f 100644 --- a/src/main/java/com/alttd/listeners/JDAListener.java +++ b/src/main/java/com/alttd/listeners/JDAListener.java @@ -36,12 +36,13 @@ public class JDAListener extends ListenerAdapter { Logger.altitudeLogs.info("JDA ready to register commands."); LockedChannel lockedChannel = new LockedChannel(); ButtonManager buttonManager = new ButtonManager(); + TagAdded tagAdded = new TagAdded(); AppealRepost appealRepost = new AppealRepost(buttonManager); ModalManager modalManager = new ModalManager(buttonManager); ContextMenuManager contextMenuManager = new ContextMenuManager(modalManager); SelectMenuManager selectMenuManager = new SelectMenuManager(); CommandManager commandManager = new CommandManager(jda, modalManager, contextMenuManager, lockedChannel, selectMenuManager, buttonManager); - jda.addEventListener(buttonManager, modalManager, commandManager, contextMenuManager, lockedChannel, appealRepost, selectMenuManager); + jda.addEventListener(buttonManager, tagAdded, modalManager, commandManager, contextMenuManager, lockedChannel, appealRepost, selectMenuManager); PollQueries.loadPolls(buttonManager); new Timer().scheduleAtFixedRate(new PollTimerTask(jda, Logger.altitudeLogs), TimeUnit.MINUTES.toMillis(1), TimeUnit.MINUTES.toMillis(5)); startSchedulers(); diff --git a/src/main/java/com/alttd/listeners/TagAdded.java b/src/main/java/com/alttd/listeners/TagAdded.java new file mode 100644 index 0000000..63db373 --- /dev/null +++ b/src/main/java/com/alttd/listeners/TagAdded.java @@ -0,0 +1,70 @@ +package com.alttd.listeners; + +import com.alttd.config.SettingsConfig; +import com.alttd.util.Logger; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; +import net.dv8tion.jda.api.events.channel.forum.ForumTagAddEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + +public class TagAdded extends ListenerAdapter { + + public void tagAdded(ForumTagAddEvent event) { + if (event.getTag().getIdLong() != 0L) {//TODO add tag id + return; + } + if (!(event.getChannel() instanceof ThreadChannel threadChannel)) { + return; + } + threadChannel.retrieveStartMessage().queue(this::forwardMessageToKanboard, error -> { + Logger.altitudeLogs.error(error); + }); + } + + //TODO move to its own util class + public void forwardMessageToKanboard(Message message) { + String contentDisplay = message.getContentDisplay(); + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request; + try { + String jsonPayload = String.format("{" + + "\"title\": \"%s\"," + + "\"description\": \"%s\"," + + "\"project_id\": \"%d\"" + + "}", "New Task" /*TODO config*/, contentDisplay, 3/*TODO config*/); + + request = HttpRequest.newBuilder() + .uri(new URI("https://kanboard.alttd.com/jsonrpc.php")) //TODO correct URL + .header("Content-Type", "application/json") + .setHeader("Authorization", SettingsConfig.KANBOARD_TOKEN) + .POST(HttpRequest.BodyPublishers.ofString(jsonPayload)) + .build(); + } catch (URISyntaxException e) { + Logger.altitudeLogs.error(e); + //TODO handle better + return; + } + + HttpResponse response; + try { + response = client.send(request, HttpResponse.BodyHandlers.ofString()); + } catch (InterruptedException | IOException e) { + Logger.altitudeLogs.error(e); + return; + } + + if (response.statusCode() == 200) { + Logger.altitudeLogs.info(response.body()); + } else { + Logger.altitudeLogs.error(String.format("Invalid response [%s]", response.body())); + } + } + +} From 32e4a669ed6ddca1e74ae49f9d4afaa720a1b2ef Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Sun, 31 Mar 2024 11:39:21 +0200 Subject: [PATCH 2/3] Add message forwarding functionality to Kanboard This update includes the creation of a new "ContextMenuForwardToKanboard" and a utility class "Kanboard", which manage the forwarding of messages to Kanboard. The "forwardMessageToKanboard" method has been extracted from "TagAdded" into "Kanboard" for better code organization. These changes also include some minor adjustments like changing the permission called in "AltitudeBot" and implementing the use of @Getter in the same class. --- src/main/java/com/alttd/AltitudeBot.java | 10 +-- .../ContextMenuManager.java | 4 +- .../ContextMenuForwardToKanboard.java | 73 ++++++++++++++++ .../java/com/alttd/listeners/TagAdded.java | 45 +--------- src/main/java/com/alttd/util/Kanboard.java | 85 +++++++++++++++++++ 5 files changed, 169 insertions(+), 48 deletions(-) create mode 100644 src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuForwardToKanboard.java create mode 100644 src/main/java/com/alttd/util/Kanboard.java diff --git a/src/main/java/com/alttd/AltitudeBot.java b/src/main/java/com/alttd/AltitudeBot.java index cd6751a..555e18e 100644 --- a/src/main/java/com/alttd/AltitudeBot.java +++ b/src/main/java/com/alttd/AltitudeBot.java @@ -7,6 +7,7 @@ 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; @@ -17,12 +18,9 @@ import java.net.URISyntaxException; public class AltitudeBot { private JDA jda; + @Getter private static AltitudeBot instance; - public static AltitudeBot getInstance() { - return instance; - } - public static void main(String[] args) { instance = new AltitudeBot(); instance.start(); @@ -33,7 +31,7 @@ public class AltitudeBot { initConfigs(); jda = JDABuilder.createDefault(SettingsConfig.TOKEN, GatewayIntent.GUILD_MEMBERS, - GatewayIntent.GUILD_BANS, + GatewayIntent.GUILD_MODERATION, GatewayIntent.GUILD_EMOJIS_AND_STICKERS, GatewayIntent.GUILD_WEBHOOKS, GatewayIntent.GUILD_PRESENCES, @@ -70,7 +68,7 @@ public class AltitudeBot { return new File(AltitudeBot.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getPath(); } catch (URISyntaxException e) { Logger.altitudeLogs.error("Unable to retrieve config directory"); - e.printStackTrace(); + Logger.altitudeLogs.error(e); } return (null); } diff --git a/src/main/java/com/alttd/contextMenuManager/ContextMenuManager.java b/src/main/java/com/alttd/contextMenuManager/ContextMenuManager.java index fa8c5e2..2d153ca 100644 --- a/src/main/java/com/alttd/contextMenuManager/ContextMenuManager.java +++ b/src/main/java/com/alttd/contextMenuManager/ContextMenuManager.java @@ -1,5 +1,6 @@ package com.alttd.contextMenuManager; +import com.alttd.contextMenuManager.contextMenus.ContextMenuForwardToKanboard; import com.alttd.contextMenuManager.contextMenus.ContextMenuRespondSuggestion; import com.alttd.modalManager.ModalManager; import com.alttd.util.Util; @@ -21,7 +22,8 @@ public class ContextMenuManager extends ListenerAdapter { public ContextMenuManager(ModalManager modalManager) { contextMenus = List.of( - new ContextMenuRespondSuggestion(modalManager) + new ContextMenuRespondSuggestion(modalManager), + new ContextMenuForwardToKanboard() ); } diff --git a/src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuForwardToKanboard.java b/src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuForwardToKanboard.java new file mode 100644 index 0000000..117bc36 --- /dev/null +++ b/src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuForwardToKanboard.java @@ -0,0 +1,73 @@ +package com.alttd.contextMenuManager.contextMenus; + +import com.alttd.contextMenuManager.DiscordContextMenu; +import com.alttd.util.Kanboard; +import com.alttd.util.Util; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; +import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent; +import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; +import net.dv8tion.jda.api.interactions.commands.build.CommandData; +import net.dv8tion.jda.api.interactions.commands.build.Commands; +import net.dv8tion.jda.api.requests.RestAction; +import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; + +import java.util.concurrent.CompletableFuture; + +public class ContextMenuForwardToKanboard extends DiscordContextMenu { + @Override + public String getContextMenuId() { + return "Forward To Kanboard"; + } + + @Override + public void execute(UserContextInteractionEvent event) { + event.getInteraction().replyEmbeds(Util.genericErrorEmbed("Error", "This interaction should have been a message interaction")) + .setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure); + } + + @Override + public void execute(MessageContextInteractionEvent event) { + if (!event.isFromGuild()) { + return; + } + Message message = event.getInteraction().getTarget(); + Member member = message.getMember(); + if (member == null) + return; + + String title = ""; + if (member.getUser().equals(event.getJDA().getSelfUser())) { + if (message.getContentRaw().startsWith("**Suggestion by:")) { + if (message.getGuildChannel() instanceof ThreadChannel threadChannel) { + title = threadChannel.getName(); + } + } + } + + ReplyCallbackAction replyCallbackAction = event.deferReply(true); + CompletableFuture booleanCompletableFuture; + if (title.isBlank()) { + booleanCompletableFuture = Kanboard.forwardMessageToKanboard(message); + } else { + booleanCompletableFuture = Kanboard.forwardMessageToKanboard(title, message.getContentDisplay()); + } + booleanCompletableFuture.thenAcceptAsync(result -> { + if (!result) { + replyCallbackAction.setEmbeds(Util.genericErrorEmbed("Error", "Unable to forward message to Kanboard")); + return; + } + replyCallbackAction.setEmbeds(Util.genericSuccessEmbed("Success", "Forwarded message to Kanboard board!")); + }); + } + + @Override + public CommandData getUserContextInteraction() { + return Commands.message(getContextMenuId()) + .setGuildOnly(true) + .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.ADMINISTRATOR)); + } +} diff --git a/src/main/java/com/alttd/listeners/TagAdded.java b/src/main/java/com/alttd/listeners/TagAdded.java index 63db373..ee45aef 100644 --- a/src/main/java/com/alttd/listeners/TagAdded.java +++ b/src/main/java/com/alttd/listeners/TagAdded.java @@ -1,6 +1,7 @@ package com.alttd.listeners; import com.alttd.config.SettingsConfig; +import com.alttd.util.Kanboard; import com.alttd.util.Logger; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; @@ -16,55 +17,17 @@ import java.net.http.HttpResponse; public class TagAdded extends ListenerAdapter { - public void tagAdded(ForumTagAddEvent event) { + @Override + public void onForumTagAdd(ForumTagAddEvent event) { if (event.getTag().getIdLong() != 0L) {//TODO add tag id return; } if (!(event.getChannel() instanceof ThreadChannel threadChannel)) { return; } - threadChannel.retrieveStartMessage().queue(this::forwardMessageToKanboard, error -> { + threadChannel.retrieveStartMessage().queue(Kanboard::forwardMessageToKanboard, error -> { Logger.altitudeLogs.error(error); }); } - //TODO move to its own util class - public void forwardMessageToKanboard(Message message) { - String contentDisplay = message.getContentDisplay(); - HttpClient client = HttpClient.newHttpClient(); - HttpRequest request; - try { - String jsonPayload = String.format("{" - + "\"title\": \"%s\"," - + "\"description\": \"%s\"," - + "\"project_id\": \"%d\"" - + "}", "New Task" /*TODO config*/, contentDisplay, 3/*TODO config*/); - - request = HttpRequest.newBuilder() - .uri(new URI("https://kanboard.alttd.com/jsonrpc.php")) //TODO correct URL - .header("Content-Type", "application/json") - .setHeader("Authorization", SettingsConfig.KANBOARD_TOKEN) - .POST(HttpRequest.BodyPublishers.ofString(jsonPayload)) - .build(); - } catch (URISyntaxException e) { - Logger.altitudeLogs.error(e); - //TODO handle better - return; - } - - HttpResponse response; - try { - response = client.send(request, HttpResponse.BodyHandlers.ofString()); - } catch (InterruptedException | IOException e) { - Logger.altitudeLogs.error(e); - return; - } - - if (response.statusCode() == 200) { - Logger.altitudeLogs.info(response.body()); - } else { - Logger.altitudeLogs.error(String.format("Invalid response [%s]", response.body())); - } - } - } diff --git a/src/main/java/com/alttd/util/Kanboard.java b/src/main/java/com/alttd/util/Kanboard.java new file mode 100644 index 0000000..8d86cd2 --- /dev/null +++ b/src/main/java/com/alttd/util/Kanboard.java @@ -0,0 +1,85 @@ +package com.alttd.util; + +import com.alttd.config.SettingsConfig; +import net.dv8tion.jda.api.entities.Message; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.Arrays; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +public class Kanboard { + + public static CompletableFuture forwardMessageToKanboard(String title, String body) { + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request; + try { + String jsonPayload = getPayload(UUID.randomUUID().toString(), title, body); + + request = HttpRequest.newBuilder() + .uri(new URI("https://kanboard.alttd.com/jsonrpc.php")) + .header("Content-Type", "application/json") + .setHeader("Authorization", SettingsConfig.KANBOARD_TOKEN) + .POST(HttpRequest.BodyPublishers.ofString(jsonPayload)) + .build(); + } catch (URISyntaxException e) { + Logger.altitudeLogs.error(e); + //TODO handle better + return CompletableFuture.completedFuture(false); + } + + return CompletableFuture.supplyAsync(() -> { + HttpResponse response; + try { + response = client.send(request, HttpResponse.BodyHandlers.ofString()); + } catch (InterruptedException | IOException e) { + Logger.altitudeLogs.error(e); + return false; + } + + if (response.statusCode() == 200) { + Logger.altitudeLogs.info(response.body()); + return true; + } else { + Logger.altitudeLogs.error(String.format("Invalid response [%s]", response.body())); + return false; + } + }); + } + + public static CompletableFuture forwardMessageToKanboard(Message message) { + String body = message.getContentDisplay(); + String[] split = body.split("\n"); + String title; + if (split.length == 1) { + title = "No title"; + } else { + title = split[0]; + body = Arrays.stream(split).skip(1).collect(Collectors.joining("\n")); + } + return forwardMessageToKanboard(title, body); + } + + private static String getPayload(String id, String title, String body) { + return String.format(""" + { + "jsonrpc": "2.0", + "method": "createTask", + "id": %s, + "params": { + "title": "%s", + "project_id": "3", + "description": "%s", + "column_id": "0", + "color_id": "red" + } + }""", id, title, body); + } + +} From 314dcd6771d96cf22554b7742c78db1a90fd0c20 Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Sun, 31 Mar 2024 14:51:13 +0200 Subject: [PATCH 3/3] Refactor code to improve message forwarding to Kanboard This commit extracts the method "forwardMessageToKanboard" from the "TagAdded" class to the new utility class "Kanboard", making the code base cleaner and more maintainable. It also introduces "ContextMenuForwardToKanboard" for better message management. Further modifications include alterations in "AltitudeBot", such as changes in permissions and implementing @Getter. --- .../ContextMenuForwardToKanboard.java | 31 ++------ .../java/com/alttd/listeners/TagAdded.java | 22 +++--- src/main/java/com/alttd/util/Kanboard.java | 71 +++++++++++++++---- 3 files changed, 71 insertions(+), 53 deletions(-) diff --git a/src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuForwardToKanboard.java b/src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuForwardToKanboard.java index 117bc36..57d6e18 100644 --- a/src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuForwardToKanboard.java +++ b/src/main/java/com/alttd/contextMenuManager/contextMenus/ContextMenuForwardToKanboard.java @@ -4,16 +4,13 @@ import com.alttd.contextMenuManager.DiscordContextMenu; import com.alttd.util.Kanboard; import com.alttd.util.Util; import net.dv8tion.jda.api.Permission; -import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent; import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.requests.RestAction; -import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; import java.util.concurrent.CompletableFuture; @@ -35,33 +32,15 @@ public class ContextMenuForwardToKanboard extends DiscordContextMenu { return; } Message message = event.getInteraction().getTarget(); - Member member = message.getMember(); - if (member == null) - return; - String title = ""; - if (member.getUser().equals(event.getJDA().getSelfUser())) { - if (message.getContentRaw().startsWith("**Suggestion by:")) { - if (message.getGuildChannel() instanceof ThreadChannel threadChannel) { - title = threadChannel.getName(); - } - } - } - - ReplyCallbackAction replyCallbackAction = event.deferReply(true); - CompletableFuture booleanCompletableFuture; - if (title.isBlank()) { - booleanCompletableFuture = Kanboard.forwardMessageToKanboard(message); - } else { - booleanCompletableFuture = Kanboard.forwardMessageToKanboard(title, message.getContentDisplay()); - } - booleanCompletableFuture.thenAcceptAsync(result -> { + CompletableFuture booleanCompletableFuture = Kanboard.forwardMessageToKanboard(message); + event.deferReply(true).queue(defer -> booleanCompletableFuture.thenAcceptAsync(result -> { if (!result) { - replyCallbackAction.setEmbeds(Util.genericErrorEmbed("Error", "Unable to forward message to Kanboard")); + defer.editOriginalEmbeds(Util.genericErrorEmbed("Error", "Unable to forward message to Kanboard")).queue(); return; } - replyCallbackAction.setEmbeds(Util.genericSuccessEmbed("Success", "Forwarded message to Kanboard board!")); - }); + defer.editOriginalEmbeds(Util.genericSuccessEmbed("Success", "Forwarded message to Kanboard board!")).queue(); + })); } @Override diff --git a/src/main/java/com/alttd/listeners/TagAdded.java b/src/main/java/com/alttd/listeners/TagAdded.java index ee45aef..3680d80 100644 --- a/src/main/java/com/alttd/listeners/TagAdded.java +++ b/src/main/java/com/alttd/listeners/TagAdded.java @@ -1,33 +1,27 @@ package com.alttd.listeners; -import com.alttd.config.SettingsConfig; import com.alttd.util.Kanboard; import com.alttd.util.Logger; -import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.ISnowflake; import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; -import net.dv8tion.jda.api.events.channel.forum.ForumTagAddEvent; +import net.dv8tion.jda.api.entities.channel.forums.ForumTag; +import net.dv8tion.jda.api.events.channel.update.ChannelUpdateAppliedTagsEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; +import java.util.List; public class TagAdded extends ListenerAdapter { @Override - public void onForumTagAdd(ForumTagAddEvent event) { - if (event.getTag().getIdLong() != 0L) {//TODO add tag id + public void onChannelUpdateAppliedTags(ChannelUpdateAppliedTagsEvent event) { + List addedTags = event.getAddedTags(); + if (addedTags.stream().map(ISnowflake::getIdLong).noneMatch(id -> id == 1020378607052398632L)) {//TODO add tag id to config return; } if (!(event.getChannel() instanceof ThreadChannel threadChannel)) { return; } - threadChannel.retrieveStartMessage().queue(Kanboard::forwardMessageToKanboard, error -> { - Logger.altitudeLogs.error(error); - }); + threadChannel.retrieveStartMessage().queue(Kanboard::forwardMessageToKanboard, error -> Logger.altitudeLogs.error(error)); } } diff --git a/src/main/java/com/alttd/util/Kanboard.java b/src/main/java/com/alttd/util/Kanboard.java index 8d86cd2..e711d7f 100644 --- a/src/main/java/com/alttd/util/Kanboard.java +++ b/src/main/java/com/alttd/util/Kanboard.java @@ -1,7 +1,9 @@ package com.alttd.util; import com.alttd.config.SettingsConfig; +import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import java.io.IOException; import java.net.URI; @@ -9,23 +11,26 @@ import java.net.URISyntaxException; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.Base64; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; public class Kanboard { - public static CompletableFuture forwardMessageToKanboard(String title, String body) { + public static CompletableFuture forwardMessageToKanboard(String title, String body, String webLink) { HttpClient client = HttpClient.newHttpClient(); HttpRequest request; + String jsonPayload = getPayload(UUID.randomUUID().toString(), title, body, webLink); try { - String jsonPayload = getPayload(UUID.randomUUID().toString(), title, body); - + byte[] kanboardTokenBytes = String.join(":", "jsonrpc", SettingsConfig.KANBOARD_TOKEN).getBytes(StandardCharsets.UTF_8); + String token = Base64.getEncoder().encodeToString(kanboardTokenBytes); request = HttpRequest.newBuilder() .uri(new URI("https://kanboard.alttd.com/jsonrpc.php")) .header("Content-Type", "application/json") - .setHeader("Authorization", SettingsConfig.KANBOARD_TOKEN) + .setHeader("Authorization", "Basic " + token) .POST(HttpRequest.BodyPublishers.ofString(jsonPayload)) .build(); } catch (URISyntaxException e) { @@ -43,11 +48,11 @@ public class Kanboard { return false; } - if (response.statusCode() == 200) { - Logger.altitudeLogs.info(response.body()); + if (response.statusCode() == 200 && response.body().matches(".*\"result\":[0-9]+,.*")) { + Logger.altitudeLogs.info(String.format("Response body: [%s]\nFor JsonPayload: [%s]", response.body(), jsonPayload)); return true; } else { - Logger.altitudeLogs.error(String.format("Invalid response [%s]", response.body())); + Logger.altitudeLogs.error(String.format("Invalid response: [%s]\nPayload: [%s]", response.body(), jsonPayload)); return false; } }); @@ -55,31 +60,71 @@ public class Kanboard { public static CompletableFuture forwardMessageToKanboard(Message message) { String body = message.getContentDisplay(); + if (messageIsSuggestion(message) && message.getGuildChannel() instanceof ThreadChannel threadChannel) { + return forwardSuggestionToKanboard(message, threadChannel); + } String[] split = body.split("\n"); String title; if (split.length == 1) { title = "No title"; } else { title = split[0]; - body = Arrays.stream(split).skip(1).collect(Collectors.joining("\n")); + body = Arrays.stream(split).skip(1).filter(str -> !str.isBlank()).collect(Collectors.joining("\n")); } - return forwardMessageToKanboard(title, body); + return forwardMessageToKanboard(title, body, getWebLink(message)); } - private static String getPayload(String id, String title, String body) { + private static CompletableFuture forwardSuggestionToKanboard(Message message, ThreadChannel threadChannel) { + String title = threadChannel.getName(); + String content = Arrays.stream(message.getContentDisplay().split("\n")).skip(1).filter(str -> !str.isBlank()).collect(Collectors.joining("\n")); + return forwardMessageToKanboard(title, content, getWebLink(message)); + } + + private static boolean messageIsSuggestion(Message message) { + Member member = message.getMember(); + if (member == null) + return false; + + if (!member.getUser().equals(message.getJDA().getSelfUser())) { + return false; + } + if (!message.getContentRaw().startsWith("**Suggestion by:")) { + return false; + } + return true; + } + + private static String getPayload(String id, String title, String body, String webLink) { return String.format(""" { "jsonrpc": "2.0", "method": "createTask", - "id": %s, + "id": "%s", "params": { "title": "%s", "project_id": "3", - "description": "%s", + "description": "%s%s", "column_id": "0", "color_id": "red" } - }""", id, title, body); + }""", + escapeSpecialJsonChars(id), + escapeSpecialJsonChars(title), + escapeSpecialJsonChars(body), + escapeSpecialJsonChars(String.format("\n\n[Discord link](%s)", webLink))); } + private static String getWebLink(Message message) { + return String.format("https://discord.com/channels/%d/%d/%d", message.getGuildIdLong(), message.getChannelIdLong(), message.getIdLong()); + } + + private static String escapeSpecialJsonChars(String s) { + return s.replace("\"", "\\\"") + .replace("\\", "\\\\") + .replace("\b", "\\b") + .replace("\f", "\\f") + .replace("\n", "\\n") + .replace("\r", "\\r") + .replace("\t", "\\t"); + } }