Gets answers automatically if in language list mode
// ==UserScript==
// @name Education Perfect Hack (Language)
// @version 1.3
// @match https://app.educationperfect.com/*
// @run-at document-start
// @description Gets answers automatically if in language list mode
// @author @blastedmeteor44
// @grant none
// @license MIT
// @namespace https://greasyfork.org/users/1232048
// ==/UserScript==
function copyToClipboard(textToCopy) {
navigator.clipboard.writeText(textToCopy).then(() => {
// Optional: Add user feedback (e.g., a "Copied!" message)
console.log("Text successfully copied to clipboard");
}).catch(err => {
// Handle errors (e.g., permissions issues)
console.error("Could not copy text: ", err);
});
}
let consoleState = {
html: "",
scroll: 0,
top: "100px",
left: "100px"
};
function startAntiSnitch() {
// Block the specified URLs
const blockedUrls = [
'services.educationperfect.com/json.rpc?target=nz.co.LanguagePerfect.Services.PortalsAsync.App.AppServicesPortal.SubmitTaskMonitoringStatus',
'services.educationperfect.com/json.rpc?target=nz.co.LanguagePerfect.Services.PortalsAsync.App.UserProfileFactsPortal.GetValues',
'services.educationperfect.com/json.rpc?target=nz.co.LanguagePerfect.Services.PortalsAsync.App.AppServicesPortal.GetTaskMonitoringStatus',
'services.educationperfect.com/json.rpc?target=nz.co.LanguagePerfect.Services.PortalsAsync.App.UserProfileFactsPortal.SetValues'
];
// Intercept and block the requests to the specified URLs
const originalOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function() {
for (const url of blockedUrls) {
if (arguments[1].includes(url)) {
console.log(`Blocked request to: ${arguments[1]}`);
return; // Cancel the request
}
}
originalOpen.apply(this, arguments);
};
// Function to remove the focus tracking popup element
function removeFocusTrackingPopup() {
const popup = document.querySelector('ep-focus-tracking-popup');
if (popup) {
popup.remove();
console.log('Focus tracking popup removed');
document.title = "EP"; // stop the popup shiiiii
}
}
// Create a MutationObserver to watch for changes in the DOM
const observer = new MutationObserver(() => {
removeFocusTrackingPopup();
});
// Start observing the document for changes
observer.observe(document, { childList: true, subtree: true });
// Initial removal of the popup if it's already present
removeFocusTrackingPopup();
}
function pressKey(key) {
const eventOptions = {
key: key,
code: key,
keyCode: key.charCodeAt(0),
which: key.charCodeAt(0),
bubbles: true
};
document.dispatchEvent(new KeyboardEvent('keydown', eventOptions));
document.dispatchEvent(new KeyboardEvent('keypress', eventOptions));
document.dispatchEvent(new KeyboardEvent('keyup', eventOptions));
}
function createConsole() {
if (document.getElementById('answer-console')) return;
const box = document.createElement('div');
box.id = 'answer-console';
box.innerHTML = `
<div id="console-header">Answer Console</div>
<div id="console-body"></div>
`;
Object.assign(box.style, {
position: 'fixed',
top: '100px',
left: '100px',
width: '300px',
maxHeight: '400px',
background: '#111',
color: '#0f0',
fontFamily: 'monospace',
fontSize: '14px',
border: '2px solid #0f0',
borderRadius: '8px',
zIndex: '999999',
overflow: 'hidden'
});
document.body.appendChild(box);
const header = box.querySelector('#console-header');
const body = box.querySelector('#console-body');
Object.assign(header.style, {
padding: '8px',
background: '#222',
cursor: 'move',
fontWeight: 'bold'
});
Object.assign(body.style, {
padding: '8px',
overflowY: 'auto',
maxHeight: '150px'
});
// Drag logic
let offsetX = 0, offsetY = 0, isDragging = false;
header.addEventListener('mousedown', (e) => {
isDragging = true;
offsetX = e.clientX - box.offsetLeft;
offsetY = e.clientY - box.offsetTop;
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
box.style.left = (e.clientX - offsetX) + 'px';
box.style.top = (e.clientY - offsetY) + 'px';
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
body.innerHTML = consoleState.html;
body.scrollTop = consoleState.scroll;
box.style.top = consoleState.top;
box.style.left = consoleState.left;
}
function destroyConsole() {
const box = document.getElementById('answer-console');
const body = document.getElementById('console-body');
if (!box || !body) return;
consoleState.html = body.innerHTML;
consoleState.scroll = body.scrollTop;
consoleState.top = box.style.top;
consoleState.left = box.style.left;
box.remove();
}
function logToConsole(message) {
const body = document.getElementById('console-body');
if (!body) return;
const line = document.createElement('div');
line.textContent = message;
body.appendChild(line);
body.scrollTop = body.scrollHeight;
}
(function () {
'use strict';
let vocabularyMap = {};
startAntiSnitch();
function extractList() {
const items = document.querySelectorAll('.preview-grid .stats-item');
if (!items.length) {
console.log("No items found.");
return;
}
const results = [];
items.forEach(item => {
const target = item.querySelector('.targetLanguage');
const base = item.querySelector('.baseLanguage');
if (target && base) {
const targetText = target.childNodes[0]?.textContent.trim();
const baseText = base.textContent.trim();
if (targetText && baseText) {
results.push({ indonesian: targetText, english: baseText });
// Store both directions for matching
vocabularyMap[targetText] = baseText;
vocabularyMap[baseText] = targetText;
}
}
});
results.sort((a, b) => a.indonesian.localeCompare(b.indonesian, 'id'));
console.log("=== Extracted Vocabulary List (Alphabetical) ===");
results.forEach((item, index) => {
console.log(`${index + 1}. ${item.indonesian} — ${item.english}`);
});
console.log("=== End of List ===");
}
function startQuestionLoop() {
createConsole(); // create our on-screen console
console.log("Starting question watcher...");
logToConsole("Starting question watcher...");
let lastQuestion = "";
let past = false // the check for lastQuestion
setInterval(() => {
const box = document.getElementById('answer-console'); //kill if there is no box
if(!box) return;
const el = document.querySelector('#question-text');
if (!el) return;
const questionText = el.textContent.trim();
if (!questionText || questionText === lastQuestion) {
past = true
}
lastQuestion = questionText;
const answer = vocabularyMap[questionText];
if(!past) {
if (answer) {
console.log("Answer:", answer);
logToConsole(answer);
} else {
console.log("No match found for:", questionText);
logToConsole("No match found for: " + questionText);
}
}
past = false
copyToClipboard(answer)
}, 500);
}
function waitForGrid() {
const observer = new MutationObserver(() => {
const grid = document.querySelector('.preview-grid');
const hasItems = grid && grid.querySelector('.stats-item');
if (hasItems) {
observer.disconnect();
console.log("Grid loaded.");
extractList();
startQuestionLoop();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
waitForGrid();
document.addEventListener('keydown', function(event) {
if (event.ctrlKey && event.key == 'k') {
const box = document.getElementById('answer-console'); //toggle console
if (box) {
destroyConsole();
} else {
createConsole();
}
}
if(event.key == "Enter") {
copyToClipboard("");
console.log("copied nothin to keyboard");
}
});
})();