您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
A powerful membean helper that uses a variety of tools to answer membean questions.
- // ==UserScript==
- // @name Membean Tracker
- // @namespace http://tampermonkey.net/
- // @version 0.1.0
- // @description A powerful membean helper that uses a variety of tools to answer membean questions.
- // @author Squidtoon99 (https://squid.pink)
- // @include https://membean.com/training_sessions/*/user_state
- // @include https://membean.com/mywords/*
- // @include https://membean.com/training_sessions/new
- // @icon https://www.google.com/s2/favicons?domain=membean.com
- // @grant GM_xmlhttpRequest
- // @connect squid.pink
- // ==/UserScript==
- var cache = {};
- var apiURL = "https://api.squid.pink";
- var q;
- const plugins = [
- autoSelectHTMLPlugin,
- questionPlugin,
- checkExampleSingleNode,
- checkExampleMultiNode,
- definitionOnHoverPlugin,
- autoNextPlugin,
- ];
- const persistent_plugins = [storeCorrectAnswerPlugin, autoTypeNewWordPlugin];
- const non_question_plugins = [
- autoAnswerTextInputPlugin,
- autoStartNewSessionPlugin,
- ];
- setInterval(() => {
- // Checking if there is a new question
- var questions = document.getElementsByClassName("question");
- if (questions.length > 0) {
- var question = questions[0];
- var iter;
- if (question !== q) {
- q = question;
- cache.q = question;
- iter = plugins; // the plugins that only need to run when the question renders
- cache.answer = false;
- } else {
- iter = persistent_plugins; // the plugins that need to be constantly running
- }
- for (var z = 0; z < iter.length; z++) {
- iter[z]();
- }
- if (cache.answer == false) {
- var choices = document.getElementsByClassName("choice");
- setTimeout(() => {
- if (
- choices === document.getElementsByClassName("choice") &&
- document.getElementsByClassName("choice correct").length == 0
- ) {
- //console.log("autoclicking");
- //document.getElementsByClassName("choice")[0].click();
- }
- }, 500);
- }
- } else {
- for (var x = 0; x < non_question_plugins.length; x++) {
- non_question_plugins[x]();
- }
- }
- if (Math.floor(Math.random() * 1000) === 5) {
- location.reload();
- window.console.log("reloading");
- }
- }, 500);
- setInterval(() => {
- if (document.getElementsByClassName("take_a_break").length == 1) {
- console.log("taking a break");
- document.getElementById("Click_me_to_stop").click();
- setTimeout(
- () =>
- (window.location.href = "https://membean.com/training_sessions/new"),
- 1000
- );
- }
- }, 1000);
- function answer(answer_num, reason) {
- var o = Object.values(document.getElementsByClassName("choice"))[answer_num];
- cache.answer = true;
- console.log(`Found correct answer (${answer_num}): [${reason}]`);
- o.className = "choice correct";
- setTimeout(() => o.click(), 8000); // set this timeout to how long you want to wait (ms) before answering questions
- //o.click();
- }
- function autoNextPlugin() {
- var c = document.getElementById("next-btn");
- if (c) {
- setTimeout(() => c.click(), 1000);
- }
- }
- function autoSelectHTMLPlugin() {
- // This plugin simply will select the choice answer html selector
- var c = document.getElementsByClassName("choice answer");
- if (c.length > 0) {
- c[0].click();
- }
- }
- function questionPlugin() {
- // This queries the api to see if there is a value stored
- var url = `${apiURL}/membean/${encodeURIComponent(q.textContent.trim())}`;
- GM_xmlhttpRequest({
- method: "GET",
- url: url,
- headers: {
- "Content-Type": "application/json",
- },
- responseType: "json",
- onload: (rspObj) => {
- if (rspObj.status == 404) {
- // temp autoclicking
- var c = document.getElementsByClassName("choice");
- return;
- } else if (rspObj.status != 200) {
- reportAJAX_Error(rspObj);
- return;
- }
- let data = rspObj.response;
- console.log(data);
- if (data.answer) {
- var choices = document.getElementsByClassName("choice");
- for (var iter = 0; iter < choices.length; iter++) {
- if (choices[iter].textContent.trim() == data.answer.trim()) {
- answer(iter, "stored question");
- }
- }
- window.console.log(`[${q}] Answer: ${data.answer[0].answer}`);
- } else {
- window.console.log("No storage for this question");
- }
- },
- onabort: reportAJAX_Error,
- onerror: reportAJAX_Error,
- ontimeout: reportAJAX_Error,
- });
- }
- function storeCorrectAnswerPlugin() {
- // stores the correct answer whenever html is updated
- var formatter = document.getElementsByClassName(
- "single-column-layout with-image"
- );
- var correct = document.getElementsByClassName("choice correct");
- if (correct.length > 0) {
- var e = correct[0];
- if (cache.e === e) {
- return;
- }
- cache.e = e;
- var data;
- if (formatter.length == 1) {
- // Images suck why membean why
- var img = formatter[0].children[1];
- data = { question: img.src, answer: e.textContent };
- } else {
- // Not an image question just a standard one
- data = {
- question: cache.q.textContent.trim(),
- answer: e.textContent.trim(),
- };
- }
- if (data.question.includes("Try again!")) {
- return;
- }
- GM_xmlhttpRequest({
- method: "POST",
- url: `${apiURL}/membean/`,
- data: JSON.stringify(data),
- headers: {
- "Content-Type": "application/json",
- },
- responseType: "json",
- onload: (rspObj) => {
- if (rspObj.status == 201) {
- window.console.log("Stored answer for: " + data.question);
- } else {
- console.log(rspObj);
- }
- },
- onabort: reportAJAX_Error,
- onerror: reportAJAX_Error,
- ontimeout: reportAJAX_Error,
- });
- }
- }
- function checkExampleSingleNode() {
- // In case they italize a word we think it's the word that's the answer and then make it look "correct"
- var nodes = cache.q.children;
- if (nodes.length == 2 && nodes[1].nodeName === "EM") {
- var word = nodes[1].textContent;
- if (
- word[word.length - 1] === "s" &&
- !"aeious".split("").includes(word[word.length - 2])
- ) {
- word = word.slice(0, word.length - 1);
- }
- if (word.replaceAll("_", "").trim() === "") {
- return;
- }
- console.log(`Question about ${word}`);
- GM_xmlhttpRequest({
- method: "GET",
- url: `https://membean.com/mywords/${word}`,
- responseType: "json",
- onload: (rspObj) => {
- var parser = new DOMParser();
- var htmlDoc = parser.parseFromString(rspObj.responseText, "text/html");
- var canswer = htmlDoc.getElementsByClassName("choice answer")[0];
- if (!canswer) {
- return;
- }
- var choices = document.getElementsByClassName("choice");
- var choicesParsed = {};
- for (var i = 0; i < choices.length; i++) {
- if (choices[i].textContent === canswer.textContent) {
- answer(i, "singleNode example");
- return;
- } else {
- choicesParsed[i] = levenshtein(
- choices[i].textContent,
- canswer.textContent
- ); // parsing the most probable answer
- }
- }
- let bestChoice = Object.keys(choicesParsed).reduce((a, b) =>
- choicesParsed[a] > choicesParsed[b] ? a : b
- );
- if (
- choicesParsed[bestChoice] >= 10 ||
- bestChoice == Object.keys(bestChoice).length - 1
- ) {
- return; // Not a good enough result
- }
- answer(bestChoice, "single node best example");
- },
- onabort: reportAJAX_Error,
- onerror: reportAJAX_Error,
- ontimeout: reportAJAX_Error,
- });
- } else if (nodes.length === 3 && nodes[1].nodeName === "EM") {
- word =
- document.getElementsByClassName("question")[0].children[2].textContent;
- console.log("stupid question about roots: " + word);
- GM_xmlhttpRequest({
- method: "GET",
- url: `https://membean.com/mywords/${word}`,
- responseType: "json",
- onload: (rspObj) => {
- var parser = new DOMParser();
- var htmlDoc = parser.parseFromString(rspObj.responseText, "text/html");
- var defData = {};
- Object.values(
- htmlDoc.getElementById("word-structure").children[1].children[0]
- .children[0].children
- ).forEach((key) => {
- defData[key.children[0].textContent.trim()] =
- key.children[2].textContent.trim();
- });
- for (let [key, value] of Object.entries(defData)) {
- if (nodes[1].textContent.trim() === key.trim()) {
- console.log(
- `checking ${key}: ${value} ? ${nodes[1].textContent.trim()}`
- );
- var choices = document.getElementsByClassName("choice");
- for (let [index] of Object.keys(choices)) {
- var answerChoice = choices[index];
- var choiceNodes = answerChoice.textContent.trim().split(", ");
- if (choiceNodes.some((k) => value.includes(k))) {
- answer(index, "root structure");
- return;
- }
- }
- }
- }
- },
- onabort: reportAJAX_Error,
- onerror: reportAJAX_Error,
- ontimeout: reportAJAX_Error,
- });
- }
- }
- function checkExampleMultiNode() {
- // In case they italize a word we think it's the word that's the answer and then make it look "correct"
- var words = document.getElementsByClassName("choice-word");
- if (
- !Object.values(words).every((word) => {
- word.textContent.trim().indexOf(" ") >= 2 ||
- word.textContent.trim() == "I'm not sure";
- })
- ) {
- // all of them are words without spaces
- //window.console.log("multi-node answers are words");
- var bestChoices = {};
- for (let iter = 0; iter < words.length; iter++) {
- let word = words[iter].textContent.trim();
- GM_xmlhttpRequest({
- method: "GET",
- url: `https://membean.com/mywords/${word}`,
- responseType: "json",
- onload: (rspObj) => {
- var parser = new DOMParser();
- var htmlDoc = parser.parseFromString(
- rspObj.responseText,
- "text/html"
- );
- var canswer = htmlDoc.getElementsByClassName("choice answer")[0];
- if (canswer) {
- var choices = document.getElementsByClassName("choice");
- var choicesParsed = {};
- for (var i = 0; i < choices.length; i++) {
- if (choices[i].textContent === canswer.textContent) {
- answer(i, "multi node answer");
- return;
- } else {
- choicesParsed[i] = levenshtein(
- choices[i].textContent,
- canswer.textContent
- ); // parsing the most probable answer
- }
- }
- let bestChoice = Object.keys(choicesParsed).reduce((a, b) =>
- choicesParsed[a] > choicesParsed[b] ? a : b
- );
- if (choicesParsed[bestChoice] >= 20) {
- return; // Not a good enough result
- }
- answer(bestChoice, "best choice parsed for multi node");
- }
- },
- onabort: reportAJAX_Error,
- onerror: reportAJAX_Error,
- ontimeout: reportAJAX_Error,
- });
- }
- }
- }
- function cdnStoragePlugin() {
- var formatter = document.getElementsByClassName(
- "single-column-layout with-image"
- );
- if (formatter.length == 1) {
- var img = formatter[0].children[1];
- if (img.alt == "constellation question") {
- // It is a constellation question - tesseract can't parse so I'm kinda fcked for now
- var src = img.src;
- GM_xmlhttpRequest({
- method: "GET",
- url: `${apiURL}/membean/${encodeURIComponent(src)}`,
- headers: {
- "Content-Type": "application/json",
- },
- responseType: "json",
- onload: (rspObj) => {
- (rspObj) => {
- if (rspObj.status != 200) {
- reportAJAX_Error(rspObj);
- return;
- }
- let data = rspObj.response;
- console.log(data);
- if (data.answer) {
- var choices = document.getElementsByClassName("choice");
- for (var iter = 0; iter < choices.length; iter++) {
- if (choices[iter].textContent.trim() == data.answer.trim()) {
- answer(iter, "stored question");
- }
- }
- window.console.log(
- `[${q}] Answer: ${rspObj.response.answer[0].answer}`
- );
- }
- };
- },
- onabort: reportAJAX_Error,
- onerror: reportAJAX_Error,
- ontimeout: reportAJAX_Error,
- });
- }
- }
- }
- function definitionOnHoverPlugin() {
- // In case they italize a word we think it's the word that's the answer and then make it look "correct"
- var words = document.getElementsByClassName("choice-word");
- if (
- !Object.values(words).every((word) => {
- word.textContent.trim().indexOf(" ") >= 2 ||
- word.textContent.trim() == "I'm not sure";
- })
- ) {
- // all of them are words without spaces
- //window.console.log("answers are sentences");
- for (let iter = 0; iter < words.length; iter++) {
- let word = words[iter].textContent.trim();
- GM_xmlhttpRequest({
- method: "GET",
- url: `https://membean.com/mywords/${word}`,
- responseType: "json",
- onload: (rspObj) => {
- var parser = new DOMParser();
- var htmlDoc = parser.parseFromString(
- rspObj.responseText,
- "text/html"
- );
- var definition = htmlDoc.getElementsByClassName("def-text");
- if (definition.length > 0) {
- words[iter].title = definition[0].textContent.trim();
- // Checking here for filler questions like 1 + _ = 3 and the def we get is 1 + 2 = 3
- var processed_question = cache.q.textContent
- .replace("_", word)
- .replaceAll("_", "")
- .trim();
- var sentences = Object.values(
- htmlDoc
- .getElementById("context-paragraph")
- .textContent.split(". ")
- ).map((val) => val.trim() + ".");
- sentences.push(definition[0].textContent.trim());
- for (var s_iter = 0; s_iter < sentences.length; s_iter++) {
- var similarity = levenshtein(
- sentences[s_iter],
- processed_question.trim()
- );
- if (similarity < 40) {
- window.console.log(
- `Similarity: ${similarity}: ${definition[0].textContent.trim()} / ${processed_question.trim()}`
- );
- answer(iter, `best similarity for def hover: ${similarity}`);
- } else {
- //window.console.log(
- // `Invalid Hint-Def: ${similarity} - ${sentences[s_iter]} != ${processed_question.trim()}`
- // );
- }
- }
- //window.console.log(`Set title for ${word}`);
- }
- },
- onabort: reportAJAX_Error,
- onerror: reportAJAX_Error,
- ontimeout: reportAJAX_Error,
- });
- }
- } else {
- //window.console.log("answers are words");
- var hint_nodes = cache.q.children;
- if (cache.hint_nodes !== hint_nodes) {
- cache.hint_nodes = hint_nodes;
- if (hint_nodes.length == 2 && hint_nodes[1].nodeName === "EM") {
- var word = hint_nodes[1].textContent;
- if (
- word[word.length - 1] === "s" &&
- !"aeious".split("").includes(word[word.length - 2])
- ) {
- word = word.slice(0, word.length - 1);
- }
- window.console.log("Italicised word " + word);
- GM_xmlhttpRequest({
- method: "GET",
- url: `https://membean.com/mywords/${word}`,
- responseType: "json",
- onload: (rspObj) => {
- var parser = new DOMParser();
- var htmlDoc = parser.parseFromString(
- rspObj.responseText,
- "text/html"
- );
- var def = htmlDoc.getElementsByClassName("def-text");
- if (def.length > 0) {
- var processed_question = cache.q.textContent
- .replace("_", word)
- .replaceAll("_", "");
- var sentences = Object.values(
- htmlDoc
- .getElementById("context-paragraph")
- .textContent.split(". ")
- ).map((val) => val.trim() + ".");
- sentences.push(def[0].textContent.trim());
- for (var iter = 0; iter < sentences.length; iter++) {
- var similarity = levenshtein(
- sentences[iter],
- processed_question.trim()
- );
- if (similarity < 30) {
- window.console.log(
- `Similarity: ${similarity}: ${definition[0].textContent.trim()} / ${processed_question.trim()}`
- );
- answer(iter, "best similarity for multi def hover");
- return;
- } else {
- //window.console.log(
- // `Invalid Hint-Def: ${similarity} - ${
- // sentences[iter]
- // } != ${processed_question.trim()}`
- // );
- }
- }
- cache.q.title = def[0].textContent.trim();
- } else {
- window.console.log("No def");
- }
- },
- onabort: reportAJAX_Error,
- onerror: reportAJAX_Error,
- ontimeout: reportAJAX_Error,
- });
- }
- }
- }
- }
- function autoTypeNewWordPlugin() {
- var letters = document.getElementsByClassName("rest-letters");
- if (letters.length >= 1) {
- var word = document.getElementById("pronounce-sound");
- if (word) {
- var text = word.attributes[3].value.split("-")[1];
- setTimeout(() => (document.getElementById("choice").value = text), 1000);
- }
- }
- }
- function autoAnswerTextInputPlugin() {
- var valid = document.getElementsByClassName("single-column-layout cloze");
- if (valid.length > 0) {
- var v = valid[0];
- if (v === cache.v) {
- return;
- }
- cache.v = v;
- console.log("typing question");
- //setTimeout(() => document.getElementById("notsure").click(), 600);
- }
- }
- function autoStartNewSessionPlugin() {
- var valid = document.getElementsByClassName("button-to");
- if (valid.length > 0) {
- Object.values(valid).reverse()[0].children[0].click();
- }
- }
- function processJSON_Response(rspObj) {
- if (rspObj.status != 200) {
- reportAJAX_Error(rspObj);
- } else {
- window.console.log(rspObj.responseText);
- }
- }
- function reportAJAX_Error(rspObj) {
- console.log(rspObj);
- window.console.log(
- `TM scrpt (membean-tracker) => Error ${rspObj.status}! ${rspObj.statusText} (${rspObj.url})`
- );
- }
- const levenshtein = function (a, b) {
- // difference between strings for helping w/ similar membean answers
- if (a.length == 0) return b.length;
- if (b.length == 0) return a.length;
- // swap to save some memory O(min(a,b)) instead of O(a)
- if (a.length > b.length) {
- var tmp = a;
- a = b;
- b = tmp;
- }
- var row = [];
- // init the row
- for (var i = 0; i <= a.length; i++) {
- row[i] = i;
- }
- // fill in the rest
- for (var z = 1; z <= b.length; z++) {
- var prev = z;
- for (var j = 1; j <= a.length; j++) {
- var val;
- if (b.charAt(z - 1) == a.charAt(j - 1)) {
- val = row[j - 1]; // match
- } else {
- val = Math.min(
- row[j - 1] + 1, // substitution
- prev + 1, // insertion
- row[j] + 1
- ); // deletion
- }
- row[j - 1] = prev;
- prev = val;
- }
- row[a.length] = prev;
- }
- return row[a.length];
- };