11package org .togetherjava .tjbot .features .utils ;
22
3+ import java .net .URI ;
4+ import java .net .http .HttpClient ;
5+ import java .net .http .HttpRequest ;
6+ import java .net .http .HttpResponse ;
7+ import java .util .concurrent .CompletableFuture ;
8+
39import com .linkedin .urls .Url ;
410import com .linkedin .urls .detection .UrlDetector ;
511import com .linkedin .urls .detection .UrlDetectorOptions ;
@@ -57,6 +63,54 @@ public static List<String> extractLinks(String content, Set<LinkFilter> filter)
5763 public static boolean containsLink (String content ) {
5864 return !(new UrlDetector (content , UrlDetectorOptions .BRACKET_MATCH ).detect ().isEmpty ());
5965 }
66+ public static CompletableFuture <Boolean > isLinkBroken (String url ) {
67+ HttpClient client = HttpClient .newHttpClient ();
68+
69+ HttpRequest request = HttpRequest .newBuilder (URI .create (url ))
70+ .method ("HEAD" , HttpRequest .BodyPublishers .noBody ())
71+ .build ();
72+
73+ return client .sendAsync (request , HttpResponse .BodyHandlers .discarding ())
74+ .thenApply (response -> response .statusCode () >= 400 )
75+ .exceptionally (ignored -> true );
76+ }
77+ public static CompletableFuture <String > replaceDeadLinks (
78+ String text ,
79+ String replacement
80+ ) {
81+ Set <LinkFilter > filters = Set .of (
82+ LinkFilter .SUPPRESSED ,
83+ LinkFilter .NON_HTTP_SCHEME
84+ );
85+
86+ List <String > links = extractLinks (text , filters );
87+
88+ if (links .isEmpty ()) {
89+ return CompletableFuture .completedFuture (text );
90+ }
91+
92+ StringBuilder result = new StringBuilder (text );
93+
94+ List <CompletableFuture <Void >> checks = links .stream ()
95+ .map (link ->
96+ isLinkBroken (link ).thenAccept (isDead -> {
97+ if (isDead ) {
98+ int index = result .indexOf (link );
99+ if (index != -1 ) {
100+ result .replace (
101+ index ,
102+ index + link .length (),
103+ replacement
104+ );
105+ }
106+ }
107+ })
108+ )
109+ .toList ();
110+
111+ return CompletableFuture .allOf (checks .toArray (new CompletableFuture [0 ]))
112+ .thenApply (v -> result .toString ());
113+ }
60114
61115 private static Optional <String > toLink (Url url , Set <LinkFilter > filter ) {
62116 String raw = url .getOriginalUrl ();
@@ -76,7 +130,6 @@ private static Optional<String> toLink(Url url, Set<LinkFilter> filter) {
76130 // Remove trailing punctuation
77131 link = link .substring (0 , link .length () - 1 );
78132 }
79-
80133 return Optional .of (link );
81134 }
82135
0 commit comments