// ==UserScript== // @name megucascript // @namespace megucasoft // @description Does a lot of stuff // @require player.js // @require secret.js // @require deleted.js // @include https://meguca.org/* // @include https://cliquechen.club/v/* // @include https://megu.ca/* // @include https://nekochen.net/tt/* // @include https://kirara.cafe/* // @connect meguca.org // @connect chiru.no // @version 3.9.7 // @author medukasthegucas // @grant GM_xmlhttpRequest // ==/UserScript== const defaultFiletypes = ".jpg .png .gif"; var chuuCount = 0; // Things the user can turn on or off, add your new feature to this list // All options will be auto generated. What you need to put: // text: PlainText (HTML is fine too) // checkbox: paramName, description[, textover tooltip] // input: paramName, defaultValGetter, description, customButtonCallbackGetter[, customButtonDescription[, customButtonId]]] // textarea: paramName, defaultValGetter, description, customButtonCallbackGetter[, customButtonDescription]] // fileInput: paramName // division: Adds a cool
<\/code>([^#<>\[\]]*)<\/del><\/code>/g
);
for (var j = secret.length - 1; j >= 0; j--) {
parseSecretPost(post, secret[j]);
}
var secretQuote = findMultipleShitFromAString(
post.innerHTML,
/[ >]>>([\d]+)(?:[ <]+)/g
);
for (var j = secretQuote.length - 1; j >= 0; j--) {
parseSecretQuote(post, secretQuote[j]);
}
}
if (currentlyEnabledOptions.has("sharesOption")) {
var shares = findMultipleShitFromAString(
post.innerHTML,
/\[([^\]\[]*)\] #(\d+)d(\d+) \(([\d +]* )*= (?:\d+)\)<\/strong>/g
);
for (var j = shares.length - 1; j >= Math.max(0, shares.length - 4); j--) {
parseShares(post, shares[j]);
}
}
if (currentlyEnabledOptions.has("pyuOption")) {
var pyu = findMultipleShitFromAString(
post.innerHTML,
/#pyu \(([\d+]*)\)<\/strong>/g
);
for (var j = pyu.length - 1; j >= 0; j--) {
parsePyu(post, pyu[j]);
}
}
if (currentlyEnabledOptions.has("mathOption")) {
var math = findMultipleShitFromAString(
post.innerHTML,
/#math\(((?:[\d-+/*%().^ ]*(?:log)*)*)\)/g
);
for (var j = math.length - 1; j >= 0; j--) {
parseMath(post, math[j]);
}
}
if (currentlyEnabledOptions.has("chuuOption")) {
var chuu = findMultipleShitFromAString(
post.innerHTML,
/#chuu\( ?(\d*) ?\)/g
);
for (var j = chuu.length - 1; j >= 0; j--) {
parseChuu(post, chuu[j]);
}
}
if (currentlyEnabledOptions.has("decideOption")) {
var decide;
decide = findMultipleShitFromAString(
post.innerHTML,
/\[([^#\]\[]*)\]\s#d([0-9]+) \(([0-9]+)\)<\/strong>/g
);
for (var j = decide.length - 1; j >= 0; j--) {
parseDecide(post, decide[j], false);
}
decide = findMultipleShitFromAString(
post.innerHTML,
/(?:|
)([^><]*)(\s|
)#d([0-9]+) \(([0-9]+)\)<\/strong>/g
);
for (var j = decide.length - 1; j >= 0; j--) {
parseDecide(post, decide[j], true);
}
}
if (currentlyEnabledOptions.has("dumbPosters")) {
checkForDumbPost(post);
}
if (currentlyEnabledOptions.has("screamingPosters")) {
checkForScreamingPost(post);
}
if (currentlyEnabledOptions.has("showDeletedPosts")) {
showDeletedPost(post);
}
if (currentlyEnabledOptions.has("showWhoDeletedPosts")) {
checkForDeletedOrBannedPost(post);
}
if (currentlyEnabledOptions.has("filterPosts")) {
filterPost(post);
}
}
function readPostsForData() {
var posts = document.getElementsByClassName("post-container");
for (var i = 0; i < posts.length; i++) {
var post = posts[i];
handlePost(post);
}
}
function parsePyu(post, pyu) {
var n = pyu[1];
var before = post.innerHTML.substring(0, pyu.index);
var after = post.innerHTML.substring(pyu.index + pyu[0].length);
if (n % 1000 == 0) {
var pyuHTML =
' 💦 ' + pyu[0].substring(8) + " 💦 ";
post.innerHTML = before + pyuHTML + after;
}
}
function parseMath(post, math) {
var expr = math[1];
expr = parseMath_addPow(expr).replace(/log/g, "Math.log");
var result;
try {
result = eval(expr);
} catch (err) {
result = "???";
}
if (isNaN(result)) result = "???";
var before = post.innerHTML.substring(0, math.index);
var after = post.innerHTML.substring(math.index + math[0].length);
var mathHTML =
"" +
math[0].substring(0, 5) +
" " +
math[0].substring(5, math[0].length - 1) +
" = " +
result +
")";
post.innerHTML = before + mathHTML + after;
}
function parseMath_addPow(str) {
for (let i = str.length - 1; i >= 0; i--) {
if (str[i] !== "^") continue;
let parentheses = 0;
const operators = /[-+*/%^]/;
// looking ahead
let j;
for (j = i + 1; j < str.length; j++) {
if (str[j] === "(") parentheses++;
else if (str[j] === ")" && parentheses > 0) parentheses--;
else if (operators.test(str[j]) && parentheses === 0) break;
}
// j is just after the term
// looking back
let k;
parentheses = 0; // so it doesn't break even more stuff;
for (k = i - 1; k >= 0; k--) {
if (str[k] === ")") parentheses++;
else if (str[k] === "(" && parentheses > 0) parentheses--;
else if (operators.test(str[k]) && parentheses === 0) break;
}
// k is just before the term
k++; // k is on the beginning of the term
str =
str.substring(0, k) +
"Math.pow(" +
str.substring(k, i) +
"," +
str.substring(i + 1, j) +
")" +
str.substring(j);
i += 9; // Due to the addition of "pow(" before i
}
return str;
}
function parseChuu(post, chuu) {
var postNum = chuu[1];
var kissedPost = document.getElementById("p" + postNum);
if (kissedPost === null || kissedPost === undefined) return;
var nametag = kissedPost.querySelector("header").getElementsByTagName("B")[0];
var before = post.innerHTML.substring(0, chuu.index);
var after = post.innerHTML.substring(chuu.index + chuu[0].length);
var chuuHTML = " You've been kissed!
if (nametag.getElementsByTagName("I").length > 0) {
var ownName = post.parentNode
.querySelector("header")
.getElementsByTagName("B")[0];
// Don't chuu yourself
if (ownName.getElementsByTagName("I").length > 0) return;
chuuHTML += ' class="lewd_color"';
chuuCount = localStorage.getItem("chuuCount", chuuCount);
chuuCount++;
localStorage.setItem("chuuCount", chuuCount);
document.getElementById("chuu-counter").innerHTML = chuuCount;
var message = "chuu~";
if (chuuCount % 10 === 0) {
message +=
"\nCongratulations on your pregnancy!\nYou now have " +
chuuCount / 10 +
" children!";
}
alert(message);
}
chuuHTML += ">#chuu~(" + chuu[1] + ")";
post.innerHTML = before + chuuHTML + after;
}
function parseDecide(post, decide, isSmart) {
var offset = isSmart ? 1 : 0;
var options = decide[1].split(",");
var n = decide[3 + offset];
var m = decide[4 + offset];
var before = post.innerHTML.substring(0, decide.index);
var after = post.innerHTML.substring(decide.index + decide[0].length);
if (options.length != n || n == 1) return;
options[m - 1] =
'' + options[m - 1] + "";
var newInner = options.toString();
var retreivedRoll;
if (decide[2 + offset] == null) {
retreivedRoll = " #d" + n + " (" + m + ")";
} else {
retreivedRoll =
" #d" + n + " (" + m + ")";
}
if (isSmart) {
if (decide[0].substring(0, 3) === "
";
else before += "";
newInner += decide[2];
}
post.innerHTML = before + newInner + retreivedRoll + after;
}
function parseShares(post, shares) {
var options = shares[1].split(",");
var n = shares[3];
var maxShares = shares[4];
var shareValues = shares[5].split(" + ");
for (var j = 0; j < shareValues.length; j++) {
shareValues[j] = Number(shareValues[j]); //Because FUCK YOU FUCKING JAVASCRIPT END YOURSELF YOU SHIT AAAAAAAAAAAAAAAA FUCK
}
var before = post.innerHTML.substring(0, shares.index);
var after = post.innerHTML.substring(shares.index + shares[0].length);
var highestValue = Math.max.apply(Math, shareValues);
if (options.length != n || n == 1 || n == 0) return;
for (var j = 0; j < shareValues.length; j++) {
var formattedRoll = " (" + shareValues[j] + "/" + maxShares + ")";
// format the options
if (shareValues[j] == highestValue) {
if (options[j].match(/(^|\W)planeptune($|\W)(?!\w)/i)) {
options[j] =
'
' +
options[j] +
formattedRoll +
"";
} else if (options[j].match(/(^|\W)lastation($|\W)(?!\w)/i)) {
options[j] =
'' +
options[j] +
formattedRoll +
"";
} else if (options[j].match(/(^|\W)lowee($|\W)(?!\w)/i)) {
options[j] =
'' +
options[j] +
formattedRoll +
"";
} else if (options[j].match(/(^|\W)leanbox($|\W)(?!\w)/i)) {
options[j] =
'' +
options[j] +
formattedRoll +
"";
} else {
options[j] =
'' +
options[j] +
formattedRoll +
"";
}
} else {
options[j] = options[j] + formattedRoll;
}
}
var newInner = options.join("
");
if (
before.substring(before.length - 4) != "
" &&
before.substring(before.length - 4) != "ote>"
) {
before += "
";
}
if (after.substring(0, 4) != "
" && after.substring(0, 4) != "" + newInner + " " + after;
}
function findMultipleShitFromAString(s, re) {
var result = [];
var m;
while (true) {
m = re.exec(s);
if (m) result.push(m);
else break;
}
return result;
}
// Observer watches the thread
function setObservers() {
var thread = document.getElementById("thread-container");
// configuration of the observers:
var config = {
attributes: true,
childList: true,
subtree: true,
attributeOldValue: true
};
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.addedNodes.length == 0) {
if (
mutation.type == "attributes" &&
mutation.attributeName == "class"
) {
// check for existing posts that have changed, ie deleted/canceled/finished
var post = mutation.target;
var postContent = post.getElementsByClassName("post-container")[0];
if (postContent != undefined) {
if (currentlyEnabledOptions.has("showWhoDeletedPosts")) {
checkForDeletedOrBannedPost(postContent);
}
if (currentlyEnabledOptions.has("showDeletedPosts")) {
showDeletedPost(postContent);
}
if (currentlyEnabledOptions.has("cancelposters")) {
// unhide removed posts, and restore their contents
if (
post.classList.contains("hidden") &&
postContent.innerText == ""
) {
// look for events that removed nodes
var cancelled = false;
for (var j = 0; j < mutations.length; j++) {
var removeEvt = mutations[j];
if (removeEvt.type == "childList") {
for (var i = 0; i < removeEvt.removedNodes.length; i++) {
var node = removeEvt.removedNodes[i];
// don't re-add the 'Hide, Report' menu if it disappeared
// or the post controls or editable textarea
if (
!(
(node.classList &&
node.classList.contains("popup-menu")) ||
node.id == "post-controls" ||
node.id == "text-input"
)
) {
removeEvt.target.appendChild(removeEvt.removedNodes[i]);
cancelled = true;
}
}
}
}
// restore the post if it was probably cancelled
if (cancelled) {
post.classList.remove("hidden");
post.style.opacity = "0.5";
// flag the post as cancelled so we add the correct 'dumb xposter' later
postContent.cancelled = true;
// somewhere along the way, the default image-hover listener breaks
// so just prevent it from running to avoid console errors
post.addEventListener("mousemove", function(e) {
e.stopPropagation();
});
}
}
}
// check for posts finishing
// (the current user deadposting will have been 'reply-form' but not 'editing')
if (
(mutation.oldValue.split(" ").includes("editing") ||
mutation.oldValue.split(" ").includes("reply-form")) &&
!post.classList.contains("editing") &&
!post.classList.contains("reply-form")
) {
handlePost(postContent);
if (currentlyEnabledOptions.has("enablemegucaplayer")) {
mgcPl_addNewSong(post.getElementsByTagName("figcaption")[0]);
}
}
}
}
} else {
// check what was added
var postItself;
if (mutation.target.nodeName == "BLOCKQUOTE") {
// could be updating the content of an existing post
// try to find the post itself
if (
mutation.target.parentNode &&
mutation.target.parentNode.parentNode &&
mutation.target.parentNode.parentNode.nodeName == "ARTICLE"
) {
postItself = mutation.target.parentNode.parentNode;
}
} else if (mutation.addedNodes[0].nodeName == "ARTICLE") {
postItself = mutation.addedNodes[0];
} else if (
mutation.addedNodes[0].classList &&
mutation.addedNodes[0].classList.contains("admin", "banned")
) {
if (currentlyEnabledOptions.has("showWhoDeletedPosts")) {
checkForDeletedOrBannedPost(mutation.target);
}
}
if (postItself == undefined) {
return;
}
var postContent = postItself.getElementsByClassName(
"post-container"
)[0];
if (postContent == undefined) {
return;
}
// still editing
if (
postItself.getAttribute("class").includes("editing") ||
postItself.getAttribute("class").includes("reply-form")
) {
// add Format button to posts the user is making
if (postItself.getAttribute("class").includes("reply-form")) {
if (currentlyEnabledOptions.has("annoyingFormatting"))
addFormatButton(postItself);
if (currentlyEnabledOptions.has("preSubmitOption"))
overrideDoneButton(postItself);
}
// but don't do anything else to editing posts
return;
}
// handlesPost (works for others' deadposts)
handlePost(postContent);
mgcPl_addNewSong(postItself.getElementsByTagName("figcaption")[0]);
}
});
});
// pass in the target node, as well as the observer options
observer.observe(thread, config);
if (currentlyEnabledOptions.has("imgsekritPosting")) {
setupSecretObserver();
}
if (currentlyEnabledOptions.has("skeletonCount")) {
var counter = document.getElementById("sync-counter");
counter.title = "Unique connected IP human/skeleton count";
var counterConfig = { childList: true };
var counterObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
const reg = /(\d+) \/ (\d+)/;
if (mutation.addedNodes && mutation.addedNodes.length > 0) {
var added = mutation.addedNodes[0];
var oldText = added.textContent;
var matches = oldText.match(reg);
if (matches.length == 3) {
var humans = parseInt(matches[1]);
var total = parseInt(matches[2]);
var skeletons = total - humans;
var s1 = humans == 1 ? " human / " : " humans / ";
var s2 = skeletons == 1 ? " skeleton" : " skeletons";
var newText;
if (currentlyEnabledOptions.has("skeletonLabels")) {
newText = humans + s1 + skeletons + s2;
} else {
newText = humans + " / " + skeletons;
}
added.textContent = newText;
}
}
});
});
counterObserver.observe(counter, counterConfig);
}
}
function addFormatButton(post) {
if (document.getElementById("format-button")) {
// button already exists
return;
}
var button = document.createElement("input");
button.name = "format";
button.value = "Format";
button.type = "button";
button.id = "format-button";
button.onclick = formatPostText;
var controls = document.getElementById("post-controls");
controls.appendChild(button);
}
function formatPostText() {
var input = document.getElementById("text-input");
input.value = input.value
.split(" ")
.map(formatWord)
.join(" ");
var evt = document.createEvent("HTMLEvents");
evt.initEvent("input", false, true);
input.dispatchEvent(evt);
}
function formatWord(s) {
// pick a random format and add it to both sides of the word
var format = ["~~", "**", "@@", "``"][Math.floor(Math.random() * 4)];
return format + s + format;
}
function setUpEdenBanner() {
var banner = document.getElementById("banner-center");
banner.innerHTML =
'[synced] DJ Song';
getInfoFromEden();
window.setInterval(getInfoFromEden, 10000);
}
function getInfoFromEden() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
updateEdenBanner(JSON.parse(this.responseText));
}
};
xhttp.open("GET", "https://edenofthewest.com/ajax/status.php", true);
xhttp.send();
}
function updateEdenBanner(edenJSON) {
var banner = document.getElementById("banner-center");
var djInfo = banner.children[0];
var songInfo = banner.children[1];
djInfo.innerHTML = "[" + edenJSON.listeners + "] " + edenJSON.dj;
songInfo.href =
"https://www.google.com.br/search?q=" +
encodeURIComponent(edenJSON.current);
songInfo.innerHTML = "" + edenJSON.current + "";
}
function checkForDumbPost(post) {
// cancelposters
if (post.cancelled) {
addToName(post, " (dumb cancelposter)");
return;
}
var text = post.textContent;
// ~posters
if (text.match("~") != null) {
addToName(post, " (dumb ~poster)");
return;
}
// Blancposters
if (
(text == "" || text == " ") &&
post.getElementsByTagName("figure").length == 0
) {
var quality = currentlyEnabledOptions.has("dumbblanc") ? "dumb" : "cute";
addToName(post, " (" + quality + " blancposter)");
return;
}
// dumbposterposters
var dumbRegex = /^(?:>>\d* (?:\(You\) )?# )*(dumb ?.{0,20}posters?)$/i;
if (text.match(dumbRegex) != null) {
var posterType = text.match(dumbRegex)[1];
addToName(post, " (dumb '" + posterType + "' poster)");
return;
}
// cuteposterposters
var cuteRegex = /^(?:>>\d* (?:\(You\) )?# )*(cute ?.{0,20}posters?)$/i;
if (text.match(cuteRegex) != null) {
var posterType = text.match(cuteRegex)[1];
addToName(post, " (cute '" + posterType + "' poster)");
return;
}
// wait anon
if (text.match(/^(?:>>\d* (?:\(You\) )?# )*wait anon$/i) != null) {
addToName(
post,
" (Dumb haiku poster / 'wait anon' is all she says / Don't wait, run away!)"
);
return;
}
// virus post
if (text.match(/virus/i) != null) {
addToName(post, " (virus post do not read)");
return;
}
// lowercaseposters
var uppers = findMultipleShitFromAString(text, /[A-Z]/g);
var Yous = findMultipleShitFromAString(text, />>\d* \(You\)/g);
if (uppers.length == Yous.length) {
var lowers = findMultipleShitFromAString(text, /[a-z]/g);
if (lowers.length >= 5) {
addToName(post, " (dumb lowercaseposter)");
return;
}
}
addToName(post, "");
}
function checkForScreamingPost(post) {
var text = post.textContent;
var wholePost = post.parentElement;
// Remove (references, Yous and spaces)
text = text
.replace(/(?:>>\d* (?:\(You\) )?#)/g, "")
.replace(/(?:>>\d*)/g, "")
.replace(/[\s\W\d_]/g, "");
var isBlanc = text.length == 0;
var hasLower = text.match("[a-z]");
var isShort = text.length <= 5;
if (
!isShort &&
!isBlanc &&
!hasLower &&
!wholePost.className.match("shaking_post")
) {
wholePost.className += " shaking_post";
}
}
function addToName(post, message) {
var name = post.parentNode.getElementsByClassName("name spaced")[0];
var newText = document.createTextNode(message);
newText.id = "dumbposter";
// remove existing names
name.parentNode.childNodes.forEach(node => {
if (node.id == "dumbposter") {
name.parentNode.removeChild(node);
}
});
name.parentNode.insertBefore(newText, name.nextSibling);
}
function filterPost(postContent) {
var post = postContent.parentNode;
if (
post.classList.contains("filtered") ||
post.classList.contains("filtered-shown")
) {
return;
}
for (var i = 0; i < customFilters.length; i++) {
var filter = customFilters[i];
var type = filter[0];
var reg = filter[1];
var textToMatch;
var selector = filterTypes.get(type);
var elt = post.querySelector(selector);
if (elt != null) {
// flags don't have text, so use the title instead
if (type == "flag") {
textToMatch = elt.title;
} else {
textToMatch = elt.innerText;
}
}
if (textToMatch != undefined && textToMatch.match(reg)) {
post.classList.add("filtered");
var stub = document.createElement("div");
stub.classList.add("filter-stub");
var name = filter[1].toString();
name = name.substring(1, name.length - 1); // strip the /s
stub.innerText = "Post filtered (" + filter[0] + ":" + name + ")";
stub.onclick = showFilteredPost;
post.appendChild(stub);
}
}
}
function showFilteredPost() {
var post = this.parentNode;
if (post.classList.contains("filtered")) {
post.classList.remove("filtered");
post.classList.add("filtered-shown");
} else {
post.classList.remove("filtered-shown");
post.classList.add("filtered");
}
}
function setup() {
getCurrentOptions();
insertCuteIntoCSS();
readPostsForData();
if (document.getElementById("thread-container") != null) setObservers();
addScriptOptionMenu();
if (currentlyEnabledOptions.has("enablemegucaplayer")) mgcPl_setupPlaylist();
if (currentlyEnabledOptions.has("edenOption")) setUpEdenBanner();
if (currentlyEnabledOptions.has("showDeletedPosts"))
watchSocketForPostsDeletedOnCreation();
}
function downloadAll(value) {
var posts = document.getElementById("thread-container").children;
var filetypes = value.split(" ");
for (var i = 0; i < posts.length; i++) {
if (
posts[i].tagName.toLowerCase() === "article" &&
posts[i].querySelector("figcaption") != null
) {
var anchor = posts[i].querySelector("figcaption").children[3];
for (var j = 0; j < filetypes.length; j++)
if (anchor.href.endsWith(filetypes[j])) anchor.click();
}
}
}
// override #d7777(7777)
function overrideDoneButton(postItself) {
if (document.getElementById("overrided-done-button")) {
// button already exists
return;
}
var button = document.createElement("input");
button.name = "over-done";
button.value = "Done";
button.type = "button";
button.id = "overrided-done-button";
button.onclick = editPostAndSubmit;
var controls = document.getElementById("post-controls");
controls.children[0].style.display = "none";
controls.insertBefore(button, controls.children[0].nextSibling);
}
function editPostAndSubmit() {
var input = document.getElementById("text-input");
handlePreSubmit(input);
var evt = document.createEvent("HTMLEvents");
evt.initEvent("input", false, true);
input.dispatchEvent(evt);
document.getElementById("post-controls").children[0].click();
}
// All functions here must edit "input.value". This is the post written content.
function handlePreSubmit(input) {
// Put memes here
}
setup();