Refactored SC login from v5.0.24

This commit is contained in:
jafea7 2025-04-19 22:19:49 +10:00
parent ae1e4d6c24
commit 3ee0011c20
3 changed files with 197 additions and 128 deletions

View File

@ -28,7 +28,7 @@ public class Stripchat extends AbstractSite {
@Getter @Getter
@Setter(AccessLevel.PROTECTED) @Setter(AccessLevel.PROTECTED)
private static String domain = "stripchat.com"; public static String domain = "stripchat.com";
@Getter @Getter
@Setter(AccessLevel.PROTECTED) @Setter(AccessLevel.PROTECTED)
private static String baseUri = "https://stripchat.com"; private static String baseUri = "https://stripchat.com";

View File

@ -4,35 +4,35 @@ import ctbrec.Config;
import ctbrec.StringUtil; import ctbrec.StringUtil;
import ctbrec.io.HttpClient; import ctbrec.io.HttpClient;
import ctbrec.io.HttpException; import ctbrec.io.HttpException;
import lombok.Getter; // import ctbrec.sites.stripchat.Stripchat;
import lombok.extern.slf4j.Slf4j; // import ctbrec.sites.stripchat.StripchatUtil;
import okhttp3.*;
import org.json.JSONException;
import org.json.JSONObject;
import java.time.Instant;
import java.util.Base64;
import java.nio.charset.StandardCharsets;
import java.io.IOException; import java.io.IOException;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Base64;
import okhttp3.Cookie;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static ctbrec.io.HttpConstants.*; public class StripchatHttpClient
import static java.nio.charset.StandardCharsets.UTF_8; extends HttpClient {
private static final Logger LOG = LoggerFactory.getLogger(StripchatHttpClient.class);
@Slf4j public static final MediaType JSON = MediaType.parse((String)"application/json; charset=utf-8");
public class StripchatHttpClient extends HttpClient { private static long userId = 0L;
private static String csrfToken;
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); private static String csrfTimestamp;
private static String csrfNotifyTimestamp;
private long userId = 0; private static String jwtToken;
private String csrfToken; private static Instant jwtTokenExp;
@Getter
private String csrfTimestamp;
@Getter
private String csrfNotifyTimestamp;
@Getter
private String jwtToken;
private Instant jwtTokenExp;
public StripchatHttpClient(Config config) { public StripchatHttpClient(Config config) {
super("stripchat", config); super("stripchat", config);
@ -40,138 +40,143 @@ public class StripchatHttpClient extends HttpClient {
@Override @Override
public boolean login() throws IOException { public boolean login() throws IOException {
// refresh token if needed this.getCsrfToken();
getCsrfToken(); if (this.loggedIn && this.isJwtTokenValid()) {
// already logged in
if (loggedIn && isJwtTokenValid()) {
return true; return true;
} }
if (this.checkLoginSuccess()) {
// persisted cookies might let us log in this.loggedIn = true;
if (checkLoginSuccess()) { LOG.debug("Logged in with cookies");
loggedIn = true;
log.debug("Logged in with cookies");
return true; return true;
} }
String url = Stripchat.getBaseUri() + "/api/front/auth/login"; String url = Stripchat.getBaseUri() + "/api/front/auth/login";
JSONObject requestParams = new JSONObject(); JSONObject requestParams = new JSONObject();
requestParams.put("loginOrEmail", config.getSettings().stripchatUsername); requestParams.put("loginOrEmail", config.getSettings().stripchatUsername);
requestParams.put("password", config.getSettings().stripchatPassword); requestParams.put("password", config.getSettings().stripchatPassword);
requestParams.put("remember", true); requestParams.put("remember", true).put("csrfToken", csrfToken);
requestParams.put("csrfToken", csrfToken);
requestParams.put("csrfTimestamp", csrfTimestamp); requestParams.put("csrfTimestamp", csrfTimestamp);
requestParams.put("csrfNotifyTimestamp", csrfNotifyTimestamp); requestParams.put("csrfNotifyTimestamp", csrfNotifyTimestamp);
RequestBody body = RequestBody.Companion.create(requestParams.toString(), JSON); RequestBody body = RequestBody.Companion.create(requestParams.toString(), JSON);
Request request = new Request.Builder() Request request = new Request.Builder()
.url(url) .url(url)
.header(ACCEPT, MIMETYPE_APPLICATION_JSON) .header("Accept", "application/json")
.header(USER_AGENT, config.getSettings().httpUserAgent) .header("User-Agent", config.getSettings().httpUserAgent)
.header(ORIGIN, Stripchat.getBaseUri()) .header("Origin", Stripchat.getBaseUri()).header("Referer", Stripchat.getBaseUri())
.header(REFERER, Stripchat.getBaseUri()) .header("Content-Type", "application/json")
.header(CONTENT_TYPE, MIMETYPE_APPLICATION_JSON)
.post(body) .post(body)
.build(); .build();
try (Response response = execute(request)) { try (Response response = this.execute(request);){
if (response.isSuccessful()) { if (response.isSuccessful()) {
JSONObject resp = new JSONObject(response.body().string()); JSONObject resp = new JSONObject(response.body().string());
if (resp.has("user")) { if (resp.has("user")) {
JSONObject user = resp.getJSONObject("user"); JSONObject user = resp.getJSONObject("user");
userId = user.optLong("id"); userId = user.optLong("id");
return true; boolean bl = true;
} else { return bl;
return false;
} }
} else { boolean bl = false;
log.info("Auto-Login failed: {} {} {}", response.code(), response.message(), url); return bl;
return false;
} }
LOG.info("Auto-Login failed: {} {} {}", new Object[]{response.code(), response.message(), url});
boolean bl = false;
return bl;
} }
} }
private void loadCsrfToken() throws IOException { private void loadCsrfToken() throws IOException {
String url = Stripchat.getBaseUri() + "/api/front/v3/config/initial?requestPath=%2F&timezoneOffset=0"; block9: {
Request request = new Request.Builder() csrfToken = "";
.url(url) csrfTimestamp = "";
.header(ACCEPT, MIMETYPE_APPLICATION_JSON) csrfNotifyTimestamp = "";
.header(USER_AGENT, config.getSettings().httpUserAgent) String url = Stripchat.getBaseUri() + "/api/front/v3/config/initial?requestPath=%2F&timezoneOffset=0&disableClient=0&uniq=" + StripchatUtil.getUniq();
.header(ORIGIN, Stripchat.getBaseUri()) Request request = new Request.Builder().url(url).header("Accept", "application/json")
.header(REFERER, Stripchat.getBaseUri()) .header("User-Agent", config.getSettings().httpUserAgent)
.header(CONTENT_TYPE, MIMETYPE_APPLICATION_JSON) .header("Origin", Stripchat.getBaseUri())
.build(); .header("Referer", Stripchat.getBaseUri())
try (Response response = execute(request)) { .header("Content-Type", "application/json")
if (response.isSuccessful()) { .build();
JSONObject resp = new JSONObject(response.body().string()); try (Response response = this.execute(request);){
JSONObject data = resp.getJSONObject("initial").getJSONObject("client"); if (response.isSuccessful()) {
csrfToken = data.optString("csrfToken"); JSONObject initial;
csrfTimestamp = data.optString("csrfTimestamp"); JSONObject resp = new JSONObject(response.body().string());
csrfNotifyTimestamp = data.optString("csrfNotifyTimestamp"); if (resp.has("initial") && (initial = resp.getJSONObject("initial")).has("client")) {
log.debug("Stripchat token CSRF: {} [{}]", csrfToken, csrfNotifyTimestamp); JSONObject client = initial.getJSONObject("client");
} else { csrfToken = client.optString("csrfToken");
csrfTimestamp = client.optString("csrfTimestamp");
csrfNotifyTimestamp = client.optString("csrfNotifyTimestamp");
LOG.debug("Stripchat CSRF token: {} [{}]", (Object)csrfToken, (Object)csrfNotifyTimestamp);
}
if (StringUtil.isBlank(csrfToken)) {
LOG.debug("Stripchat CSRF token not found");
}
break block9;
}
throw new HttpException(response.code(), response.message()); throw new HttpException(response.code(), response.message());
} }
} }
} }
private void loadJwtToken() throws IOException { private void loadJwtToken() throws IOException {
String url = Stripchat.getBaseUri() + "/api/front/v3/config/dynamic"; block11: {
Request request = new Request.Builder() jwtToken = "";
.url(url) String url = Stripchat.getBaseUri() + "/api/front/v3/config/dynamic?uniq=" + StripchatUtil.getUniq();
.header(ACCEPT, MIMETYPE_APPLICATION_JSON) Request request = new Request.Builder().url(url)
.header(USER_AGENT, config.getSettings().httpUserAgent) .header("Accept", "application/json")
.header(ORIGIN, Stripchat.getBaseUri()) .header("User-Agent", config.getSettings().httpUserAgent)
.header(REFERER, Stripchat.getBaseUri()) .header("Origin", Stripchat.getBaseUri())
.header(CONTENT_TYPE, MIMETYPE_APPLICATION_JSON) .header("Referer", Stripchat.getBaseUri())
.build(); .header("Content-Type", "application/json")
try (Response response = execute(request)) { .build();
if (response.isSuccessful()) { try (Response response = execute(request);){
JSONObject resp = new JSONObject(response.body().string()); if (response.isSuccessful()) {
JSONObject dynamic = resp.getJSONObject("dynamic"); JSONObject resp = new JSONObject(response.body().string());
jwtToken = dynamic.optString("jwtToken"); if (resp.has("dynamic")) {
if (StringUtil.isNotBlank(jwtToken)) { JSONObject dynamic = resp.getJSONObject("dynamic");
String[] parts = jwtToken.split("\\."); jwtToken = dynamic.optString("jwtToken");
if (parts.length > 1) {
String decString = new String(Base64.getDecoder().decode(parts[1]), StandardCharsets.UTF_8);
JSONObject body = new JSONObject(decString);
jwtTokenExp = Instant.ofEpochSecond(body.optLong("exp"));
} }
if (StringUtil.isNotBlank(jwtToken)) {
String[] parts = jwtToken.split("\\.");
if (parts.length > 1) {
String decString = new String(Base64.getDecoder().decode(parts[1]), StandardCharsets.UTF_8);
JSONObject body = new JSONObject(decString);
jwtTokenExp = Instant.ofEpochSecond(body.optLong("exp"));
}
LOG.debug("Stripchat JWT token: {} [{}]", (Object)jwtToken, (Object)jwtTokenExp);
} else {
LOG.debug("Stripchat JWT token not found");
}
break block11;
} }
log.debug("Stripchat token JWT: {} [{}]", jwtToken, jwtTokenExp);
} else {
throw new HttpException(response.code(), response.message()); throw new HttpException(response.code(), response.message());
} }
} }
} }
/**
* check, if the login worked
* @throws IOException
*/
public boolean checkLoginSuccess() throws IOException { public boolean checkLoginSuccess() throws IOException {
getJwtToken(); getJwtToken();
getCsrfToken(); getCsrfToken();
return StringUtil.isNotBlank(jwtToken) && StringUtil.isNotBlank(csrfToken); return StringUtil.isNotBlank(jwtToken) && StringUtil.isNotBlank(csrfToken);
} }
public long getUserId() throws JSONException, IOException { public long getUserId() throws JSONException, IOException {
if (userId == 0) { block8: {
String url = Stripchat.getBaseUri() + "/api/front/users/username/" + config.getSettings().stripchatUsername; if (userId == 0L) {
Request request = new Request.Builder() String url = Stripchat.getBaseUri() + "/api/front/users/username/" + config.getSettings().stripchatUsername;
.url(url) Request request = new Request.Builder()
.header(ACCEPT, MIMETYPE_APPLICATION_JSON) .url(url)
.header(USER_AGENT, config.getSettings().httpUserAgent) .header("Accept", "application/json")
.header(ORIGIN, Stripchat.getBaseUri()) .header("User-Agent", config.getSettings().httpUserAgent)
.header(REFERER, Stripchat.getBaseUri()) .header("Origin", Stripchat.getBaseUri())
.header(CONTENT_TYPE, MIMETYPE_APPLICATION_JSON) .header("Referer", Stripchat.getBaseUri())
.build(); .header("Content-Type", "application/json")
try (Response response = execute(request)) { .build();
if (response.isSuccessful()) { try (Response response = execute(request);){
JSONObject resp = new JSONObject(response.body().string()); if (response.isSuccessful()) {
JSONObject user = resp.getJSONObject("user"); JSONObject resp = new JSONObject(response.body().string());
userId = user.optLong("id"); JSONObject user = resp.getJSONObject("user");
} else { userId = user.optLong("id");
break block8;
}
throw new HttpException(url, response.code(), response.message()); throw new HttpException(url, response.code(), response.message());
} }
} }
@ -179,13 +184,15 @@ public class StripchatHttpClient extends HttpClient {
return userId; return userId;
} }
public JSONObject getAmpl() { public JSONObject getAmpl() {
try { try {
Cookie cookie = getCookieJar().getCookie(HttpUrl.parse(Stripchat.getBaseUri()), "baseAmpl"); Cookie cookie = getCookieJar().getCookie(HttpUrl.parse((String)Stripchat.getBaseUri()), "baseAmpl");
String json = URLDecoder.decode(cookie.value(), UTF_8); String json = URLDecoder.decode(cookie.value(), "utf-8");
JSONObject ampl = new JSONObject(json); JSONObject ampl = new JSONObject(json);
ampl.put("ep", (Object)new JSONObject());
return ampl; return ampl;
} catch (Exception ex) { }
catch (Exception ex) {
return new JSONObject(); return new JSONObject();
} }
} }
@ -203,41 +210,45 @@ public class StripchatHttpClient extends HttpClient {
if (!isTokenValid()) { if (!isTokenValid()) {
loadCsrfToken(); loadCsrfToken();
} }
} catch (Exception e) { }
log.debug("Invalid CSRF Token {}: {}", csrfToken, e.getMessage()); catch (Exception e) {
LOG.debug("Invalid CSRF Token {}: {}", (Object)csrfToken, (Object)e.getMessage());
csrfToken = ""; csrfToken = "";
} }
return csrfToken; return csrfToken;
} }
public String getJwtToken() { public String getJwtToken() {
try { try {
if (!isJwtTokenValid()) { if (!isJwtTokenValid()) {
loadJwtToken(); loadJwtToken();
} }
} catch (Exception e) { }
log.debug("Invalid JWT Token {}: {}", jwtToken, e.getMessage()); catch (Exception e) {
LOG.debug("Invalid JWT Token {}: {}", (Object)jwtToken, (Object)e.getMessage());
jwtToken = ""; jwtToken = "";
} }
return jwtToken; return jwtToken;
} }
private boolean isJwtTokenValid() { private boolean isJwtTokenValid() {
if (StringUtil.isBlank(jwtToken) || jwtTokenExp == null) { if (StringUtil.isBlank(jwtToken) || jwtTokenExp == null) {
return false; return false;
} }
return jwtTokenExp.isAfter(Instant.now()); return jwtTokenExp.isAfter(Instant.now().minus(3L, ChronoUnit.HOURS));
} }
private boolean isTokenValid() { private boolean isTokenValid() {
if (StringUtil.isBlank(csrfToken) || StringUtil.isBlank(csrfTimestamp) || StringUtil.isBlank(csrfNotifyTimestamp)) { if (StringUtil.isBlank(csrfToken) || StringUtil.isBlank(csrfTimestamp) || StringUtil.isBlank(csrfNotifyTimestamp)) {
return false; return false;
} }
try { try {
Instant notifyTime = Instant.parse(csrfNotifyTimestamp); Instant notifyTime = Instant.parse(csrfNotifyTimestamp);
return notifyTime.isAfter(Instant.now()); return notifyTime.isAfter(Instant.now().minus(3L, ChronoUnit.HOURS));
} catch (Exception e) { }
catch (Exception e) {
return false; return false;
} }
} }
} }

View File

@ -0,0 +1,58 @@
package ctbrec.sites.stripchat;
// import ctbrec.sites.stripchat.Stripchat;
import java.text.MessageFormat;
import java.time.Instant;
import java.util.Random;
import org.json.JSONObject;
public class StripchatUtil {
private static final Random RNG = new Random();
private static final String HEX = "0123456789abcdef";
private static final String ALPHA = "0123456789abcdefghijklmnopqrstuvwxyz";
public static String getUniq() {
return StripchatUtil.randomString(16, ALPHA);
}
public static String randomString(int length, String dict) {
char[] text = new char[length];
for (int i = 0; i < length; ++i) {
text[i] = dict.charAt(RNG.nextInt(dict.length()));
}
return new String(text);
}
public static String getTabId() {
return StripchatUtil.randomString(60, HEX);
}
public static String hexString(int length) {
return StripchatUtil.randomString(length, HEX);
}
public static String alphaString(int length) {
return StripchatUtil.randomString(length, ALPHA);
}
public static String getEventId() {
return MessageFormat.format("{0}-{1}-{2}-{3}-{4}", StripchatUtil.hexString(8), StripchatUtil.hexString(4), StripchatUtil.hexString(4), StripchatUtil.hexString(4), StripchatUtil.hexString(12));
}
public static JSONObject getEventData() {
JSONObject data = new JSONObject();
data.put("ek.platformVersion", "10.92.4");
data.put("ek.tabId", StripchatUtil.getTabId());
data.put("ek.timestampCreated", Instant.now().getEpochSecond());
data.put("ek.deviceFlags", "2073600|10001110000001000");
data.put("ek.httpHost", Stripchat.domain);
data.put("ek.httpPath", "/");
data.put("ek.isDocumentHidden", 0);
data.put("ek.isTabFocused", 1);
data.put("ek.pageClass", "viewcam");
data.put("ek.contractVersion", "v0.2.1");
data.put("ek.eventId", StripchatUtil.getEventId());
return data;
}
}