7 Commits

Author SHA1 Message Date
yoannchb-pro 98728b52e7 Update README.md 2023-06-12 23:37:54 -04:00
yoannchb-pro 78942c20dd Merge pull request #3 from yoannchb-pro/dev
v1.0.2
2023-06-12 23:36:40 -04:00
yoannchb-pro 5fe25d0b34 Update README.md 2023-06-12 23:35:40 -04:00
yoannchb-pro e7a6e07856 extension assets 2023-06-12 23:34:54 -04:00
yoannchb-pro 0458137f0c v1.0.2 2023-06-12 23:13:47 -04:00
yoannchb-pro 7af54c40ad v1.0.2 2023-06-12 22:49:46 -04:00
yoannchb-pro 8292ae42a4 v1.0.2 2023-06-12 22:36:34 -04:00
22 changed files with 256 additions and 43 deletions
+4
View File
@@ -1,5 +1,9 @@
# CHANGELOG
## v1.0.2
- Added `mode`
## v1.0.1
- Removed langage
+53 -4
View File
@@ -2,14 +2,48 @@
href="https://www.flaticon.com/free-icons/mortarboard" target="_blank" rel="noopener noreferrer"
title="Mortarboard icons created by itim2101 - Flaticon" ><img src="./extension/icon.png" alt="Mortarboard icons created by itim2101 - Flaticon" width="150" style="display:block; margin:auto;"></a></p>
# MoodleGPT v1.0.1
# MoodleGPT v1.0.2
This extension allows you to hide CHAT-GPT in a Moodle quiz. You just need to enter <b>the code configured in the extension</b> on the keyboard and then click on the question you want to solve, and CHAT-GPT will automatically provide the answer. However, one needs to be careful because as we know, CHAT-GPT can make errors especially in calculations.
## Webstore
Find it on the chrome webstore "MoodleGPT"
## Summary
- [MoodleGPT v1.0.2](#moodlegpt-v102)
- [Webstore](#webstore)
- [Summary](#summary)
- [Disclaimer !](#disclaimer-)
- [Support](#support)
- [Update](#update)
- [MoodleGPT don't complete my quiz ?](#moodlegpt-dont-complete-my-quiz-)
- [Set up](#set-up)
- [Inject the code into the moodle](#inject-the-code-into-the-moodle)
- [Remove injection](#remove-injection)
- [Mode](#mode)
- [Settings](#settings)
- [Supported questions type](#supported-questions-type)
- [Select](#select)
- [Put in order question](#put-in-order-question)
- [Resolve equation](#resolve-equation)
- [One response (radio button)](#one-response-radio-button)
- [Multiples responses (checkbox)](#multiples-responses-checkbox)
- [True or false](#true-or-false)
- [Number](#number)
- [Text](#text)
- [What about if the question can't be completed ?](#what-about-if-the-question-cant-be-completed-)
- [Test](#test)
## Disclaimer !
I hereby declare that I am not responsible for any misuse or illegal activities carried out using my program. The code is provided for educational and research purposes only, and any use of it outside of these purposes is at the user's own risk.
## Support
Will be a pleasure if you want to supprot this project :) -> Just right [here](https://www.buymeacoffee.com/yoannchbpro)
## Update
See [changelog](./CHANGELOG.md)
@@ -22,6 +56,10 @@ If MoodleGPT cannot complete one of your moodle quiz please provide the html cod
> NOTE: This extension only works on Chromium-based browsers like Edge, Chrome, etc. Unfortunately, Firefox requires a click on the extension, which is not very discreet.
<p align="center">
<img src="./assets/setup.png" alt="Popup" width="300">
</p>
Go to <b>"Manage my extensions"</b> on your browser, then click on <b>"Load unpacked extension"</b> and select the <b>"extension"</b> folder. Afterwards, click on the extension icon and enter the apiKey obtained from [openai](https://platform.openai.com/) and enter a <b>code</b> that will activate the extension on your moodle page. Finally, click on the <b>reload button</b> next to model (it should give you the last ChatGPT version, otherwise enter it by your self) and click on the save button (The extension need to be configured before entering the moodle quiz).
## Inject the code into the moodle
@@ -32,10 +70,21 @@ You just need to enter on the keyboard the <b>code</b> you have set into the ext
Type back the <b>code</b> on the keyboard and the code will be removed from the current page.
## Options
## Mode
<p align="center">
<img src="./assets/popup.png" alt="Popup" height="300">
<img src="./assets/mode.png" alt="Popup" width="300">
</p>
- <b>Autocomplete:</b> The extension will complete the question for you.
- <b>Clipboard:</b> The response is copied into the clipboard.
- <b>Question to answer:</b> The question is converted to the answer and you can click on it to show back the question (or show back the answer).
<br/><img src="./assets/question-to-answer.gif" alt="Question to Answer">
## Settings
<p align="center">
<img src="./assets/settings.png" alt="Popup" width="300">
</p>
- <b>Api key</b>: the openai api key.
@@ -97,7 +146,7 @@ Type back the <b>code</b> on the keyboard and the code will be removed from the
![Text](./assets/text.gif)
## If it can't complete the question, the answer will be copied to your clipboard
## What about if the question can't be completed ?
To know if the answer has been copied to the clipboard, you can look at the title of the page which will become <b>"Copied to clipboard"</b> for 3 seconds.
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 21 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+18 -2
View File
@@ -61,8 +61,8 @@
*/
function normalizeText(text) {
return text
.replace(/\n+/g, "\n")
.replace(/[ \t]+/g, " ")
.replace(/(\n\s*)+/gi, "\n")
.replace(/[ \t]+/gi, " ")
.toLowerCase()
.trim()
.replace(/^[a-z\d]\.\s/gi, "") //a. text, b. text, c. text, 1. text, 2. text, 3.text
@@ -382,6 +382,21 @@
Logs.question(question);
Logs.response(response);
}
if (config.mode === "clipboard") {
return handleClipboard(config, response);
}
if (config.mode === "question-to-answer") {
const questionBackup = form.textContent;
const questionContainer = form.querySelector(".qtext");
questionContainer.textContent = response;
questionContainer.addEventListener("click", function () {
questionContainer.textContent =
questionContainer.textContent === questionBackup
? response
: questionBackup;
});
return;
}
const handlers = [
handleContentEditable,
handleTextbox,
@@ -393,6 +408,7 @@
if (handler(config, inputList, response))
return;
}
/** In the case we can't auto complete the question */
handleClipboard(config, response);
});
}
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

+2 -8
View File
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "MoodleGPT",
"version": "1.0.1",
"version": "1.0.2",
"description": "Hidden chat-gpt for your moodle quiz",
"permissions": ["activeTab", "tabs", "storage"],
"action": {
@@ -18,13 +18,7 @@
"content_scripts": [
{
"matches": [
"*://*/**/mod/quiz/*",
"*://*/mod/quiz/*",
"http://localhost:*/*",
"http://127.0.0.1:*/*",
"file:///*"
],
"matches": ["*://*/**/mod/quiz/*", "*://*/mod/quiz/*", "file:///*"],
"js": ["MoodleGPT.js"],
"run_at": "document_end"
}
+22 -1
View File
@@ -48,7 +48,28 @@
title="Provide an api key first"
></i>
</div>
<div class="line center" style="margin-top: 1rem">
<div class="line mt">
<i class="fa-solid fa-robot"></i>
<p>Mode:</p>
</div>
<div class="line">
<ul id="mode" class="line center">
<li><button value="autocomplete">autocomplete</button></li>
<li>
<button value="clipboard" class="not-selected">clipboard</button>
</li>
<li>
<button value="question-to-answer" class="not-selected">
question to answer
</button>
</li>
</ul>
</div>
<div class="line mt">
<i class="fa-solid fa-gear"></i>
<p>Settings:</p>
</div>
<div class="line center">
<div class="col">
<div class="line">
<input id="typing" type="checkbox" />
+87 -23
View File
@@ -1,6 +1,7 @@
const saveBtn = document.querySelector(".save");
const message = document.querySelector("#message");
/* inputs id */
const inputsText = ["apiKey", "code", "model"];
const inputsCheckbox = [
"logs",
@@ -13,6 +14,21 @@ const inputsCheckbox = [
"timeout",
];
const mode = document.querySelector("#mode");
const modes = mode.querySelectorAll("button");
let actualMode = "autocomplete";
/* inputs id that need to be disabled for a specific mode */
const disabledForThisMode = {
autocomplete: [],
clipboard: ["typing", "mouseover"],
"question-to-answer": ["typing", "infinite", "mouseover"],
};
/**
* Show message into the popup
* @param {string} messageTxt
* @param {boolean} valide
*/
function showMessage(messageTxt, valide) {
message.style.color = valide ? "limegreen" : "red";
message.textContent = messageTxt;
@@ -20,15 +36,48 @@ function showMessage(messageTxt, valide) {
setTimeout(() => (message.style.display = "none"), 5000);
}
//save the configuration
/**
* Handle when a mode change to show specific input
*/
function handleModeChange() {
const needDisable = disabledForThisMode[actualMode];
const dontNeedDisable = inputsCheckbox.filter(
(input) => !needDisable.includes(input)
);
for (const id of needDisable) {
document.querySelector("#" + id).parentElement.style.display = "none";
}
for (const id of dontNeedDisable) {
document.querySelector("#" + id).parentElement.style.display = null;
}
}
/* Mode handler */
modes.forEach((button) => {
button.addEventListener("click", function () {
const value = button.value;
actualMode = value;
for (const mode of modes) {
if (mode.value !== value) {
mode.classList.add("not-selected");
} else {
mode.classList.remove("not-selected");
}
}
handleModeChange();
});
});
/* Save the configuration */
saveBtn.addEventListener("click", function () {
const [apiKey, code, model] = inputsText.map((selector) =>
document.querySelector("#" + selector).value.trim()
);
const [logs, title, cursor, typing, mouseover, infinite, table, timeout] =
inputsCheckbox.map(
(selector) => document.querySelector("#" + selector).checked
);
inputsCheckbox.map((selector) => {
const element = document.querySelector("#" + selector);
return element.checked && element.parentElement.style.display !== "none";
});
if (!apiKey || !code || !model) {
showMessage("Please complete all the form");
@@ -53,31 +102,16 @@ saveBtn.addEventListener("click", function () {
infinite,
table,
timeout,
mode: actualMode,
},
});
showMessage("Configuration saved", true);
});
//we load back the configuration
chrome.storage.sync.get(["moodleGPT"]).then(function (storage) {
const config = storage.moodleGPT;
if (config) {
inputsText.forEach((key) =>
config[key]
? (document.querySelector("#" + key).value = config[key])
: null
);
inputsCheckbox.forEach(
(key) => (document.querySelector("#" + key).checked = config[key] || "")
);
}
getLastChatGPTVersion();
});
//getting the last chatgpt version
/**
* Get the last ChatGPT version
*/
function getLastChatGPTVersion() {
const apiKeySelector = document.querySelector("#apiKey");
const reloadModel = document.querySelector("#reloadModel");
@@ -119,3 +153,33 @@ function getLastChatGPTVersion() {
}
});
}
/* we load back the configuration */
chrome.storage.sync.get(["moodleGPT"]).then(function (storage) {
const config = storage.moodleGPT;
if (config) {
if (config.mode) {
actualMode = config.mode;
for (const mode of modes) {
if (mode.value === config.mode) {
mode.classList.remove("not-selected");
} else {
mode.classList.add("not-selected");
}
}
}
inputsText.forEach((key) =>
config[key]
? (document.querySelector("#" + key).value = config[key])
: null
);
inputsCheckbox.forEach(
(key) => (document.querySelector("#" + key).checked = config[key] || "")
);
}
handleModeChange();
getLastChatGPTVersion();
});
+14 -1
View File
@@ -1,6 +1,10 @@
const currentVersion = "1.0.1";
const currentVersion = "1.0.2";
const versionDisplay = document.querySelector("#version");
/**
* Get the last version from the github
* @returns
*/
async function getLastVersion() {
const req = await fetch(
"https://raw.githubusercontent.com/yoannchb-pro/MoodleGPT/main/package.json"
@@ -9,6 +13,12 @@ async function getLastVersion() {
return rep.version;
}
/**
* Display the version or an update message
* @param {string} version
* @param {boolean} isCurrent
* @returns
*/
function setVersion(version, isCurrent = true) {
if (isCurrent) {
versionDisplay.textContent = "v" + version;
@@ -24,6 +34,9 @@ function setVersion(version, isCurrent = true) {
versionDisplay.appendChild(document.createTextNode(" is now available !"));
}
/**
* Check the extension neeed an update or no
*/
async function notifyUpdate() {
const lastVersion = await getLastVersion().catch((err) => {
console.error(err);
+29
View File
@@ -52,6 +52,7 @@ a {
.line {
display: flex;
flex-direction: row;
width: 100%;
gap: 0.5rem;
}
@@ -98,6 +99,34 @@ a {
margin-bottom: 0.75rem;
}
.mt {
margin-top: 1rem;
}
.not-selected {
opacity: 0.4;
}
#mode li {
list-style: none;
flex-grow: 1;
}
#mode {
flex-wrap: wrap;
}
#mode button {
background-color: var(--btn-color);
border: none;
text-align: center;
padding: 0.3rem 0.75rem;
cursor: pointer;
width: 100%;
border-radius: 0.5rem;
text-transform: uppercase;
}
#version {
font-size: 0.75rem;
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "moodlegpt",
"version": "1.0.1",
"version": "1.0.2",
"description": "This extension allows you to hide CHAT-GPT in a Moodle quiz.",
"scripts": {
"build": "rollup -c"
+4
View File
@@ -16,3 +16,7 @@ for (const input of document.querySelectorAll(
for (const icon of document.querySelectorAll(".text-danger, .text-success")) {
icon.remove();
}
for (const feedback of document.querySelectorAll(".specificfeedback")) {
feedback.remove();
}
+18
View File
@@ -49,6 +49,23 @@ async function reply(
Logs.response(response);
}
if (config.mode === "clipboard") {
return handleClipboard(config, response);
}
if (config.mode === "question-to-answer") {
const questionBackup = form.textContent;
const questionContainer = form.querySelector(".qtext");
questionContainer.textContent = response;
questionContainer.addEventListener("click", function () {
questionContainer.textContent =
questionContainer.textContent === questionBackup
? response
: questionBackup;
});
return;
}
const handlers = [
handleContentEditable,
handleTextbox,
@@ -61,6 +78,7 @@ async function reply(
if (handler(config, inputList, response)) return;
}
/** In the case we can't auto complete the question */
handleClipboard(config, response);
}
+1
View File
@@ -10,6 +10,7 @@ type Config = {
title?: boolean;
table?: boolean;
timeout?: boolean;
mode?: "autocomplete" | "question-to-answer" | "clipboard";
};
export default Config;
+2 -2
View File
@@ -4,8 +4,8 @@
*/
function normalizeText(text: string) {
return text
.replace(/\n+/g, "\n")
.replace(/[ \t]+/g, " ")
.replace(/(\n\s*)+/gi, "\n")
.replace(/[ \t]+/gi, " ")
.toLowerCase()
.trim()
.replace(/^[a-z\d]\.\s/gi, "") //a. text, b. text, c. text, 1. text, 2. text, 3.text