Respawn Lot Message Delete

Suppression msg lot

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Respawn Lot Message Delete
// @version      5.9.2
// @description  Suppression msg lot
// @namespace    Respawn Lot Message Delete
// @author       Craftbukkit debug par ROMANCE_DAWN adapt for payload
// @require      https://update.greasyfork.org/scripts/578578/1827981/JVCGetPayLoad.js
// @icon         https://images.emojiterra.com/microsoft/fluent-emoji/15.1/128px/1f5d1_color.png
// @match        https://www.jeuxvideo.com/profil/*?mode=historique_forum*
// @license MIT
// ==/UserScript==

//Global Variables
let nMessageAlreadyDelete;
let nMessageNonDelete;
let nMessageDelete;
let freshHash;

//Show Update UI
function hydrateUI(nPage) {
    document.getElementById("papage").innerHTML = `
            <b>Vidage Message :</b><br>
            Message supprimé : ${nMessageDelete} <br>
            Message déjà supprimé : ${nMessageAlreadyDelete} <br>
            Message non supprimé : ${nMessageNonDelete} <br>
            Page n°${nPage}<br><br>`;
}

const sleep = ms => new Promise(r => setTimeout(r, ms));

const monthList = ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"];


//Fetch 403 / 429 - JSON / HTML
async function fetchGetPage403(url) {
    const jsonHeaders = { 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' };
    for (let attempt = 1; attempt <= 6; attempt++) {
        await sleep(500);
        const response = await fetch(url, { headers : jsonHeaders });
        if (response.status === 403 || response.status === 429) {
            console.log(`Rate limit ${response.status}, attente 3s (essai ${attempt}/6)`);
            await sleep(3000);
            continue;
        }
        return response;
    }
    return null;
}

// Get_Hash_By_Fetch
async function fechGetHash() {
    const response = await fetch('https://www.jeuxvideo.com/forums/0-36-0-1-0-1-0-guerre-des-consoles.htm');
    const data = await response.text();
    const jsonData = getJsonPayloadRaw(data);
    return jsonData?.ajaxModerationToken;
}

//REQUEST DELETE MESSAGE
async function requestDeleteMessage(idMessages, retryOnce = true) {
    const reponse = await fetch(`https://www.jeuxvideo.com/forums/message/delete?ids=${idMessages.join(',')}&type=delete&ajax_hash=${freshHash}`, { method: 'POST' });
    if (!reponse.ok && retryOnce) {
        freshHash = await fechGetHash();
        return requestDeleteMessage(idMessages, false);
    }
    return reponse;
}

async function videPage(nPage, data) {
    //Lecture champs User
    const [yearMin, monthMin, dayMin] = document.getElementById('mindate').value.split('-');
    const [yearMax, monthMax, dayMax] = document.getElementById('maxdate').value.split('-');
    let minDate = new Date(yearMin, monthMin - 1, dayMin).getTime(); //TimeStamp Min
    let maxDate = new Date(yearMax, monthMax - 1, dayMax).getTime(); //TimeStamp Max


    let jsonData;
    try { 
        jsonData = JSON.parse(data); //Json from serv
    } catch { 
        jsonData = getJsonPayloadRaw(data); //FallBack Html Payload
    }

    const messages = jsonData?.listMessage ?? [];

    const messageToDelete = [];
    for (const message of messages) {
        if (message.stateMessage === 'msg-visible') {
            const idMessage = message.id;

            // publishedDate format: "19 sptembre 1999 à 00:00:00"
            const [day, monthStr, year] = message.publishedDate.split(' ');
            const messageTimestamp = new Date(year, monthList.indexOf(monthStr), day).getTime();

            if (!monthList.includes(monthStr)) throw new Error('Mois invalide => Annulation'); //Guard

            // DATES MATCH => DELETE
            if (messageTimestamp >= minDate && messageTimestamp <= maxDate) {
                /*
                const deletedMessage = await requestDeleteMessage([idMessage]);
                if (!deletedMessage?.ok) {
                    throw new Error(`Échec suppression message ${idMessage}`);
                }
                nMessageDelete++;
                */
                messageToDelete.push(idMessage);
            } else {
                nMessageNonDelete++;
            }
        } else {
            nMessageAlreadyDelete++;
        }
    }

    // SUPPRESSION EN UNE SEULE REQUETTE POUR EVITER DE SPAM
    if (messageToDelete.length) {
        const deletedMessage = await requestDeleteMessage(messageToDelete);
        if (!deletedMessage?.ok) {
            throw new Error(`Échec suppression de (${messageToDelete.length} messages)`);
        }
        nMessageDelete += messageToDelete.length;
    }
    hydrateUI(nPage);

    const nextPage = jsonData?.pagerView?.next?.url;
    if (nextPage) {
        console.log(nextPage);
        const response = await fetchGetPage403(nextPage);
        const nextData = await response.text();
        await videPage(nPage + 1, nextData);
    } else {
        alert('Dernière Page Traité')
    }
}

document.querySelector(".titre-bloc").insertAdjacentHTML('beforeend', `
    <div style="font-size: 1.2rem;"><br>  -> Du : 
    <input type="date" id="mindate" value="1970-01-01" class="form-control" style="width: 230px; font-weight: 600; font-size: 1rem; display: inline-flex; margin: 5px;">
     au 
    <input type="date" id="maxdate" value="1998-01-01" class="form-control" style="width: 230px; font-weight: 600; font-size: 1rem; display: inline-flex; margin: 5px;">
    <button class="btn icon-bin" title="Tout supprimer" id="viderAll" style="display: inline-flex; align-items: center; font-size: 1.1rem; height: 2.2rem;">Vider</button></div>`);

document.getElementById("viderAll").onclick = async function() {
    if (confirm("Le script va commencer de la page en cours\n/!\ Êtes-vous sûr de vouloir tout supprimer ? /!\\ ")) {

        this.style.display = 'none';
        document.getElementById('forum-main-col').style.display = 'none';
        document.getElementById('mindate').disabled = true;
        document.getElementById('maxdate').disabled = true;

        nMessageDelete = 0;
        nMessageAlreadyDelete = 0;
        nMessageNonDelete = 0;
        const nPage = 1;

        document.querySelector(".titre-head-bloc").insertAdjacentHTML("afterend", "<span id='papage' style='display: block;'></span>");
        hydrateUI(nPage);

        //Get hash from forum (Le hash de suppression nest plus dans lhisto on va le chercher sur les forums).
        freshHash = await fechGetHash();

        const startUrl = location.href;

        const response = await fetchGetPage403(startUrl);
        const startData = await response.text();
        await videPage(nPage, startData);
    }
};