Pass any tests 100% with the power of GPT in your browser

In today's world, where learning is becoming increasingly complex and tests are a real challenge for students and apprentices, as well as for beginners who work in companies where grading is highly developed, we are constantly looking for effective ways to make the process of acquiring knowledge easier. Especially for you, I am analyzing a powerful browser plugin that uses the capabilities of GPT to solve tests on any topic. This is not an innovative tool, but I have not found such examples of analysis on the Internet. In this article, we will tell you how this plugin works, what advantages it offers and how you can use it to achieve 100% results. Let's figure out how to make your studies easier and more effective with the help of this unique technology!

In my example I used the Chrome browser. Well, let's get started.

The plugin itself consists of 2 files end.js, which contain the main functionality of the plugin, and manifest.json, which contains instructions for sites and which uses our main file end.js.

The manifest.json file contains:

{
    "name": "Тест",
    "version": "1.0",
    "manifest_version": 3,
    "content_scripts": [
        {
            "matches": ["https://Здесь ссылка на сайт/*"],
            "js": [ "end.js" ]
        }
    ]	
}

In general, all that is required of us here is a mere trifle: specify your plugin name in name and the website address. I decided not to bother with manifest.json, since this is not some grandiose project, but an ordinary assistant in tests.

Now let's move on to the most delicious part – end.js. Especially for this article, I left maximum comments to the lines. The first function I decided to add to the site was a button, so that later I could hang a call to gpt on it.

// Добавление кнопки для отображения вопроса и отправки в GPT
function addButton() {
    const button = document.createElement("button");
    button.innerText = "Правильный ответ";
    button.style.position = "fixed"; // Фиксированное положение
    button.style.top = "10px"; // Позиция от верхнего края
    button.style.right = "60px"; // Позиция от правого края
    button.style.zIndex = 1000; // Чтобы кнопка была сверху
	
    // Создаем индикатор загрузки
    const loadingIndicator = document.createElement("span");
    loadingIndicator.style.position = "fixed"; // Фиксированное положение
    loadingIndicator.style.top = "10px"; // Позиция от верхнего края
    loadingIndicator.style.right = "60px"; // Позиция от правого края
    loadingIndicator.style.zIndex = 1000; // Чтобы кнопка была сверху
    loadingIndicator.innerText = "Загрузка...";
    loadingIndicator.style.display = "none"; // Скрываем индикатор по умолчанию
    loadingIndicator.style.marginLeft = "10px"; // Отступ от кнопки
    loadingIndicator.style.color = "red"; // Цвет текста индикатора

    let intervalId; // Переменная для хранения идентификатора интервала

    // Функция для обновления текста индикатора
    const updateLoadingText = () => {
        if (loadingIndicator.innerText === "Загрузка...") {
            loadingIndicator.innerText = "Загрузка.";
        } else if (loadingIndicator.innerText === "Загрузка.") {
            loadingIndicator.innerText = "Загрузка..";
        } else {
            loadingIndicator.innerText = "Загрузка...";
        }
    };

    // Добавляем обработчик событий для нажатия кнопки
    button.onclick = async () => {
        loadingIndicator.style.display = "inline"; // Показываем индикатор загрузки
        button.style.display = "none"; // Скрываем кнопку
        intervalId = setInterval(updateLoadingText, 500); // Запускаем интервал для обновления текста
        await showMessageFromIframe(); // Ждем завершения обработки
        loadingIndicator.style.display = "none";  // Скрываем индикатор загрузки
        button.style.display = "inline";  // Показываем кнопку
        clearInterval(intervalId); // Очищаем интервал
        loadingIndicator.innerText = "Загрузка..."; // Сбрасываем текст
    };

    // Добавляем индикатор загрузки и кнопку в тело документа
    document.body.appendChild(button);
    document.body.appendChild(loadingIndicator);
    console.log("Кнопка добавлена на страницу.");
}

Actually, nothing so grandiose. A regular button and a loading indicator replacing it, visible until the function calling gpt showMessageFromIframe() finishes executing. Now let's move on to it directly.

I want to warn you right away. In my case, when I went to the test, another page opened. It's hard to say why they did it, but oh well. By the way, this gave me a lot of headaches when I tried to read the text from the site. So you will definitely have to rewrite this area for yourself.

