Files
MoodleGPT/extension/moodle-gpt.js
T
yoannchb-pro f1f271c66e v1.0.0
2023-03-20 21:17:29 -04:00

255 lines
7.4 KiB
JavaScript

chrome.storage.sync.get(["moodleGPT"]).then(function (storage) {
const config = storage.moodleGPT;
if (!config) throw new Error("Please configure MoodleGPT into the extension");
//listening to the keys to inject moodleGPT
const pressedKeys = [];
const listeners = [];
document.body.addEventListener("keypress", function (e) {
pressedKeys.push(e.key);
if (pressedKeys.length > config.code) pressedKeys.shift();
if (pressedKeys.join("") === config.code) {
pressedKeys.length = 0;
setUpMoodleGpt();
}
});
/**
* Show some informations into the document title and remove it after 3000ms
* @param {*} text
*/
function titleIndications(text) {
const backTitle = document.title;
document.title = text;
setTimeout(() => (document.title = backTitle), 3000);
}
/**
* Setup moodleGPT into the page (remove/injection)
*/
function setUpMoodleGpt() {
//removing events
if (listeners.length > 0) {
for (const listener of listeners) {
if (config.cursor) listener.element.style.cursor = "initial";
listener.element.removeEventListener("click", listener.fn, {
once: true,
});
}
if (config.title) titleIndications("Removed");
listeners.length = 0;
return;
}
//injection
const inputQuery = ["checkbox", "radio", "text"]
.map((e) => `input[type="${e}"]`)
.join(",");
const query = inputQuery + ", textarea, select";
const forms = Array.from(document.querySelectorAll(".formulation"));
for (const form of forms) {
const hiddenButton = form.querySelector(".qtext");
if (config.cursor) hiddenButton.style.cursor = "pointer";
const fn = reply.bind(null, hiddenButton, form, query);
listeners.push({ element: hiddenButton, fn });
hiddenButton.addEventListener("click", fn, { once: true });
}
if (config.title) titleIndications("Injected");
}
/**
* Normlize text
* @param {*} text
*/
function normalizeText(text) {
return text.replace(/\n+/gi, "\n").toLowerCase().trim();
}
/**
* Get the response from chatGPT api
* @param {*} question
* @returns
*/
async function getChatGPTResponse(question) {
const req = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${config.apiKey}`,
},
body: JSON.stringify({
model: "gpt-3.5-turbo",
messages: [{ role: "user", content: question }],
temperature: 0.8,
top_p: 1.0,
presence_penalty: 1.0,
stop: null,
}),
});
const rep = await req.json();
const response = rep.choices[0].message.content;
return normalizeText(response);
}
/**
* Handling logs into the console
*/
class Logs {
static question(text) {
const css = "color: blue";
console.log("%c[QUESTION]: %s", css, text);
}
static responseTry(text, valide) {
const css = "color: " + (valide ? "green" : "red");
console.log("%c[CHECKING]: %s", css, text);
}
static array(arr) {
console.log("[CORRECTS] ", arr);
}
static response(text) {
console.log(text);
}
}
/**
* Reply to the question
* @param {*} form
* @param {*} query
* @returns
*/
async function reply(hiddenButton, form, query) {
if (config.cursor) hiddenButton.style.cursor = "wait";
const question = normalizeText(
form.textContent.replace("Texte de la question", "")
);
const inputList = form.querySelectorAll(query);
const response = await getChatGPTResponse(
`Give a short response as possible for this question, reply in this langage "${config.langage}", only show the result:
${question}
(if you have to choose between multiple results only show the corrects one)`
);
if (config.logs) {
Logs.question(question);
Logs.response(response);
}
if (config.cursor) hiddenButton.style.cursor = "initial";
//if we dont find the input we copy into the clipboard
if (inputList.length === 0) {
if (config.title) titleIndications("Copied to clipboard");
navigator.clipboard.writeText(response);
return;
}
//if it's a text
if (
(inputList.length === 1 && inputList[0].type === "text") ||
inputList[0].tagName === "TEXTAREA"
) {
if (config.typing) {
for (let i = 0; i < response.length; ++i) {
setTimeout(
() => (inputList[0].value = response.slice(0, i + 1)),
i * 50
);
}
} else {
inputList[0].value = response;
}
return;
}
//if it's a select
if (inputList[0].tagName === "SELECT") {
let correct = response.split("\n");
if (correct.length === 1 && correct.length !== inputList)
correct = response.split(",");
if (config.logs) Logs.array(correct);
for (let j = 0; j < inputList.length; ++j) {
const options = inputList[j].querySelectorAll("option");
for (const option of options) {
const content = option.textContent.toLocaleLowerCase().trim();
const valide = correct[j].includes(content);
//if it's a put in order
if (!isNaN(parseInt(content))) {
const content = normalizeText(
option.parentNode.closest("tr").querySelector(".text").textContent
);
const index = correct.findIndex((c) => {
const valide = c.includes(content);
if (config.logs) Logs.responseTry(content, valide);
return valide;
});
if (index !== -1) {
if (config.mouseover) {
options[index + 1].closest("select").addEventListener(
"click",
function () {
options[index + 1].selected = "selected";
},
{ once: true }
);
} else {
options[index + 1].selected = "selected";
}
break;
}
}
if (config.logs) Logs.responseTry(content, valide);
if (valide) {
if (config.mouseover) {
option.closest("select").addEventListener(
"click",
function () {
option.selected = "selected";
},
{ once: true }
);
} else {
option.selected = "selected";
}
break;
}
}
}
return;
}
//if it's a radio button or checkbox
for (const input of inputList) {
const content = normalizeText(input.parentNode.textContent);
const valide = response
.replace(/^[a-z\d]\.\s/gi, "")
.includes(content.replace(/^[a-z\d]\.\s/gi, ""));
if (config.logs) Logs.responseTry(content, valide);
if (valide) {
if (config.mouseover) {
input.addEventListener(
"mouseover",
function (event) {
event.target.checked = true;
},
{ once: true }
);
} else {
input.checked = true;
}
}
}
}
});