ChatGPT Utils

Modifies the behavior of the chat interface on the OpenAI website

26.12.2022 itibariyledir. En son verisyonu görün.

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

You will need to install an extension such as Tampermonkey to install this script.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         ChatGPT Utils
// @description  Modifies the behavior of the chat interface on the OpenAI website
// @namespace    ChatGPTUtils
// @version      1.7.4
// @author       CriDos
// @match        https://chat.openai.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=chat.openai.com
// @require      https://cdnjs.cloudflare.com/ajax/libs/he/1.2.0/he.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js
// @grant        GM_xmlhttpRequest
// @run-at       document-end
// @license      MIT
// ==/UserScript==

'use strict';

console.log(`ChatGPT Utils initializing...`);

let debug = true;

setInterval(() => {
    try {
        addAutoTranslate();
        //addTranslateButtons();
    } catch (error) {
        console.error(error);
    }

    try {
        findAndHookTextareaElement();
    } catch (error) {
        console.error(error);
    }
}, 100);

function addAutoTranslate() {
    var messages = document.querySelectorAll(".markdown.prose");

    for (var i = 0; i < messages.length; i++) {
        const msgMarkdownNode = messages[i];
        const parentMsgMarkdown = msgMarkdownNode.parentElement;

        if (parentMsgMarkdown.isAutoTranslate) {
            continue;
        }
        parentMsgMarkdown.isAutoTranslate = true;

        setInterval(async () => {
            await translateNode(msgMarkdownNode);
        }, 500);
    }
}

function addTranslateButtons() {
    var messages = document.querySelectorAll(".markdown.prose");

    for (var i = 0; i < messages.length; i++) {
        const msgMarkdownNode = messages[i];
        const msgIcon = msgMarkdownNode.parentElement.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;";
            msgIcon.insertBefore(btn, msgIcon.firstChild);

            btn.addEventListener("click", async () => {
                await translateNode(msgMarkdownNode);
            });
        }
    }
}

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 translate(request, "ru", "en", "text");

        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 translateNode(node) {
    const translateClassName = "translate-markdown";
    const parentNode = node.parentElement;
    const nodeContent = $(node).html();

    if (node.storeContent == nodeContent) {
        return;
    }
    node.storeContent = nodeContent;

    var translateNode = parentNode.querySelector(`.${translateClassName}`);
    if (translateNode == null) {
        translateNode = node.cloneNode(true);
        translateNode.classList.add(translateClassName);
        parentNode.insertBefore(translateNode, parentNode.firstChild);
    }

    var translatedContent = await translateHTML(nodeContent, "en", navigator.language)
    translatedContent = translatedContent.replace(/<\/div>$/, '');

    $(translateNode).html(translatedContent + `<p>.......... конец_перевода ..........</p></div>`);
}

async function translateHTML(html, sLang, tLang) {
    const excludeTagRegex = /<(pre|code)[^>]*>([\s\S]*?)<\/(pre|code)>/g;
    const excludeTags = [];
    const excludePlaceholder = 'E0X1C2L3U4D5E';

    let htmlContent = html;

    let excludeTagsMatch;
    while (excludeTagsMatch = excludeTagRegex.exec(html)) {
        excludeTags.push(excludeTagsMatch[0]);
        htmlContent = htmlContent.replace(excludeTagsMatch[0], `${excludePlaceholder}${excludeTags.length - 1}`);
    }

    if (debug) {
        console.log(`preTranslateHTML: ${html}`);
    }

    htmlContent = await translate(htmlContent, sLang, tLang, "html", true);

    for (let i = 0; i < excludeTags.length; i++) {
        htmlContent = htmlContent.replace(`${excludePlaceholder}${i}`, excludeTags[i]);
    }

    if (debug) {
        console.log(`postTranslateHTML: ${htmlContent}`);
    }

    return htmlContent;
}

async function translate(text, sLang, tLang, encode, format) {
    try {
        if (debug) {
            console.log(`preTranslate: ${text}`);
        }

        if (encode === true) {
            text = he.encode(text);
        }

        if (format == null) {
            format = "text";
        }

        const result = await new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "POST",
                url: "https://translate.google.com/translate_a/single",
                data: `format=${format}&client=gtx&sl=${sLang}&tl=${tLang}&dt=t&q=${encodeURIComponent(text)}`,
                headers: {
                    "Content-Type": "application/x-www-form-urlencoded",
                    // "Authorization": `Bearer ${API_KEY}`
                },
                onload: response => {
                    const translatedText = JSON.parse(response.responseText)[0]
                        .map(text => text[0])
                        .join(" ");

                    resolve(translatedText);
                },
                onerror: response => {
                    reject(response.statusText);
                }
            });
        });

        if (debug) {
            console.log(`postTranslate: ${result}`);
        }

        return result;
    } catch (error) {
        console.error(error);
    }
}