- // v1.4
-
- const puppeteer = require('puppeteer');
-
- (async () => {
- const DIR = {
- email: 'YOUR EMAIL', // replace YOUR EMAIL with your email for auto login (keep everything else the same)
- password: 'YOUR PASSWORD', // replace YOUR PASSWORD with your password for auto login
-
- login_url: 'https://app.educationperfect.com/app/login',
-
- // log-in page elements
- username_css: '#loginId',
- password_css: '#password',
-
- // home page elements
- home_css: 'div.view-segment-dashboard',
-
- // task-starter page elements
- baseList_css: 'div.baseLanguage',
- targetList_css: 'div.targetLanguage',
- start_button_css: 'button#start-button-main',
-
- // task page elements
- modal_question_css: 'td#question-field',
- modal_correct_answer_css: 'td#correct-answer-field',
- modal_user_answered_css: 'td#users-answer-field',
- modal_css: 'div[uib-modal-window=modal-window]',
- modal_backdrop_css: 'div[uib-modal-backdrop=modal-backdrop]',
-
- question_css: '#question-text',
- answer_box_css: 'input#answer-text',
-
- exit_button_css: 'button.exit-button',
- exit_continue_button_css: 'button.continue-button',
-
- continue_button_css: 'button#continue-button',
- }
-
- // launch browser
- puppeteer.launch({
- headless: false,
- defaultViewport: null,
- handleSIGINT: false
- })
- .then(async browser => {
- const page = (await browser.pages())[0];
-
- // Open EP page and log in
- console.log('Opening EP page...');
- await page.goto(DIR.login_url);
- console.log('Waiting for login page to load...');
- await page.waitForSelector(DIR.username_css);
-
- // THIS FILLS IN YOUR DETAILS TO LOG IN AUTOMATICALLY
- console.log('Filling in login details...');
- await page.type(DIR.username_css, DIR.email);
- await page.type(DIR.password_css, DIR.password);
- await page.keyboard.press('Enter');
-
- console.log('Waiting for home page to load...');
- await page.waitForSelector(DIR.home_css, { timeout: 0 });
- console.log('EP Home page loaded; Logged in.');
-
-
- // ===== Auto-answer code starts here ===== //
- let TOGGLE = false;
- let ENTER = true;
- let fullDict = {};
- let cutDict = {};
-
- // Basic answer-parsing
- function cleanString(string) {
- return String(string)
- .replace(/\([^)]*\)/g, "").trim()
- .split(";")[0].trim()
- .split(",")[0].trim()
- .split("|")[0].trim();
- }
-
- // Get words from the main task page
- async function wordList(selector) {
- return await page.$$eval(selector, els => {
- let words = [];
- els.forEach(i => words.push(i.textContent));
- return words;
- });
- }
-
- // Refreshes the world lists on the main task page to enhance our vocabulary
- async function refreshWords() {
- const l1 = await wordList(DIR.baseList_css);
- const l2 = await wordList(DIR.targetList_css);
- for (let i = 0; i < l1.length; i++) {
- fullDict[l2[i]] = cleanString(l1[i]);
- fullDict[l1[i]] = cleanString(l2[i]);
- cutDict[cleanString(l2[i])] = cleanString(l1[i]);
- cutDict[cleanString(l1[i])] = cleanString(l2[i]);
- }
- console.log('Word Lists Refreshed.');
- await alert('Word Lists Refreshed.');
- }
-
-
- // extracts what (EP detected as) the user typed, from the fancy multicolored display
- // appended to logs for debugging/self-learning purposes
- async function getModalAnswered() {
- return await page.$$eval('td#users-answer-field > *', el => {
- let answered = '';
- el.forEach(i => {
- if (i.textContent !== null && i.style.color !== 'rgba(0, 0, 0, 0.25)') answered = answered + i.textContent;
- })
- return answered;
- });
- }
-
- // Learn from the mistakes :)
- async function correctAnswer(question, answer) {
- // wait until modal content is fully loaded
- await page.waitForFunction((css) => {
- return document.querySelector(css).textContent !== "blau";
- }, {}, DIR.modal_question_css);
-
- // extract modal contents (for debugging and correcting answers)
- let modalQuestion = await page.$eval(DIR.modal_question_css, el => el.textContent);
- let modalAnswer = await page.$eval(DIR.modal_correct_answer_css, el => el.textContent);
- let modalCutAnswer = cleanString(modalAnswer);
- let modalAnswered = await getModalAnswered();
-
- // dismisses the modal (bypasses the required cooldown)
- await page.$eval(DIR.continue_button_css, el => el.disabled = false);
- await page.click(DIR.continue_button_css);
-
- // update/correct answer dictionary
- fullDict[question] = modalCutAnswer;
-
- // logging for debugging if needed
- let log = "===== Details after Incorrect Answer: =====\n"
- log = log + `Detected Question: \n => ${question}\n`;
- log = log + `Inputted Answer: \n => ${answer}\n\n`;
- log = log + `Modal Question: \n => ${modalQuestion}\n`;
- log = log + `Modal Full Answer: \n => ${modalAnswer}\n`;
- log = log + `Modal Cut Answer: \n => ${modalCutAnswer}\n`;
- log = log + `Modal Detected Answered: \n => ${modalAnswered}\n\n\n`;
-
- console.log(log);
- }
-
- // deletes all existing modals and backdrops. Used to force-speed things up
- async function deleteModals() {
- await page.$$eval(DIR.modal_css, el => {
- el.forEach(i => i.remove())
- });
- await page.$$eval(DIR.modal_backdrop_css, el => {
- el.forEach(i => i.remove())
- });
- }
-
- // very advanced logic (ofc) used to find matching answer
- function findAnswer(question) {
- let answer = fullDict[question];
- if (answer) return answer;
- answer = fullDict[question.replace(",", ";")];
- if (answer) return answer;
- answer = cutDict[cleanString(question)];
- if (answer) return answer;
- console.log(`No answer found for ${question}`);
- return generateRandomString(8, 10);
- }
-
- // i love creating functions so here's one for the random string instead of just returning idk answer
- // -joshatticus
- function generateRandomString(minLength, maxLength) {
- const length = Math.floor(Math.random() * (maxLength - minLength + 1)) + minLength;
- const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
- let result = '';
- for (let i = 0; i < length; i++) {
- result += characters.charAt(Math.floor(Math.random() * characters.length));
- }
- return result;
- }
-
- // main function that continually answers questions until completion modal pops up or hotkey pressed again
- async function answerLoop() {
- if (TOGGLE) throw Error("Tried to initiate answerLoop while it is already running");
-
- TOGGLE = true;
- console.log("answerLoop entered.");
-
- while (TOGGLE) {
- let question = await page.$eval(DIR.question_css, el => el.textContent);
- let answer = findAnswer(question);
-
- await page.click(DIR.answer_box_css, { clickCount: 3 });
- await page.keyboard.sendCharacter(answer);
- ENTER && page.keyboard.press('Enter');
-
- // special case: modal pops up
- if (await page.$(DIR.modal_css)) {
- // incorrect answer and modal pops up; initiate answer-correction procedure
- if (await page.$(DIR.modal_question_css) !== null) {
- await correctAnswer(question, answer);
- await deleteModals();
- // list complete; clicks button to exit
- } else if (await page.$(DIR.exit_button_css)) {
- await page.click(DIR.exit_button_css);
- break;
- } else if (await page.$(DIR.exit_continue_button_css)) {
- await page.click(DIR.exit_continue_button_css);
- break;
- } else {
- // no idea what the modal is for so let's just pretend it doesn't exist
- await deleteModals();
- }
- }
- }
-
- await deleteModals();
- TOGGLE = false;
- console.log('answerLoop Exited.');
- }
-
- // takes care of answerLoop toggling logic
- async function toggleLoop() {
- if (TOGGLE) {
- TOGGLE = false;
- console.log("Stopping answerLoop.");
- } else {
- console.log("Starting answerLoop.");
- answerLoop().catch(e => {
- console.error(e);
- TOGGLE = false
- });
- }
- }
-
- async function toggleAuto() {
- if (ENTER) {
- ENTER = false;
- console.log("Switched to semi-auto mode.");
- await alert("Switched to semi-auto mode.");
- } else {
- ENTER = true;
- console.log("Switched to auto mode.");
- await alert("Switched to auto mode.");
- }
- }
-
- async function alert(msg) {
- await page.evaluate(m => window.alert(m), msg);
- }
-
- // Expose API functions to the page (for hotkey event listeners to call)
- await page.exposeFunction('refresh', refreshWords);
- await page.exposeFunction('startAnswer', toggleLoop);
- await page.exposeFunction('toggleMode', toggleAuto);
-
- // Add event listeners for hotkeys ON the page
- await page.evaluate(() => {
- document.addEventListener("keyup", async (event) => {
- let key = event.key.toLowerCase();
- if (key !== 'alt') {
- if ((event.altKey && key === "r") || (key === "®")) {
- await window.refresh();
- } else if ((event.altKey && key === "s") || (key === "ß")) {
- await window.startAnswer();
- } else if ((event.altKey && key === "a") || (key === "å")) {
- await window.toggleMode();
- }
- }
- });
- });
- console.log('Education Perfected V2 fully Loaded.');
- });
- })();
-
- // ==UserScript==
- // @name Auto Clicker for Example Website
- // @namespace http://example.com/
- // @version 0.1
- // @description Automates clicking on certain buttons on example.com
- // @author Your Name
- // @match http://example.com/*
- // @license MIT
- // @grant none
- // ==/UserScript==
-
- // Your script code goes here