您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Modifies the behavior of the chat interface on the OpenAI website
当前为
// ==UserScript== // @name ChatGPT Utils // @description Modifies the behavior of the chat interface on the OpenAI website // @namespace ChatGPTUtils // @version 1.5.4 // @author CriDos // @match https://chat.openai.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=chat.openai.com // @grant GM_xmlhttpRequest // @run-at document-end // @license MIT // ==/UserScript== 'use strict'; console.log(`ChatGPT Utils initializing...`); setInterval(() => { try { addTranslateButtons(); } catch (error) { console.error(error); } try { findAndHookTextareaElement(); } catch (error) { console.error(error); } }, 100); function addTranslateButtons() { var messages = document.querySelectorAll(".markdown.prose"); for (var i = 0; i < messages.length; i++) { const msgMarkdown = messages[i]; const parentMsgMarkdown = msgMarkdown.parentElement; const msgIcon = parentMsgMarkdown.parentElement.parentElement.previousElementSibling; if (!msgIcon.querySelector(".translate-button")) { var btn = document.createElement("button"); btn.textContent = "Tr"; btn.classList.add("translate-button"); btn.style.cssText = "width: 30px; height: 30px;"; // margin-left: 8px; display: flex; flex-direction: column; msgIcon.insertBefore(btn, msgIcon.firstChild); // Add event listener for click event on translate button btn.addEventListener("click", () => { const translateClassName = "translate-markdown"; var translateNode = parentMsgMarkdown.querySelector(`.${translateClassName}`); if (translateNode) { parentMsgMarkdown.removeChild(translateNode); delete parentMsgMarkdown.translateNode; } translateNode = msgMarkdown.cloneNode(true); translateNode.classList.add(translateClassName); const htmlContent = translateNode.outerHTML; parentMsgMarkdown.translateNode = translateNode; parentMsgMarkdown.insertBefore(translateNode, parentMsgMarkdown.firstChild); translateHTML(htmlContent, "auto", navigator.language).then(translatedContent => { let index = translatedContent.lastIndexOf('</div>'); let before = translatedContent.slice(0, index); const endTranslate = `<p>.......... конец_перевода ..........</p>`; translatedContent = before.concat(endTranslate, '</div>'); //console.log("Translated content: " + translatedContent); translateNode.outerHTML = translatedContent; }); }); } } } function findAndHookTextareaElement() { const targetElement = document.querySelector("textarea"); if (targetElement === null) { return; } if (targetElement.isAddHookKeydownEvent === true) { return; } targetElement.isAddHookKeydownEvent = true; console.log(`Textarea element found. Adding keydown event listener.`); targetElement.addEventListener("keydown", async event => await handleSubmit(event, targetElement), true); } async function handleSubmit(event, targetElement) { console.log(`Keydown event detected: type - ${event.type}, key - ${event.key}`); if (event.shiftKey && event.key === "Enter") { return; } if (window.isActiveOnSubmit === true) { return; } if (event.key === "Enter") { window.isActiveOnSubmit = true; event.stopImmediatePropagation(); const request = targetElement.value; targetElement.value = ""; const translatedText = await translateText(request, "ru", "en"); targetElement.focus(); targetElement.value = translatedText; const enterEvent = new KeyboardEvent("keydown", { bubbles: true, cancelable: true, key: "Enter", code: "Enter" }); targetElement.dispatchEvent(enterEvent); window.isActiveOnSubmit = false; } } async function translateHTML(html, sLang, tLang) { const excludeTagRegex = /<(pre|code)[^>]*>([\s\S]*?)<\/(pre|code)>/g; const excludeTags = []; const excludePlaceholder = 'e0x'; const replaceTagRegex = /<[^>]*>/g; const replaceTags = []; const replacePlaceholder = 'r0e'; let translateHTML = html; let excludeTagsMatch; while (excludeTagsMatch = excludeTagRegex.exec(html)) { excludeTags.push(excludeTagsMatch[0]); translateHTML = translateHTML.replace(excludeTagsMatch[0], `[${excludePlaceholder}${excludeTags.length - 1}]`); } let replaceTagsMatch; while (replaceTagsMatch = replaceTagRegex.exec(html)) { replaceTags.push(replaceTagsMatch[0]); translateHTML = translateHTML.replace(replaceTagsMatch[0], `[${replacePlaceholder}${replaceTags.length - 1}]`); } console.log(`preTranslateHTML: ${html}`); translateHTML = await translateText(translateHTML, sLang, tLang); translateHTML = removeSpaces(translateHTML); for (let i = 0; i < replaceTags.length; i++) { translateHTML = translateHTML.replace(`[${replacePlaceholder}${i}]`, replaceTags[i]); } for (let i = 0; i < excludeTags.length; i++) { translateHTML = translateHTML.replace(`[${excludePlaceholder}${i}]`, excludeTags[i]); } console.log(`postTranslateHTML: ${translateHTML}`); return translateHTML; } async function translateText(text, sLang, tLang) { const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${sLang}&tl=${tLang}&dt=t&q=${encodeURIComponent(text)}`; console.log(`preTranslate: ${text}`); try { const response = await doXHR(url); const responseText = JSON.parse(response.responseText); let postTranslate = ""; responseText[0].forEach(part => { postTranslate += part[0]; }); console.log(`postTranslate: ${postTranslate}`); return postTranslate; } catch (error) { console.error(error); } } async function doXHR(url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open("GET", url); xhr.onload = () => resolve(xhr); xhr.onerror = () => reject(xhr.statusText); xhr.send(); }); } function removeSpaces(string) { // Use a regular expression to find [] blocks in the string const regex = /\[([^\[\]]*)\]/g; let result; // Keep searching for [] blocks until there are no more while ((result = regex.exec(string)) !== null) { // Replace all spaces within the block with an empty string string = string.replace(result[1], result[1].replace(/\s/g, '')); } return string; }