diff --git a/src/main/java/com/rarchives/ripme/ripper/rippers/Rule34Ripper.java b/src/main/java/com/rarchives/ripme/ripper/rippers/Rule34Ripper.java index c7245739e..675e341a0 100644 --- a/src/main/java/com/rarchives/ripme/ripper/rippers/Rule34Ripper.java +++ b/src/main/java/com/rarchives/ripme/ripper/rippers/Rule34Ripper.java @@ -10,20 +10,43 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import com.rarchives.ripme.ripper.AbstractHTMLRipper; +import com.rarchives.ripme.ui.RipStatusMessage.STATUS; import com.rarchives.ripme.utils.Http; +import com.rarchives.ripme.utils.Utils; public class Rule34Ripper extends AbstractHTMLRipper { + private static final Logger logger = LogManager.getLogger(Rule34Ripper.class); + private static final String DOMAIN = "rule34.xxx"; + private static final String API_BASE_URL = "https://" + DOMAIN + "/index.php"; + public Rule34Ripper(URL url) throws IOException { super(url); } private String apiUrl; private int pageNumber = 0; + private String apiKey; + private String userId; + + private void loadConfig() { + apiKey = Utils.getConfigString("rule34.api_key", ""); + userId = Utils.getConfigString("rule34.user_id", ""); + if (apiKey.isEmpty() || userId.isEmpty()) { + sendUpdate(STATUS.DOWNLOAD_WARN, + DOMAIN + " requires API credentials. Set rule34.api_key and rule34.user_id in config. " + + "Get them from " + API_BASE_URL + "?page=account&s=options"); + logger.warn("Missing rule34 API credentials. Requests may fail with 403."); + } else { + logger.info("Using rule34 API credentials for user_id: " + userId); + } + } @Override public String getHost() { @@ -32,37 +55,43 @@ public String getHost() { @Override public String getDomain() { - return "rule34.xxx"; + return DOMAIN; } @Override public boolean canRip(URL url){ - Pattern p = Pattern.compile("https?://rule34.xxx/index.php\\?page=post&s=list&tags=([\\S]+)"); + Pattern p = Pattern.compile("https?://" + DOMAIN + "/index.php\\?page=post&s=list&tags=([\\S]+)"); Matcher m = p.matcher(url.toExternalForm()); return m.matches(); } @Override public String getGID(URL url) throws MalformedURLException { - Pattern p = Pattern.compile("https?://rule34.xxx/index.php\\?page=post&s=list&tags=([\\S]+)"); + Pattern p = Pattern.compile("https?://" + DOMAIN + "/index.php\\?page=post&s=list&tags=([\\S]+)"); Matcher m = p.matcher(url.toExternalForm()); if (m.matches()) { return m.group(1); } - throw new MalformedURLException("Expected rule34.xxx URL format: " + - "rule34.xxx/index.php?page=post&s=list&tags=TAG - got " + url + " instead"); + throw new MalformedURLException("Expected " + DOMAIN + " URL format: " + + DOMAIN + "/index.php?page=post&s=list&tags=TAG - got " + url + " instead"); } public URL getAPIUrl() throws MalformedURLException, URISyntaxException { - URL urlToReturn = new URI("https://rule34.xxx/index.php?page=dapi&s=post&q=index&limit=100&tags=" + getGID(url)).toURL(); - return urlToReturn; + if (apiKey == null || apiKey.isEmpty() || userId == null || userId.isEmpty()) { + throw new MalformedURLException( + DOMAIN + " requires API credentials. Set rule34.api_key and rule34.user_id in config. " + + "Get them from " + API_BASE_URL + "?page=account&s=options"); + } + String baseUrl = API_BASE_URL + "?page=dapi&s=post&q=index&limit=100&tags=" + getGID(url) + + "&api_key=" + apiKey + "&user_id=" + userId; + return new URI(baseUrl).toURL(); } @Override public Document getFirstPage() throws IOException, URISyntaxException { + loadConfig(); apiUrl = getAPIUrl().toExternalForm(); - // "url" is an instance field of the superclass - return Http.url(getAPIUrl()).get(); + return Http.url(apiUrl).get(); } @Override diff --git a/src/main/resources/rip.properties b/src/main/resources/rip.properties index 7b45974e4..4c5b1f319 100644 --- a/src/main/resources/rip.properties +++ b/src/main/resources/rip.properties @@ -28,6 +28,10 @@ twitter.auth = VW9Ybjdjb1pkd2J0U3kwTUh2VXVnOm9GTzVQVzNqM29LQU1xVGhnS3pFZzhKbGVqb tumblr.auth = JFNLu3CbINQjRdUvZibXW9VpSEVYYtiPJ86o8YmvgLZIoKyuNX gw.api = gonewild +# Rule34 API credentials - get from https://rule34.xxx/index.php?page=account&s=options +# rule34.api_key = +# rule34.user_id = + twitter.max_requests = 10 twitter.rip_retweets = false twitter.exclude_replies = true diff --git a/src/test/java/com/rarchives/ripme/tst/ripper/rippers/Rule34RipperTest.java b/src/test/java/com/rarchives/ripme/tst/ripper/rippers/Rule34RipperTest.java index 5f5b03622..c5da4e724 100644 --- a/src/test/java/com/rarchives/ripme/tst/ripper/rippers/Rule34RipperTest.java +++ b/src/test/java/com/rarchives/ripme/tst/ripper/rippers/Rule34RipperTest.java @@ -1,6 +1,7 @@ package com.rarchives.ripme.tst.ripper.rippers; import java.io.IOException; +import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; @@ -10,11 +11,12 @@ import org.junit.jupiter.api.Test; import com.rarchives.ripme.ripper.rippers.Rule34Ripper; +import com.rarchives.ripme.utils.Utils; public class Rule34RipperTest extends RippersTest { @Test @Tag("flaky") - public void testShesFreakyRip() throws IOException, URISyntaxException { + public void testRule34Rip() throws IOException, URISyntaxException { Rule34Ripper ripper = new Rule34Ripper( new URI("https://rule34.xxx/index.php?page=post&s=list&tags=bimbo").toURL()); testRipper(ripper); @@ -27,4 +29,46 @@ public void testGetGID() throws IOException, URISyntaxException { Assertions.assertEquals("bimbo", ripper.getGID(url)); } + @Test + public void testGetAPIUrlWithCredentials() throws IOException, URISyntaxException { + Utils.setConfigString("rule34.api_key", "testapikey123"); + Utils.setConfigString("rule34.user_id", "12345"); + try { + URL url = new URI("https://rule34.xxx/index.php?page=post&s=list&tags=bimbo").toURL(); + Rule34Ripper ripper = new Rule34Ripper(url); + + // Trigger loadConfig() via getFirstPage(); HTTP call will fail in test env + try { + ripper.getFirstPage(); + } catch (IOException e) { + // Expected in test environment + } + + String apiUrlString = ripper.getAPIUrl().toExternalForm(); + Assertions.assertTrue(apiUrlString.contains("api_key=testapikey123"), + "API URL should contain api_key parameter"); + Assertions.assertTrue(apiUrlString.contains("user_id=12345"), + "API URL should contain user_id parameter"); + } finally { + Utils.setConfigString("rule34.api_key", ""); + Utils.setConfigString("rule34.user_id", ""); + } + } + + @Test + public void testGetAPIUrlThrowsWithoutCredentials() throws IOException, URISyntaxException { + Utils.setConfigString("rule34.api_key", ""); + Utils.setConfigString("rule34.user_id", ""); + try { + URL url = new URI("https://rule34.xxx/index.php?page=post&s=list&tags=bimbo").toURL(); + Rule34Ripper ripper = new Rule34Ripper(url); + + // Trigger loadConfig() via getFirstPage(); should throw due to missing credentials + Assertions.assertThrows(MalformedURLException.class, () -> ripper.getFirstPage(), + "getFirstPage should throw when API credentials are missing"); + } finally { + Utils.setConfigString("rule34.api_key", ""); + Utils.setConfigString("rule34.user_id", ""); + } + } }