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