Douban Comment Deletion

Delete your comments in group posts

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Douban Comment Deletion
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Delete your comments in group posts
// @author       https://www.douban.com/people/seebyl (viasyla)
// @match        *://www.douban.com/group/topic/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @license MIT
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// ==/UserScript==

;(async () => {
    'use strict';

    // —— fetch/prompt userId + menu command ——

    async function fetchOrPromptUserId() {
        let stored = await GM_getValue('userId', null);
        if (!stored) {
            const input = prompt('🚀 请输入你的用户 ID:');
            if (input) {
                await GM_setValue('userId', input);
                return input;
            }
            return null;
        }
        return stored;
    }

    const userId = await fetchOrPromptUserId();

    GM_registerMenuCommand('✏️ 修改用户 ID', async () => {
        const newId = prompt('✏️ 重新输入你的用户 ID:', userId || '');
        if (newId) {
            await GM_setValue('userId', newId);
            location.reload();
        }
    });

    const box = document.createElement('div');
    box.textContent = `你的 ID = ${userId}`;
    Object.assign(box.style, {
        position: 'fixed',
        bottom: '10px',
        left: '10px',
        padding: '8px',
        background: 'rgba(0,0,0,0.6)',
        color: '#fff',
        borderRadius: '4px',
        zIndex: 9999,
    });
    document.body.appendChild(box);

    // —— now it’s safe to await every GM_… call ——

    console.log('Page Matched:', /\/group\/topic\//.test(window.location.href));
    const targetUserId = Number(await GM_getValue('userId', null));
    console.log('target user id:', targetUserId);

    const topicOpt = $('.topic-opt');
    const topicAdminOpts = $('.topic-admin-opts');
    const tid = location.href.match(/topic\/(\d+)\//)[1];
    const ck = get_cookie("ck");
    const pageStart = 0;

    // 顺手把ad关了
    $('#gdt-ad-container').remove();
    $('#dale_group_topic_inner_middle').remove();

    function randomDelay(min = 500, max = 1500) {
        return new Promise(resolve => setTimeout(resolve, Math.random() * (max - min) + min));
    }

    async function autoDeleteAllComments(p) {
        let hasNextPage = true;
        let pageCounter = p;
        while (hasNextPage) {
            await randomDelay();
            await delPageComment();
            hasNextPage = await gotoNextPage();
            topicAdminOpts.append(`<div>页码${pageCounter}处理完毕</div>`);
            pageCounter++;
        }
    }


    async function delPageComment() {
        let topicReply = $('.topic-reply li');
        for (let i = 0; i < topicReply.length; i++) {
            const comment = topicReply[i];
            const authorId = $(comment).data('author-id');
            // console.log('found id', authorId);

            // Check if the comment belongs to the target user
            if (authorId === targetUserId) {
                console.log('User id matched, delete it', authorId);
                await delComment(i, comment);
            }
        }
    }



    if (topicAdminOpts.children.length > 0) {
        topicAdminOpts.append(`
        <div id="auto-del-wrapper" style="display: flex; align-items: center; margin-top: 10px; gap: 8px;">
            <label for="page-start-input" style="font-weight:bold; color:#ff0000;">起始页:</label>
            <input
                type="number"
                id="page-start-input"
                placeholder="1"
                style="width: 60px; padding: 4px; border: 1px solid #ccc; border-radius: 4px;">
            <a
                id="auto-del"
                href="javascript:void(1);"
                style="padding: 6px 12px; background-color: #ff4d4f; color: white; border-radius: 4px; font-weight: bold; text-decoration: none;">
                自动删除我的评论
            </a>
        </div>
    `);

        $('#auto-del').click(async e => {
            e.stopImmediatePropagation();

            let pageStartInput = parseInt($('#page-start-input').val(), 10);
            let pageStart = (!isNaN(pageStartInput) && pageStartInput >= 1) ? pageStartInput : 1;

            console.log('Deletion start from page:', pageStart);

            await goToPage(pageStart);
            await autoDeleteAllComments(pageStart);
            topicAdminOpts.append(`<div>全部评论已删除。若意外中断,刷新后输入当前页码继续。</div>`);
            // setTimeout(() => location.reload(), 5000);
        });
    }



    function delComment(i, e) {
        return new Promise(function (resolve, reject) {
            let cid = $(e).data('cid')
            $.post(`/j/group/topic/${tid}/remove_comment`, {
                ck: ck,
                cid: cid
            }, function(){
                let targetText = $(e)[0].querySelector('.markdown').textContent.trim()
                topicAdminOpts.append(`<div>成功删除第${i+1}条评论:${targetText.substring(0, 20)}</div>`)
                resolve()
            })
        });
    }



    function gotoNextPage() {
        return new Promise((resolve) => {
            let nextLink = $('a:contains("后页")').attr('href');

            if (nextLink) {
                console.log('Next page link:', nextLink);
                $.ajax({
                    url: nextLink,
                    method: 'GET',
                    success: function(data) {
                        let newDom = $('<div></div>').html(data);

                        // ✅ 更新普通评论
                        $('#comments').html(newDom.find('#comments').html());

                        // ✅ 清除最赞评论区域(第一页才有)
                        $('#popular-comments').remove();
                        $('#content > div > div.article > h3').remove();

                        // ✅ 更新分页器
                        let newPaginator = newDom.find('.paginator');
                        if (newPaginator.length > 0) {
                            $('.paginator').html(newPaginator.html());
                        } else {
                            console.warn('新页面没有分页器');
                        }

                        resolve(true);
                    },
                    error: function() {
                        console.error('加载下一页失败');
                        resolve(false);
                    }
                });
            } else {
                console.log('Last page reached. Congrats!');
                resolve(false);
            }
        });
    }


    function goToPage(pageNum) {
        // 如果是第 1 页,直接 resolve,不跳转
        if (pageNum === 1) {
            console.log('Already at the 1st page.');
            $('#popular-comments').remove();
            $('#content > div > div.article > h3').remove();
            return Promise.resolve();
        }

        return new Promise((resolve, reject) => {
            let pageUrl = `/group/topic/${tid}/?start=${(pageNum - 1) * 100}`;
            console.log(`Jump to page ${pageNum}`, pageUrl);

            $.ajax({
                url: pageUrl,
                method: 'GET',
                success: function(data) {
                    let newDom = $('<div></div>').html(data);

                    // 更新评论列表
                    $('.topic-reply').html(newDom.find('.topic-reply').html());

                    // 更新分页导航栏
                    let newPaginator = newDom.find('.paginator');
                    if (newPaginator.length > 0) {
                        $('.paginator').html(newPaginator.html());
                    } else {
                        console.warn('目标页面没有分页器');
                    }

                    resolve();
                },
                error: function() {
                    console.error('跳转页面失败');
                    reject();
                }
            });
        });
    }

})();