Curse detector

Detect curses on MZ forum posts. Works with Spanish words only.

// ==UserScript==
// @name             Curse detector
// @namespace        mz-curse-detector
// @description      Detect curses on MZ forum posts. Works with Spanish words only.
// @description:es   Detectar groserías en los mensajes de los foros de MZ. Solamente detecta palabras en español.
// @homepage         https://github.com/rhonaldomaster/mz-curse-detector
// @icon             https://www.managerzone.com/favicon.ico?v2
// @include          https://*managerzone.*p=forum&sub=topic*
// @grant            GM_getValue
// @grant            GM_setValue
// @grant            GM_xmlhttpRequest
// @version          0.4
// @copyright        GNU/GPL v3
// @author           rhonaldomaster
// @license          GPL-3.0-or-later
// @compatible       chrome
// @compatible       firefox
// @compatible       opera
// @compatible       safari
// @compatible       edge
// ==/UserScript==


// Default words in case the fetch fails
const DEFAULT_CURSE_WORDS = [
  'boba', 'bobo', 'boluda', 'bolu.da', 'boludo', 'bolu.do', 'bosta',
  'bostera', 'bostero', 'burrazo', 'burro', 'cometrava', 'cometraba',
  'concha', ' culo', 'estupida', 'estúpida', 'estupido', 'estúpido',
  'forra', 'forro', 'gil', 'gilipolla', 'gonorrea', 'hdp', 'hipocrita',
  'hipócrita', 'hijo de puta', 'hitler', 'idiota', 'imbecil', 'imbécil',
  'kkk', 'kuka', 'lacra', 'la tenés adentro', 'la tenes adentro',
  'la tenes bien adentro', 'la tenés bien adentro', 'la tenéis bien adentro',
  ' lta', 'malcogida', 'malcogido', 'mal cogida', 'mal cogido', 'malparida',
  'malparido', 'mal parida', 'mal parido', 'marica', 'marmota', 'mediocre',
  'mierda', 'miserable', 'mogolico', 'mogólico', 'montonero', 'mu ',
  'muerde almohada', 'muerdealmohada', 'negro cabeza', 'patetico', 'patético',
  'payasa', 'payaso', 'pelotuda', 'pelotudo', 'pene', 'perra', 'puta',
  'putear', 'puto', 'retrasada', 'retrasado', 'ridicula', 'ridícula',
  'ridiculo', 'ridículo', 'salame', 'sorete', 'sucio', 'subnormal',
  'tarada', 'tarado', 'tonta', 'tontaza', 'tontazo', 'tonto', 'trampa',
  'tramposa', 'tramposo', 'vende ajo', 'vendo ajo', 'verduler'
];

// Cache configuration
const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours
const CACHE_KEY = 'curseWordsCache';
const CACHE_TIMESTAMP = 'curseWordsTimestamp';
const CACHE_VERSION = '1.0.0';

// GitHub Pages URL where the JSON is hosted
const WORDS_JSON_URL = 'https://rhonaldomaster.github.io/mz-curse-detector/curse-words.json';

// Store the current list of curse words
let curseWords = [...DEFAULT_CURSE_WORDS];

function searchAndHighlightWord(word, textContainer) {
  const warningColor = 'var(--curseColor)';
  const originalText = textContainer.innerHTML;
  if (originalText.indexOf(`<span style="color:${warningColor};font-weight:bold;text-decoration:underline;">`) > -1) {
    return;
  }

  const regex = new RegExp(`\\b${word}\\b`, 'gi');
  const highlightedText = originalText.replace(
    regex,
    `<span style="color:${warningColor};font-weight:bold;text-decoration:underline;">$&</span>`
  );

  if (highlightedText !== originalText) {
    textContainer.innerHTML = highlightedText;
    const editPostButton = textContainer.parentNode.querySelector('.fa-edit');
    if (editPostButton) {
      editPostButton.style.color = warningColor;
    }
  }
}

async function loadCurseWords() {
  // Try to get cached data first
  try {
    const cachedData = GM_getValue(CACHE_KEY);
    const cachedTime = GM_getValue(CACHE_TIMESTAMP);
    const cachedVersion = GM_getValue('curseWordsVersion');
    
    // If we have valid cached data and it's not expired
    if (cachedData && cachedTime && cachedVersion === CACHE_VERSION) {
      const age = Date.now() - parseInt(cachedTime, 10);
      if (age < CACHE_DURATION) {
        return JSON.parse(cachedData).words;
      }
    }
    
    // Fetch fresh data
    const response = await new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        method: 'GET',
        url: WORDS_JSON_URL,
        onload: resolve,
        onerror: reject,
        timeout: 5000 // 5 second timeout
      });
    });
    
    if (response.status >= 200 && response.status < 300) {
      const data = JSON.parse(response.responseText);
      
      // Cache the result
      GM_setValue(CACHE_KEY, JSON.stringify(data));
      GM_setValue(CACHE_TIMESTAMP, Date.now().toString());
      GM_setValue('curseWordsVersion', CACHE_VERSION);
      
      return data.words || DEFAULT_CURSE_WORDS;
    }
  } catch (error) {
    console.error('Failed to load curse words:', error);
  }
  
  // Fallback to default words
  return DEFAULT_CURSE_WORDS;
}

function detectCurses() {
  const messages = document.querySelectorAll('.forum-post-content');

  messages.forEach(message => {
    for (let i = 0; i < curseWords.length; i++) {
      searchAndHighlightWord(curseWords[i], message);
    }
  });
}

function addCSSVariables() {
  const root = document.querySelector(':root');
  root.style.setProperty('--curseColor', '#ff4800');
}

// Initialize the script
async function init() {
  try {
    // Load curse words
    curseWords = await loadCurseWords();
    
    // Set up mutation observer
    const observer = new MutationObserver(mutations => {
      mutations.forEach(mutation => {
        if (mutation.type === 'childList') {
          mutation.addedNodes.forEach(node => {
            if (node.nodeType === 1 && node.classList.contains('forum-post-content')) {
              detectCurses();
            }
          });
        }
      });
    });

    // Find posts container and start observing
    const postsContainer = document.querySelector('.forum_content');
    if (postsContainer) {
      addCSSVariables();
      observer.observe(postsContainer, { childList: true, subtree: true });
      
      // Initial check
      detectCurses();
      
      // Add a small indicator
      addStatusIndicator();
    } else {
      console.error('Posts container not found.');
    }
  } catch (error) {
    console.error('Error initializing script:', error);
  }
}

// Add a small status indicator
function addStatusIndicator() {
  const indicator = document.createElement('div');
  indicator.style.position = 'fixed';
  indicator.style.bottom = '10px';
  indicator.style.right = '10px';
  indicator.style.padding = '5px 10px';
  indicator.style.background = '#333';
  indicator.style.color = '#fff';
  indicator.style.borderRadius = '4px';
  indicator.style.fontSize = '12px';
  indicator.style.zIndex = '9999';
  indicator.textContent = `MZ Curse Detector v${GM_info.script.version} (${curseWords.length} palabras)`;
  document.body.appendChild(indicator);
}

// Start the script
init();
长期地址
遇到问题?请前往 GitHub 提 Issues,或加Q群1031348184

赞助商

Fishcpy

广告

Rainyun

一年攒够 12 元