// Функция для отображения сообщения из iframe
async function showMessageFromIframe() {
    const iframe = document.querySelector("iframe.content_frame"); // Получаем iframe
    if (iframe) {
        const iframeDocument = iframe.contentDocument || iframe.contentWindow.document; // Получаем доступ к документу iframe
        const questionElement = iframeDocument.querySelector("#q_6879ubhfbka7-myuq0b3e1owp > div.quiz-player-skin__main-container > div.quiz-slide-container > div.quiz-session-view > div > div.slide-layout > div > div:nth-child(1) > div > p > span"); // Укажите путь к вопросу
		const variandElement = iframeDocument.querySelector("#q_6879ubhfbka7-myuq0b3e1owp > div.quiz-player-skin__main-container > div.quiz-slide-container > div.quiz-session-view > div > div.slide-layout > div > div:nth-child(2) > div > div"); // Указываем путь к вариантом ответа
		
        if (questionElement && variandElement) {
            const question = questionElement.innerText; // Получаем текст вопроса
			const variants = Array.from(variandElement.querySelectorAll("div")).map(variant => variant.innerText); // Получаем текст вариантов ответов
			
			const questionWithVariants = question + " " + variants.join(" "); // Объединяем вопрос и варианты ответов
			
            await main(questionWithVariants); // Отправляем вопрос в GPT
        } else {
            alert("Вопрос не найден в iframe.");
        }
    } else {
        alert("Iframe не найден.");
    }
}

This is where we get the question itself and the answer options, which are in one block in different

, and collect them all. Then we combine them and get a single message to send to GPT.

// Основной процесс
async function main(question) {
    const threadId = await createThread(); // Шаг 1: Создаем тему
    if (threadId) {
        await addMessageToThread(threadId, question); // Шаг 2: Добавляем сообщение
        const runId = await createRun(threadId, "asst_код_асисстента", "Тебе даются вопросы с вариантоми ответов и ты на основе базы данных выдаёшь правильный ответ один. Ты выдаёшь только один правильный вариант ответа без каких либо пояснений."); // Шаг 3: Создаем запуск
		
		// Проверяем статус выполнения запуска
        await checkRunStatus(threadId, runId); // Шаг 4: Проверка статуса выполнения

        // Получение сообщений после создания запуска
        await getMessagesFromThread(threadId); // Шаг 4: Получаем сообщения
    }
}

Since we are not using any cool features in this example, and the GPT request will be a regular fetch request, the checkRunStatus item is very important. The technical documentation does not say much about it, but I found on the forums that it is absolutely necessary for this approach.

Well, now let's dive into the depths of GPT.

Let's start everything as per the technical documentation from open.ai: we create a topic for our previously created assistant in the createThread() function.

async function createThread() {
    console.log("Создание темы...");

    try {
        const response = await fetch("https://api.openai.com/v1/threads", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "Authorization": "Bearer уникальный_ключ_выданный_openai",
                "OpenAI-Beta": "assistants=v2"
            },
            body: JSON.stringify({})
        });

        if (!response.ok) {
            throw new Error(`Ошибка HTTP: ${response.status}`);
        }

        const data = await response.json();
        console.log("Тема создана, ID:", data.id);
        return data.id; // Вернуть ID темы для дальнейшего использования
    } catch (error) {
        console.error("Ошибка при создании темы:", error);
    }
}

Especially to track everything and monitor in case of errors, I also left the output in the console console.log(«…»); in almost all functions. In each request we will use “Authorization”: “Bearer unique_key_issued_by_openai” and Bearer does not need to be deleted from here, as advised on many forums))). The function returns us a unique ID, which we will use in the future.

Now we move on to the next function addMessageToThread. Finally, let's send our message to GPT.

async function addMessageToThread(threadId, message) {
    console.log("Добавление сообщения в тему...");

    try {
        const response = await fetch(`https://api.openai.com/v1/threads/${threadId}/messages`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "Authorization": "Bearer уникальный_ключ_выданный_openai",
                "OpenAI-Beta": "assistants=v2"
            },
            body: JSON.stringify({
                role: "user",
                content: message
            })
        });

        if (!response.ok) {
            throw new Error(`Ошибка HTTP: ${response.status}`);
        }

        const data = await response.json();
        console.log("Сообщение добавлено в тему:", data);
    } catch (error) {
        console.error("Ошибка при добавлении сообщения в тему:", error);
    }
}

Yes, yes. From the lines:

const data = await response.json();
console.log("Сообщение добавлено в тему:", data);

can be eliminated, but I was still interested in what open.ai would give in response. The function itself does not return anything to us, it only sends a message to GPT. There is little interesting here, so let's move on to the function createRun.

async function createRun(threadId, assistantId, instructions) {
    console.log("Создание запуска...");

    try {
        const response = await fetch(`https://api.openai.com/v1/threads/${threadId}/runs`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "Authorization": "Bearer уникальный_ключ_выданный_openai",
                "OpenAI-Beta": "assistants=v2"
            },
            body: JSON.stringify({
                assistant_id: assistantId,
                instructions: instructions
            })
        });

        if (!response.ok) {
            throw new Error(`Ошибка HTTP: ${response.status}`);
        }

        const data = await response.json();
        console.log("Запуск создан:", data);
        return data.id; // Вернуть ID созданного запуска для последующего использования
    } catch (error) {
        console.error("Ошибка при создании запуска:", error);
    }
}

