Twitter DM Cleaner

One-click remove all the potential harassment spams in twitter's direct messages area.

当前为 2024-07-24 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Twitter DM Cleaner
// @homepage     https://github.com/daymade/Twitter-DM-Cleaner
// @namespace    https://greasyfork.org/users/1121182
// @version      0.6.1
// @author       daymade
// @license      MIT
// @description  One-click remove all the potential harassment spams in twitter's direct messages area.
// @description:zh-CN 在Twitter私信中识别并高亮显示可能的骚扰信息,一键批量删除这些对话。
// @match        https://x.com/messages
// @match        https://x.com/messages/*
// @match        https://x.com/messages/requests
// @run-at       document-end
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function() {
    'use strict';

    // 添加 Tampermonkey 菜单选项
    GM_registerMenuCommand("批量删除骚扰消息", () => {
        const highlightedConversations = document.querySelectorAll('[data-testid="conversation"][data-highlighted="true"]');
        const highlightedCount = highlightedConversations.length;
        const confirmed = confirm(`检测到 ${highlightedCount} 条已标记为骚扰的消息。是否批量删除?`);

        if (confirmed) {
            const deleteChoice = prompt("选择要删除的消息数量:1, 10, 或 全部", "全部");
            if (deleteChoice === "1") {
                bulkDeleteHarassmentMessages(1);
            } else if (deleteChoice === "10") {
                bulkDeleteHarassmentMessages(10);
            } else if (deleteChoice.toLowerCase() === "全部") {
                bulkDeleteHarassmentMessages(Infinity);
            }
        }
    });

    // 白名单存储
    const WHITELIST_STORAGE_KEY = 'harassmentWhitelist';

    // 初始化或获取白名单
    function getWhitelist() {
        return GM_getValue(WHITELIST_STORAGE_KEY, []);
    }

    // 添加用户到白名单
    function addToWhitelist(screenname) {
        const whitelist = getWhitelist();
        if (!whitelist.includes(screenname)) {
            whitelist.push(screenname);
            GM_setValue(WHITELIST_STORAGE_KEY, whitelist);
        }
    }

    // 判断是否在白名单中
    function isInWhitelist(screenname) {
        const whitelist = getWhitelist();
        return whitelist.includes(screenname);
    }

    // 判断是否为潜在骚扰消息
    function isPotentialHarassment(screenname) {
        if (isInWhitelist(screenname)) {
            return false;
        }

        // 移除 @ 符号
        screenname = screenname.replace('@', '');

        const totalLength = screenname.length;
        const digitCount = (screenname.match(/\d/g) || []).length;
        const letterCount = (screenname.match(/[a-zA-Z]/g) || []).length;
        const specialCharCount = totalLength - digitCount - letterCount;

        // 计算各种字符的比例
        const digitRatio = digitCount / totalLength;
        const letterRatio = letterCount / totalLength;
        const specialCharRatio = specialCharCount / totalLength;

        // 检查是否存在连续的数字
        const hasConsecutiveDigits = /\d{4,}/.test(screenname);

        // 检查是否存在年份样式的数字(如2020, 2021等)
        const hasYearLikeNumber = /(?:19|20)\d{2}/.test(screenname);

        // 检查是否存在过多的大写字母
        const uppercaseRatio = (screenname.match(/[A-Z]/g) || []).length / letterCount;

        // 评分系统
        let score = 0;

        if (digitRatio > 0.3) score += 2;
        if (specialCharRatio > 0.1) score += 1;
        if (hasConsecutiveDigits) score += 2;
        if (hasYearLikeNumber) score -= 1;
        if (uppercaseRatio > 0.5) score += 1;
        if (totalLength > 15) score += 1;

        // 如果用户名中包含常见的名字,减少分数
        const commonNames = ['peter', 'lin', 'andrew', 'adams', 'ollie', 'denise', 'nahum'];
        if (commonNames.some(name => screenname.toLowerCase().includes(name))) {
            score -= 1;
        }

        return score >= 3;
    }

    // 高亮潜在骚扰消息
    function highlightHarassmentMessages() {
        const conversations = document.querySelectorAll('[data-testid="conversation"]');

        conversations.forEach(conversation => {
            const textElements = conversation.querySelectorAll('div[dir="ltr"]');
            const messageElement = conversation.querySelector('span[data-testid="tweetText"]');

            if (textElements.length >= 3) {
                const name = textElements[0].textContent.trim();
                const screenName = textElements[2].textContent.trim().replace('@', '');
                const message = messageElement?.textContent.trim() || 'non-text-message';

                // 避免重复高亮
                if (conversation.dataset.highlighted) return;

                // 判断是否为潜在骚扰消息
                const isHarassment = isPotentialHarassment(screenName);
                console.log(`User: ${name}, Screen name: ${screenName}, Message: ${message}, IsHarassment: ${isHarassment}`);

                if (isHarassment) {
                    console.log(`Highlighting conversation for user ${name} because screenname "${screenName}" is all lowercase.`);
                    conversation.style.opacity = '0.2';
                    conversation.style.backgroundColor = '#f0f0f0';
                    conversation.dataset.highlighted = 'true'; // 标记为已高亮

                    // 添加白名单按钮
                    const whitelistButton = document.createElement('button');
                    whitelistButton.textContent = "添加到白名单";
                    whitelistButton.style.marginRight = "10px";
                    whitelistButton.style.width = "106px";
                    whitelistButton.onclick = () => {
                        addToWhitelist(screenName);
                        conversation.style.opacity = '1';
                        conversation.style.backgroundColor = '';
                        conversation.dataset.highlighted = '';
                    };
                    conversation.appendChild(whitelistButton);
                }
            } else {
                console.log("Skipping conversation due to insufficient text elements or missing message element.");
            }
        });
    }

    // 批量删除骚扰私信
    function bulkDeleteHarassmentMessages(deleteCount) {
        const conversations = document.querySelectorAll('[data-testid="conversation"][data-highlighted="true"]');
        let deletedCount = 0;

        conversations.forEach(conversation => {
            if (deletedCount < deleteCount) {
                deleteConversation(conversation);
                deletedCount++;
            }
        });
    }

    // 删除私信
    function deleteConversation(conversation) {
        // 模拟点击 X 按钮
        const optionsButton = conversation.querySelector('button[aria-label="Options menu"]');
        if (optionsButton) {
            optionsButton.click();

            // 等待弹出菜单出现再点击删除
            setTimeout(() => {
                const deleteButton = Array.from(document.querySelectorAll('div[role="menuitem"]'))
                    .find(item => item.textContent.includes('Delete conversation') ||
                        item.textContent.includes('删除对话'));
                if (deleteButton) {
                    deleteButton.click();
                }
            }, 2000);
        }
    }

    // 监听页面变化,能高亮新收到的私信
    function observePageChanges() {
        const observer = new MutationObserver((mutations) => {
            for (let mutation of mutations) {
                if (mutation.type === 'childList') {
                    highlightHarassmentMessages();
                }
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    // 初始化
    function init() {
        highlightHarassmentMessages();
        observePageChanges();
    }

    // 等待页面加载完成后执行
    window.addEventListener('load', init);
})();