Ignore an unlimited amount of users with other custom settings: deleting the entire post, replace the content of the message with a custom message, replace or delete the avatar, keep or delete the signature.
当前为
// ==UserScript==
// @name Unlimited MAL Ignore list
// @namespace http://tampermonkey.net/
// @version 0.60
// @description Ignore an unlimited amount of users with other custom settings: deleting the entire post, replace the content of the message with a custom message, replace or delete the avatar, keep or delete the signature.
// @author Only_Brad
// @match https://myanimelist.net/*
// @run-at document-end
// ==/UserScript==
(function() {
const POSTS_URL = "forum/?topicid";
const TOPICS_URL = "forum/?board";
const CLUB_TOPICS_URL_1 = "clubs.php";
const CLUB_TOPICS_URL_2 = "forum/?clubid";
const BLACKLIST_URL = "https://myanimelist.net/blacklist";
const BLACKLIST_KEY = "ignore-list";
const SETTINGS_KEY = "ignore-list-settings";
const POST_USERS_SELECTOR = ".forum_boardrow2 > div";
const TOPIC_USERS_SELECTOR = ".forum_postusername a";
const MESSAGE_SELECTOR = ".forum_boardrow1 [id^=message]";
const AVATAR_SELECTOR = ".forum-icon";
const USER_PROFILE_SELECTOR = "[href^='/profile']";
const USER_INFO_SELECTOR = "[id^=messageuser]";
const SIGNATURE_SELECTOR = ".sig";
const FORUM_MESSAGE_SELECTOR = "[id^=forumMsg]";
const ACTION_BAR_SELECTOR = "[id^=postEditButtons]";
const IGNORE_POSTS = 0;
const REPLACE_POSTS = 1;
const DO_NOTHING_POSTS = 2;
let blacklist;
let settings;
//routing
if (window.location.href.includes(POSTS_URL)) {
handlePosts();
} else if (
window.location.href.includes(TOPICS_URL) ||
window.location.href.includes(CLUB_TOPICS_URL_1) ||
window.location.href.includes(CLUB_TOPICS_URL_2)
) {
handleTopics();
} else if (window.location.href === BLACKLIST_URL) {
handleBlacklist();
}
//GM_addStyle equivalent that works on firefox
function addStyle(css) {
const style = document.getElementById("addStyleBy8626") || (function() {
const style = document.createElement('style');
style.type = 'text/css';
style.id = "addStyleBy8626";
document.head.appendChild(style);
return style;
})();
style.innerHTML += css;
}
//helper functions to load from localStorage
function loadBlackList() {
blacklist = JSON.parse(localStorage.getItem(BLACKLIST_KEY)) || [];
}
function saveBlackList() {
localStorage.setItem(BLACKLIST_KEY, JSON.stringify(blacklist));
}
function loadSettings() {
settings = JSON.parse(localStorage.getItem(SETTINGS_KEY)) || {
replaceAvatar: false,
removeSignatures: true,
postMode: IGNORE_POSTS,
removeTopics: true,
customPost: "",
customAvatar: ""
};
}
function saveSetting(key, value) {
settings[key] = value;
localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));
}
//functions called by the routers
function handlePosts() {
loadBlackList();
loadSettings();
switch (settings.postMode) {
case IGNORE_POSTS:
ignorePosts();
return;
case REPLACE_POSTS:
replacePosts();
break;
default:
break;
}
addBlackListButtons();
if (settings.replaceAvatar) replaceAvatar();
if (settings.removeSignatures) removeSignatures();
}
function handleTopics() {
loadBlackList();
loadSettings();
if (settings.removeTopics) removeTopics();
}
function handleBlacklist() {
loadBlackList();
loadSettings();
document.title = "Blacklist - MyAnimeList.net";
//remove the 404 stuff
document.querySelector("h1").textContent = "Ignore List";
document.querySelector(".error404").remove();
//CSS
addStyle(".user{display:flex;margin:10px}.name{margin-right:20px}.name{border-bottom:solid #000 1px}.name[contenteditable]{min-width:100px;border-bottom:solid #000 1px}.name[contenteditable]:focus{border:none;outline:solid red 5px}.page-common #content{display:flex;justify-content:center;}.settings{display:flex;gap:25px;}.settings>*{padding: 25px;}.customPost{width:100% !important;}");
//HTML for the blacklist
document.getElementById("content").innerHTML =
`<div data-blacklist class="black-list"></div>
<div data-add-user class="add-user">
<div data-user class="user">
<div data-name class="name" contenteditable="true" onclick="this.focus()"></div>
<button data-add class="add">Add</div>
</div>
</div>`
//HTML for the settings
const settings = document.createElement("div");
settings.innerHTML = `
<h2>Settings</h2>
<form>
<div class="settings">
<div class="posts">
<h3>Posts</h3>
<div class="form-check">
<input class="form-check-input" type="radio" name="posts" id="doNothingPosts" data-clickable-setting="doNothingPosts">
<label class="form-check-label" for="doNothingPosts">
Do Nothing
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="posts" id="hidePosts" data-clickable-setting="hidePosts">
<label class="form-check-label" for="hidePosts">
Hide Posts
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="posts" id="replacePosts" data-clickable-setting="replacePosts">
<label class="form-check-label" for="replacePosts">
Replace posts with a custom message
</label>
</div>
<textarea class="form-control customPost" name="customPost" id="customPost" data-text-setting="customPost"></textarea>
</div>
<div class="avatar">
<h3>Posts extra options</h3>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="replaceAvatar" id="replaceAvatar" data-clickable-setting="replaceAvatar">
<label class="form-check-label" for="replaceAvatar">
Replace avatars with a custom avatar
</label>
<input class="form-control" type="text" name="customAvatar" id="customAvatar" data-text-setting="customAvatar">
<br>
<small>Leave it empty to remove the avatar</small>
</div>
<div class="form-check" style="margin-top: 10px;">
<input class="form-check-input" type="checkbox" name="removeSignatures" id="removeSignatures" data-clickable-setting="removeSignatures">
<label class="form-check-label" for="removeSignatures">
Hide the signature
</label>
</div>
<small style="margin-top: 20px; display: block;"><strong>These settings have no effect if the Posts setting is set to "Hide Posts"</strong></small>
</div>
<div class="topics">
<h3>Topics</h3>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="removeTopics" id="removeTopics" data-clickable-setting="removeTopics">
<label class="form-check-label" for="removeTopics">
Hide Topics
</label>
</div>
</div>
</div>
</form>`;
document.getElementById("content").insertAdjacentElement("afterend", settings);
document.querySelector("[data-add]").addEventListener("click", addNode);
document.querySelectorAll("[data-clickable-setting]").forEach(clickable => {
clickable.addEventListener("click", clickedSetting);
});
document.querySelectorAll("[data-text-setting]").forEach(text => {
text.addEventListener("input", textSetting);
});
blacklist.forEach(createNode);
loadSettingsIntoInputs();
}
function clickedSetting(e) {
const input = e.target;
switch (input.dataset.clickableSetting) {
case "doNothingPosts":
saveSetting("postMode", DO_NOTHING_POSTS);
break;
case "hidePosts":
saveSetting("postMode", IGNORE_POSTS);
break;
case "replacePosts":
saveSetting("postMode", REPLACE_POSTS);
break;
case "replaceAvatar":
saveSetting("replaceAvatar", input.checked);
break;
case "removeTopics":
saveSetting("removeTopics", input.checked);
break;
case "removeSignatures":
saveSetting("removeSignatures", input.checked);
break;
default:
return;
}
}
function textSetting(e) {
const input = e.target;
switch (input.dataset.textSetting) {
case "customPost":
saveSetting("customPost", input.value);
break;
case "customAvatar":
saveSetting("customAvatar", input.value);
break;
default:
return;
}
}
function loadSettingsIntoInputs() {
switch (settings.postMode) {
case DO_NOTHING_POSTS:
document.getElementById("doNothingPosts").checked = true;
break;
case IGNORE_POSTS:
document.getElementById("hidePosts").checked = true;
break;
case REPLACE_POSTS:
document.getElementById("replacePosts").checked = true;
break;
}
if (settings.removeTopics) {
document.getElementById("removeTopics").checked = true;
}
if (settings.removeSignatures) {
document.getElementById("removeSignatures").checked = true;
}
document.getElementById("customPost").value = settings.customPost || "";
document.getElementById("customAvatar").value = settings.customAvatar || "";
}
function alterPosts(action) {
document.querySelectorAll(POST_USERS_SELECTOR).forEach(user => {
if (!blacklist.includes(user.querySelector("strong").textContent)) return;
let post = user.parentNode;
for (let i = 0; i < 4; i++) {
post = post.parentNode;
}
action(post, user);
});
}
function removeTopics() {
document.querySelectorAll(TOPIC_USERS_SELECTOR).forEach(user => {
if (!blacklist.includes(user.textContent)) return;
user.closest("tr").style.display = "none";
});
}
function ignorePosts() {
alterPosts(post => {
post.style.display = "none";
post.previousElementSibling.style.display = "none";
});
}
function replacePosts() {
alterPosts(post => {
const message = post.querySelector(MESSAGE_SELECTOR);
message.innerHTML = settings.customPost;
});
}
function replaceAvatar() {
alterPosts((post, user) => {
const avatar = user.querySelector(AVATAR_SELECTOR);
if (!avatar) {
if (settings.customAvatar === "") return;
const avatar = document.createElement("a");
avatar.href = user.querySelector(USER_PROFILE_SELECTOR).href
avatar.className = "forum-icon";
avatar.innerHTML = `
<img class=" lazyloaded" data-src="${settings.customAvatar}" vspace="2" border="0" src="${settings.customAvatar}" width="100" height="125">`;
user.querySelector(USER_INFO_SELECTOR).insertAdjacentElement('afterend', avatar);
} else {
if (settings.customAvatar === "") {
avatar.style.display = "none";
return;
}
const img = avatar.querySelector("img");
img.src = settings.customAvatar;
img.setAttribute("data-src", settings.customAvatar);
img.setAttribute("width", 100);
img.setAttribute("height", 125);
}
});
}
function removeSignatures() {
alterPosts(post => {
const signature = post.querySelector(SIGNATURE_SELECTOR);
if (!signature) return;
signature.style.display = "none";
});
}
function addBlackListButtons() {
document.querySelectorAll(FORUM_MESSAGE_SELECTOR).forEach(forumMessage => {
const actionBar = forumMessage.querySelector(ACTION_BAR_SELECTOR);
const a = document.createElement("a");
a.href = "#!";
a.textContent = "Blacklist User";
a.dataset.username = forumMessage.querySelector("strong").textContent;
a.onclick = blacklistUser;
actionBar.prepend(document.createTextNode(" - "));
actionBar.prepend(a);
});
}
function blacklistUser(e) {
addUser(e.target.dataset.username);
window.location.reload();
}
//Add a user to the blacklist if its not already there
function addUser(username) {
blacklist.push(username);
saveBlackList();
}
//Remove a user from the blacklist if it's there
function removeUser(userName) {
blacklist = blacklist.filter(name => userName !== name);
saveBlackList();
}
//remove the user node from the html code and then update the localStorage
function removeNode(e) {
const row = e.target.parentNode;
const name = row.querySelector("[data-name]").textContent;
row.remove();
removeUser(name);
}
//modify the user node from the html code and then update the localStorage
function saveNode(e) {
const newName = e.target.textContent;
const previousName = e.target.dataset.previousName;
previousName && removeUser(previousName);
if (newName !== "") {
addUser(newName);
e.target.dataset.previousName = newName;
} else {
e.target.parentNode.remove();
}
}
//add a new user node to the html code and then update the localStorage
function addNode(e) {
const node = e.target.parentNode;
const usernameNode = node.querySelector("[data-name]");
const username = usernameNode.textContent;
usernameNode.textContent = "";
if (!blacklist.includes(username)) {
createNode(username);
addUser(username);
}
}
//create the user node then add it the html code
function createNode(username) {
const newUser = document.createElement("div");
newUser.setAttribute("data-user", "");
newUser.className = "user";
newUser.innerHTML = `<div data-name class="name" contenteditable="true" onclick="this.focus()" data-previous-name="${username}">${username}</div>
<button data-remove class="remove">Remove</button>`;
newUser.querySelector("[data-name]").addEventListener("focusout", saveNode);
newUser.querySelector("[data-remove]").addEventListener("click", removeNode);
document.querySelector("[data-blacklist]").append(newUser);
}
})();