Clean Clipboard

ตรวจ clipboard ทุก 3 วิ, ทำงานเฉพาะเมื่อ tab โฟกัส, ถ้ามีข้อความไทยเกิน 100 ตัว → clean และคัดลอกกลับ (เลือกเก็บ format หรือไม่) + ตรวจจับข้อความใหม่

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Clean Clipboard
// @namespace    http://tampermonkey.net/
// @version      1.5
// @description  ตรวจ clipboard ทุก 3 วิ, ทำงานเฉพาะเมื่อ tab โฟกัส, ถ้ามีข้อความไทยเกิน 100 ตัว → clean และคัดลอกกลับ (เลือกเก็บ format หรือไม่) + ตรวจจับข้อความใหม่
// @author       You
// @match        https://*/*
// @grant        GM_setClipboard
// @license      MIT
// ==/UserScript==
/* eslint-disable no-multi-spaces */
(function () {
    'use strict';
    /** ================== CONFIG ================== */
    const CONFIG = {
        DEBUG: false,           // ปิดเปิดฟังก์ชันดีบัก
        THAI_LIMIT: 10,         // จำนวนขั้นต่ำของอักษรไทย
        CHINESE_LIMIT: 300,     // ถ้าจำนวนอักษรจีนเกินไม่ต้องทำงาน
        CHECK_INTERVAL: 1000,   // ตรวจเช็คทุกๆ x วินาที
        KEEP_FORMAT: false,     // true = เก็บ format, false = plain text only

        BEEP_FREQUENCY: 3000,   // Hz
        BEEP_DURATION: 0.3,     // s
        BEEP_VOLUME: 0.1,      // 0.0-1
        ENABLE_SOUND: true      // เปิดเสียงแจ้งเตือน
    };
    /* ================== STATE ================== */
    let audioCtx = null;
    let audioUnlocked = false;

    /** ================== LOGGER OVERLAY ================== */
    function createLoggerOverlay() {
        const box = document.createElement('div');
        box.id = 'clipboard-logger';
        box.style.cssText = `
            position: fixed; bottom: 0; left: 0; width: 100%; max-height: 2%;
            overflow-y: auto; font-size: 6px; background: rgba(0,0,0,0.85);
            color: #0f0; padding: 6px; z-index: 2147483646;
            white-space: pre-wrap; font-family: monospace;
        `;
        document.body.appendChild(box);

        const clearBtn = document.createElement('button');
        clearBtn.textContent = '🧹 Clear';
        clearBtn.style.cssText = `
            position: absolute; right: 5px; top: 5px;
            background: #222; color: #fff; border: none;
            padding: 2px 6px; font-size: 12px; cursor: pointer;
        `;
        clearBtn.addEventListener('click', () => { box.textContent = ""; box.appendChild(clearBtn); });
        box.appendChild(clearBtn);

        return box;
    }

    const loggerBox = CONFIG.DEBUG ? createLoggerOverlay() : null;

    function log(msg) {
        if (!CONFIG.DEBUG) return;
        console.log(msg);
        if (loggerBox) {
            const line = document.createElement('div');
            line.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`;
            loggerBox.appendChild(line);
            loggerBox.scrollTop = loggerBox.scrollHeight;
        }
    }

    /** ================== UTILITIES ================== */
    function stripHTML(text) {
        return text.replace(/]+(>|$)/g, "");
    }
    function cleanText(text) {
        return text.replace(/\n\s*\n/g, "\n").trim();
    }
    function countThaiChars(text) {
        const matches = text.match(/[\u0E00-\u0E7F]/g);
        return matches ? matches.length : 0;
    }
    function countChineseChars(text){
        const matches = text.match(/[\u4e00-\u9fff]/g);
        return matches ? matches.length : 0;
    }
    /* ================== SOUND ================== */
    function setupAudioUnlock() {
        const unlock = () => {
            try {
                if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
                if (audioCtx.state === "suspended") {
                    audioCtx.resume().then(() => { audioUnlocked = true; });
                } else {
                    audioUnlocked = true;
                }
            } catch (e) {
                log(`⚠️ setupAudioUnlock: ${e.message}`, "warn");
            }
            document.removeEventListener("click", unlock, true);
            document.removeEventListener("keydown", unlock, true);
            document.removeEventListener("touchstart", unlock, { capture: true, passive: true });
        };
        document.addEventListener("click", unlock, true);
        document.addEventListener("keydown", unlock, true);
        document.addEventListener("touchstart", unlock, { capture: true, passive: true });
    }

    function beep(freq = CONFIG.BEEP_FREQUENCY) {
        if (!CONFIG.ENABLE_SOUND) return;
        try {
            if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
            if (audioCtx.state === "suspended" && !audioUnlocked) {
                log("🔇 ยังไม่ได้ปลดล็อคเสียง (ต้องแตะจอ/คีย์บอร์ดก่อน)", "warn");
                return;
            }
            const osc = audioCtx.createOscillator();
            const gain = audioCtx.createGain();
            osc.type = "sine";
            osc.frequency.value = freq;
            gain.gain.value = CONFIG.BEEP_VOLUME;
            osc.connect(gain);
            gain.connect(audioCtx.destination);
            osc.start();
            osc.stop(audioCtx.currentTime + CONFIG.BEEP_DURATION);
        } catch (e) {
            log(`❌ beep error: ${e.message}`, "error");
        }
    }
    /** ================== MAIN ================== */
    let lastRaw = "";   // ข้อความดิบครั้งก่อน
    let lastClean = ""; // ข้อความที่ clean ครั้งก่อน
    async function checkClipboard() {
        try {
            if (!document.hasFocus()) return;
            const raw = await navigator.clipboard.readText();
            if (!raw || raw === lastRaw) return; // ไม่เปลี่ยน → ไม่ทำงานซ้ำ
            // ✅ ถ้าไม่มีขึ้นบรรทัดใหม่เลย ไม่ต้องทำงาน
//             if (!raw.includes("\n")) {
//                 log("⏩ ข้าม: ไม่มีขึ้นบรรทัดใหม่");
//                 return;
//             }
            lastRaw = raw;
            const rawCount = raw.length;
            const chineseCount = countChineseChars(raw);
            const thaiCount = countThaiChars(raw);

            log(`CN: ${chineseCount}  TH: ${thaiCount}`);

            if (chineseCount > CONFIG.CHINESE_LIMIT) return;// ถ้าจำนวนอักษรจีนเกินไม่ต้องทำงาน

            if (thaiCount > CONFIG.THAI_LIMIT) {
                const clean = cleanText(stripHTML(raw));
                if (clean && clean !== lastClean) {
                    if (CONFIG.KEEP_FORMAT) {
                        GM_setClipboard(clean, "text");
                        log(`Copied with FORMAT kept (${rawCount} chars)`);
                        beep();
                    } else {
                        try {
                            await navigator.clipboard.writeText(clean);
                            log(`Copied as PLAIN text (${rawCount} chars)`);
                            beep();
                        } catch (e) {
                            GM_setClipboard(clean);
                            log(`Copied as PLAIN text (fallback)`);
                            beep();
                        }
                    }
                    lastClean = clean;
                }
            }
        } catch (err) {
            log("⚠️ Clipboard read failed: " + err.message);
        }
    }
    /** ================== LOOP ================== */
    setInterval(checkClipboard, CONFIG.CHECK_INTERVAL);
})();