From 848748c4b9bf9239ede18f447550d87dc7763bb9 Mon Sep 17 00:00:00 2001 From: 00asdf Date: Wed, 21 Jun 2023 01:45:22 +0200 Subject: [PATCH] added discord webhook support --- .../general/utils/discord/DiscordHook.java | 111 ++++++++++++++++++ .../asdf00/general/utils/extras/Tuple.java | 11 ++ src/module-info.java | 3 + 3 files changed, 125 insertions(+) create mode 100644 src/dev/asdf00/general/utils/discord/DiscordHook.java create mode 100644 src/dev/asdf00/general/utils/extras/Tuple.java diff --git a/src/dev/asdf00/general/utils/discord/DiscordHook.java b/src/dev/asdf00/general/utils/discord/DiscordHook.java new file mode 100644 index 0000000..b8261f8 --- /dev/null +++ b/src/dev/asdf00/general/utils/discord/DiscordHook.java @@ -0,0 +1,111 @@ +package dev.asdf00.general.utils.discord; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +public final class DiscordHook { + private static final HttpClient httpClient = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + + private static final Map currentHooks = new HashMap<>(); + + private final String webhook; + + private final Queue msgQueue = new ConcurrentLinkedQueue<>(); + + private final Executor thread = Executors.newSingleThreadExecutor(); + + private final Object lockObject = new Object(); + + private int timeoutInMillis = 5000; + + private DiscordHook(String webhook) { + this.webhook = webhook; + } + + /** + * Send a message to the associated discord webhook. + * + * @param splitMessage determines if the message should be pruned or split into + * multiple messages if it is lager than 2000 characters + * @param msg the message to be sent + * @param args arguments to be formatted into the message + */ + public void sendMsg(boolean splitMessage, String msg, Object... args) { + var pmsg = String.format(msg, args); + int cnt = 1; + if (!splitMessage && pmsg.length() > 1994) { + // large message with pruning + msgQueue.add(pmsg.substring(0, 1995) + " [...]"); + } else if (pmsg.length() > 2000) { + // large message with splitting + var ms = new ArrayList(pmsg.length() / 2000 + 1); + for (int i = 0; i < ms.size(); i++) { + ms.add(pmsg.substring(i * 2000, Math.min(pmsg.length(), (i + 1) * 2000 + 1))); + } + msgQueue.addAll(ms); + cnt = ms.size(); + } else { + // small message + msgQueue.add(pmsg); + } + + // schedule all messages inserted into the queue + for (; cnt > 0; cnt--) { + thread.execute(this::scheduleMsg); + } + } + + private static String wrapIntoJson(String msg) { + return String.format("{\"content\": \"%s\"}", msg); + } + + private void scheduleMsg() { + try { + var json = msgQueue.remove(); + synchronized (lockObject) { + var postRequest = HttpRequest.newBuilder() + .POST(HttpRequest.BodyPublishers.ofString(json)) + .uri(URI.create(webhook)) + .setHeader("User-Agent", "JavaCrawler") + .header("Content-Type", "application/json") + .build(); + httpClient.send(postRequest, HttpResponse.BodyHandlers.ofString()); + Thread.sleep(timeoutInMillis); + } + } catch (InterruptedException | IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Sets the timeout for rate limiting. Default is 5 seconds. + */ + public void setTimeout(int timeout) { + timeoutInMillis = timeout; + } + + /** + * Gets the instance associated with this webhook or creates a new one if no instance was found. + * + * @param webhook the URI of the discord webhook + * @return instance associated with the given webhook + */ + public static synchronized DiscordHook getInstance(String webhook) { + if (!currentHooks.containsKey(webhook)) { + currentHooks.put(webhook, new DiscordHook(webhook)); + } + return currentHooks.get(webhook); + } +} diff --git a/src/dev/asdf00/general/utils/extras/Tuple.java b/src/dev/asdf00/general/utils/extras/Tuple.java new file mode 100644 index 0000000..8038e92 --- /dev/null +++ b/src/dev/asdf00/general/utils/extras/Tuple.java @@ -0,0 +1,11 @@ +package dev.asdf00.general.utils.extras; + +public class Tuple { + public A a; + public B b; + public Tuple() { } + public Tuple(A a, B b) { + this.a = a; + this.b = b; + } +} diff --git a/src/module-info.java b/src/module-info.java index e9f306d..870935d 100644 --- a/src/module-info.java +++ b/src/module-info.java @@ -1,4 +1,7 @@ module GeneralUtils { requires jdk.unsupported; + requires java.net.http; + exports dev.asdf00.general.utils.discord; + exports dev.asdf00.general.utils.extras; exports dev.asdf00.general.utils.list; } \ No newline at end of file