Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions src/main/java/org/enginehub/discord/EngineHubBot.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import org.enginehub.discord.module.RoryFetch;
import org.enginehub.discord.module.SetProfilePicture;
import org.enginehub.discord.module.errorhelper.ErrorHelper;
import org.enginehub.discord.util.HttpUtil;
import org.enginehub.discord.util.PermissionRole;
import org.enginehub.discord.util.command.CommandArgParser;
import org.enginehub.discord.util.command.CommandRegistrationHandler;
Expand Down Expand Up @@ -158,6 +159,7 @@ private EngineHubBot() throws LoginException, InterruptedException {
bot = this;
LOGGER.info("Connecting...");
api = JDABuilder.create(Settings.token, intents)
.setHttpClient(HttpUtil.getClient())
.setAutoReconnect(true)
.addEventListeners(this)
.enableCache(CacheFlag.EMOJI)
Expand Down
17 changes: 6 additions & 11 deletions src/main/java/org/enginehub/discord/module/RoryFetch.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import okhttp3.Request;
import org.enginehub.discord.util.HttpResult;
import org.enginehub.discord.util.HttpUtil;
import org.enginehub.discord.util.command.CommandRegistrationHandler;
import org.enginehub.piston.CommandManager;
import org.enginehub.piston.annotation.Command;
Expand All @@ -40,9 +43,6 @@
import java.net.MalformedURLException;
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.time.Instant;
import java.util.HashMap;
import java.util.Map;
Expand All @@ -58,8 +58,6 @@ public class RoryFetch implements Module {
new TypeReference<>() {
};

private final HttpClient client = HttpClient.newHttpClient();

static {
roryOverrides.put("gilmore", "https://i.pinimg.com/736x/25/ee/b7/25eeb71bb71aeee5574c50f96205d871.jpg");
}
Expand Down Expand Up @@ -89,12 +87,9 @@ public void rory(Message message, @Arg(desc = "The ID of the rory image", def =
url = url + '/' + roryId;
}
try {
HttpResponse<String> response = client.send(
HttpRequest.newBuilder(new URI(url)).build(),
HttpResponse.BodyHandlers.ofString()
);
HttpResult response = HttpUtil.send(new Request.Builder().url(new URI(url).toURL()).build());

if (response.statusCode() == 404) {
if (response.code() == 404) {
message.getChannel().sendMessage(message.getAuthor().getEffectiveName() + ", that's not a valid rory pic").queue();
return;
}
Expand All @@ -103,7 +98,7 @@ public void rory(Message message, @Arg(desc = "The ID of the rory image", def =
message.getChannel().sendMessageEmbeds(createRoryEmbed(parsedResponse.get("id"), parsedResponse.get("url"))).queue();
} catch (MalformedURLException | URISyntaxException _) {
message.getChannel().sendMessage(message.getAuthor().getEffectiveName() + ", that's an invalid URL!").queue();
} catch (InterruptedException | IOException _) {
} catch (IOException _) {
message.getChannel().sendMessage(message.getAuthor().getEffectiveName() + ", failed to lookup rory pic!").queue();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import net.sourceforge.tess4j.Tesseract;
import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.objectmapping.ObjectMappingException;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.enginehub.discord.EngineHubBot;
Expand All @@ -44,18 +46,14 @@
import org.enginehub.discord.module.errorhelper.resolver.MCLogsResolver;
import org.enginehub.discord.module.errorhelper.resolver.RawSubdirectoryUrlResolver;
import org.enginehub.discord.module.errorhelper.resolver.RawSubdomainUrlResolver;
import org.enginehub.discord.util.HttpUtil;
import org.enginehub.discord.util.PasteUtil;

import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
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.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -76,22 +74,18 @@
public class ErrorHelper extends ListenerAdapter implements Module {

private static final Logger LOGGER = LogManager.getLogger();
private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
private static final int LOG_SCAN_LIMIT = 1024 * 1024 * 50; // 50MB

private final List<ErrorResolver> resolvers = List.of(
List::of,
new RawSubdirectoryUrlResolver("pastebin.com", "raw"), // PastebinResolver
new RawSubdirectoryUrlResolver("hastebin.com", "raw"), // HastebinResolver
new RawSubdirectoryUrlResolver("paste.helpch.at", "raw"), // PasteHelpchatResolver
new RawSubdirectoryUrlResolver("paste.md-5.net", "raw"), // md-5.net
new RawSubdomainUrlResolver("pastes.dev", "api"), // Pastes.dev
new GhostbinResolver(),
new GistResolver(),
new MCLogsResolver(),
new RawSubdirectoryUrlResolver("paste.enginehub.org", "documents", true), // EngineHubResolver
new RawSubdirectoryUrlResolver("paste.enginehub.org", "documents"), // EngineHubResolver
new IncompatibleResolver("mcpaste.io")
);

Expand Down Expand Up @@ -141,17 +135,13 @@ public void scanMessage(Message message, User author, MessageChannel channel) {
LOGGER.error("Failed to read attachment", e);
}

try {
var _ = PasteUtil.sendToPastebin(messageText.toString()).thenAccept(url -> {
String responseUrl = url.toString();
if (attachment.getFileName().equals("report.txt")) {
responseUrl = responseUrl + ".report";
}
channel.sendMessage("[AutoReply] Here's a pasted version, " + responseUrl).queue();
});
} catch (IOException | URISyntaxException | InterruptedException e) {
LOGGER.error("Failed to send to EH Paste Service", e);
}
var _ = PasteUtil.sendToPastebin(messageText.toString()).thenAccept(url -> {
String responseUrl = url.toString();
if (attachment.getFileName().equals("report.txt")) {
responseUrl = responseUrl + ".report";
}
channel.sendMessage("[AutoReply] Here's a pasted version, " + responseUrl).queue();
});
}
}
resolvers.parallelStream()
Expand Down Expand Up @@ -196,15 +186,15 @@ private static String getStringFromUrl0(String url, int tries) {
StringBuilder main = new StringBuilder();

try {
HttpResponse<Stream<String>> response = HTTP_CLIENT.send(
HttpRequest.newBuilder(new URI(url)).build(),
HttpResponse.BodyHandlers.ofLines()
);
try (Stream<String> lines = response.body()) {
if (response.statusCode() >= 400) {
throw new IOException("HTTP " + response.statusCode());
Request request = new Request.Builder().url(url).build();
try (Response response = HttpUtil.getClient().newCall(request).execute()) {
if (response.code() >= 400) {
throw new IOException("HTTP " + response.code());
}

try (BufferedReader bodyStream = new BufferedReader(response.body().charStream())) {
bodyStream.lines().forEach(main::append);
}
lines.forEach(main::append);
}
} catch (Throwable e) {
LOGGER.warn("Failed to load URL " + url + " (Tries " + tries + ')', e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,19 @@ public class RawSubdirectoryUrlResolver implements ErrorResolver {
private final Pattern urlPattern;
private final String baseUrl;
private final String subUrl;
private final boolean secure;

public RawSubdirectoryUrlResolver(String baseUrl, String subUrl) {
this(baseUrl, subUrl, false);
}

public RawSubdirectoryUrlResolver(String baseUrl, String subUrl, boolean secure) {
this.baseUrl = baseUrl;
this.subUrl = subUrl;
this.urlPattern = Pattern.compile(baseUrl + "/([A-Za-z0-9._-]*)");
this.secure = secure;
}

@Override
public List<String> foundText(String message) {
List<String> foundText = new ArrayList<>();
Matcher matcher = urlPattern.matcher(message);
while (matcher.find()) {
foundText.add(ErrorHelper.getStringFromUrl((secure ? "https" : "http") + "://" + baseUrl + '/' + subUrl + '/' + matcher.group(1)));
foundText.add(ErrorHelper.getStringFromUrl("https://" + baseUrl + '/' + subUrl + '/' + matcher.group(1)));
}

return foundText;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,19 @@ public class RawSubdomainUrlResolver implements ErrorResolver {
private final Pattern urlPattern;
private final String baseUrl;
private final String subDomain;
private final boolean secure;

public RawSubdomainUrlResolver(String baseUrl, String subDomain) {
this(baseUrl, subDomain, false);
}

public RawSubdomainUrlResolver(String baseUrl, String subDomain, boolean secure) {
this.baseUrl = baseUrl;
this.subDomain = subDomain;
this.urlPattern = Pattern.compile(baseUrl + "/([A-Za-z0-9._-]*)");
this.secure = secure;
}

@Override
public List<String> foundText(String message) {
List<String> foundText = new ArrayList<>();
Matcher matcher = urlPattern.matcher(message);
while (matcher.find()) {
foundText.add(ErrorHelper.getStringFromUrl((secure ? "https" : "http") + "://" + subDomain + "." + baseUrl + '/' + matcher.group(1)));
foundText.add(ErrorHelper.getStringFromUrl("https://" + subDomain + "." + baseUrl + '/' + matcher.group(1)));
}

return foundText;
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/org/enginehub/discord/util/HttpResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (c) EngineHub and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package org.enginehub.discord.util;

public record HttpResult(int code, String body) {
}
102 changes: 102 additions & 0 deletions src/main/java/org/enginehub/discord/util/HttpUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright (c) EngineHub and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package org.enginehub.discord.util;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.ConnectionPool;
import okhttp3.Dispatcher;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public final class HttpUtil {

private static final String USER_AGENT = "EngineHub-Bot (https://github.com/EngineHub/EngineHub-Bot)";
private static final OkHttpClient CLIENT = createClient();

/**
* Get an HTTP client, using JDA's settings for connection pooling and request dispatching.
* We share this client across all requests to avoid creating too many connections and threads.
*
* <p>
* Prefer using {@link #send(Request)} or {@link #sendAsync(Request)} for sending requests if possible in order to
* keep things simple.
* </p>
*
* @return the client
*/
public static OkHttpClient getClient() {
return CLIENT;
}

private static OkHttpClient createClient() {
Dispatcher dispatcher = new Dispatcher();
dispatcher.setMaxRequestsPerHost(25);
return new OkHttpClient.Builder()
.addInterceptor(chain -> {
Request request = chain.request();
if (request.header("User-Agent") == null) {
request = request.newBuilder().header("User-Agent", USER_AGENT).build();
}
return chain.proceed(request);
})
.dispatcher(dispatcher)
.connectionPool(new ConnectionPool(5, 10, TimeUnit.SECONDS))
.followSslRedirects(false)
.build();
}

public static HttpResult send(Request request) throws IOException {
try (Response response = CLIENT.newCall(request).execute()) {
return new HttpResult(response.code(), response.body().string());
}
}

public static CompletableFuture<HttpResult> sendAsync(Request request) {
var future = new CompletableFuture<HttpResult>();
CLIENT.newCall(request).enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
try (response) {
future.complete(new HttpResult(response.code(), response.body().string()));
} catch (Throwable t) {
future.completeExceptionally(t);
}
}

@Override
public void onFailure(Call call, IOException e) {
future.completeExceptionally(e);
}
});
return future;
}

private HttpUtil() {
}
}
Loading