您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Version de tests. Met en évidence l'auteur (OP), les citations, les PEMT, vos messages et les DDB sur tous les forums JVC.
// ==UserScript== // @name Surlignage Post // @namespace http://tampermonkey.net/ // @version 0.9.2 // @description Version de tests. Met en évidence l'auteur (OP), les citations, les PEMT, vos messages et les DDB sur tous les forums JVC. // @author FaceDePet // @match *://www.jeuxvideo.com/forums/*-*-*-*-*-*-*-*.htm // @connect *.jeuxvideo.com // @grant GM_xmlhttpRequest // @run-at document-end // @license MIT // ==/UserScript== (function() { 'use strict'; class JVCTopicEnhancer { constructor() { this.CACHE_KEY_OP = 'jvcTopicEnhancerCacheOP'; this.CACHE_KEY_POSTS = 'jvcUserPostsCache'; this.CACHE_KEY_DDB = 'jvcDdbStatusCache'; this.MAX_CACHED_POSTS_PER_TOPIC = 20; this.pemtColors = ['#45a093', '#5c8fba', '#896a9e', '#c77e4d', '#c06158']; this.init(); } async init() { this.topicId = this.getTopicId(); if (!this.topicId) return; this.currentPage = this.getCurrentPageNumber(); this.currentUser = this.getCurrentUsername(); this.addPostListener(); try { const [opUsername, userMessages] = await Promise.all([ this.getOpUsername(), this.currentUser ? this.getAllUserMessageContents() : [] ]); this.opUsername = opUsername; this.currentUserMessages = userMessages; } catch (error) { console.error("JVC Enhancer: Erreur lors de la récupération des données.", error); return; } this.highlightRules = [ { name: 'Original Poster', condition: (author) => author === this.opUsername }, { name: 'User Mention', condition: (author, el) => this.isUserMentioned(author, el) }, { name: 'Self', condition: (author) => author === this.currentUser } ]; this.injectStyles(); this.processMessages(); this.checkFirstMessagesReportStatus(); } getTopicId() { const match = window.location.href.match(/\/forums\/\d+-\d+-(\d+)-/); return match ? match[1] : null; } getCurrentPageNumber() { const match = window.location.href.match(/-(\d+)-0-1-0-/); return match ? parseInt(match[1], 10) : 1; } async getOpUsername() { const cache = JSON.parse(sessionStorage.getItem(this.CACHE_KEY_OP)) || {}; if (cache[this.topicId]) return cache[this.topicId]; let newOpUsername = (this.currentPage === 1) ? this.parseOpUsernameFromDocument(document) : await this.fetchOpUsernameFromPageOne(); if (newOpUsername) { cache[this.topicId] = newOpUsername; sessionStorage.setItem(this.CACHE_KEY_OP, JSON.stringify(cache)); } return newOpUsername; } parseOpUsernameFromDocument(doc, isApi = false) { const selector = isApi ? '.post .bloc-pseudo-msg' : '.bloc-message-forum .bloc-pseudo-msg'; return doc.querySelector(selector)?.textContent.trim() || null; } fetchOpUsernameFromPageOne() { return this.fetchApiPageContent(1).then(doc => this.parseOpUsernameFromDocument(doc, true)); } fetchApiPageContent(pageNumber) { return new Promise((resolve, reject) => { const url = window.location.href.replace(`-${this.currentPage}-0-1-0-`, `-${pageNumber}-0-1-0-`).replace('www.jeuxvideo.com', 'api.jeuxvideo.com'); GM_xmlhttpRequest({ method: "GET", url, onload: (res) => resolve(new DOMParser().parseFromString(res.responseText, "text/html")), onerror: (err) => reject(err) }); }); } fetchUrlContent(url) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url, onload: (res) => resolve(res.responseText), onerror: (err) => reject(err) }); }); } async getAllUserMessageContents() { const cachedPosts = this.loadUserPostsFromCache(); let pagePosts = this.parseMessagesFromDocument(document); let prevPagePosts = []; if (this.currentPage > 1) { try { const prevPageDoc = await this.fetchApiPageContent(this.currentPage - 1); prevPagePosts = this.parseMessagesFromDocument(prevPageDoc, true); } catch (e) { console.error("JVC Enhancer: Impossible de charger la page N-1.", e); } } // Fusionner et dédoublonner par contenu HTML pour être plus précis const allMessages = [...cachedPosts, ...pagePosts, ...prevPagePosts]; const uniqueMessages = Array.from(new Map(allMessages.map(item => [item.html, item])).values()); return uniqueMessages; } parseMessagesFromDocument(doc, isApi = false) { const messages = []; const sel = { msg: isApi ? '.post' : '.bloc-message-forum', pseudo: isApi ? '.bloc-pseudo-msg' : '.bloc-pseudo-msg', content: isApi ? '.message' : '.txt-msg' }; doc.querySelectorAll(sel.msg).forEach(msg => { if (msg.querySelector(sel.pseudo)?.textContent.trim() === this.currentUser) { const contentElement = msg.querySelector(sel.content); if (contentElement) { const clone = contentElement.cloneNode(true); clone.querySelectorAll('blockquote.blockquote-jv').forEach(bq => bq.remove()); messages.push({ text: this.normalizeText(clone.textContent), html: this.normalizeHtml(clone.innerHTML) }); } } }); return messages; } addPostListener() { const postButton = document.querySelector('.postMessage'); if (postButton) { postButton.addEventListener('click', () => { const messageTextarea = document.querySelector('#message_topic, .messageEditor__edit'); if (messageTextarea && this.topicId) { // On sauvegarde le texte brut, la normalisation se fera à la lecture this.saveUserPostToCache(messageTextarea.value); } }, true); } } saveUserPostToCache(rawText) { try { const cache = JSON.parse(localStorage.getItem(this.CACHE_KEY_POSTS)) || {}; if (!cache[this.topicId]) cache[this.topicId] = []; // On stocke le texte brut cache[this.topicId].unshift(rawText); cache[this.topicId] = cache[this.topicId].slice(0, this.MAX_CACHED_POSTS_PER_TOPIC); localStorage.setItem(this.CACHE_KEY_POSTS, JSON.stringify(cache)); } catch (e) { console.error("JVC Enhancer: Erreur de sauvegarde du cache.", e); } } loadUserPostsFromCache() { try { const cache = JSON.parse(localStorage.getItem(this.CACHE_KEY_POSTS)) || {}; const posts = cache[this.topicId] || []; // On transforme le texte brut en objets {text, html} return posts.map(post => ({ text: this.normalizeText(post), html: this.normalizeHtml(post) })); } catch (e) { console.error("JVC Enhancer: Erreur de lecture du cache.", e); return []; } } isUserMentioned(author, el) { if (!this.currentUser || author === this.currentUser) return false; const content = el.querySelector('.txt-msg'); if (!content) return false; const normText = this.normalizeText(content.textContent); if (normText.includes(this.normalizeText(this.currentUser))) return true; for (const quote of el.querySelectorAll('blockquote.blockquote-jv')) { const cleanQuoteHtml = this.normalizeHtml(quote.innerHTML); if (this.currentUserMessages.some(myMsg => { // Si mon message a du texte, on compare le texte if (myMsg.text && this.normalizeText(quote.textContent).includes(myMsg.text)) return true; // Si mon message n'a que du HTML (sticker), on compare le HTML if (!myMsg.text && myMsg.html && cleanQuoteHtml.includes(myMsg.html)) return true; return false; })) { return true; } } return false; } normalizeText(text) { return text ? text.toLowerCase().replace(/\s+/g, ' ').trim() : ''; } normalizeHtml(html) { if (!html) return ''; // CORRECTION: La regex ne supprime que la date, pas le <p> return html.replace(/Le .+? :<br>/i, '').replace(/\s+/g, ' ').trim(); } getCurrentUsername() { return document.querySelector('.headerAccount__pseudo')?.textContent.trim() || null; } injectStyles() { const styles = ` .jvc-enhancer-mention { background-color: rgba(230, 195, 92, 0.07); border-left: 2px solid rgba(230, 195, 92, 0.75); } .jvc-enhancer-op-mention { background-color: rgba(157, 105, 212, 0.07); border-left: 2px solid rgba(157, 105, 212, 0.75); } .jvc-enhancer-self { box-shadow: inset 0 0 0 1px rgba(128, 128, 128, 0.2); } html:not(.theme-light) .jvc-enhancer-self { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1); } .jvc-enhancer-pemt { border-right-width: 2px; border-right-style: solid; } .jvc-enhancer-ddb-reported { background-color: rgba(220, 53, 69, 0.07) !important; } .jvc-enhancer-ddb-administered { background-color: rgba(25, 135, 84, 0.07) !important; } .bloc-avatar-msg { position: relative; } .jvc-enhancer-avatar-badge { position: absolute; bottom: -12px; left: 50%; transform: translateX(-50%); z-index: 2; padding: 2px 6px; font-size: 9px; font-weight: bold; border-radius: 5px; white-space: nowrap; text-transform: uppercase; letter-spacing: 0.5px; backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); border: 1px solid rgba(255, 255, 255, 0.1); box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); } .badge-mention { background-color: rgba(230, 195, 92, 0.4); color: #212529; } .badge-op-mention { background-color: rgba(157, 105, 212, 0.4); color: white; } .badge-op, .badge-self, .badge-pemt { background-color: rgba(80, 80, 80, 0.5); color: rgba(255, 255, 255, 0.8); } `; const styleSheet = document.createElement("style"); styleSheet.innerText = styles; document.head.appendChild(styleSheet); } processMessages() { const messages = Array.from(document.querySelectorAll('.bloc-message-forum')); messages.forEach(el => { const author = el.querySelector('.bloc-pseudo-msg')?.textContent.trim(); if (!author) return; const isOp = this.highlightRules[0].condition(author), isMention = this.highlightRules[1].condition(author, el), isSelf = this.highlightRules[2].condition(author); let hClass = '', bText = '', bClass = ''; if (isSelf) { hClass = 'jvc-enhancer-self'; bText = 'VOUS'; bClass = 'badge-self'; } else if (isOp && isMention) { hClass = 'jvc-enhancer-op-mention'; bText = 'OP (CITÉ)'; bClass = 'badge-op-mention'; } else if (isOp) { hClass = 'jvc-enhancer-op'; bText = 'OP'; bClass = 'badge-op'; } else if (isMention) { hClass = 'jvc-enhancer-mention'; bText = 'CITÉ'; bClass = 'badge-mention'; } if (hClass) el.classList.add(hClass); if (bText) this.createAvatarBadge(el, bText, bClass); }); this.processPEMT(messages); } processPEMT(messages) { let pemtGroupIndex = 0; for (let i = 0; i < messages.length - 1; i++) { const time = messages[i].querySelector('.bloc-date-msg a')?.textContent.match(/\d{2}:\d{2}:\d{2}$/)?.[0]; if (!time) continue; const group = [messages[i]]; for (let j = i + 1; j < messages.length; j++) { if (messages[j].querySelector('.bloc-date-msg a')?.textContent.match(/\d{2}:\d{2}:\d{2}$/)?.[0] === time) { group.push(messages[j]); } else { break; } } if (group.length > 1) { const color = this.pemtColors[pemtGroupIndex % this.pemtColors.length]; group.forEach(msg => { msg.classList.add('jvc-enhancer-pemt'); msg.style.borderRightColor = color; if (!msg.querySelector('.jvc-enhancer-avatar-badge')) { this.createAvatarBadge(msg, 'PEMT', 'badge-pemt'); } }); pemtGroupIndex++; i += group.length - 1; } } } createAvatarBadge(el, text, bClass, color = null) { if (el.querySelector(`.jvc-enhancer-avatar-badge`)) return; const container = el.querySelector('.bloc-avatar-msg'); if (!container) return; const badge = document.createElement('span'); badge.className = `jvc-enhancer-avatar-badge ${bClass}`; badge.textContent = text; if (color) badge.style.backgroundColor = `${color}66`; container.appendChild(badge); } async checkFirstMessagesReportStatus() { if (this.currentPage !== 1) return; const firstTwoMessages = Array.from(document.querySelectorAll('.bloc-message-forum')).slice(0, 2); const ddbCache = JSON.parse(sessionStorage.getItem(this.CACHE_KEY_DDB)) || {}; for (const messageElement of firstTwoMessages) { const reportIcon = messageElement.querySelector('.picto-msg-exclam'); const messageId = messageElement.dataset.id; if (!reportIcon || !messageId) continue; if (ddbCache[messageId]) { this.applyDdbHighlight(messageElement, ddbCache[messageId]); continue; } const selectorUrl = reportIcon.dataset.selector; if (!selectorUrl) continue; const reportUrl = new URL(selectorUrl, "https://www.jeuxvideo.com").href; try { const responseText = await this.fetchUrlContent(reportUrl); let status = 'clean'; if (responseText.includes("Ce contenu a déjà été signalé")) status = 'reported'; else if (responseText.includes("Ce contenu a déjà été modéré")) status = 'administered'; if (status !== 'clean') { ddbCache[messageId] = status; this.applyDdbHighlight(messageElement, status); } } catch (error) { console.error(`JVC Enhancer: Erreur DDB pour msg ${messageId}`, error); } } sessionStorage.setItem(this.CACHE_KEY_DDB, JSON.stringify(ddbCache)); } applyDdbHighlight(messageElement, status) { messageElement.classList.remove('jvc-enhancer-mention', 'jvc-enhancer-op-mention'); if (status === 'reported') { messageElement.classList.add('jvc-enhancer-ddb-reported'); } else if (status === 'administered') { messageElement.classList.add('jvc-enhancer-ddb-administered'); } } } new JVCTopicEnhancer(); })();