Greasy Fork is available in English.
Reads answers, spoofs API with dynamic values, real progress tracking, HUD stays alive
// ==UserScript==
// @name MathsOnline HACKS!! {CHEAT} {HACKS} {EXPLOIT} {API} {DECOMPILED}
// @namespace http://tampermonkey.net/
// @version 2.8
// @description Reads answers, spoofs API with dynamic values, real progress tracking, HUD stays alive
// @author Phil 🥹👍 & DynaHacks
// @license MIT
// @match *://*.mathsonline.com.au/*
// @grant none
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
// ---------- GLOBALS ----------
let latestAnswers = [];
let hudContainer = null;
let hudContent = null;
let isHidden = false;
let progressData = { pointsCompleted: 0, pointsRequired: 0, totalCorrect: 0, totalIncorrect: 0 };
// Dynamically captured values from the page
let dynamicValues = {
userQuestionSetId: null,
worksheetId: null,
taskId: null,
menuId: null,
diagnosticTypeId: null,
tutorial: null,
tutorialMaster: null,
svgInteractiveId: null
};
// ---------- STYLES ----------
const styles = `
#hacks-hud {
position: fixed;
top: 20px;
right: 20px;
width: 340px;
background: rgba(20, 20, 20, 0.95);
color: #ffffff;
border: 1px solid #00ff00;
border-radius: 8px;
font-family: monospace;
box-shadow: 0 0 15px rgba(0, 255, 0, 0.2);
z-index: 999999;
transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
user-select: none;
font-size: 12px;
}
#hacks-hud-header {
background: #111;
padding: 8px 12px;
font-weight: bold;
font-size: 12px;
border-bottom: 1px solid #333;
display: flex;
justify-content: space-between;
align-items: center;
cursor: grab;
}
#hacks-hud-header:active {
cursor: grabbing;
}
#hacks-hud-title {
color: #00ff00;
font-size: 11px;
}
#hacks-hud-btn-group {
display: flex;
gap: 6px;
}
.hacks-hud-btn {
background: #333;
border: 1px solid #555;
color: #aaa;
padding: 2px 8px;
border-radius: 3px;
cursor: pointer;
font-size: 10px;
font-family: monospace;
}
.hacks-hud-btn:hover {
background: #444;
color: #fff;
}
#hacks-hud-spoof-btn {
border-color: #00ff00;
color: #00ff00;
}
#hacks-hud-spoof-btn:hover {
background: #00ff00;
color: #000;
}
#hacks-hud-body {
padding: 10px 12px;
max-height: 350px;
overflow-y: auto;
font-size: 11px;
}
.hacks-answer-row {
display: flex;
justify-content: space-between;
padding: 4px 0;
border-bottom: 1px solid #222;
}
.hacks-answer-row:last-child {
border-bottom: none;
}
.hacks-label {
color: #888;
}
.hacks-val {
color: #00ff00;
font-weight: bold;
}
.hacks-progress {
background: #111;
padding: 6px 10px;
border-radius: 4px;
margin-bottom: 8px;
font-size: 11px;
border-left: 2px solid #00ff00;
}
.hacks-progress-bar {
background: #222;
height: 4px;
border-radius: 2px;
margin-top: 4px;
overflow: hidden;
}
.hacks-progress-bar-fill {
height: 100%;
background: #00ff00;
border-radius: 2px;
transition: width 0.3s ease;
}
.hacks-hidden-hud {
opacity: 0 !important;
pointer-events: none !important;
transform: scale(0.95) translateY(-10px);
}
.hacks-progress-text {
display: flex;
justify-content: space-between;
color: #aaa;
}
.hacks-progress-text span:first-child {
color: #00ff00;
}
.hacks-progress-text span:last-child {
color: #888;
}
.hacks-status-ok {
color: #00ff00;
}
.hacks-status-warning {
color: #ffaa00;
}
.hacks-section-title {
color: #888;
font-size: 10px;
text-transform: uppercase;
letter-spacing: 1px;
margin: 8px 0 4px 0;
border-bottom: 1px solid #222;
padding-bottom: 2px;
}
`;
function injectStyles() {
if (document.getElementById('hacks-styles')) return;
const styleSheet = document.createElement('style');
styleSheet.id = 'hacks-styles';
styleSheet.innerText = styles;
document.head.appendChild(styleSheet);
}
// ---------- DETECTION FUNCTIONS ----------
function getTutorialFromPage() {
const urlParams = new URLSearchParams(window.location.search);
let tutorial = urlParams.get('tutorial') || urlParams.get('tutorialId');
if (!tutorial) {
const pathParts = window.location.pathname.split('/');
for (let i = 0; i < pathParts.length; i++) {
if (pathParts[i] === 'lessons' && pathParts[i+1]) {
tutorial = pathParts[i+2] || pathParts[i+1];
break;
}
}
}
if (!tutorial && typeof window.tutorial !== 'undefined') {
tutorial = window.tutorial;
}
if (!tutorial && window.currentQuestionSet) {
tutorial = window.currentQuestionSet.tutorial;
}
return tutorial || null;
}
function getTutorialMasterFromPage() {
const urlParams = new URLSearchParams(window.location.search);
let tutorialMaster = urlParams.get('tutorialMaster') || urlParams.get('tutorialmaster');
if (!tutorialMaster && typeof window.tutorialMaster !== 'undefined') {
tutorialMaster = window.tutorialMaster;
}
if (!tutorialMaster && window.currentQuestionSet) {
tutorialMaster = window.currentQuestionSet.tutorialMaster;
}
if (!tutorialMaster) {
for (let key in window) {
if (key.toLowerCase().includes('tutorialmaster') || key.toLowerCase().includes('tutorial_master')) {
const val = window[key];
if (typeof val === 'string' && /^\d+$/.test(val)) {
tutorialMaster = val;
break;
}
}
}
}
return tutorialMaster || null;
}
// ---------- HUD ----------
function createHUD() {
if (document.getElementById('hacks-hud')) return;
hudContainer = document.createElement('div');
hudContainer.id = 'hacks-hud';
hudContainer.innerHTML = `
<div id="hacks-hud-header">
<span id="hacks-hud-title">MathsOnline Hacks v2.4</span>
<div id="hacks-hud-btn-group">
<button class="hacks-hud-btn" id="hacks-hud-spoof-btn">SPOOF</button>
<button class="hacks-hud-btn" id="hacks-hud-toggle-btn">HIDE</button>
</div>
</div>
<div id="hacks-hud-body">
<div id="hacks-progress-container" style="display:none;"></div>
<div id="hacks-answers-container">
<div style="color: #888; text-align: center;">Awaiting question...</div>
</div>
</div>
`;
document.documentElement.appendChild(hudContainer);
hudContent = document.getElementById('hacks-hud-body');
document.getElementById('hacks-hud-toggle-btn').addEventListener('click', toggleHUD);
document.getElementById('hacks-hud-spoof-btn').addEventListener('click', spoofAnswers);
makeDraggable(hudContainer);
console.log('[HUD] Created v2.4');
}
function ensureHUD() {
if (!document.getElementById('hacks-hud')) {
console.log('[HUD] Re-injecting...');
createHUD();
if (isHidden && hudContainer) {
hudContainer.classList.add('hacks-hidden-hud');
}
}
}
const observer = new MutationObserver(() => ensureHUD());
observer.observe(document.documentElement, { childList: true, subtree: true });
setInterval(ensureHUD, 3000);
function makeDraggable(element) {
const header = document.getElementById('hacks-hud-header');
if (!header) return;
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
header.onmousedown = dragMouseDown;
function dragMouseDown(e) {
if (e.target.classList && e.target.classList.contains('hacks-hud-btn')) return;
e = e || window.event;
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
element.style.top = (element.offsetTop - pos2) + "px";
element.style.left = (element.offsetLeft - pos1) + "px";
element.style.right = "auto";
}
function closeDragElement() {
document.onmouseup = null;
document.onmousemove = null;
}
}
function toggleHUD() {
isHidden = !isHidden;
if (hudContainer) {
hudContainer.classList.toggle('hacks-hidden-hud', isHidden);
}
}
window.addEventListener('keydown', (e) => {
if (e.key.toLowerCase() === 'h' && !e.ctrlKey && !e.metaKey) {
toggleHUD();
}
});
function updateProgress(pointsCompleted, pointsRequired, totalCorrect, totalIncorrect) {
const progressContainer = document.getElementById('hacks-progress-container');
if (!progressContainer) return;
if (pointsRequired > 0) {
progressContainer.style.display = 'block';
const percent = Math.min((pointsCompleted / pointsRequired) * 100, 100);
progressContainer.innerHTML = `
<div class="hacks-progress">
<div class="hacks-progress-text">
<span>${pointsCompleted}</span>
<span>/${pointsRequired} points</span>
<span style="color:#888;">✔ ${totalCorrect} | ✘ ${totalIncorrect}</span>
</div>
<div class="hacks-progress-bar">
<div class="hacks-progress-bar-fill" style="width:${percent}%;"></div>
</div>
</div>
`;
} else {
progressContainer.style.display = 'none';
}
}
function updateHUD(answers) {
if (!hudContent) return;
// Update answers
const answersContainer = document.getElementById('hacks-answers-container');
if (!answersContainer) return;
if (!answers || answers.length === 0) {
answersContainer.innerHTML = `<div style="color: #888; text-align: center;">⏳ No answers captured yet.</div>`;
return;
}
let html = `<div class="hacks-section-title">Answers</div>`;
answers.forEach((item, idx) => {
html += `
<div class="hacks-answer-row">
<span class="hacks-label">Q${idx+1}:</span>
<span class="hacks-val">${item.Answer}</span>
</div>
`;
});
html += `<div style="margin-top:6px;text-align:right;font-size:10px;color:#555;">${answers.length} answer(s) ready</div>`;
answersContainer.innerHTML = html;
latestAnswers = answers;
}
// ---------- ANSWER INTERCEPTOR ----------
function analyzeAndProcess(rawText) {
const trimmed = rawText.trim();
if (trimmed.startsWith('{') && (trimmed.includes('"components"') || trimmed.includes('"userQuestionSetId"'))) {
try {
const data = JSON.parse(trimmed);
// Capture ALL dynamic values from the response
if (data.userQuestionSetId) {
dynamicValues.userQuestionSetId = data.userQuestionSetId;
}
if (data.worksheetId) dynamicValues.worksheetId = data.worksheetId;
if (data.taskId) dynamicValues.taskId = data.taskId;
if (data.menuId) dynamicValues.menuId = data.menuId;
if (data.diagnosticTypeId) dynamicValues.diagnosticTypeId = data.diagnosticTypeId;
if (data.tutorial) dynamicValues.tutorial = data.tutorial;
if (data.tutorialMaster) dynamicValues.tutorialMaster = data.tutorialMaster;
if (data.svgInteractiveId) dynamicValues.svgInteractiveId = data.svgInteractiveId;
// Also capture progress data if available
if (data.pointsCompleted !== undefined) {
progressData.pointsCompleted = data.pointsCompleted;
progressData.pointsRequired = data.pointsRequired || 0;
progressData.totalCorrect = data.totalCorrect || 0;
progressData.totalIncorrect = data.totalIncorrect || 0;
updateProgress(
progressData.pointsCompleted,
progressData.pointsRequired,
progressData.totalCorrect,
progressData.totalIncorrect
);
}
console.log('[CAPTURED] Dynamic values:', dynamicValues);
console.log('[CAPTURED] Progress:', progressData);
if (data && data.components) {
const answersFound = [];
data.components.forEach(comp => {
if (comp.ComponentTypeID === "2" && comp.Data) {
try {
const parsedData = JSON.parse(comp.Data);
if (parsedData.Answers) {
answersFound.push({
ComponentID: comp.ComponentID,
Answer: parsedData.Answers
});
}
} catch (innerErr) {}
}
});
if (answersFound.length > 0) {
updateHUD(answersFound);
}
}
} catch (e) {}
}
}
const originalFetch = window.fetch;
window.fetch = async function(...args) {
const response = await originalFetch(...args);
try {
const clone = response.clone();
const text = await clone.text();
analyzeAndProcess(text);
} catch (e) {}
return response;
};
const originalOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function() {
this.addEventListener('readystatechange', function() {
if (this.readyState === 4 && this.status === 200) {
try {
analyzeAndProcess(this.responseText);
} catch (e) {}
}
});
return originalOpen.apply(this, arguments);
};
// ---------- SPOOF (FIXED v2.4) ----------
async function spoofAnswers() {
if (!latestAnswers || latestAnswers.length === 0) {
alert('No answers captured yet. Wait for a question to load.');
return;
}
// Use dynamic values if available, fallback to page detection
const tutorial = dynamicValues.tutorial || getTutorialFromPage();
const tutorialMaster = dynamicValues.tutorialMaster || getTutorialMasterFromPage();
const userQuestionSetId = dynamicValues.userQuestionSetId;
const worksheetId = dynamicValues.worksheetId || '0';
const taskId = dynamicValues.taskId || '0';
const menuId = dynamicValues.menuId || '102';
const diagnosticTypeId = dynamicValues.diagnosticTypeId || '0';
const svgInteractiveId = dynamicValues.svgInteractiveId || null;
console.log('[SPOOF] Using dynamic values:', {
userQuestionSetId,
worksheetId,
taskId,
menuId,
diagnosticTypeId,
tutorial,
tutorialMaster,
svgInteractiveId
});
if (!userQuestionSetId) {
alert('No userQuestionSetId captured. Wait for a question to load first.');
return;
}
if (!tutorial || !tutorialMaster) {
alert('Could not detect tutorial or tutorialMaster.\n' +
'Make sure you are inside an active lesson.\n' +
`Tutorial: ${tutorial || 'NOT FOUND'}\n` +
`TutorialMaster: ${tutorialMaster || 'NOT FOUND'}`);
return;
}
// Build submission data
const inputData = [{
questionId: 1,
inputs: latestAnswers.map((item, idx) => ({
id: `input${idx+1}`,
value: item.Answer,
correct: "true"
})),
checkboxes: [],
radiobuttons: [],
selectables: [],
draggables: [],
marks: latestAnswers.map((item, idx) => ({
id: `mark${idx+1}`,
correct: "true"
})),
solutions: []
}];
const payload = {
userQuestionSetId: userQuestionSetId,
svgInteractiveId: svgInteractiveId,
tutorial: tutorial,
tutorialMaster: tutorialMaster,
menuId: menuId,
worksheetId: worksheetId,
taskId: taskId,
diagnosticTypeId: diagnosticTypeId,
userQuestionSetTypeId: '7',
difficulty: '1',
correct: latestAnswers.length,
incorrect: 0,
duration: Math.floor(8000 + Math.random() * 15000),
data: JSON.stringify(inputData)
};
console.log('[SPOOF] Final payload:', payload);
try {
const submitRes = await fetch(
`https://www.mathsonline.com.au/ajax/svg_interactives/submitResult?r=${Math.random()}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Requested-With': 'XMLHttpRequest'
},
body: new URLSearchParams(payload)
}
);
const rawText = await submitRes.text();
console.log('[SPOOF] Raw response:', rawText);
try {
const result = JSON.parse(rawText);
console.log('[SPOOF] Success!', result);
// Update progress from response
if (result.pointsCompleted !== undefined) {
progressData.pointsCompleted = result.pointsCompleted;
progressData.pointsRequired = result.pointsRequired || 0;
progressData.totalCorrect = result.totalCorrect || 0;
progressData.totalIncorrect = result.totalIncorrect || 0;
updateProgress(
progressData.pointsCompleted,
progressData.pointsRequired,
progressData.totalCorrect,
progressData.totalIncorrect
);
}
alert(`Submitted ${latestAnswers.length} answer(s)! Points: ${progressData.pointsCompleted}/${progressData.pointsRequired}`);
setTimeout(() => window.location.reload(), 800);
} catch (parseError) {
console.error('[SPOOF] Server returned non-JSON:', rawText);
alert(`Server error:\n${rawText.substring(0, 100)}`);
}
} catch (e) {
console.error('[SPOOF] Submission failed:', e);
alert('Spoof failed — check console.');
}
}
// ---------- INIT ----------
window.addEventListener('DOMContentLoaded', () => {
injectStyles();
createHUD();
console.log('[INIT] Tutorial:', getTutorialFromPage(), 'Master:', getTutorialMasterFromPage());
});
console.log('%c[MathsOnline ULTIMATE] Loaded v2.4', 'color: #00ff00; font-weight: bold;');
})();