본문 바로가기

[템퍼몽키] 개드립 댓글 클리너 v02

@Q002025. 6. 29. 21:22
// ==UserScript==
// @name         개드립 댓글 클리너
// @namespace    http://tampermonkey.net/
// @version      1.3.2
// @description  댓글 자동 삭제 + 개드립콘 포함 + 원문 표시 + 진행상황 표시 개선
// @author       Qoo
// @include      https://www.dogdrip.net/index.php?mid=front&act=dispMemberOwnComment*
// @include      https://www.dogdrip.net/index.php?act=dispMemberOwnComment*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const btn = document.createElement("button");
    btn.innerText = "댓글삭제";
    btn.style.position = "fixed";
    btn.style.top = "10px";
    btn.style.left = "10px";
    btn.style.zIndex = "9999";
    btn.style.padding = "10px";
    btn.style.backgroundColor = "#ff6600";
    btn.style.color = "#fff";
    btn.style.border = "none";
    btn.style.cursor = "pointer";
    document.body.appendChild(btn);

    const statusText = document.createElement("div");
    statusText.style.position = "fixed";
    statusText.style.top = "50px";
    statusText.style.left = "10px";
    statusText.style.zIndex = "9999";
    statusText.style.backgroundColor = "rgba(0,0,0,0.7)";
    statusText.style.color = "#fff";
    statusText.style.padding = "8px 12px";
    statusText.style.borderRadius = "4px";
    statusText.style.fontSize = "14px";
    statusText.textContent = "준비 완료";
    document.body.appendChild(statusText);

    const checkboxWrapper = document.createElement("label");
    checkboxWrapper.style.position = "fixed";
    checkboxWrapper.style.top = "90px";
    checkboxWrapper.style.left = "10px";
    checkboxWrapper.style.zIndex = "9999";
    checkboxWrapper.style.backgroundColor = "rgba(255,255,255,0.9)";
    checkboxWrapper.style.padding = "6px 10px";
    checkboxWrapper.style.borderRadius = "4px";
    checkboxWrapper.style.fontSize = "14px";
    checkboxWrapper.style.display = "inline-block";
    checkboxWrapper.style.cursor = "pointer";

    const checkbox = document.createElement("input");
    checkbox.type = "checkbox";
    checkbox.style.marginRight = "5px";

    const checkboxText = document.createTextNode("개드립콘 포함");
    checkboxWrapper.appendChild(checkbox);
    checkboxWrapper.appendChild(checkboxText);
    document.body.appendChild(checkboxWrapper);

    async function deleteComment(postNumber, commentNumber) {
        const tokenMeta = document.querySelector('meta[name="csrf-token"]');
        if (!tokenMeta) {
            statusText.textContent = "CSRF 토큰이 없습니다.";
            return false;
        }
        const token = tokenMeta.getAttribute('content');

        const deleteUrl = "https://www.dogdrip.net/";
        const bodyData = new URLSearchParams({
            _filter: 'delete_comment',
            error_return_url: '',
            act: 'procBoardDeleteComment',
            mid: 'free',
            page: 1,
            document_srl: postNumber,
            comment_srl: commentNumber,
            module: 'board',
            _rx_ajax_compat: 'XMLRPC',
            _rx_csrf_token: token,
        });

        try {
            const response = await fetch(deleteUrl, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                    'X-CSRF-Token': token,
                    'X-Requested-With': 'XMLHttpRequest',
                    'Accept': 'application/json, text/javascript, */*; q=0.01',
                },
                referrer: `https://www.dogdrip.net/index.php?mid=free&document_srl=${postNumber}&act=dispBoardDeleteComment&comment_srl=${commentNumber}`,
                referrerPolicy: 'no-referrer-when-downgrade',
                body: bodyData.toString(),
                credentials: 'include',
                mode: 'cors',
            });

            const text = await response.text();
            return text.includes("삭제가 완료되었습니다");
        } catch (error) {
            statusText.textContent = `오류: ${error}`;
            return false;
        }
    }

    async function deleteComments() {
        const baseUrl = "https://www.dogdrip.net/index.php?act=dispMemberOwnComment&mid=front&page=";
        let page = 1;
        let allComments = [];
        const includeDogdripcon = checkbox.checked;

        async function fetchPage(pageNumber) {
            const response = await fetch(`${baseUrl}${pageNumber}`);
            if (!response.ok) {
                statusText.textContent = `페이지 ${pageNumber} 로딩 실패`;
                return null;
            }
            const text = await response.text();
            return new DOMParser().parseFromString(text, "text/html");
        }

        async function getCommentsOnPage(pageNumber) {
            const doc = await fetchPage(pageNumber);
            if (!doc) return [];
            const rows = doc.querySelectorAll("table tbody tr td a");
            const comments = [];
            if (rows.length === 0) return null;

            for (let aTag of rows) {
                const content = aTag.textContent.trim();
                const href = aTag.getAttribute("href");
                if (!href || content === "[삭제 되었습니다]" || content === "삭제 된 글입니다.") continue;

                if (!includeDogdripcon) {
                    const html = aTag.innerHTML;
                    if (html.includes("@dogdrip") || html.includes("dogcon") || html.includes("emoticon")) continue;
                }

                const match = href.match(/^\/(\d+)\?comment_srl=(\d+)/);
                if (match) {
                    comments.push({ postNumber: match[1], commentNumber: match[2], content });
                }
            }
            return comments;
        }

        while (true) {
            statusText.textContent = `페이지 ${page} 댓글 수집 중...`;
            const comments = await getCommentsOnPage(page);
            if (!comments) break;
            allComments = allComments.concat(comments);
            page++;
            await new Promise(resolve => setTimeout(resolve, 1000));
        }

        if (allComments.length === 0) {
            statusText.textContent = "삭제할 댓글이 없습니다.";
            return;
        }

        let processed = 0;
        let deletedCount = 0;

        for (let { postNumber, commentNumber, content } of allComments) {
            processed++;
            statusText.textContent = `[${processed} / ${allComments.length}] "${content}" 댓글 삭제 진행중...`;
            const success = await deleteComment(postNumber, commentNumber);
            if (success) deletedCount++;
            await new Promise(resolve => setTimeout(resolve, 3000));
        }

        statusText.textContent = `모든 댓글 삭제 완료 (${deletedCount}/${allComments.length})`;
    }

    btn.addEventListener("click", deleteComments);
})();

 

 

크롬 템퍼몽키 확장프로그램을 까신 다음에 해당 코드를 복붙하시면 됩니다.

 

그럼 내가 쓴 댓글창에 

 

 

좌측상단 해당 UI가 생기게 됩니다.

 

개드립콘 포함 체크 여부에 따라서 개드립콘을 남기거나 함께 지울 수 있습니다.

 

댓글삭제 버튼을 누르게 되면 

댓글페이지를 차례대로 체크하면서 댓글을 수집합니다.

 

이후 댓글삭제 진행 상황이 텍스트로 표시되게 되며

 

 

마지막으로 삭제 완료로 끝나게 됩니다. 해당 스크립트가 작동중일때는 페이지를 이탈하지 말아주세요.

 

 

#개드립 지우개 #개드립 클리너

'Development' 카테고리의 다른 글

[템퍼몽키] 개드립 차단메모  (0) 2025.06.29
개드립 댓글 클리너  (0) 2024.12.29
목차