diff --git a/application/src/main/java/org/togetherjava/tjbot/features/utils/LinkDetection.java b/application/src/main/java/org/togetherjava/tjbot/features/utils/LinkDetection.java index 3b6dc18112..d36f257b31 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/utils/LinkDetection.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/utils/LinkDetection.java @@ -4,9 +4,14 @@ import com.linkedin.urls.detection.UrlDetector; import com.linkedin.urls.detection.UrlDetectorOptions; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.concurrent.CompletableFuture; /** * Utility class to detect links. @@ -58,6 +63,43 @@ public static boolean containsLink(String content) { return !(new UrlDetector(content, UrlDetectorOptions.BRACKET_MATCH).detect().isEmpty()); } + public static CompletableFuture isLinkBroken(String url) { + HttpClient client = HttpClient.newHttpClient(); + + HttpRequest request = HttpRequest.newBuilder(URI.create(url)) + .method("HEAD", HttpRequest.BodyPublishers.noBody()) + .build(); + + return client.sendAsync(request, HttpResponse.BodyHandlers.discarding()) + .thenApply(response -> response.statusCode() >= 400) + .exceptionally(ignored -> true); + } + + public static CompletableFuture replaceDeadLinks(String text, String replacement) { + Set filters = Set.of(LinkFilter.SUPPRESSED, LinkFilter.NON_HTTP_SCHEME); + + List links = extractLinks(text, filters); + + if (links.isEmpty()) { + return CompletableFuture.completedFuture(text); + } + + StringBuilder result = new StringBuilder(text); + + List> checks = + links.stream().map(link -> isLinkBroken(link).thenAccept(isDead -> { + if (isDead) { + int index = result.indexOf(link); + if (index != -1) { + result.replace(index, index + link.length(), replacement); + } + } + })).toList(); + + return CompletableFuture.allOf(checks.toArray(new CompletableFuture[0])) + .thenApply(v -> result.toString()); + } + private static Optional toLink(Url url, Set filter) { String raw = url.getOriginalUrl(); if (filter.contains(LinkFilter.SUPPRESSED) && raw.contains(">")) { @@ -76,7 +118,6 @@ private static Optional toLink(Url url, Set filter) { // Remove trailing punctuation link = link.substring(0, link.length() - 1); } - return Optional.of(link); }