推特 - 翻译机

2025/9/6 13:12:58

// ==UserScript==
// @name        推特 - 翻译机
// @namespace   Violentmonkey Scripts
// @match       *://x.com/*
// @version     1.0
// @author      -
// @description 2025/9/6 13:12:58
// @grant       GM_xmlhttpRequest
// @grant       GM_getValue
// @grant       GM_setValue
// ==/UserScript==
const max_concurrent = 2; // 并发限制
const queue = [];
let cur = 0;
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
function translation(raw) {
    return new Promise((resolve) => {
        const task = async () => {
            cur++;

            let attempts = 0;
            let result;

            while (attempts < 3) {  // 最多重试三次
                attempts++;
                try {
                    result = await big_model(raw);
                    if (!result.error) {
                        break;  // 成功,跳出重试
                    }
                } catch (e) {
                    // 出现异常也算一次重试
                }

                if (attempts < 3) {
                    await sleep(1000); // 失败后延迟1秒再重试
                }
            }

            resolve(result);
            cur--;

            if (queue.length && cur < max_concurrent) {
                const next = queue.shift();
                next();
            }
        };

        if (cur < max_concurrent) {
            task();
        } else {
            queue.push(task);
        }
    });
}
function big_model(raw) {
    return new Promise((resolve) => {
        GM_xmlhttpRequest({
            method: "POST",
            url: "https://open.bigmodel.cn/api/paas/v4/chat/completions",
            headers: {
                "Content-Type": "application/json",
                "Authorization": "Bearer " + window.big_token
            },
            data: JSON.stringify({
                "model": "GLM-4.5-Flash",
                "temperature": 0,
                "messages": [
                    { "role": "system", "content": "翻译为中文,不要解释或包含其他内容" },
                    { "role": "user", "content": raw }
                ],
                "thinking": { "type": "disabled" }
            }),
            timeout: 10000,
            onload: function (response) {
                try {
                    const data = JSON.parse(response.responseText);
                    if (data.choices?.[0]?.message) {
                        resolve({ "error": false, "msg": data.choices[0].message.content, raw });
                    } else {
                        resolve({ "error": true, "msg": data, raw });
                    }
                } catch (e) {
                    resolve({ "error": true, "msg": e, raw });
                }
            },
            onerror: function (err) {
                resolve({ "error": true, "msg": err, raw });
            },
            ontimeout: function () {
                resolve({ "error": true, "msg": "timeout", raw });
            }
        });
    });
}
function observe() {
    const observer = new MutationObserver(mutations => {
        for (const mutation of mutations) {
            for (const node of mutation.addedNodes) {
                if (!(node instanceof HTMLElement)) continue;
                if (node.matches?.('div[dir="auto"][lang]')) {
                    handle(node);
                }
                const descendants = node.querySelectorAll?.('div[dir="auto"][lang]');
                if (descendants?.length) {
                    descendants.forEach(handle);
                }
            }
        }
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });
}
function handle(e) {
    if ("raw" in e) return;
    const text = raw(e);
    e.raw = text;
    if (!filter(text) && e.raw) {
        translation(text).then(r => {
            if (!r.error || r.error) {
                const p = document.createElement('p');
                p.textContent = "\n\n-----------智谱4.5Flash翻译-----------\n\n\n";
                p.style.fontFamily = "'Microsoft YaHei', 微软雅黑, sans-serif";
                e.appendChild(p);
                // 译文
                const div = document.createElement('div');
                div.textContent = to(r.msg);
                div.style.fontFamily = "'Microsoft YaHei', 微软雅黑, sans-serif";
                e.appendChild(div);
            }
        })
    }
    function raw(e) {
        e.querySelectorAll('img[alt]').forEach(img => {
            const emoji = img.alt;
            const textNode = document.createTextNode(emoji);
            img.replaceWith(textNode);
        });
        return e.textContent;
    }
    function filter(text) {
        const chinese_regex = /[\u4e00-\u9fff]/;
        const japanese_regex = /[\u3040-\u30ff]/;
        const contains_chinese = chinese_regex.test(text);
        const contains_japanese = japanese_regex.test(text);
        return contains_chinese && !contains_japanese;
    }
    function to(value) {
        if (value === null || value === undefined) {
            return String(value);
        }
        if (typeof value === 'object') {
            try {
                return JSON.stringify(value);
            } catch (e) {
                return String(value);
            }
        }
        return String(value);
    }
}
(function main() {
    window['big_token'] = GM_getValue("big_token", null)
    if (big_token == null) {
        big_token = prompt("请填写智谱Token:")
        if (big_token.length != 0) {
            GM_setValue("big_token", big_token)
        } else {
            alert("如果没有请禁用,避免无意义弹窗");
        }
    }
    document.querySelectorAll('div[dir="auto"][lang]').forEach(handle);
    observe();
})();