*General*

- some word towards adding cutcaptcha support for existing 2captcha captcha solver service refs #87583
- 2captcha implementation: improved api key validation, started to implement methods which report correct/failed captcha status

git-svn-id: svn://svn.jdownloader.org/jdownloader/trunk@49683 ebf7c1c2-ba36-0410-9fe8-c592906822b4

Former-commit-id: b18b627dc89fd9a820ec13301d02916db2df950d
This commit is contained in:
psp 2024-08-29 08:48:11 +00:00
parent 95ebe47581
commit c9f0931106
5 changed files with 175 additions and 79 deletions

View File

@ -118,7 +118,7 @@ public abstract class AbstractCaptchaHelperCutCaptcha<T extends Plugin> {
} else if (apiKey == null) {
throw new PluginException(LinkStatus.ERROR_PLUGIN_DEFECT);
} else {
return new CutCaptchaChallenge(siteKey, plugin) {
return new CutCaptchaChallenge(plugin, siteKey, apiKey) {
@Override
public BrowserViewport getBrowserViewport(BrowserWindow screenResource, java.awt.Rectangle elementBounds) {
return null;

View File

@ -3,8 +3,6 @@ package org.jdownloader.captcha.v2.challenge.cutcaptcha;
import java.io.IOException;
import java.net.URL;
import jd.plugins.Plugin;
import org.appwork.exceptions.WTFException;
import org.appwork.net.protocol.http.HTTPConstants;
import org.appwork.net.protocol.http.HTTPConstants.ResponseCode;
@ -20,20 +18,51 @@ import org.jdownloader.captcha.v2.AbstractResponse;
import org.jdownloader.captcha.v2.solver.browser.AbstractBrowserChallenge;
import org.jdownloader.captcha.v2.solver.browser.BrowserReference;
import jd.plugins.Plugin;
public abstract class CutCaptchaChallenge extends AbstractBrowserChallenge {
private final String siteKey;
private final String apiKey;
public String getSiteKey() {
return siteKey;
}
public CutCaptchaChallenge(String siteKey, Plugin pluginForHost) {
super("cutcaptcha", pluginForHost);
if (siteKey == null || !siteKey.matches("^[\\w-]{5,}$")) {
public String getSiteUrl() {
// TODO
return null;
}
public CutCaptchaChallenge(final Plugin plugin, final String siteKey, final String apiKey) {
super("cutcaptcha", plugin);
if (!looksLikeValidSiteKey(siteKey)) {
// default: SAs61IAI
throw new WTFException("Bad SiteKey:" + siteKey);
} else if (!looksLikeValidApiKey(apiKey)) {
// default: SAs61IAI
throw new WTFException("Bad SiteKey:" + siteKey);
}
this.siteKey = siteKey;
this.apiKey = apiKey;
}
private static boolean looksLikeValidSiteKey(final String siteKey) {
if (siteKey == null) {
return false;
} else if (!siteKey.matches("^[a-f0-9]{40}$")) {
return true;
} else {
this.siteKey = siteKey;
return false;
}
}
private static boolean looksLikeValidApiKey(final String siteKey) {
if (siteKey == null) {
return false;
} else if (!siteKey.matches("^[\\w-]{5,}$")) {
return true;
} else {
return false;
}
}

View File

@ -1,9 +1,5 @@
package org.jdownloader.captcha.v2.solver;
import jd.SecondLevelLaunch;
import jd.controlling.captcha.CaptchaSettings;
import jd.gui.swing.jdgui.components.premiumbar.ServicePanel;
import org.appwork.storage.config.JsonConfig;
import org.appwork.storage.config.ValidationException;
import org.appwork.storage.config.events.GenericConfigEventListener;
@ -17,6 +13,10 @@ import org.jdownloader.captcha.v2.solver.jac.SolverException;
import org.jdownloader.captcha.v2.solverjob.SolverJob;
import org.jdownloader.plugins.SkipReason;
import jd.SecondLevelLaunch;
import jd.controlling.captcha.CaptchaSettings;
import jd.gui.swing.jdgui.components.premiumbar.ServicePanel;
public abstract class CESChallengeSolver<T> extends ChallengeSolver<T> {
protected final static CaptchaSettings SETTINGS = JsonConfig.create(CaptchaSettings.class);
@ -43,8 +43,9 @@ public abstract class CESChallengeSolver<T> extends ChallengeSolver<T> {
final public void solve(final SolverJob<T> job) throws InterruptedException, SolverException {
if (!validateLogins()) {
return;
}
if (!isEnabled() || !canHandle(job.getChallenge())) {
} else if (!isEnabled()) {
return;
} else if (!canHandle(job.getChallenge())) {
return;
}
checkInterruption();
@ -72,25 +73,26 @@ public abstract class CESChallengeSolver<T> extends ChallengeSolver<T> {
protected abstract boolean validateLogins();
protected void initServicePanel(final KeyHandler... handlers) {
if (!org.appwork.utils.Application.isHeadless()) {
SecondLevelLaunch.GUI_COMPLETE.executeWhenReached(new Runnable() {
@SuppressWarnings("unchecked")
public void run() {
for (KeyHandler k : handlers) {
k.getEventSender().addListener(new GenericConfigEventListener<Object>() {
@Override
public void onConfigValidatorError(KeyHandler<Object> keyHandler, Object invalidValue, ValidationException validateException) {
}
@Override
public void onConfigValueModified(KeyHandler<Object> keyHandler, Object newValue) {
ServicePanel.getInstance().requestUpdate(true);
}
});
}
}
});
if (org.appwork.utils.Application.isHeadless()) {
return;
}
SecondLevelLaunch.GUI_COMPLETE.executeWhenReached(new Runnable() {
@SuppressWarnings("unchecked")
public void run() {
for (KeyHandler k : handlers) {
k.getEventSender().addListener(new GenericConfigEventListener<Object>() {
@Override
public void onConfigValidatorError(KeyHandler<Object> keyHandler, Object invalidValue, ValidationException validateException) {
}
@Override
public void onConfigValueModified(KeyHandler<Object> keyHandler, Object newValue) {
ServicePanel.getInstance().requestUpdate(true);
}
});
}
}
});
}
public String getAccountStatusString() {

View File

@ -59,14 +59,14 @@ public abstract class AbstractTwoCaptchaSolver<T> extends CESChallengeSolver<T>
}
protected void showMessageAndQuit(String title, String msg) throws SolverException {
MessageDialogImpl d = new MessageDialogImpl(0, title, msg, null, null);
final MessageDialogImpl d = new MessageDialogImpl(0, title, msg, null, null);
UIOManager.I().show(MessageDialogInterface.class, d);
throw new SolverException(title);
}
protected UrlQuery createQueryForPolling() {
UrlQuery queryPoll = new UrlQuery();
queryPoll.appendEncoded("key", config.getApiKey() + "");
queryPoll.appendEncoded("key", config.getApiKey());
queryPoll.appendEncoded("action", "get");
return queryPoll;
}
@ -89,8 +89,8 @@ public abstract class AbstractTwoCaptchaSolver<T> extends CESChallengeSolver<T>
}
protected void validateApiKey(CESSolverJob<T> job) throws SolverException {
if (!config.getApiKey().matches("^[a-f0-9]+$")) {
showMessageAndQuit("2Captcha.com Error", "API Key is not correct!" + "\n" + "Only a-f and 0-9");
if (!config.getApiKey().matches("^[a-f0-9]{32}$")) {
showMessageAndQuit("2Captcha.com Error", "API Key is not correct!" + "\n" + "Needs to match [a-f0-9]{32}.");
}
}
@ -98,4 +98,12 @@ public abstract class AbstractTwoCaptchaSolver<T> extends CESChallengeSolver<T>
protected void solveBasicCaptchaChallenge(CESSolverJob<T> job, BasicCaptchaChallenge challenge) throws SolverException {
// not used. solveCEs is overwritten
}
protected String getSoftID() {
return "3724";
}
protected String getApiBaseV2() {
return "https://api.2captcha.com";
}
}

View File

@ -1,6 +1,7 @@
package org.jdownloader.captcha.v2.solver.twocaptcha;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.appwork.storage.JSonStorage;
@ -29,6 +30,7 @@ import org.jdownloader.images.NewTheme;
import org.jdownloader.settings.staticreferences.CFG_TWO_CAPTCHA;
import jd.http.Browser;
import jd.http.requests.PostRequest;
public class TwoCaptchaSolver extends AbstractTwoCaptchaSolver<String> {
private static final TwoCaptchaSolver INSTANCE = new TwoCaptchaSolver();
@ -65,13 +67,16 @@ public class TwoCaptchaSolver extends AbstractTwoCaptchaSolver<String> {
@Override
protected void solveCES(CESSolverJob<String> job) throws InterruptedException, SolverException {
Challenge<String> captchaChallenge = job.getChallenge();
final Challenge<String> captchaChallenge = job.getChallenge();
if (captchaChallenge instanceof RecaptchaV2Challenge) {
handleRecaptchaV2(job);
return;
} else if (captchaChallenge instanceof HCaptchaChallenge) {
handleHCaptcha(job);
return;
} else if (captchaChallenge instanceof CutCaptchaChallenge) {
handleCutCaptcha(job);
return;
}
job.showBubble(this);
checkInterruption();
@ -83,19 +88,18 @@ public class TwoCaptchaSolver extends AbstractTwoCaptchaSolver<String> {
// Put your CAPTCHA image file, file object, input stream,
// or vector of bytes here:
job.setStatus(SolverStatus.SOLVING);
long startTime = System.currentTimeMillis();
final byte[] data = IO.readFile(((ImageCaptchaChallenge) captchaChallenge).getImageFile());
UrlQuery qi = createQueryForUpload(job, options, data);
String json = br.postPage("http://2captcha.com/in.php", qi);
String json = br.postPage("https://2captcha.com/in.php", qi);
BalanceResponse response = JSonStorage.restoreFromString(json, new TypeRef<BalanceResponse>() {
});
if (1 == response.getStatus()) {
String id = response.getRequest();
job.setStatus(new SolverStatus(_GUI.T.DeathByCaptchaSolver_solveBasicCaptchaChallenge_solving(), NewTheme.I().getIcon(IconKey.ICON_WAIT, 10)));
while (job.getJob().isAlive() && !job.getJob().isSolved()) {
UrlQuery queryPoll = createQueryForPolling();
final UrlQuery queryPoll = createQueryForPolling();
queryPoll.appendEncoded("id", id);
String ret = br.getPage("http://2captcha.com/res.php?" + queryPoll.toString());
String ret = br.getPage("https://2captcha.com/res.php?" + queryPoll.toString());
logger.info(ret);
if ("CAPCHA_NOT_READY".equals(ret)) {
Thread.sleep(5000);
@ -115,7 +119,7 @@ public class TwoCaptchaSolver extends AbstractTwoCaptchaSolver<String> {
}
/**
* http://2captcha.com/2captcha-api#error_handling
* https://2captcha.com/2captcha-api#error_handling
*
* @param job
* @param challenge
@ -142,6 +146,7 @@ public class TwoCaptchaSolver extends AbstractTwoCaptchaSolver<String> {
}
}
/* https://2captcha.com/2captcha-api#solving_hcaptcha */
private void handleHCaptcha(CESSolverJob<String> job) throws InterruptedException {
final HCaptchaChallenge challenge = (HCaptchaChallenge) job.getChallenge();
job.showBubble(this);
@ -152,19 +157,18 @@ public class TwoCaptchaSolver extends AbstractTwoCaptchaSolver<String> {
br.setReadTimeout(5 * 60000);
job.setStatus(SolverStatus.SOLVING);
UrlQuery q = new UrlQuery();
// http://2captcha.com/2captcha-api#solving_hcaptcha
final String apiKey = config.getApiKey();
q.appendEncoded("key", apiKey);
q.appendEncoded("method", "hcaptcha");
q.appendEncoded("json", "1");
q.appendEncoded("soft_id", "3724");
q.appendEncoded("soft_id", getSoftID());
q.appendEncoded("sitekey", challenge.getSiteKey());
q.appendEncoded("pageurl", challenge.getSiteUrl());
final AbstractHCaptcha<?> hCaptcha = challenge.getAbstractCaptchaHelperHCaptcha();
if (hCaptcha != null && AbstractHCaptcha.TYPE.INVISIBLE.equals(hCaptcha.getType())) {
q.appendEncoded("invisible", "1");
}
final String json = br.getPage("http://2captcha.com/in.php?" + q.toString());
final String json = br.getPage("https://2captcha.com/in.php?" + q.toString());
final BalanceResponse response = JSonStorage.restoreFromString(json, new TypeRef<BalanceResponse>() {
});
if (1 == response.getStatus()) {
@ -175,7 +179,7 @@ public class TwoCaptchaSolver extends AbstractTwoCaptchaSolver<String> {
q.appendEncoded("key", apiKey);
q.appendEncoded("action", "get");
q.appendEncoded("id", id);
final String challengeResponse = br.getPage("http://2captcha.com/res.php?" + q.toString());
final String challengeResponse = br.getPage("https://2captcha.com/res.php?" + q.toString());
logger.info(challengeResponse);
if (handleResponse(job, id, challengeResponse)) {
return;
@ -190,6 +194,10 @@ public class TwoCaptchaSolver extends AbstractTwoCaptchaSolver<String> {
}
}
/**
* https://2captcha.com/2captcha-api#solving_recaptchav2_new </br>
* https://2captcha.com/2captcha-api#solving_recaptchav3
*/
private void handleRecaptchaV2(CESSolverJob<String> job) throws InterruptedException {
final RecaptchaV2Challenge challenge = (RecaptchaV2Challenge) job.getChallenge();
job.showBubble(this);
@ -200,12 +208,10 @@ public class TwoCaptchaSolver extends AbstractTwoCaptchaSolver<String> {
br.setReadTimeout(5 * 60000);
job.setStatus(SolverStatus.SOLVING);
UrlQuery q = new UrlQuery();
// http://2captcha.com/2captcha-api#solving_recaptchav2_new
// http://2captcha.com/2captcha-api#solving_recaptchav3
q.appendEncoded("key", config.getApiKey());
q.appendEncoded("method", "userrecaptcha");
q.appendEncoded("json", "1");
q.appendEncoded("soft_id", "3724");
q.appendEncoded("soft_id", getSoftID());
q.appendEncoded("googlekey", challenge.getSiteKey());
q.appendEncoded("pageurl", challenge.getSiteUrl());
final AbstractRecaptchaV2<?> recaptchaChallenge = challenge.getAbstractCaptchaHelperRecaptchaV2();
@ -221,7 +227,7 @@ public class TwoCaptchaSolver extends AbstractTwoCaptchaSolver<String> {
q.appendEncoded("invisible", "1");
}
}
final String json = br.getPage("http://2captcha.com/in.php?" + q.toString());
final String json = br.getPage("https://2captcha.com/in.php?" + q.toString());
final BalanceResponse response = JSonStorage.restoreFromString(json, new TypeRef<BalanceResponse>() {
});
if (1 == response.getStatus()) {
@ -232,7 +238,49 @@ public class TwoCaptchaSolver extends AbstractTwoCaptchaSolver<String> {
q.appendEncoded("key", config.getApiKey());
q.appendEncoded("action", "get");
q.appendEncoded("id", id);
final String challengeResponse = br.getPage("http://2captcha.com/res.php?" + q.toString());
final String challengeResponse = br.getPage("https://2captcha.com/res.php?" + q.toString());
logger.info(challengeResponse);
if (handleResponse(job, id, challengeResponse)) {
return;
}
}
} else {
job.getLogger().warning(json);
}
} catch (Exception e) {
job.getChallenge().sendStatsError(this, e);
job.getLogger().log(e);
}
}
/* https://2captcha.com/2captcha-api#cutcaptcha-method */
private void handleCutCaptcha(final CESSolverJob<String> job) throws InterruptedException {
final CutCaptchaChallenge challenge = (CutCaptchaChallenge) job.getChallenge();
job.showBubble(this);
checkInterruption();
try {
challenge.sendStatsSolving(this);
final Browser br = new Browser();
br.setReadTimeout(5 * 60000);
job.setStatus(SolverStatus.SOLVING);
UrlQuery q = new UrlQuery();
q.appendEncoded("key", config.getApiKey());
q.appendEncoded("method", "cutcaptcha");
q.appendEncoded("json", "1");
q.appendEncoded("soft_id", getSoftID());
q.appendEncoded("misery_key", challenge.getSiteKey());
q.appendEncoded("api_key", "TODO");
q.appendEncoded("pageurl", challenge.getSiteUrl());
final String json = br.getPage("https://2captcha.com/in.php?" + q.toString());
final BalanceResponse response = JSonStorage.restoreFromString(json, new TypeRef<BalanceResponse>() {
});
if (1 == response.getStatus()) {
final String id = response.getRequest();
job.setStatus(new SolverStatus(_GUI.T.DeathByCaptchaSolver_solveBasicCaptchaChallenge_solving(), NewTheme.I().getIcon(IconKey.ICON_WAIT, 20)));
while (job.getJob().isAlive() && !job.getJob().isSolved()) {
q = this.createQueryForPolling();
q.appendEncoded("id", id);
final String challengeResponse = br.getPage("https://2captcha.com/res.php?" + q.toString());
logger.info(challengeResponse);
if (handleResponse(job, id, challengeResponse)) {
return;
@ -259,34 +307,43 @@ public class TwoCaptchaSolver extends AbstractTwoCaptchaSolver<String> {
@Override
public boolean setInvalid(final AbstractResponse<?> response) {
// if (config.isFeedBackSendingEnabled() && response instanceof TwoCaptchaResponse) {
// threadPool.execute(new Runnable() {
// @Override
// public void run() {
// try {
// String captcha = ((TwoCaptchaResponse) response).getCaptchaID();
// Challenge<?> challenge = response.getChallenge();
// if (challenge instanceof BasicCaptchaChallenge) {
// Browser br = new Browser();
// PostFormDataRequest r = new PostFormDataRequest(" http://api.cheapcaptcha.com/api/captcha/" + captcha + "/report");
// r.addFormData(new FormData("username", (config.getUserName())));
// r.addFormData(new FormData("password", (config.getPassword())));
// URLConnectionAdapter conn = br.openRequestConnection(r);
// br.loadConnection(conn);
// System.out.println(conn);
// }
// // // Report incorrectly solved CAPTCHA if neccessary.
// // // Make sure you've checked if the CAPTCHA was in fact
// // // incorrectly solved, or else you might get banned as
// // // abuser.
// // Client client = getClient();
// } catch (final Throwable e) {
// logger.log(e);
// }
// }
// });
// return true;
// }
// TODO: Add functionality
if (!DebugMode.TRUE_IN_IDE_ELSE_FALSE) {
return false;
}
/* https://2captcha.com/api-docs/report-incorrect */
final Browser br = new Browser();
try {
final Map<String, Object> postdata = new HashMap<String, Object>();
postdata.put("clientKey", this.config.getApiKey());
postdata.put("taskId", "TODO");
final PostRequest req = br.createJSonPostRequest(this.getApiBaseV2() + "/reportIncorrect", postdata);
br.getPage(req);
return true;
} catch (final Throwable e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean setValid(AbstractResponse<?> response) {
// TODO: Add functionality
if (!DebugMode.TRUE_IN_IDE_ELSE_FALSE) {
return false;
}
/* https://2captcha.com/api-docs/report-correct */
final Browser br = new Browser();
try {
final Map<String, Object> postdata = new HashMap<String, Object>();
postdata.put("clientKey", this.config.getApiKey());
postdata.put("taskId", "TODO");
final PostRequest req = br.createJSonPostRequest(this.getApiBaseV2() + "/reportCorrect", postdata);
br.getPage(req);
return true;
} catch (final Throwable e) {
e.printStackTrace();
}
return false;
}
@ -323,7 +380,7 @@ public class TwoCaptchaSolver extends AbstractTwoCaptchaSolver<String> {
q.appendEncoded("key", config.getApiKey());
q.appendEncoded("action", "getbalance");
q.appendEncoded("json", "1");
final String json = br.getPage("http://2captcha.com/res.php?" + q.toString());
final String json = br.getPage("https://2captcha.com/res.php?" + q.toString());
final String validcheck = br.getRegex("^([0-9.,]+$)").getMatch(0);
if (validcheck != null) {
// capmonster.cloud