您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
提高效率
当前为
// ==UserScript== // @name 大模型多站点 // @namespace http://tampermonkey.net/ // @version 1.1.4 // @description 提高效率 // @author wz // @match https://www.kimi.com/* // @match https://chat.deepseek.com/* // @match https://www.tongyi.com/* // @match https://chatgpt.com/* // @match https://www.doubao.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 T = "tool-"; const QUEUE = "tool-queue"; const LEN = "len"; const LAST_Q = "lastQ"; const UID_KEY = "uid"; const SPLIT_CHAR = ",,,"; const MAX_QUEUE = 3; let MAIN_SITE = 0; let site = 0; let url = window.location.href; const keywords = { "kimi": 0, "deepseek": 1, "tongyi": 2, "chatgpt": 3, "doubao": 4 }; 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/", 4: "https://www.doubao.com/chat/" } const newSites = { 0: "https://www.kimi.com/", 1: "https://chat.deepseek.com/", 2: "https://www.tongyi.com/", 3: "https://chatgpt.com/", 4: "https://www.doubao.com/chat" } 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='; if(url.indexOf(mark) === -1){ return ""; } let tmp = url.lastIndexOf(mark) + mark.length; return url.substring(tmp); }else if(site === 3){ if(subStr.indexOf("auto") > -1){ return ""; } return subStr; }else if(site === 4){ if(subStr.indexOf("local") > -1){ return ""; } return subStr; }else{ return subStr; } } function getUrl(){ return window.location.href; } // 队列头部添加元素 function enqueue(element) { let queue = JSON.parse(localStorage.getItem(QUEUE) || "[]"); if (queue.length > 0 && queue[0] === element) { return; } queue.unshift(element); localStorage.setItem(QUEUE, JSON.stringify(queue)); } // 当队列长度超过阈值,删除队尾元素 function dequeue() { let queue = JSON.parse(localStorage.getItem(QUEUE) || "[]"); let len = queue.length; if(len > MAX_QUEUE){ let removeKey = T + queue[len - 1]; let aa = "uid-" +JSON.parse(getS(removeKey)).uid; console.log("remove uid"+aa); localStorage.removeItem(aa); localStorage.removeItem(removeKey); queue.pop(); localStorage.setItem(QUEUE, JSON.stringify(queue)); } } function hgetS(key, jsonKey){ let json = localStorage.getItem(key); if(isEmpty(json)){ return ""; } json = JSON.parse(json); return json[jsonKey]; } function hsetS(key, jsonKey, val){ let json = JSON.parse(localStorage.getItem(key) || "{}"); json[jsonKey] = val; localStorage.setItem(key, JSON.stringify(json)); } function getS(key){ return localStorage.getItem(key); } function setS(key, val){ localStorage.setItem(key, val); } function setGV(key, value){ GM_setValue(key, value); } function getGV(key){ return GM_getValue(key); } let hasChatId = false; let lock = false; // setInterval(function(){ // masterCheckNew(); // receiveNew(); // }, 3000); setTimeout(function(){ setInterval(function(){ masterCheckNew(); receiveNew(); }, 4000); }, 1000); function getQuestionList(){ let questions = []; if(site == 0){ questions = document.getElementsByClassName("user-content"); }else if(site === 1){ let scrollable = document.getElementsByClassName("scrollable")[1]; if(!isEmpty(scrollable)){ let list = scrollable.firstElementChild.firstElementChild.children let elementsArray = Array.from(list); questions = elementsArray.filter((item, index) => index % 2 === 0); } }else if(site === 2){ questions = document.querySelectorAll('[class^="bubble-"]'); }else if(site === 3){ questions = document.querySelectorAll('[data-message-author-role="user"]'); }else if(site === 4){ let list = document.querySelectorAll('[data-testid="message_text_content"]'); let elementsArray = Array.from(list); questions = elementsArray.filter((item, index) => index % 2 === 0); } return questions; } // 发送端 let masterId = ""; function masterCheckNew(){ let questions = getQuestionList(); let lenNext = questions.length; if(lenNext > 0){ masterId = getChatId(); let len = hgetS(T + masterId, LEN) || 0; // 由于作为从节点时send后未更新缓存的len,导致这里会比缓存len大,但由于加了相等性判断,没有实际影响。 // 但如果提到的send(click)失败,会导致“相等性”失效,继续往下执行 console.log("lenNext: "+lenNext+", len: "+len); if(lenNext - len === 1){ console.log("master lock "+lock); if(lock){ return; } let lastestQ = questions[lenNext - 1].textContent; let lastQuestion = hgetS(T + masterId, LAST_Q); if(!isEmpty(lastQuestion) && lastestQ === lastQuestion){ return; } masterReq(masterId, lastestQ); hasChatId = true; hsetS(T + masterId, LEN, lenNext); } } }; function masterReq(masterId, lastestQ){ let uid = hgetS(T + masterId, UID_KEY); if(isEmpty(uid)){ uid = guid(); hsetS(T + masterId, UID_KEY, uid); } let message = { uid: uid, question: lastestQ }; console.log(new Date()); console.log(message); setGV("msg", message); hsetS(T + masterId, LAST_Q, lastestQ); let uidJson = getGV(uid); // 若json非空,则其中一定有首次提问的主节点的信息; // 故json若空则必为首次,只有首次会走如下逻辑 if(isEmpty(uidJson)){ uidJson = {}; uidJson[site] = masterId; console.log("master print uidJson: "+JSON.stringify(uidJson)); setGV(uid, uidJson); // 存储管理(删除与添加) dequeue(); enqueue(masterId); } } function receiveNew(){ let curSlaveId = getChatId(); if(curSlaveId.length < 12){ curSlaveId = ""; } let msg = getGV("msg"); if(isEmpty(msg)){ return; } if(lock){ return; } let question = msg.question; let lastQuestion = hgetS(T + curSlaveId, LAST_Q); let sameQuestion = !isEmpty(lastQuestion) && question === lastQuestion; console.log(new Date()); console.log("question: "+question+", lastQuestion: "+lastQuestion); if(sameQuestion){ return; } let questionBeforeJump = getS("questionBeforeJump"); // 如果是经跳转而来,无需处理主节点信息,直接从缓存取对话内容 if(!isEmpty(questionBeforeJump)){ console.log("questionBeforeJump: " + questionBeforeJump); let splits = questionBeforeJump.split(SPLIT_CHAR); let cachedQuestion = splits[0]; let cachedUid = splits[1]; let cachedSlaveId = ""; if(!isEmpty(curSlaveId)){ cachedSlaveId = splits[2]; if(curSlaveId !== cachedSlaveId){ return; } hsetS(T + curSlaveId, LAST_Q, cachedQuestion); } // 清空跳转用的缓存 setS("questionBeforeJump", ""); console.log("h1 send"); abstractSend(site, cachedQuestion, cachedSlaveId); if(isEmpty(curSlaveId)){ setUid(cachedUid, cachedQuestion); } return; } let uid = msg.uid; // 当前空,且之前chatId有值,则认为是手动打开的页面(若是从节点跟随跳转新页面的情况,前面已经拦截处理了) if(isEmpty(curSlaveId)){ if(hasChatId){ return; } }else{ hasChatId = true; } let targetUrl = ""; let slaveIdFlag = false; let slaveId = ""; let uidJson = getGV(uid); let lastQuestionOfComingSlaveId = ""; // 来者消息的uid,是否关联了从节点的chatId? if(!isEmpty(uidJson)){ console.log("uidJson " + JSON.stringify(uidJson)); slaveId = uidJson[site]; lastQuestionOfComingSlaveId = hgetS(T + slaveId, LAST_Q); console.log("lastQuestionOfComingSlaveId "+lastQuestionOfComingSlaveId); if(question === lastQuestionOfComingSlaveId){ return; } if(!isEmpty(slaveId)){ slaveIdFlag = true; } } let curIdFlag = !isEmpty(curSlaveId); // 从节点已进行过来者的uid对应的对话 if(slaveIdFlag){ // 当前页面有chatId if(curIdFlag){ // chatId相同则对话,不同则跳转 if(curSlaveId === slaveId){ if(!sameQuestion){ hsetS(T + curSlaveId, LAST_Q, question); console.log("h2 send"); abstractSend(site, question, curSlaveId); } }else{ targetUrl = historySites[site] + slaveId; } // 当前页面是空白,需跳转 }else{ targetUrl = historySites[site] + slaveId; } // 对从节点而言是新对话 }else{ // 当前页面有chatId,则跳转空白页 if(curIdFlag){ targetUrl = newSites[site]; // 当前页面已经是空白页 }else{ console.log("h3 send"); abstractSend(site, question, ""); setUid(uid, question); } } if(!isEmpty(targetUrl)){ setS("questionBeforeJump", question + SPLIT_CHAR + uid + SPLIT_CHAR + slaveId); window.location.href = targetUrl; } } function setUid(uid, question){ let intervalId; let lastUrl = getUrl(); let count = 0; let gap = 100; console.log("ready to setUid"); intervalId = setInterval(function() { count ++; if(count > 10000 / gap){ clearInterval(intervalId); } let currentUrl = getUrl(); if (currentUrl !== lastUrl) { let chatId = getChatId(); hasChatId = true; let uidJson = getGV(uid); if(!isEmpty(uidJson)){ if(isEmpty(uidJson[site])){ uidJson[site] = chatId; } }else{ uidJson = {}; uidJson[site] = chatId; } hsetS(T + chatId, LAST_Q, question); console.log("slave print uidJson: "+JSON.stringify(uidJson)); setGV(uid, uidJson); setS("uid-" + uid, JSON.stringify(uidJson)); lock = false; console.log("setUid lock "+lock); hsetS(T + chatId, UID_KEY, uid); // 存储管理(删除与添加) dequeue(); enqueue(chatId); clearInterval(intervalId); } }, gap); } function abstractSend(site, content, chatId){ let intervalId; let count = 0; let gap = 100; lock = true; intervalId = setInterval(function() { count ++; if(count > 5000 / gap){ clearInterval(intervalId); } const textarea = getTextArea(site); if (!isEmpty(textarea)) { clearInterval(intervalId); sendContent(textarea, site, content, chatId); } }, gap); } function sendContent(textarea, site, content, chatId){ textarea.focus(); document.execCommand('insertText', false, content); clickAndCheckLen(site, chatId); } function clickAndCheckLen(site, chatId) { const checkGap = 100; const maxCheckTries = 50; // 最多尝试5秒 let tryCount = 0; const sendBtnCheckInterval = setInterval(() => { let sendBtn = getBtn(site); if (sendBtn) { clearInterval(sendBtnCheckInterval); // 停止轮询 sendBtn.click(); checkQuestionList(site, chatId, checkGap, maxCheckTries); } else { tryCount++; if (tryCount > maxCheckTries) { clearInterval(sendBtnCheckInterval); console.warn("sendBtn未找到,超时"); return; } } }, checkGap); } function checkQuestionList(site, chatId, checkGap, maxCheckTries) { let tryCount = 0; let cachedLen = hgetS(T + chatId, LEN); let newFlag = isEmpty(chatId) || isEmpty(cachedLen) || cachedLen === 0; const checkInterval = setInterval(() => { tryCount++; // 定时器:检查问题列表长度大于上次,则停止,并设置lock // 注意,若是chat首个问题,则只要求len=1 let len = getQuestionList().length; let questionDisplayFlag = false; if(newFlag){ if(len === 1){ questionDisplayFlag = true; } }else{ if(len > cachedLen){ questionDisplayFlag = true; } } if (questionDisplayFlag) { clearInterval(checkInterval); setTimeout(function(){ lock = false; // 解锁 hsetS(T + getChatId(), LEN, len); console.log("abstractSend unlock"); // 其实只有对话首个问题需要延时,是为了确保此时有chatId,这个待优化 }, 300); } else if (tryCount > maxCheckTries) { clearInterval(checkInterval); lock = false; console.warn("问题列表长度未符合判据,超时"); } }, checkGap); } function getTextArea(site){ if(site == 0){ return document.getElementsByClassName('chat-input-editor')[0]; }else if(site === 1){ return document.getElementById('chat-input'); }else if([2, 4].includes(site)){ 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'); }else if(site === 4){ return document.getElementById('flow-end-msg-send'); } } function isEmpty(item){ if(item===null || item===undefined || item.length===0 || item === "null"){ return true; }else{ return false; } } function guid() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } })();