// ==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()
})()