LZTClown

Персональный список клоунов на форуме LolzTeam

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         LZTClown
// @namespace    https://zelenka.guru/urmom/
// @version      1.3
// @description  Персональный список клоунов на форуме LolzTeam
// @author       Интерпол
// @icon         https://zelenka.guru/favicon.ico
// @match        https://zelenka.guru/*
// @match        https://lolz.guru/*
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @license MIT
// ==/UserScript==

;(function () {
  "use strict"

  // *********** Настройки ***********

  const emojiIcon = "🤡" // эмодзи возле ника
  const clownButton = "https://i.imgur.com/Wyqq6g5.png" // URL картинки для назначения клоуна (PNG)
  const showButton = true // false скрыть кнопку, true показать

  const clownAdded = "объявлен клоуном" // уведомление при назначении
  const clownDelete = "больше не является клоуном" // уведомление при выносе

  const clownStyle = true // изменять ли аватарку и ник пользователя
  const hideMessagesFromClowns = true // нужно ли скрывать сообщения от клоунов, последние 5 в учет
  const clownAvatar = "https://i.imgur.com/MBDKn9l.png" // URL новой аватарки
  const clownName = "Клоун" // новый ник для пользователя - не работает пока что!!!

  // *********** Настройки ***********

  let blacklist = GM_getValue("blacklist", {})

  function getUserIdFromAvatarUrl(url) {
    const urlParts = url.split("/")
    const lastPart = urlParts[urlParts.length - 1]
    return lastPart.split(".")[0]
  }

  function getBlacklistUserIds() {
    const avatars = GM_getValue("originalAvatars", {})
    // console.log("user avatars: ", avatars);
    const allUserIds = Object.values(avatars)
      .map((url) => getUserIdFromAvatarUrl(url))
      .filter((id) => id)
    const userIds = allUserIds.slice(-5)
    // console.log("last 5 user IDs in blacklist: ", userIds);
    return userIds
  }

  function addMessageSendHandler() {
    const button = document.querySelector("button.lzt-fe-se-sendMessageButton")
    const div = document.querySelector(
      "div.fr-element.fr-view.fr-element-scroll-visible"
    )
    if (button && div) {
      button.addEventListener("click", function () {
        const userIds = getBlacklistUserIds()
        if (userIds.length > 0) {
          const elements = []
          const childElements = div.children
          for (var i = 0; i < childElements.length; i++) {
            elements.push(childElements[i].outerHTML)
          }
          if (elements.length !== 0) {
            div.innerHTML =
              `[exceptids=${userIds.join(",")}]` +
              elements.join("<br>") +
              "[/exceptids]"
          }
          setTimeout(function () {
            div.innerHTML = ``
          }, 1)
        }
      })
    }
  }
  function initMessageHandler() {
    const userIds = getBlacklistUserIds()
    if (userIds.length > 0 && hideMessagesFromClowns) {
      addMessageSendHandler()
    }
  }

  function getOriginalAvatar(username) {
    const avatars = GM_getValue("originalAvatars", {})
    return avatars[username]
  }

  function setOriginalAvatar(username, url) {
    const avatars = GM_getValue("originalAvatars", {})
    avatars[username] = url
    GM_setValue("originalAvatars", avatars)
  }

  function updateClownAppearance(username, add) {
    if (!clownStyle) return

    document.querySelectorAll("li.message").forEach((messageElem) => {
      if (messageElem.getAttribute("data-author") === username) {
        const avatarElem = messageElem.querySelector(".avatar span.img")
        if (avatarElem) {
          if (add) {
            if (!getOriginalAvatar(username)) {
              setOriginalAvatar(username, avatarElem.style.backgroundImage)
            }
            avatarElem.style.backgroundImage = `url('${clownAvatar}')`
            avatarElem.style.backgroundSize = "cover"
            avatarElem.style.backgroundPosition = "center"
            avatarElem.style.backgroundRepeat = "no-repeat"
          } else {
            const originalAvatar = getOriginalAvatar(username)
            if (originalAvatar) {
              avatarElem.style.backgroundImage = originalAvatar
            }
          }
        }
      }
    })
  }

  function updateClownAppearanceOnLoad() {
    document.querySelectorAll("li.message").forEach((messageElem) => {
      const author = messageElem.getAttribute("data-author")
      const avatarElem = messageElem.querySelector(".avatar span.img")
      if (avatarElem && blacklist.hasOwnProperty(author) && clownStyle) {
        avatarElem.style.backgroundImage = `url('${clownAvatar}')`
        avatarElem.style.backgroundSize = "cover"
        avatarElem.style.backgroundPosition = "center"
        avatarElem.style.backgroundRepeat = "no-repeat"
      }
    })
  }

  function lztClown() {
    document.querySelectorAll(".username").forEach((elem) => {
      let username = elem.textContent.trim()
      let isBlacklisted = blacklist.hasOwnProperty(username)
      let nextSiblingIsIcon =
        elem.nextElementSibling &&
        elem.nextElementSibling.classList.contains("blacklist-icon")

      if (isBlacklisted && !nextSiblingIsIcon) {
        const blacklistIcon = document.createElement("span")
        blacklistIcon.textContent = ` ${emojiIcon}`
        blacklistIcon.className = "blacklist-icon"
        blacklistIcon.style.cursor = "pointer"
        blacklistIcon.title = blacklist[username]
        elem.parentNode.insertBefore(blacklistIcon, elem.nextSibling)
      } else if (!isBlacklisted && nextSiblingIsIcon) {
        elem.nextElementSibling.remove()
      }
      elem.classList.toggle("blacklisted-user", isBlacklisted)
    })
  }

  function addClown(username, reason) {
    blacklist[username] = reason
    GM_setValue("blacklist", blacklist)
    lztClown()
    updateClownAppearance(username, true)
    XenForo.alert(`Пользователь ${username} ${clownAdded}`, "LZTClown", 2000)
  }

  function removeClown(username) {
    delete blacklist[username]
    lztClown()
    updateClownAppearance(username, false)
    const avatars = GM_getValue("originalAvatars", {})
    if (avatars[username]) {
      delete avatars[username]
      GM_setValue("originalAvatars", avatars)
    }
    GM_setValue("blacklist", blacklist)
    XenForo.alert(`Пользователь ${username} ${clownDelete}`, "LZTClown", 2000)
  }

  function createBlacklistModal() {
    const blacklistModal = document.createElement("div")
    blacklistModal.id = "blacklistModal"
    blacklistModal.className = "modal fade in"
    blacklistModal.style = "z-index: 10003; padding-right: 15px; outline: none;"
    blacklistModal.tabIndex = -1

    let blacklistContent = "<ul>"
    for (let user in blacklist) {
      blacklistContent += `<li>${user} - ${blacklist[user]}</li>`
    }
    blacklistContent += "</ul>"

    blacklistModal.innerHTML = `
<div class="xenOverlay" style="top: 10%;">
    <div class="errorOverlay">
        <a class="close OverlayCloser"></a>
        <h2 class="heading">Список клоунов</h2>
        <div class="baseHtml errorDetails">
            <div class="modalContent">
                <div class="blacklistContent">${blacklistContent}</div>
                <div class="centerButton">
                    <button class="button primary" id="closeBlacklistModal">Закрыть</button>
                </div>
            </div>
        </div>
    </div>
</div>

    `
    document.body.appendChild(blacklistModal)

    setTimeout(() => {
      blacklistModal.style.opacity = 1
    }, 10)

    document
      .getElementById("closeBlacklistModal")
      .addEventListener("click", function () {
        blacklistModal.style.opacity = 0
        setTimeout(() => {
          blacklistModal.parentNode.removeChild(blacklistModal)
        }, 500)
      })
  }

  function createModal(username) {
    const existingModal = document.getElementById("blacklistModalWrapper")
    if (existingModal) {
      existingModal.remove()
    }

    const modalWrapper = document.createElement("div")
    modalWrapper.id = "blacklistModalWrapper"
    modalWrapper.className = "modal fade in"
    modalWrapper.style = "z-index: 10002; padding-right: 15px; outline: none;"
    modalWrapper.tabIndex = -1
    modalWrapper.innerHTML = `
            <div class="xenOverlay" style="top: 10%;">
                <div class="errorOverlay">
                    <a class="close OverlayCloser"></a>
                    <h2 class="heading">LZTClown > Добавить</h2>
                    <div class="baseHtml errorDetails">
                        <div style="display: flex; flex-direction: column; align-items: center; margin: auto;">
                            <p>Введите причину по которой ${username} является клоуном</p>
                            <input type="text" id="blacklistReason" class="textCtrl" placeholder="Причина" autocomplete="off" style="width: 60%;">
                            <button id="addClownButton" class="button primary" style="margin-top: 1rem;">Добавить</button>
                            <button id="showBlacklistButton" class="button" style="margin-top: 1rem;">Показать список</button>
                        </div>
                    </div>
                </div>
            </div>
        `
    document.body.appendChild(modalWrapper)

    setTimeout(() => {
      modalWrapper.style.opacity = 1
    }, 10)

    const addClownButton = document.getElementById("addClownButton")
    const clownReasonInput = document.getElementById("blacklistReason")

    addClownButton.addEventListener("click", function () {
      let reason = clownReasonInput.value
      addClown(username, reason)
      closeModal()
    })

    document
      .getElementById("showBlacklistButton")
      .addEventListener("click", function () {
        createBlacklistModal()
      })

    clownReasonInput.addEventListener("keypress", function (event) {
      if (event.key === "Enter") {
        let reason = clownReasonInput.value
        addClown(username, reason)
        closeModal()
      }
    })

    const overlayCloser = modalWrapper.querySelector(".OverlayCloser")
    overlayCloser.addEventListener("click", function () {
      closeModal()
    })

    modalWrapper.addEventListener("click", function (event) {
      if (event.target === modalWrapper) {
        closeModal()
      }
    })
  }

  function closeModal() {
    const modalWrapper = document.getElementById("blacklistModalWrapper")
    if (modalWrapper) {
      modalWrapper.style.opacity = 0
      setTimeout(() => {
        modalWrapper.parentNode.removeChild(modalWrapper)
      }, 500)
    }
  }

  function promptClown(username) {
    createModal(username)
  }

  function init() {
    document.body.addEventListener("click", function (event) {
      if (event.target.classList.contains("blacklist-icon")) {
        let username = event.target.previousElementSibling.textContent.trim()
        removeClown(username)
      } else if (event.target.classList.contains("blacklist-button")) {
        let username = event.target
          .closest(".message")
          .querySelector("a.username")
          .textContent.trim()
        promptClown(username)
      }
    })

    const observer = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        if (mutation.type === "childList") {
          lztClown()
          addClownButtonToCommentContainers()
        }
      })
    })

    observer.observe(document.body, { childList: true, subtree: true })
    lztClown()
    addClownButtonToCommentContainers()
    initMessageHandler()
    updateClownAppearance()
    updateClownAppearanceOnLoad()
  }

  function tippyInit() {
    document.querySelectorAll(".blacklist-icon").forEach((icon) => {
      const iconTitle = icon.getAttribute("title")
      icon.removeAttribute("title")
      tippy(icon, {
        content: iconTitle,
        arrow: true,
      })
    })
  }

  function addClownButtonToCommentContainers() {
    document
      .querySelectorAll(".message .publicControls:not(.blacklist-button-added)")
      .forEach((commentContainer) => {
        if (showButton) {
          const img = document.createElement("img")
          img.src = clownButton
          img.className = "item control blacklist-button"
          img.style.cursor = "pointer"
          img.title = "Назначить клоуном"
          img.style.width = "22px"
          img.style.height = "22px"
          img.addEventListener("click", function () {
            const username = commentContainer
              .closest(".message")
              .querySelector(".username")
              .textContent.trim()
            promptClown(username)
          })
          commentContainer.appendChild(img)
        }
        commentContainer.classList.add("blacklist-button-added")
      })

    tippyInit()
  }

  GM_addStyle(`
    .blacklisted-user {
        // text-decoration: underline red;
    }
    .blacklist-icon {
        margin-left: 5px;
        color: red;
    }
    .blacklist-button {
        display: inline-block;
        margin-left: 5px;
        color: red;
    }
    #blacklistModalWrapper {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.8);
        display: flex;
        justify-content: center;
        align-items: center;
        opacity: 0;
        transition: opacity 0.3s ease;
    }
    
    .modalContent {
    display: flex;
    flex-direction: column;
    align-items: start;
    margin: auto;
    }

    .blacklistContent {
        align-self: flex-start;
        margin-bottom: 1rem;
    }

    .centerButton {
        display: flex;
        justify-content: center;
        width: 100%;
    }
`)

  init()
})()