Greasy Fork

Greasy Fork is available in English.

LLM多站点

提高效率

当前为 2025-05-28 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         LLM多站点
// @namespace    http://tampermonkey.net/
// @version      1.0.3
// @description  提高效率
// @author       wz
// @match        https://www.kimi.com/*
// @match        https://chat.deepseek.com/*
// @match        https://www.tongyi.com/*
// @match        https://chatgpt.com/*
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @license      GPL-3.0-only
// ==/UserScript==

(function () {
    'use strict';
    console.log("ai script, start");

    const CACHE_PREFIX = "tool-";
    const SPLIT_CHAR = ",,,";
    const url = window.location.href;

    let MAIN_SITE = 0;
    let activeSites = [1, 2];
    let site = 0;
    let lock = false;

    const keywords = {
        "kimi": 0,
        "deepseek": 1,
        "tongyi": 2,
        "chatgpt": 3
    };
    for (const keyword in keywords) {
        if (url.indexOf(keyword) > -1) {
            site = keywords[keyword];
            break;
        }
    }

    const historySites = {
        0: "https://www.kimi.com/chat/",
        1: "https://chat.deepseek.com/a/chat/s/",
        2: "https://www.tongyi.com/?sessionId=",
        3: "https://chatgpt.com/c/"
    }
    const newSites = {
        0: "https://www.kimi.com/",
        1: "https://chat.deepseek.com/",
        2: "https://www.tongyi.com/",
        3: "https://chatgpt.com/"
    }

    function getChatId(){
        let url = getUrl();
        let subStr = url.substring(url.lastIndexOf('/') + 1);
        // console.log("subStr: "+subStr);
        if(isEmpty(subStr)){
            return "";
        }
        if(site === 2){
            let mark = 'sessionId=';
            let tmp = url.lastIndexOf(mark) + mark.length;
            return url.substring(tmp);
        }else{
            return subStr;
        }
    }

    function getUrl(){
        return window.location.href;
    }

    function getS(key){
        return localStorage.getItem(key);
    }
    function setS(key, val){
        return localStorage.setItem(key, val);
    }
    function setGV(key, value){
        GM_setValue(key, value);
    }
    function getGV(key){
        return GM_getValue(key);
    }


    let len = 0;

    // setInterval(function(){
    //     masterCheckNew();
    //     receiveNew();
    // }, 2000);

    // 发送端
    let masterId = "";
    if(site === MAIN_SITE){
        setInterval(masterCheckNew, 2000);
    }
    // 接收端
    if(activeSites.includes(site)){
        setInterval(receiveNew, 2000);
    };

    function masterCheckNew(){
        masterId = getChatId();
        let bindPrefix = "bind-" + masterId + "-";

        // 不同sourceId的bindJson的key不同,挨个读取处理
        for(let sourceId of activeSites){
            // 如果当前masterId的该slaveId已处理过,则跳过
            let bindKey = bindPrefix + sourceId;
            if(getS(bindKey)){
                continue;
            }

            let bindIdJson = getGV("slaveId-"+sourceId);
            if(!isEmpty(bindIdJson)){
                let slaveId = bindIdJson.slaveId;
                let bindMasterId = bindIdJson.masterId;
                console.log("bindIdJson: "+JSON.stringify(bindIdJson));

                // 来者的masterId与当前masterId相同吗?不同则无视
                if(masterId === bindMasterId){
                    // 相同则取出绑定关系的json,空则new一个写入,非空且不存在该slaveId则写入
                    let oldJson = getS(bindMasterId);
                    if(!isEmpty(oldJson)){
                        oldJson = JSON.parse(oldJson);
                        console.log("masterId:"+bindMasterId+", oldJson: "+JSON.stringify(oldJson));
                        let specificSlaveId = oldJson[sourceId];
                        if(isEmpty(specificSlaveId)){
                            oldJson[sourceId] = slaveId;
                            setS(bindMasterId, JSON.stringify(oldJson));
                        }
                    }else{
                        oldJson = {};
                        oldJson[sourceId] = slaveId;
                        setS(bindMasterId, JSON.stringify(oldJson));
                    }
                    // bind关系已完成写入,下次不再读取GV内容
                    setS(bindKey, true);
                }

            }
        }
        let questions = [];
        if(site == 0){
            questions = document.getElementsByClassName("user-content");
        }else if(site == 1){
            // let scrollable = document.getElementsByClassName("scrollable")[1];
            // if(!isEmpty(scrollable)){
            //     questions = new Array(Math.floor(scrollable.firstElementChild.firstElementChild.children.length / 2));
            // }else{
            //     questions = [];
            // }
        }else if(site == 2){
            questions = document.querySelectorAll('[class^="bubble-"]');
        }
        let lenNext = questions.length;
        if(lenNext > 0){
            len = getS(CACHE_PREFIX + masterId);
            if(lenNext > len){
                let lastQ = questions[lenNext - 1];
                masterReq(masterId, lastQ);
                setS(CACHE_PREFIX + masterId, lenNext);
            }
        }
    };

    function masterReq(masterId, lastQ){
        let slaveIdJson = getS(masterId);
        var message = {
            masterId: masterId,
            question: lastQ.textContent,
            slaveId: slaveIdJson,
            site: site
        };
        console.log(message);
        setGV("question", message);
    }


    function receiveNew(){
        let curSlaveId = getChatId();
        if(curSlaveId.length < 12){
            curSlaveId = "";
        }

        let questionBeforeJump = getS("questionBeforeJump");

        // 如果是经跳转而来,无需处理主节点信息,直接从缓存取对话内容
        if(!isEmpty(questionBeforeJump)){
            console.log("questionBeforeJump: " + questionBeforeJump);
            let splits = questionBeforeJump.split(SPLIT_CHAR);
            let cachedQuestion = splits[0];
            let cachedMasterId = splits[1];

            let lastQuestion = getS("lastQuestion");

            if(!isEmpty(cachedQuestion) && cachedQuestion !== lastQuestion){
                // 清空跳转用的缓存
                setS("questionBeforeJump", "");
                abstractSend(site, cachedQuestion);

                if(isEmpty(curSlaveId)){
                    bindIdPair(cachedMasterId);
                }

                // 给lastQuestion缓存设值
                setS("lastQuestion", cachedQuestion);
            }
            return;
        }

        // 读取主节点信息并处理
        let msg = getGV("question");
        if(!isEmpty(msg)){
            let masterId = msg.masterId;
            let question = msg.question;

            // 如果问题已经问过,不再处理
            let lastQuestion = getS("lastQuestion");
            let sameQuestion = !isEmpty(lastQuestion) && question === lastQuestion;
            if(sameQuestion){
                return;
            }

            console.log("slave msg: " + msg);
            let slaveIdFlag = false;
            let mSlaveId = "";
            let slaveIdJson = msg.slaveId;
            // 是否传递了当前网站的slaveId
            if(!isEmpty(slaveIdJson)){
                mSlaveId = JSON.parse(slaveIdJson)[site];
                if(!isEmpty(mSlaveId)){
                    slaveIdFlag = true;
                }
            }
            let curIdFlag = !isEmpty(curSlaveId);

            let targetUrl = "";
            // 下面的逻辑分支看着复杂,但根本是关于 slaveIdFlag 和 curIdFlag 的不同布尔值的分支
            if(slaveIdFlag){
                if(curIdFlag){
                    if(curSlaveId === mSlaveId){
                        if(!sameQuestion){
                            setS("lastQuestion", question);
                            abstractSend(site, question);
                        }
                    }else{
                        targetUrl = historySites[site] + mSlaveId;
                    }
                }else{
                    targetUrl = historySites[site] + mSlaveId;
                }
            }else{
                if(curIdFlag){
                    targetUrl = newSites[site];
                }else{
                    setS("lastQuestion", question);
                    abstractSend(site, question);

                    bindIdPair(masterId);
                }
            }

            if(!isEmpty(targetUrl)){
                setS("questionBeforeJump", question + SPLIT_CHAR + masterId);
                window.location.href = targetUrl;
            }

        }
    };

    function bindIdPair(masterId){
        let intervalId;
        let lastUrl = getUrl();
        let count = 0;
        let gap = 100;

        intervalId = setInterval(function() {
            count ++;
            if(count > 5000 / gap){
                clearInterval(intervalId);
            }
            let currentUrl = getUrl();
            if (currentUrl !== lastUrl) {
                let bindIdJson = {
                    slaveId: getChatId(),
                    masterId: masterId
                };
                console.log("set json: "+ JSON.stringify(bindIdJson));
                setGV('slaveId-'+site, bindIdJson);
                clearInterval(intervalId);
            }
        }, gap);

    }

    function abstractSend(site, content){
        let intervalId;
        let count = 0;
        let gap = 100;

        intervalId = setInterval(function() {
            count ++;
            if(count > 5000 / gap){
                clearInterval(intervalId);
            }
            let textarea = getTextArea(site);
            if (!isEmpty(textarea)) {
                textarea.focus();
                document.execCommand('insertText', false, content);
                setTimeout(function(){
                    let sendBtn = getBtn(site);
                    sendBtn.click();
                }, 100);
                clearInterval(intervalId);
            }
        }, gap);
    }

	function getTextArea(site){
        if(site == 0){
            return document.getElementsByClassName('chat-input-editor')[0];
        }else if(site === 1){
            return document.getElementById('chat-input');
        }else if(site === 2){
            return document.getElementsByTagName('textarea')[0];
        }else if(site === 3){
            return document.getElementById('prompt-textarea');
        }
	}
	function getBtn(site){
        if(site == 0){
            return document.getElementsByClassName('send-button-container')[0];
        }else if(site === 1){
            var btns = document.querySelectorAll('[role="button"]');
            return btns[btns.length - 1];
        }else if(site === 2){
            return document.querySelectorAll('[class^="operateBtn-"], [class*=" operateBtn-"]')[0];
        }else if(site === 3){
            return document.getElementById('composer-submit-button');
        }
	}


    function isEmpty(item){
        if(item===null || item===undefined || item.length===0 || item === "null"){
            return true;
        }else{
            return false;
        }
    }


})();