Greasy Fork

LLM多站点

提高效率

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

// ==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;
        }
    }


})();