She, in turn, as I understand it, sends our assistant a command to generate the long-awaited response and also gives us a unique ID, which we will subsequently refer to for the response readiness status.

Now let's move on to it… That very function that periodically queries whether our answer is ready or not. checkRunStatus

async function checkRunStatus(threadId, runId) {
    let status="";
    try {
        while (status !== 'completed') {
            const response = await fetch(`https://api.openai.com/v1/threads/${threadId}/runs/${runId}`, {
                method: "GET",
                headers: {
                    "Authorization": "Bearer уникальный_ключ_выданный_openai",
                    "OpenAI-Beta": "assistants=v1"
                }
            });

            if (!response.ok) {
                throw new Error(`Ошибка HTTP: ${response.status}`);
            }

            const data = await response.json();
            status = data.status;
            console.log("Статус запуска:", status);

            // Подождать перед следующей проверкой
            await new Promise(res => setTimeout(res, 2000));
        }
    } catch (error) {
        console.error("Ошибка при проверке статуса запуска:", error);
    }
}

Once our assistant has completed the response, its identifier will change to completed, and our response will be ready for us to pick up.

To be honest, I spent a long time fiddling around and trying to figure out why I wasn't getting a response after sending it to GPT. After all, I was trying to pick it up, and I was getting some kind of crap.

Well, finally we get the answer, which I decided to display in an alert:

// Получение сообщений после завершения выполнения
async function getMessagesFromThread(threadId) {
    console.log("Получение сообщений из темы...");

    try {
        const response = await fetch(`https://api.openai.com/v1/threads/${threadId}/messages`, {
            method: "GET",
            headers: {
                "Content-Type": "application/json",
                "Authorization": "Bearer уникальный_ключ_выданный_openai",
                "OpenAI-Beta": "assistants=v2"
            }
        });

        if (!response.ok) {
            throw new Error(`Ошибка HTTP: ${response.status}`);
        }

        const data = await response.json();
        console.log("Полученный ответ:", data);

        // Проверяем, если данные содержат сообщения внутри объекта data
        if (Array.isArray(data.data) && data.data.length > 0) {
            const messages = data.data[0].content; // Получаем содержимое сообщения
			const answer = messages[0].text.value; // Получаем текст ответа
			
			const cleanedAnswer = answer.replace(/【\d+:\d+†source】/g, '').trim(); // Удаляем все вхождения формата 【x:y†source】
			
            alert(`Правильный вариант: ${cleanedAnswer}`);
        } else {
            alert("Ассистент не вернул ответ.");
        }
    } catch (error) {
        console.error("Ошибка при получении сообщений из темы:", error);

When outputting, I noticed that after the answer, we get the identifier of the files that the assistant accessed to find the answer. Yes, I know that this is a small thing, but it still hurts the eyes. Therefore, the line:
const cleanedAnswer = answer.replace(/【\d+:\d+†source】/g, '').trim(); // Удаляем все вхождения формата 【x:y†source】
We delete everything unnecessary after the answer.

For those who love clean code, I apologize for all the left-handed output to the console. I like to see what exactly works for me, and in case of an error, it is much easier to identify the problematic segment.

Yes, and if you decide to simply copy these queries, don’t forget to arrange them at least in the correct order, so that the function comes first, and only then it is called.

The final line of code is:
addButton();
actually, the call to the initial function itself.

Now to the very essence of the assistant

I started this stage by first creating a vector database, and then I wrote my assistant.

So, we will need a base for the assistant to search for answers to questions. I was lucky with this: I had manuals and technical documentation in digital form. The main thing is that everything is in text format!

We take our database. I didn't prepare it in advance, since the assistant is already well versed in it, and we put it in a separate text file on our PC.

Next, go to the openai personal account and select Storage, there we select Vector stores (Vector stores), I hope I translated it correctly)
We click on creating a new one and upload our database file to it.
After that, in the created database, we click on creating an assistant. Let me remind you: we do not go to assistants, but create it directly from the database. This is how the database is linked to the assistant when created.

Yes, it might be possible to connect it separately, but I still haven't found where exactly this can be done.

And only after that can we take our assistant identifier and write code for it.

I want to note that I chose the GPT version in the plugin gpt4o-mini. And the plugin from Russia works for me without VPN.

I spent, in general, a couple of hours on this code and analysis, and I hope it will save someone a little time:-)

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *