AS mmd cleanup

This script is to clean up the spam in the Anime sharing MMD sharethread.

目前為 2025-01-29 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name AS mmd cleanup
  3. // @version 1.2
  4. // @namespace mailto:percfag@gmail.com
  5. // @author perckle
  6. // @description This script is to clean up the spam in the Anime sharing MMD sharethread.
  7. // @license MIT
  8. // @match https://www.anime-sharing.com/threads/mmd-discussion-and-sharing.1532636/*
  9. // @grant none
  10. // @run-at document-end
  11. // ==/UserScript==
  12.  
  13. // Configuration flags
  14. const CONFIG = {
  15. enableWordFilter: true,
  16. enableLinkCheck: true,
  17. enableHideBlockCheck: true,
  18. removeFormatting: false
  19. };
  20.  
  21. // Function to check if text contains target words (case insensitive)
  22. function containsTargetWords(text) {
  23. const normalizedText = text.toLowerCase();
  24. return normalizedText.includes('anyone') ||
  25. normalizedText.includes('any one') ||
  26. normalizedText.includes('anybody') ||
  27. normalizedText.includes('hero') ||
  28. normalizedText.includes('plz') ||
  29. normalizedText.includes('pls') ||
  30. normalizedText.includes('thx') ||
  31. normalizedText.includes('in advance');
  32. }
  33.  
  34. // Function to check if element contains a direct child link
  35. function containsLink(bbWrapper) {
  36. if (!bbWrapper || typeof bbWrapper.children === 'undefined') {
  37. console.log('Invalid element passed to containsLink');
  38. return false;
  39. }
  40.  
  41. // Convert children to array and check only direct children
  42. const children = Array.from(bbWrapper.children);
  43. return children.some(child => {
  44. // Check if the child is an 'a' tag with href
  45. if (child.tagName === 'A' && child.hasAttribute('href')) {
  46. return true;
  47. }
  48. // Check if the child directly contains an 'a' tag (one level deep)
  49. return child.querySelector(':scope > a[href]') !== null;
  50. });
  51. }
  52.  
  53. // Function to check if element contains hideBlock class
  54. function containsHideBlock(bbWrapper) {
  55. if (!bbWrapper || typeof bbWrapper.querySelector !== 'function') {
  56. console.log('Invalid element passed to containsHideBlock');
  57. return false;
  58. }
  59. return bbWrapper.querySelector(':scope > .bbCodeBlock--hide, :scope > .hideBlock') !== null;
  60. }
  61.  
  62. // Function to get text content excluding content within blockquotes
  63. function getTextExcludingQuotes(element) {
  64. const clone = element.cloneNode(true);
  65. const blockquotes = clone.querySelectorAll('blockquote');
  66. blockquotes.forEach(quote => quote.remove());
  67. return clone.textContent.trim();
  68. }
  69.  
  70. // Function to remove b and span tags while preserving their content
  71. function removeFormattingTags(element) {
  72. if (!CONFIG.removeFormatting) return;
  73.  
  74. const formattingTags = element.querySelectorAll('b, span');
  75. formattingTags.forEach(tag => {
  76. const textContent = document.createTextNode(tag.textContent);
  77. tag.parentNode.replaceChild(textContent, tag);
  78. });
  79. }
  80.  
  81. // Main function to filter forum posts
  82. function filterForumPosts() {
  83. const container = document.querySelector('.block--messages');
  84. if (!container) {
  85. console.log('Forum container not found, retrying...');
  86. return false;
  87. }
  88.  
  89. const posts = container.querySelectorAll('.message-inner');
  90. if (posts.length === 0) {
  91. console.log('No posts found, retrying...');
  92. return false;
  93. }
  94.  
  95. posts.forEach(post => {
  96. const bbWrapper = post.querySelector('.bbWrapper');
  97. if (!bbWrapper) return;
  98.  
  99. let shouldRemove = true; // Start with true, set to false if any condition is met
  100.  
  101.  
  102.  
  103.  
  104. // Link check
  105. if (CONFIG.enableLinkCheck && shouldRemove) {
  106. if (containsLink(bbWrapper)) {
  107. shouldRemove = false;
  108. }
  109. }
  110.  
  111. // HideBlock check
  112. if (CONFIG.enableHideBlockCheck && shouldRemove) {
  113. if (containsHideBlock(bbWrapper)) {
  114. shouldRemove = false;
  115. }
  116. }
  117. if (CONFIG.enableWordFilter) {
  118. const textContent = getTextExcludingQuotes(bbWrapper);
  119. if (containsTargetWords(textContent)) {
  120. shouldRemove = true;
  121. }
  122. }
  123.  
  124. if (shouldRemove) {
  125. post.remove();
  126. } else if (CONFIG.removeFormatting) {
  127. removeFormattingTags(bbWrapper);
  128. }
  129. });
  130.  
  131. return true;
  132. }
  133.  
  134. // Function to wait for page to be fully rendered
  135. function waitForPageLoad() {
  136. const observer = new MutationObserver((mutations, obs) => {
  137. const success = filterForumPosts();
  138. if (success) {
  139. obs.disconnect();
  140. console.log('Forum filtering complete');
  141. }
  142. });
  143.  
  144. observer.observe(document.body, {
  145. childList: true,
  146. subtree: true,
  147. attributes: false,
  148. characterData: false
  149. });
  150.  
  151. document.addEventListener('DOMContentLoaded', () => {
  152. if (filterForumPosts()) {
  153. observer.disconnect();
  154. console.log('Forum filtering complete on initial load');
  155. }
  156. });
  157.  
  158. setTimeout(() => {
  159. observer.disconnect();
  160. console.log('Stopped watching for page changes after timeout');
  161. }, 30000);
  162. }
  163.  
  164. // Initialize the script
  165. waitForPageLoad();