Filtre automatiquement un topic pour ne garder que les messages de l'auteur. Version adaptée à la MAJ de jvc de 2026. Code en partie repris de https://jvscript.fr/script/jvchat-premium (JVChat Premium FORK by Rand0max).
// ==UserScript==
// @name OP Only 2026
// @namespace OP Only 2026
// @author navet-navrant
// @description Filtre automatiquement un topic pour ne garder que les messages de l'auteur. Version adaptée à la MAJ de jvc de 2026. Code en partie repris de https://jvscript.fr/script/jvchat-premium (JVChat Premium FORK by Rand0max).
// @version 1.0.0
// @license MIT
// @match https://www.jeuxvideo.com/forums/42-*
// ==/UserScript==
// Délai d'attente entre le chargement de deux pages (ms)
const DELAY = 2500;
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function parseURL(url) {
const regex = /^(.*?)(\/\d+-\d+-\d+-)(\d+)(-\d+-\d+-\d+-)(.*?)(\.htm)(.*)$/i;
const [_, domain, ids, page, nums, title, htm, anchor] = url.match(regex);
return { domain: domain, ids: ids, page: page, nums: nums, title: title, htm: htm, anchor: anchor };
}
function buildURL(dict) {
return `${dict.domain}${dict.ids}${dict.page}${dict.nums}${dict.title}${dict.htm}${dict.anchor}`;
}
function jvCake(cls) {
const base16 = '0A12B34C56D78E9F';
const s = cls.split(' ')[1];
let link = '';
for (let i = 0; i < s.length; i += 2) {
link += String.fromCharCode(base16.indexOf(s.charAt(i)) * 16 + base16.indexOf(s.charAt(i + 1)));
}
return link;
}
function decodeJvCare(doc) {
const jvcares = doc.getElementsByClassName('JvCare');
for (let i = jvcares.length - 1; i >= 0; i--) {
const jvcare = jvcares[i];
const a = doc.createElement('a');
a.setAttribute('target', '_blank');
a.setAttribute('href', jvCake(jvcare.getAttribute('class')));
a.innerHTML = jvcare.innerHTML;
const otherClasses = jvcare.getAttribute('class').split(' ').slice(2).join(' ');
if (otherClasses) a.classList.add(...otherClasses.split(' ').filter(Boolean));
jvcare.replaceWith(a);
}
}
function getOPPseudo(doc) {
return doc.querySelector('#listMessages .messageUser__label')?.textContent.trim();
}
function getLastPage(document) {
const spans = document.querySelectorAll('.pagination__item, .pagination__button, .pagination__navigation a, .pagination__navigation span');
let lastPage = 1;
for (let span of spans) {
const txt = span.textContent.trim();
if (/^\d+$/.test(txt)) {
const page = parseInt(txt);
if (page > lastPage) {
lastPage = page;
}
}
}
return lastPage;
}
async function fetchPage(dict) {
const res = await fetch(buildURL(dict));
const html = await res.text();
const doc = new DOMParser().parseFromString(html, 'text/html');
decodeJvCare(doc);
return doc;
}
function expandNestedQuotes(msgEl) {
msgEl.querySelectorAll('.message__blockquote').forEach(bq => {
bq.classList.add('message__blockquote--visible');
});
msgEl.querySelectorAll('.message__collapsedQuote').forEach(btn => {
btn.remove();
});
}
function addMessageLink(msgEl, pageDict) {
const msgId = msgEl.id?.replace('message-', '');
if (!msgId) return;
const anchoredDict = { ...pageDict, anchor: `#post_${msgId}` };
const messageURL = buildURL(anchoredDict);
const linkIcon = document.createElement('a');
linkIcon.href = messageURL;
linkIcon.target = '_blank';
linkIcon.rel = 'noopener noreferrer';
linkIcon.title = 'Voir le message dans son contexte';
linkIcon.className = 'oponly-message-link';
linkIcon.textContent = '↗';
const card = msgEl.querySelector('.messageUser__card') || msgEl;
card.appendChild(linkIcon);
}
function restoreLinks(msgEl) {
msgEl.querySelectorAll('a').forEach(a => {
if (a.classList.contains('messageUser__label') ||
a.classList.contains('messageUser__date') ||
a.classList.contains('messageUser__dateEditLink') ||
a.classList.contains('avatar')) {
return;
}
a.style.pointerEvents = 'auto';
a.style.cursor = 'pointer';
if (!a.querySelector('img')) {
a.style.color = '#3690e0';
}
});
}
function addOPMessages(doc, OP, container, pageDict) {
let count = 0;
doc.querySelectorAll('#listMessages .messageUser').forEach(msg => {
const pseudo = msg.querySelector('.messageUser__label')?.textContent.trim();
if (pseudo === OP) {
const clone = msg.cloneNode(true);
clone.querySelector('.messageUser__headerActions')?.remove();
expandNestedQuotes(clone);
restoreLinks(clone);
addMessageLink(clone, pageDict);
container.appendChild(clone);
count++;
}
});
return count;
}
function showStatus(text, loading = true) {
const paginationTop = document.querySelector('.js-pagination-top');
const paginationBot = document.querySelector('.container__pagination:not(.js-pagination-top)');
let statusEl = document.querySelector('#oponly-status');
if (!statusEl) {
if (paginationTop) paginationTop.style.display = 'none';
if (paginationBot) paginationBot.style.display = 'none';
const container = document.querySelector('#listMessages');
if (!container) return;
container.insertAdjacentHTML('beforebegin', `<div id='oponly-status' style='padding: 0.5rem 1rem; font-weight: bold;'></div>`);
statusEl = document.querySelector('#oponly-status');
}
statusEl.innerHTML = loading
? `<span class='oponly-spinner'></span><span>${text}</span>`
: `<span>${text}</span>`;
}
async function triggerOpOnly(event) {
const clickedBtn = event.currentTarget;
clickedBtn.classList.add('btn-oponly--activated');
clickedBtn.disabled = true;
try {
let topicUrl = document.URL;
let urlToFetch = parseURL(topicUrl);
urlToFetch.page = 1;
urlToFetch.anchor = '';
const container = document.querySelector('#listMessages');
if (!container) return;
container.innerHTML = '';
showStatus('Chargement page 1...');
const firstPageDoc = await fetchPage(urlToFetch);
const OP = getOPPseudo(firstPageDoc);
if (!OP) {
showStatus('Impossible de récupérer l\'auteur du topic.', false);
clickedBtn.classList.remove('btn-oponly--activated');
clickedBtn.disabled = false;
return;
}
const lastPage = getLastPage(firstPageDoc);
let opMessageCount = addOPMessages(firstPageDoc, OP, container, urlToFetch);
showStatus(`1 sur ${lastPage} pages chargées - ${opMessageCount} messages de l'OP récupérés`, lastPage > 1);
for (let page = 2; page <= lastPage; page++) {
await sleep(DELAY);
urlToFetch.page = page;
const doc = await fetchPage(urlToFetch);
opMessageCount += addOPMessages(doc, OP, container, urlToFetch);
const isLast = page === lastPage;
showStatus(`${page} sur ${lastPage} pages chargées - ${opMessageCount} messages de l'OP récupérés`, !isLast);
}
} catch (e) {
alert(`OPOnly error : ${e.message}`);
console.error('OPOnly error : ', e.message);
clickedBtn.classList.remove('btn-oponly--activated');
clickedBtn.disabled = false;
}
}
function initializeButton() {
let css = `<style type='text/css'>
.btn-oponly.buttonsNavbar__button {
font-weight: bold;
}
.btn-oponly.buttonsNavbar__button.btn-oponly--activated {
color: #f66031;
cursor: not-allowed;
}
#oponly-status {
display: flex;
align-items: center;
gap: 0.5rem;
}
.oponly-spinner {
width: 14px;
height: 14px;
border: 2px solid #ccc;
border-top-color: #ff6333;
border-radius: 50%;
animation: oponly-spin 0.7s linear infinite;
}
@keyframes oponly-spin {
to { transform: rotate(360deg); }
}
.messageUser__card {
position: relative;
}
.oponly-message-link {
position: absolute;
top: 1rem;
right: 1rem;
text-decoration: none;
cursor: pointer;
font-size: 1.5rem;
opacity: 0.6;
}
.oponly-message-link:hover {
opacity: 1;
}
@media (min-width: 1000px) and (max-width: 1199px) {
.buttonsNavbar {
padding-left: 0;
padding-right: 0;
}
.icon-refresh + .buttonsNavbar__label {
display : none;
}
}
</style>`
document.head.insertAdjacentHTML('beforeend', css);
const opOnlyButton = `<span class='buttonsNavbar__space'></span><button class='buttonsNavbar__button btn-oponly' type='button'><i class='buttonsNavbar__icon icon-comments'></i><div class='buttonsNavbar__label'>OP</div></button>`;
const buttonsNavbar = document.querySelectorAll('.buttonsNavbar');
for (let bloc of buttonsNavbar) {
const refreshBtn = bloc.querySelector('.icon-refresh')?.closest('button');
refreshBtn.insertAdjacentHTML('afterend', opOnlyButton);
}
let buttons = document.getElementsByClassName('btn-oponly');
for (let btn of buttons) {
btn.addEventListener('click', triggerOpOnly);
}
}
function main() {
if (document.querySelector('.buttonsNavbar')) {
initializeButton();
return;
}
let observer = new MutationObserver(function (mutations, obs) {
if (document.querySelector('.buttonsNavbar')) {
obs.disconnect();
initializeButton();
}
});
observer.observe(document.body, { childList: true, subtree: true });
setTimeout(() => observer.disconnect(), 5000);
}
main();