mirror of
https://github.com/barelyprofessional/KfChatDotNet.git
synced 2026-04-30 03:22:04 -04:00
Experimental ttrs support
This commit is contained in:
@@ -43,21 +43,8 @@ public class KfTokenService
|
||||
return handler;
|
||||
}
|
||||
|
||||
private async Task CheckClearanceToken()
|
||||
private async Task GetNewClearanceToken()
|
||||
{
|
||||
var clearanceCookie = _cookies.GetAllCookies()["sssg_clearance"];
|
||||
_logger.Debug($"Got clearance cookie with value: {clearanceCookie}");
|
||||
if (await _kiwiFlare.CheckAuth(Guid.NewGuid().ToString()))
|
||||
{
|
||||
_logger.Debug("KiwiFlare has been turned off as it's accepting a non-existent sssg_clearance");
|
||||
return;
|
||||
};
|
||||
if (clearanceCookie != null)
|
||||
{
|
||||
if (await _kiwiFlare.CheckAuth(clearanceCookie.Value)) return;
|
||||
_logger.Debug("Cookie is no longer valid, removing");
|
||||
_cookies.GetAllCookies().Remove(clearanceCookie);
|
||||
}
|
||||
_logger.Debug("Getting a new clearance token");
|
||||
var i = 0;
|
||||
// Shitty retry logic as the forum is still annoyingly unstable
|
||||
@@ -73,8 +60,18 @@ public class KfTokenService
|
||||
return;
|
||||
}
|
||||
var solution = await _kiwiFlare.SolveChallenge(challenge);
|
||||
var token = await _kiwiFlare.SubmitAnswer(solution);
|
||||
_cookies.Add(new Cookie("sssg_clearance", token, "/", _kfDomain));
|
||||
string token;
|
||||
if (challenge.IsTtrs)
|
||||
{
|
||||
token = await _kiwiFlare.SubmitAnswerTtrs(solution);
|
||||
_cookies.Add(new Cookie("ttrs_clearance", token, "/", _kfDomain));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
token = await _kiwiFlare.SubmitAnswer(solution);
|
||||
_cookies.Add(new Cookie("sssg_clearance", token, "/", _kfDomain));
|
||||
}
|
||||
_logger.Debug("Successfully retrieved a new token and added to the cookie container");
|
||||
return;
|
||||
}
|
||||
@@ -90,19 +87,17 @@ public class KfTokenService
|
||||
|
||||
private async Task<string> GetLoginPage()
|
||||
{
|
||||
_logger.Debug("Checking clearance token is actually valid first");
|
||||
await CheckClearanceToken();
|
||||
using var client = new HttpClient(GetHttpClientHandler());
|
||||
var response = await client.GetAsync($"https://{_kfDomain}/login", _ctx);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var content = await response.Content.ReadAsStringAsync(_ctx);
|
||||
var document = new HtmlDocument();
|
||||
document.LoadHtml(content);
|
||||
var challengeData = document.DocumentNode.SelectSingleNode("//html[@id=\"sssg\"]");
|
||||
if (response.StatusCode == HttpStatusCode.NonAuthoritativeInformation && challengeData != null)
|
||||
if (response.StatusCode == HttpStatusCode.NonAuthoritativeInformation)
|
||||
{
|
||||
_logger.Error("Caught a 203 response when trying to load logon page which means we were KiwiFlare challenged");
|
||||
throw new KiwiFlareChallengedException();
|
||||
_logger.Info("Caught a 203 response when trying to load the logon page which means we have to solve a KiwiFlare challenge");
|
||||
await GetNewClearanceToken();
|
||||
_logger.Info("Solved the challenge, now going to grab the login page again");
|
||||
response = await client.GetAsync($"https://{_kfDomain}/login", _ctx);
|
||||
content = await response.Content.ReadAsStringAsync(_ctx);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
@@ -184,6 +179,5 @@ public class KfTokenService
|
||||
_cookies = new CookieContainer();
|
||||
}
|
||||
|
||||
public class KiwiFlareChallengedException : Exception;
|
||||
public class KiwiFarmsLogonFailedException : Exception;
|
||||
}
|
||||
@@ -36,25 +36,37 @@ public class KiwiFlare(string kfDomain, string? proxy = null, CancellationToken?
|
||||
var response = await client.GetAsync($"https://{kfDomain}/", _ctx);
|
||||
var document = new HtmlDocument();
|
||||
document.Load(await response.Content.ReadAsStreamAsync(_ctx));
|
||||
var challengeData = document.DocumentNode.SelectSingleNode("//html[@id=\"sssg\"]");
|
||||
var pow = "sssg";
|
||||
var challengeData = document.DocumentNode.SelectSingleNode($"//html[@id=\"{pow}\"]");
|
||||
if (challengeData == null)
|
||||
{
|
||||
_logger.Info("challengeData was null. Couldn't find html element with id = sssg, returning null");
|
||||
_logger.Info("challengeData was null. Couldn't find html element with id = sssg, trying ttrs");
|
||||
pow = "ttrs";
|
||||
challengeData = document.DocumentNode.SelectSingleNode($"//html[@id=\"{pow}\"]");
|
||||
if (challengeData == null)
|
||||
{
|
||||
_logger.Info("challengeData was still null even looking for ttrs");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!challengeData.Attributes.Contains("data-sssg-challenge")) throw new Exception("data-sssg-challenge attribute missing");
|
||||
if (!challengeData.Attributes.Contains("data-sssg-difficulty")) throw new Exception("data-sssg-difficulty attribute missing");
|
||||
if (!challengeData.Attributes.Contains("data-sssg-patience")) throw new Exception("data-sssg-patience attribute missing");
|
||||
var salt = challengeData.Attributes["data-sssg-challenge"].Value;
|
||||
var difficulty = Convert.ToInt32(challengeData.Attributes["data-sssg-difficulty"].Value);
|
||||
var patience = TimeSpan.FromMinutes(Convert.ToDouble(challengeData.Attributes["data-sssg-patience"].Value));
|
||||
_logger.Info($"Got sssg challenge parameters. Salt = {salt}, Difficulty = {difficulty}, Patience = {patience.TotalMinutes} minutes");
|
||||
if (!challengeData.Attributes.Contains($"data-{pow}-challenge")) throw new Exception($"data-{pow}-challenge attribute missing");
|
||||
if (!challengeData.Attributes.Contains($"data-{pow}-difficulty")) throw new Exception($"data-{pow}-difficulty attribute missing");
|
||||
var patience = TimeSpan.MaxValue;
|
||||
// ttrs has no patience value
|
||||
if (challengeData.Attributes.Contains("data-sssg-patience"))
|
||||
{
|
||||
patience = TimeSpan.FromMinutes(Convert.ToDouble(challengeData.Attributes["data-sssg-patience"].Value));
|
||||
}
|
||||
var salt = challengeData.Attributes[$"data-{pow}-challenge"].Value;
|
||||
var difficulty = Convert.ToInt32(challengeData.Attributes[$"data-{pow}-difficulty"].Value);
|
||||
_logger.Info($"Got {pow} challenge parameters. IsTtrs = {pow == "ttrs"}, Salt = {salt}, Difficulty = {difficulty}, Patience = {patience.TotalMinutes} minutes");
|
||||
return new KiwiFlareChallengeModel
|
||||
{
|
||||
Salt = salt,
|
||||
Difficulty = difficulty,
|
||||
Patience = patience
|
||||
Patience = patience,
|
||||
IsTtrs = pow == "ttrs"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -147,37 +159,33 @@ public class KiwiFlare(string kfDomain, string? proxy = null, CancellationToken?
|
||||
_logger.Error(json.GetRawText());
|
||||
throw new Exception($"Auth property was missing from sssg response: {json.GetRawText()}");
|
||||
}
|
||||
|
||||
public async Task<bool> CheckAuth(string authToken)
|
||||
|
||||
public async Task<string> SubmitAnswerTtrs(KiwiFlareChallengeSolutionModel solution)
|
||||
{
|
||||
using var client = new HttpClient(GetHttpClientHandler());
|
||||
var handler = GetHttpClientHandler();
|
||||
var container = new CookieContainer();
|
||||
handler.CookieContainer = container;
|
||||
handler.UseCookies = true;
|
||||
using var client = new HttpClient();
|
||||
client.Timeout = TimeSpan.FromSeconds(10);
|
||||
var formData = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new("f", authToken),
|
||||
new("salt", solution.Salt),
|
||||
new("nonce", solution.Nonce.ToString())
|
||||
});
|
||||
|
||||
var response = await client.PostAsync($"https://{kfDomain}/.sssg/api/check", formData, _ctx);
|
||||
// KiwiFlare has been turned off
|
||||
if (response.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var response = await client.PostAsync($"https://{kfDomain}/.ttrs/challenge", formData, _ctx);
|
||||
var json = await response.Content.ReadFromJsonAsync<JsonElement>(_ctx);
|
||||
if (json.TryGetProperty("error", out var error))
|
||||
var success = json.GetProperty("success").GetBoolean();
|
||||
if (!success)
|
||||
{
|
||||
_logger.Error($"Received error when checking the auth token: {error.GetString()}");
|
||||
return false;
|
||||
var reason = json.GetProperty("reason").GetString();
|
||||
_logger.Error($"ttrs didn't accept our solution with reason: {reason}");
|
||||
throw new Exception($"ttrs didn't accept our solution with reason: {reason}");
|
||||
}
|
||||
|
||||
if (json.TryGetProperty("auth", out _))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
_logger.Error("Auth property was missing from sssg response");
|
||||
_logger.Error(json.GetRawText());
|
||||
return false;
|
||||
var cookies = container.GetAllCookies();
|
||||
return cookies["ttrs_clearance"]?.Value ?? throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,6 +194,7 @@ public class KiwiFlareChallengeModel
|
||||
public required string Salt { get; set; }
|
||||
public required int Difficulty { get; set; }
|
||||
public required TimeSpan Patience { get; set; }
|
||||
public required bool IsTtrs { get; set; }
|
||||
}
|
||||
|
||||
public class KiwiFlareChallengeSolutionModel
|
||||
|
||||
Reference in New Issue
Block a user