Hide Bot Comments

Removes comments made by bots on websites such as YouTube.

当前为 2022-03-26 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Hide Bot Comments
  3. // @namespace https://theusaf.org
  4. // @version 1.7.2
  5. // @description Removes comments made by bots on websites such as YouTube.
  6. // @author theusaf
  7. // @match https://www.youtube.com/**
  8. // @match https://www.facebook.com/plugins/comments.php*
  9. // @match https://www.facebook.com/plugins/feedback.php*
  10. // @copyright 2022 theusaf
  11. // @license MIT
  12. // @grant none
  13. // ==/UserScript==
  14.  
  15. const SITES = Object.freeze({
  16. YOUTUBE: {
  17. checks: [
  18. // starts with too much whitespace
  19. /^\s{2,}/,
  20. // only links and other punctuation
  21. /^(\s*@.+)?\s*(https:\/\/[^\s]+)(https:\/\/[^\s]+|\n.\s])+$/,
  22. // all caps and a link
  23. /^(\s*@.+)?\s*[A-Z\s\r\n!]*https:\/\/[^\s]+[A-Z\s\r\n!]*$/,
  24. // A link and a random message afterwards
  25. /^(\s*@.+)?\s*https:\/\/[^\s]+(\n|.|\s)*(It'll blow your mind\.|[dD]on'?t [mM]iss|Bots for u|Finally|💜|fax|only until|Bots are|:]|I found it :|Do not miss this|:\)|Ye[sp] ¤? (true|exactly)|(...?$))/i,
  26. // word + link
  27. /^(\s*@.+)?\s*(This|[Ww]ow!?)\s*https:\/\/[^\s]+/,
  28. // phrase + line + link
  29. /(Finally it's here\.?|deceives.*subscribers:\.{1,}|you .*will never love|[\u0401\u0451\u0410-\u044f,.:]{15,}.*|EXPOSED:|IS FREAK!|IS GARBAGE!{1,}|shocking truth.*|his subscribers.*|will stop watching.*|yes\.?|THE GAME.*|After watching this video you will never love.*)(\n|\s)(\n|.)*https:\/\/[^\s]+/,
  30. // link + random "word"
  31. /^(\s*@.+)?\s*https:\/\/[^\s]+\s*[a-z]+\s*$/,
  32. // link with a star at the end??
  33. /https:\/\/youtu.be\/\w+\*/,
  34. // ...
  35. /PRIVATE S\*X|over 18|Anna is a beautiful girl/i,
  36. // suspicious websites
  37. /beautyzone\.\w+|\.cam|lust\.\w+|\.host|asian\w*\.\w+|\w*teen\.\w+/i,
  38. // too many "-"
  39. /-{5,}/,
  40. // single, somewhat strange word
  41. /^(Hii|Ye|Bruhh|Aawww?)$/,
  42. // common phrase
  43. / (● ´ω ●) ✨💕|I can read you mind brother|SPECIAL FOR YOU|l1ke my v1deo|small channel trying to grow| YouT\*ber|MY CONTENT|My video|pedophile😱|MY WORLD RECORD|(^Yes.{0,5}$)|said this to a fan|Read my name|[Mm]y mom.*subscribers|literally begging|MY VIDEOS?|my playlist|fucking cringe|[Dd][Oo][Nn]'?[Tt] read my name/,
  44. // replies to bots
  45. /@Don'?t read my|^(ro)?bot+$/i,
  46. // upside down chars
  47. /[ㄥϛㄣƐᄅƖ⅄Λ∩┴ɹԀ˥ʞſפℲƎƆ∀ʎʍʌʇɹɯʞɾᴉɥƃɟǝɔɐ]/,
  48. // just a single, weird character
  49. /^.$/,
  50. // invisible characters
  51. /[\u200e]/u,
  52. (text) => {
  53. const charSets = [
  54. {
  55. regex: /[\u{fe27}-\u{fe2f}\u{1df5}-\u{1dff}\u{1dc0}-\u{1de6}\u{1ab0}-\u{1abe}\u{0300}-\u{0333}\u{0339}-\u{033f}\u{0346}-\u{034a}\u{034b}-\u{034e}\u{0350}-\u{0357}\u{0358}-\u{035b}]/gu, // weird combining characters
  56. matchPercent: 0.4
  57. },
  58. {
  59. regex: /[ᴀʙᴄᴅᴇғɢʜɪᴊᴋʟᴍɴᴏᴘᴏ̨ʀsᴛᴜᴠᴡxʏᴢ\s]/g,
  60. matchPercent: 0.5
  61. },
  62. {
  63. regex: /[\u{1D538}-\u{1D56B}\u{1D400}-\u{1D433}]/gu, // math letter symbols
  64. matchPercent: 0.3
  65. }
  66. ];
  67. for (const check of charSets) {
  68. const { regex, matchPercent } = check,
  69. matches = text.match(regex)?.length ?? 0;
  70. if (matches / text.length > matchPercent && text.length > 10) {
  71. return true;
  72. }
  73. }
  74. }
  75. ]
  76. },
  77. FACEBOOK_EMBED: {
  78. checks: [
  79. // "Easy cash" scams
  80. /easy cash|work online|real passive income/,
  81. // Scammy manga sites
  82. /(must check this out|read more:|300 or more chapters|\*{1,} SPOILER ALERT \*{1,}|FREE (TO|FOR) READ).*(\n\s)*(https?:\/\/[^\s]+|\n.\s])+/
  83. ],
  84. options: {
  85. initialScan: () => {
  86. return document.querySelectorAll(".clearfix");
  87. }
  88. }
  89. }
  90. }),
  91. site = getCurrentSite(),
  92. commentMutationListener = new MutationObserver((mutations) => {
  93. for (const mutation of mutations) {
  94. for (const node of mutation.addedNodes) {
  95. const text = getCommentText(node, site);
  96. if (text) {
  97. if (isCommentLikelyBotComment(text, site)) {
  98. node.style.display = "none";
  99. }
  100. }
  101. }
  102. }
  103. });
  104.  
  105. commentMutationListener.observe(document.body, {
  106. subtree: true,
  107. childList: true
  108. });
  109.  
  110. /**
  111. * Determines whether a comment is likely spam.
  112. *
  113. * @param {String} text The comment's content
  114. * @param {Object} site The website the comment is from
  115. * @return {Boolean}
  116. */
  117. function isCommentLikelyBotComment(text, site) {
  118. for (const check of site.checks) {
  119. if (typeof check === "function") {
  120. if (check(text)) {
  121. console.log("Filter Check Failed");
  122. console.log(text);
  123. return true;
  124. }
  125. } else {
  126. // assume regex
  127. if (check.test(text)) {
  128. console.log("Regex Check Failed");
  129. console.log(check);
  130. console.log(text);
  131. return true;
  132. }
  133. }
  134. }
  135. return false;
  136. }
  137.  
  138. function getCommentText(node, site) {
  139. switch (site) {
  140. case SITES.YOUTUBE: {
  141. if (node.nodeName === "YTD-COMMENT-RENDERER") {
  142. return node.querySelector("#content-text").textContent;
  143. }
  144. break;
  145. }
  146. case SITES.FACEBOOK_EMBED: {
  147. if (node.classList?.contains("clearfix")) {
  148. try {
  149. return node?.lastElementChild
  150. .lastElementChild
  151. .lastElementChild
  152. .firstElementChild
  153. .children[1]
  154. .textContent;
  155. } catch (err) {
  156. return null;
  157. }
  158. }
  159. }
  160. }
  161. return null;
  162. }
  163.  
  164. function getCurrentSite() {
  165. switch (location.hostname) {
  166. case "www.youtube.com": {
  167. return SITES.YOUTUBE;
  168. }
  169. case "www.facebook.com": {
  170. return SITES.FACEBOOK_EMBED;
  171. }
  172. }
  173. }
  174.  
  175. if (site.options?.initialScan) {
  176. const items = site.options.initialScan();
  177. for (const node of items) {
  178. const text = getCommentText(node, site);
  179. if (text) {
  180. if (isCommentLikelyBotComment(text, site)) {
  181. node.style.display = "none";
  182. }
  183. }
  184. }
  185. }