mirror of
https://github.com/lebr0nli/slader-extension.git
synced 2025-06-13 06:05:45 +00:00
Automatic new-account creation.
This commit is contained in:
parent
d11b378670
commit
607338b3f1
|
@ -1,152 +1,74 @@
|
||||||
// TODO: Sidestep race condition in a less patchwork manner
|
// Remove 'x' free solutions badge.
|
||||||
setTimeout(function(){ renderBypass(); /* TODO: Check if it actually went through */ }, 1250);
|
if (document.querySelector('.b1if8dab')) { document.querySelector('.b1if8dab').style.display = "none"; }
|
||||||
|
|
||||||
|
checkIfNewAccountNeeded();
|
||||||
|
|
||||||
function renderBypass()
|
function checkIfNewAccountNeeded()
|
||||||
{
|
{
|
||||||
// Setup page elements
|
// Three cases: Almost out of solutions, not logged in at all, or out of solutions.
|
||||||
|
|
||||||
//Get Katex CSS
|
|
||||||
var katex_css = document.createElement('link');
|
|
||||||
katex_css.setAttribute('rel','stylesheet');
|
|
||||||
katex_css.setAttribute('href','https://cdn.jsdelivr.net/npm/katex@0.13.11/dist/katex.min.css ');
|
|
||||||
document.head.appendChild(katex_css);
|
|
||||||
|
|
||||||
// Get the webpage data, devoid of any headers or cookies. Acts as if the user is not logged in at all.
|
// Almost out of solutions
|
||||||
doFetch(window.location).then(data => { processData(data); })
|
if (document.querySelector('.b1m16m5o') && document.querySelector('.b1m16m5o').textContent == "This is your last free explanation")
|
||||||
|
|
||||||
}
|
|
||||||
function processData(data){
|
|
||||||
// Clear the "hidden explanation" out and replace it with a blank explanation area.
|
|
||||||
// The innermost item is ".s1i7awl8"
|
|
||||||
document.querySelector('main .mwhvwas').innerHTML = '<div class="c18oith1 sladerBypass"><div class="s1oluvjw"><h4 class="h1cwp1lk">Explanation</h4><div class="as7m9cv snqbbas"><div data-testid="ExplanationsSolution" class="e1sw891e"><div class="s1i7awl8"></div></div></div></div></div>';
|
|
||||||
var expArea = document.querySelector('.sladerBypass .s1i7awl8')
|
|
||||||
|
|
||||||
// Render new stuff
|
|
||||||
|
|
||||||
// Is this an abomination? Yes.
|
|
||||||
// Does it work? Also yes.
|
|
||||||
//
|
|
||||||
// ...
|
|
||||||
//
|
|
||||||
// I don't want to talk about it.
|
|
||||||
//
|
|
||||||
var json = data.match(/(?<=window.Quizlet\["(questionDetailsPageData|textbookExercisePageData)"] = ).+?(?=; QLoad\("Quizlet.(questionDetailsPageData|textbookExercisePageData)")/gm);
|
|
||||||
if (!json)
|
|
||||||
{
|
{
|
||||||
// Display ratelimit message
|
signUpNewAccount(false);
|
||||||
expArea.innerHTML = `<div style="color: red;"><h1>You have been ratelimited by Quizlet</h1><br>Try clearing your cookies for Quizlet and reloading this page, this usually fixes it.<br>— Slader Bypass.</div>`;
|
|
||||||
}
|
}
|
||||||
|
// Not logged in
|
||||||
|
else if (document.querySelector('.t1qexa4p') && document.querySelector('.t1qexa4p').textContent == "Create a free account to see explanations")
|
||||||
|
{
|
||||||
|
signUpNewAccount(true);
|
||||||
|
}
|
||||||
|
// Out of solutions
|
||||||
|
else if (document.querySelector('.sukvi6d') && document.querySelector('.sukvi6d').textContent == "YOU'VE REACHED YOUR FREE LIMIT")
|
||||||
|
{
|
||||||
|
signUpNewAccount(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Parse JSON data
|
|
||||||
var qDetails = JSON.parse(json[0]);
|
|
||||||
var solutions = (qDetails.question || qDetails.exercise).solutions;
|
|
||||||
// Display JSON data as answer
|
|
||||||
solutionNum = 0;
|
|
||||||
solutions.forEach(solution => {
|
|
||||||
solutionNum++;
|
|
||||||
expArea.appendChild(document.createElement('hr'));
|
|
||||||
var h1 = document.createElement('h1');
|
|
||||||
h1.textContent = 'Solution ' + solutionNum + ":";
|
|
||||||
expArea.appendChild(h1);
|
|
||||||
|
|
||||||
const numSteps = solution.steps.length;
|
function signUpNewAccount(doesReload)
|
||||||
solution.steps.forEach(step => {
|
{
|
||||||
const stepNum = step.stepNumber;
|
// We're just gonna assume this is a large enough characterspace for it to never matter.
|
||||||
// Create card element
|
var email = "sq_bypass_" + randomString(10, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
|
||||||
var div = document.createElement('div');
|
|
||||||
// Insert boilerplate card data
|
|
||||||
div.innerHTML = '<div class="r8gl7vf" data-testid="ExplanationsSolutionStep" style="padding-top: 0.8rem;"><div class="AssemblyCard AssemblyMediumCard"><div class="ExplanationsSolutionCard c5ngj6s"><div class="h1ejaztj"><h4 class="s39tzu2"></h4><span class="sb9ch1t"></span></div><div class="s1wwu7g8"><div class="s1x7f4sz"><div class=""><div class="s1xkd811"><div class="mi4ni5d sladerBypassKatex" style="white-space: pre-wrap;"></div></div></div></div></div></div></div></div>';
|
|
||||||
// Step X: .s39tzu2
|
|
||||||
div.querySelector('.s39tzu2').textContent = (numSteps === stepNum) ? "Result" : "Step " + stepNum;
|
|
||||||
// x of x: .sb9ch1t
|
|
||||||
div.querySelector('.sb9ch1t').textContent = stepNum + ' of ' + numSteps;
|
|
||||||
|
|
||||||
// I'm not 100% on the structure of these, but I'll take a stab at it.
|
|
||||||
// It seems that only one column is ever used. I'll iterate anyways, because I can't trust that.
|
|
||||||
step.columns.forEach(column => {
|
|
||||||
var columnDiv = document.createElement('div');
|
|
||||||
// Insert inner text
|
|
||||||
if (column.text)
|
|
||||||
{
|
|
||||||
var textDiv = document.createElement('div');
|
|
||||||
textDiv.classList.add('sladerBypassKatexTextArea')
|
|
||||||
textDiv.textContent = column.text.replaceAll('\n\n\n', '\n\n'); // The replace call is a bit funky but it works.
|
|
||||||
columnDiv.appendChild(textDiv);
|
|
||||||
}
|
|
||||||
// Insert image, if applicable
|
|
||||||
if (column.images.additional)
|
|
||||||
{
|
|
||||||
var image = document.createElement('img');
|
|
||||||
image.setAttribute('src', column.images.additional.regular.srcUrl);
|
|
||||||
columnDiv.appendChild(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append server-rendered image src url. Used in errorhandling. (See: handleKatexError())
|
// Same thing ^
|
||||||
if (column.images.latex && column.images.latex.large)
|
var username = "sq_bypass_" + randomString(10, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
|
||||||
{
|
|
||||||
div.querySelector('.sladerBypassKatex').setAttribute('katexsrc', column.images.latex.large.srcUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
div.querySelector('.sladerBypassKatex').appendChild(columnDiv);
|
var request = fetch("https://quizlet.com/webapi/3.2/direct-signup", {
|
||||||
});
|
"headers": {
|
||||||
|
"accept": "application/json",
|
||||||
// Append card to explanation area.
|
"accept-language": "en-US,en;q=0.9",
|
||||||
expArea.appendChild(div);
|
"content-type": "application/json",
|
||||||
});
|
"cs-token": getToken(),
|
||||||
});
|
"sec-ch-ua": "\" Not;A Brand\";v=\"99\", \"Google Chrome\";v=\"91\", \"Chromium\";v=\"91\"",
|
||||||
|
"sec-ch-ua-mobile": "?0",
|
||||||
// Render Katex for each card
|
"sec-fetch-dest": "empty",
|
||||||
expArea.querySelectorAll('.sladerBypassKatex').forEach(textArea => {
|
"sec-fetch-mode": "cors",
|
||||||
try {
|
"sec-fetch-site": "same-origin",
|
||||||
renderMathInElement(textArea, {
|
"x-requested-with": "XMLHttpRequest"
|
||||||
delimiters: [
|
},
|
||||||
{left: "$$", right: "$$", display: true},
|
"referrer": "https://quizlet.com/goodbye",
|
||||||
{left: "$", right: "$", display: false},
|
"referrerPolicy": "origin-when-cross-origin",
|
||||||
{left: "\\(", right: "\\)", display: false},
|
"body": "{\"TOS\":false,\"birth_day\":\"5\",\"birth_month\":\"5\",\"birth_year\":\"2000\",\"email\":\"" + email + "@example.com\",\"is_free_teacher\":\"2\",\"is_parent\":false,\"password1\":\"SladerBypassPassword\",\"redir\":\"https://quizlet.com/goodbye\",\"signupOrigin\":\"global-header-link\",\"screenName\":\"Logout/logoutMobileSplash\",\"username\":\"" + username + "\",\"marketing_opt_out\":false}",
|
||||||
{left: "\\begin{equation}", right: "\\end{equation}", display: true},
|
"method": "POST",
|
||||||
{left: "\\begin{align}", right: "\\end{align}", display: true},
|
"mode": "cors",
|
||||||
{left: "\\begin{alignat}", right: "\\end{alignat}", display: true},
|
"credentials": "include"
|
||||||
{left: "\\begin{gather}", right: "\\end{gather}", display: true},
|
}).then(function()
|
||||||
{left: "\\begin{CD}", right: "\\end{CD}", display: true},
|
{
|
||||||
{left: "\\[", right: "\\]", display: true}
|
if (doesReload)
|
||||||
],
|
{
|
||||||
throwOnError : true,
|
location.reload();
|
||||||
errorCallback : function(a, b){ handleKatexError(a, b, textArea); }
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Katex experienced an error. Attempting to load image replacement.');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleKatexError(a, b, textArea)
|
function randomString(length, chars) {
|
||||||
{
|
var result = '';
|
||||||
// Display Katex error
|
for (var i = length; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)];
|
||||||
console.error(a);
|
return result;
|
||||||
console.error(b);
|
};
|
||||||
|
|
||||||
// Remove broken Katex
|
function getToken(){
|
||||||
textArea.querySelector('.sladerBypassKatexTextArea').innerHTML = '';
|
token = document.cookie.match("(?:^|;)\\s*" + "qtkn".replace(/[\-\[\]{}()*+?.,\\^$|#\s]/g, "$&") + "=([^;]*)");
|
||||||
|
return decodeURIComponent(token[1]);
|
||||||
// Fallback to server-rendered katex (I'm assuming they have better error detection there.)
|
};
|
||||||
var image = document.createElement('img');
|
|
||||||
image.setAttribute('src', textArea.getAttribute('katexsrc'));
|
|
||||||
textArea.appendChild(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function doFetch(url)
|
|
||||||
{
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: 'GET', // *GET, POST, PUT, DELETE, etc.
|
|
||||||
mode: 'cors', // no-cors, *cors, same-origin
|
|
||||||
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
|
|
||||||
credentials: 'omit', // include, *same-origin, omit
|
|
||||||
headers: {
|
|
||||||
},
|
|
||||||
redirect: 'follow', // manual, *follow, error
|
|
||||||
referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
|
|
||||||
body: null // body data type must match "Content-Type" header
|
|
||||||
});
|
|
||||||
return response.text();
|
|
||||||
}
|
|
Loading…
Reference in a new issue