// ==UserScript== // @name LLM多站点 // @namespace http://tampermonkey.net/ // @version 1.0.1 // @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 // @downloadURL none // ==/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, 3]; let site = 0; 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; // 发送端 let masterId = ""; if(site === MAIN_SITE){ setInterval(masterCheckNew, 2000); } function masterCheckNew(){ masterId = getChatId(); for(let sourceId of activeSites){ let bindIdJson = getGV("slaveId-"+sourceId); if(!isEmpty(bindIdJson)){ let slaveId = bindIdJson.slaveId; let bindMasterId = bindIdJson.masterId; console.log("bindIdJson: "+JSON.stringify(bindIdJson)); if(masterId === bindMasterId){ // masterId和slaveId的绑定关系的json,是追加还是新建写入 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)); } } } } 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, sites: activeSites }; console.log(message); setGV("question", message); } // 接收端(deepseek) if(activeSites.includes(site)){ setInterval(function(){ let msg = getGV("question"); console.log('接收:', msg); receiveNew(msg); }, 2000); }; function receiveNew(msg){ let curSlaveId = getChatId(); if(curSlaveId.length < 12){ curSlaveId = ""; } let questionBeforeJump = getS("questionBeforeJump"); // 如果是经跳转而来,无需处理主节点信息,直接从缓存取对话内容 if(!isEmpty(questionBeforeJump)){ let splitContents = questionBeforeJump.split(SPLIT_CHAR); let cachedQuestion = splitContents[0]; let cachedMasterId = splitContents[1]; let cachedSlaveId = splitContents[2]; let lastQuestion = getS("lastQuestion"); if(!isEmpty(cachedQuestion) && cachedQuestion !== lastQuestion){ // 清空跳转用的缓存 setS("questionBeforeJump", ""); abstractSend(site, cachedQuestion); bindIdPair(cachedMasterId); // 给lastQuestion缓存设值 setS("lastQuestion", cachedQuestion); } return; } if(!isEmpty(msg)){ let masterId = msg.masterId; let question = msg.question; let slaveIdJson = msg.slaveId; let slaveIdFlag = false; let mSlaveId = ""; // 是否传递了当前网站的slaveId if(!isEmpty(slaveIdJson)){ mSlaveId = JSON.parse(slaveIdJson)[site]; if(!isEmpty(mSlaveId)){ slaveIdFlag = true; } } let curIdFlag = !isEmpty(curSlaveId); let lastQuestion = getS("lastQuestion"); let targetUrl = ""; let sameQuestion = !isEmpty(lastQuestion) && question === lastQuestion; if(sameQuestion){ return; } // 下面的逻辑分支看着复杂,但根本是关于 slaveIdFlag 和 curIdFlag 的不同布尔值的分支 if(slaveIdFlag){ if(curIdFlag){ if(curSlaveId === mSlaveId){ if(!sameQuestion){ setS("lastQuestion", question); abstractSend(site, question); bindIdPair(masterId); } }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 + SPLIT_CHAR + getChatId()); 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; } } })();