From 9004e6baf44200a51c59ea786886bb49c98e0250 Mon Sep 17 00:00:00 2001 From: barsh404error Date: Mon, 29 Dec 2025 13:47:52 +0300 Subject: [PATCH] Add utilities to detect and replace broken links. --- .../tjbot/features/utils/LinkDetection.java | 55 ++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) 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..62883044e9 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 @@ -1,5 +1,11 @@ package org.togetherjava.tjbot.features.utils; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.concurrent.CompletableFuture; + import com.linkedin.urls.Url; import com.linkedin.urls.detection.UrlDetector; import com.linkedin.urls.detection.UrlDetectorOptions; @@ -57,6 +63,54 @@ public static List extractLinks(String content, Set filter) 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(); @@ -76,7 +130,6 @@ private static Optional toLink(Url url, Set filter) { // Remove trailing punctuation link = link.substring(0, link.length() - 1); } - return Optional.of(link); }