├── icon.png ├── tabcontent.js ├── manifest.json ├── popup.css ├── README.md ├── popup.htm ├── background.js ├── openai.css ├── contentScript_chatgpt.js └── popup.js /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XenocodeRCE/GPTgo/main/icon.png -------------------------------------------------------------------------------- /tabcontent.js: -------------------------------------------------------------------------------- 1 | chrome.runtime.onMessage.addListener((request) => { 2 | if (request.Phase == 1) // l'onglet popup nous demande quelle est la sélection 3 | { 4 | //console.log("On nous a demandé la sélection :", window.getSelection().toString()); // sur certaines pages, cette fonction renvoie une chaîne vide, alors qu'on a bien sélectionné quelque chose. Je ne sais pas pourquoi. Je me demande si ce n'est pas lié à un problème de cache ? 5 | chrome.runtime.sendMessage({ // on envoie un message au background.js avec la sélection 6 | Phase : 2 , 7 | Message : window.getSelection().toString() 8 | }); 9 | } 10 | }) -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "GPTgo", 3 | "version": "1.0.0", 4 | "manifest_version": 3, 5 | "description" : "Utiliser ChatGPT sur n'importe quelle page web", 6 | "icons": { 7 | "128": "icon.png" 8 | }, 9 | "background": { 10 | "service_worker" : "Background.js" 11 | }, 12 | "action": { 13 | "default_icon": "icon.png", 14 | "default_popup": "popup.htm" 15 | }, 16 | "content_scripts": [ 17 | { 18 | "matches": ["https://chat.openai.com/chat*"], 19 | "js": ["contentScript_chatgpt.js"] 20 | }, 21 | { 22 | "matches": [""], 23 | "js": ["tabcontent.js"] 24 | } 25 | ], 26 | "permissions":[ 27 | "tabs", 28 | "cookies" 29 | ] 30 | } -------------------------------------------------------------------------------- /popup.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family : Söhne,ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,Helvetica Neue,Arial,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji; 3 | margin:0; 4 | background-color:#f7f7f8; 5 | font-size: .875rem; 6 | line-height: 1.25rem; 7 | } 8 | h1 { 9 | background-color:#202123; 10 | color: white; /* couleur du texte en blanc */ 11 | padding: 20px 0; /* espace intérieur de 20 pixels en haut et en bas, sans espace à gauche ou à droite */ 12 | margin: 0; /* pas de marges à l'extérieur */ 13 | } 14 | 15 | textarea { 16 | font-family : Söhne,ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,Helvetica Neue,Arial,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji; 17 | border-radius: 0.375rem; 18 | padding: 5px; 19 | } 20 | input { 21 | position : absolute; 22 | top: 515px; 23 | left:190px; 24 | width:100px; 25 | text-align:center; 26 | justify-content:center; 27 | cursor: pointer; 28 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GPTgo est une extension chrome destinée à permettre l'utilisation de chatGPT comme une API, à partir de n'importe quel onglet de chrome. 2 | 3 | 1. Installer l'extension non-empaquetée, en activant le mode développeur de chrome 4 | 2. Ouvrir une nouvelle page dans chrome, et sélectionner du texte. 5 | 3. Ensuite, appuyez sur le bouton de l'extension, dans la barre d'outils. 6 | 4. Si aucun onglet ne fait tourner chatGPT, la page s'ouvre dans un nouvel onglet : il suffit de se connecter avec un compte gratuit. Revenez à l'étape 3. 7 | 5. La popup s'ouvre : on remplit le pré-prompt, qui sera complété par la sélection pour former le prompt complet. 8 | 6. On appuie sur le bouton : la réponse de ChatGPT s'affiche au fur et à mesure. Il est possible que vous ne voyez qu'un carré noir pendant quelques secondes : c'est le cas si ChatGPT est encombré. 9 | 7. En cas de problème, allez dans l'onglet de ChatGPT pour contrôler l'état du système. 10 | 11 | N'hésitez pas à faire remonter les bugs que vous rencontrerez ! 12 | 13 | **Quelques suggestions de pré-prompts :** 14 | 15 | * "Fais-moi un commentaire sarcastique sur la phrase suivante :" 16 | * "Rédige une réponse polie pour décliner l'invitation suivante :" 17 | * "Explique-moi la phrase suivante comme si j'avais cinq ans :" 18 | * "Traduis-moi la phrase suivante en anglais :" 19 | * "Reformule le passage suivant avec un style plus correct et plus agréable :" 20 | 21 | -------------------------------------------------------------------------------- /popup.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

GPTgo

11 |
12 |
Tapez ici le prompt qui doit précéder le texte sélectionné : 13 |
14 |
Sélection reçue de la page active :
15 |
16 |
17 |
18 | 25 | 26 | -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | // On écoute les message des ContentScripts 2 | chrome.runtime.onMessage.addListener(async request => { 3 | if(request.Phase){ 4 | 5 | switch (request.Phase) { 6 | case 1: // on demande la sélection au tab actif 7 | chrome.tabs.query({url: "https://chat.openai.com/chat*"}, tabGPTs => { 8 | if (tabGPTs.length < 1) { // s'il n'y a pas d'onglet avec chatGPT ouvert 9 | chrome.tabs.create({ url: "https://chat.openai.com/chat" }); 10 | } else { 11 | chrome.tabs.query({ active: true, currentWindow: true }, tabs => { 12 | chrome.tabs.sendMessage(tabs[0].id, request); // on transmet le "getSelection" à l'onglet actif 13 | }); 14 | } 15 | }); 16 | break; 17 | //case 2: géré directement par le popup.htm 18 | case 3: // on envoie le prompt à chatGPT 19 | var [tab] = await chrome.tabs.query({url: "https://chat.openai.com/chat*"}); // on cherche l'ID du tab où tourne ChatGPT 20 | chrome.tabs.sendMessage(tab.id , request); 21 | break; 22 | //case 4: géré directement par le popup.htm 23 | case -1: // Il y a une erreur sur la page ChatGPT 24 | chrome.tabs.query({url: "https://chat.openai.com/chat*"}, tabGPT => { //on récupère l'id de l'onglet chatgpt 25 | chrome.tabs.update(tabGPT[0].id, { active: true }); // on le rend actif pour que l'utilisateur voie l'erreur 26 | chrome.windows.update(tabGPT[0].windowId, {focused: true}, function(window) { 27 | console.log('La fenêtre a été mise à jour :', window); 28 | }); 29 | }); 30 | break; 31 | case -2: // On demande un arrêt. identique à case 3 (on pourrait les regrouper, mais pour des raisons de clarté...) 32 | var [tab] = await chrome.tabs.query({url: "https://chat.openai.com/chat*"}); // on cherche l'ID du tab où tourne ChatGPT 33 | chrome.tabs.sendMessage(tab.id , request); 34 | break; 35 | 36 | } 37 | } 38 | }) 39 | 40 | 41 | -------------------------------------------------------------------------------- /openai.css: -------------------------------------------------------------------------------- 1 | button, 2 | input, 3 | optgroup, 4 | select, 5 | textarea { 6 | color: inherit; 7 | font-family: inherit; 8 | font-size: 100%; 9 | font-weight: inherit; 10 | line-height: inherit; 11 | margin: 0; 12 | } 13 | 14 | button, 15 | select { 16 | text-transform: none 17 | } 18 | 19 | button { 20 | -webkit-appearance: button; 21 | appearance:button; 22 | background-color: transparent; 23 | background-image: none 24 | } 25 | [role=button], 26 | button { 27 | cursor: pointer 28 | } 29 | 30 | .btn { 31 | align-items: center; 32 | border-color: transparent; 33 | border-radius: .25rem; 34 | border-width: 1px; 35 | display: inline-flex; 36 | font-size: .875rem; 37 | line-height: 1.25rem; 38 | padding: .5rem .75rem; 39 | pointer-events: auto 40 | } 41 | 42 | .btn:focus { 43 | outline: 2px solid transparent; 44 | outline-offset: 2px 45 | } 46 | 47 | .btn:disabled { 48 | cursor: not-allowed; 49 | opacity: .5 50 | } 51 | 52 | .btn-neutral { 53 | --tw-bg-opacity: 1; 54 | --tw-text-opacity: 1; 55 | background-color: rgba(255, 255, 255, var(--tw-bg-opacity)); 56 | border-color: rgba(0, 0, 0, .1); 57 | border-width: 1px; 58 | color: rgba(64, 65, 79, var(--tw-text-opacity)); 59 | font-size: .875rem; 60 | line-height: 1.25rem 61 | } 62 | 63 | .btn-neutral:hover { 64 | --tw-bg-opacity: 1; 65 | background-color: rgba(236, 236, 241, var(--tw-bg-opacity)) 66 | } 67 | 68 | .btn-neutral:focus { 69 | --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); 70 | --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); 71 | --tw-ring-offset-width: 2px; 72 | box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), 0 0 transparent; 73 | box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 transparent) 74 | } 75 | 76 | .btn-neutral.focus-visible { 77 | --tw-ring-opacity: 1; 78 | --tw-ring-color: rgba(99, 102, 241, var(--tw-ring-opacity)) 79 | } 80 | 81 | .btn-neutral:focus-visible { 82 | --tw-ring-opacity: 1; 83 | --tw-ring-color: rgba(99, 102, 241, var(--tw-ring-opacity)) 84 | } 85 | 86 | .border-0 { 87 | border-width: 0 88 | } 89 | 90 | .md\:border { 91 | border-width: 1px 92 | } -------------------------------------------------------------------------------- /contentScript_chatgpt.js: -------------------------------------------------------------------------------- 1 | function askGPT(texte) { 2 | if (document.getElementsByTagName("textarea")[0] != null) { 3 | if (isThinking()) { 4 | console.log ("ChatGPT n'est pas prêt !"); 5 | return false; 6 | } else { // on envoie la requête 7 | console.log(document.getElementsByTagName("textarea")[0]); 8 | document.getElementsByTagName("textarea")[0].value=texte; //forcer texte 9 | document.getElementsByClassName("absolute p-1")[0].click(); // valider requête 10 | return true; 11 | } 12 | } else { // Si il y a une erreur sur la page (pas de zone de texte) 13 | console.log("On passe par ici"); 14 | chrome.runtime.sendMessage({ // dernière phase, on envoie la réponse finale de chatgpt 15 | Phase : -1, 16 | Message : "Erreur sur la page ChatGPT. Veuillez vous reconnecter." 17 | }); 18 | return false; 19 | } 20 | } 21 | 22 | function isThinking() { // si chatGPT est en train de réfléchir 23 | return (document.getElementsByTagName("polygon").length < 1); 24 | } 25 | 26 | 27 | async function verifierNoeud () { 28 | var nextDiv = null; 29 | while (nextDiv == null) // on attend que le div qui contient la réponse soit prêt 30 | { 31 | var allBlanc = document.querySelectorAll("div.dark\\:bg-gray-800.group"); // on sélectionne l'ensemble des div qui contiennent les prompts 32 | var dernierPrompt = allBlanc[allBlanc.length - 1]; // on prend le dernier prompt 33 | if (dernierPrompt == null) { // s'il n'est pas encore inscrit dans le DOM, on attend (cas du tout premier prompt) 34 | nextDiv = null; 35 | } else { 36 | var nextDiv = dernierPrompt.nextElementSibling; // s'il existe, on prend le div qui suit (qui va contenir la réponse de chatGPT) 37 | } 38 | await new Promise (resolve => setTimeout (resolve, 100)); // attendre 100 millisecondes } // appeler l’observateur de mutation 39 | } 40 | let callback = function(mutations) { // fonction exécutée quand on détecte un changement dans la réponse qui est en train d'être produite 41 | newtext = (document.querySelectorAll(".group.w-full")[document.querySelectorAll(".group.w-full").length -1].innerText); 42 | // ON VA TESTER LA FIN DU MESSAGE 43 | var el = document.querySelectorAll(".group.w-full p:last-of-type")[document.querySelectorAll(".group.w-full p:last-of-type").length -1]; // sélectionner l’élément 44 | var after = getComputedStyle (el, "::after").content; // obtenir le contenu du pseudo-élément ::after 45 | //console.log ("On traite une modification : ", mutations) 46 | if (after !== "none") { // si on n'a pas terminé 47 | chrome.runtime.sendMessage({ // On envoie la réponse temporaire de ChatGPT 48 | Phase : 4, 49 | Message : newtext 50 | }); 51 | } else { // si on a terminé 52 | observer.disconnect(); 53 | observer = null; 54 | console.log("déconnecté"); 55 | chrome.runtime.sendMessage({ // dernière phase, on envoie la réponse finale de chatgpt 56 | Phase : 5, 57 | Message : newtext 58 | }) 59 | } 60 | }; 61 | 62 | // Créer un observateur avec la fonction de rappel 63 | let observer = new MutationObserver(callback); 64 | 65 | // Définir la configuration de l'observateur : observer les changements de caractères du div 66 | let config = {characterData: true, subtree: true, attributes:true}; 67 | console.log(document.querySelectorAll(".group.w-full")[document.querySelectorAll(".group.w-full").length -1]); 68 | 69 | observer.observe(document.querySelectorAll(".group.w-full")[document.querySelectorAll(".group.w-full").length -1], config); 70 | 71 | } 72 | 73 | // On écoute les messages venant du background 74 | chrome.runtime.onMessage.addListener((request) => { 75 | console.log("on reçoit une requête", request); 76 | // On montre le message 77 | 78 | switch (request.Phase) { 79 | case 3 : // on a envoyé le prompt, on attend la réponse 80 | console.log("phase 3"); 81 | if (askGPT(request.Message)) { // si tout se passe bien quand on envoie la requête 82 | console.log("Requête envoyée :", request.Message); 83 | verifierNoeud (); 84 | } 85 | break; 86 | case -2 : // on veut arrêter 87 | var currentBouton = document.querySelector("button.btn.relative.btn-neutral.border-0.md\\:border"); 88 | if (currentBouton != null) 89 | { 90 | if (currentBouton.textContent == "Stop generating") 91 | { 92 | currentBouton.click(); 93 | } 94 | } 95 | break; 96 | } 97 | }); 98 | 99 | -------------------------------------------------------------------------------- /popup.js: -------------------------------------------------------------------------------- 1 | // Déclarer une variable globale pour stocker le caractère d'attente 2 | var waitChar = " "; 3 | var reponseGPT = ""; 4 | var onAfficheReponse = false; 5 | var enEcriture = false; 6 | 7 | function toggleWaitChar() { 8 | if (waitChar === "▮") { 9 | waitChar = " "; 10 | setTimeout(toggleWaitChar, 250); 11 | } 12 | else { 13 | waitChar = "▮"; 14 | setTimeout(toggleWaitChar, 750); 15 | } 16 | if (enEcriture) document.getElementById("outputGPT").value = reponseGPT + waitChar; 17 | } 18 | 19 | 20 | function afficherReponse(){ 21 | document.getElementById("requete").style.display = "none"; // 22 | document.getElementById("resultat").style.display = "inline"; // 23 | onAfficheReponse = true; 24 | } 25 | function onEcrit(i) { 26 | enEcriture = i; 27 | if (i) { // si on écrit 28 | document.getElementById("arreter").style.display = "inline"; //on affiche le bouton arrêter 29 | } else { 30 | document.getElementById("arreter").style.display = "none"; // on le masque 31 | } 32 | 33 | } 34 | 35 | document.addEventListener("DOMContentLoaded", (event) => { // quand la page a fini de charger, on ajoute la fonction click aux boutons, et on gère le chargement du cookie 36 | var prompt = document.cookie.split(';')[0].split('=')[1]; 37 | document.getElementById("prompt").value = (decodeURIComponent(prompt) == "undefined") ? "" : decodeURIComponent(prompt); 38 | toggleWaitChar(); 39 | onEcrit(false); 40 | document.getElementById("bouton").addEventListener("click", () => { 41 | afficherReponse(); 42 | onEcrit(true); // sert pour l'affichage du caractère d'attente 43 | reponseGPT = ""; 44 | document.getElementById("info").innerHTML = ""; 45 | chrome.runtime.sendMessage({ 46 | Phase : 3, 47 | Message : document.getElementById("prompt").value + " " + document.getElementById("selectionInput").value 48 | }) 49 | }); 50 | document.getElementById("fermer").addEventListener("click", () => { 51 | window.close(); 52 | }); 53 | document.getElementById("arreter").addEventListener("click", () => { 54 | console.log("on demande un arrêt"); 55 | chrome.runtime.sendMessage({ 56 | Phase : -2, 57 | Message : "Arrêter" 58 | }) 59 | console.log("Arrêt demandé"); 60 | }); 61 | document.getElementById("prompt").addEventListener("change", () => { 62 | // Définir la date d'expiration dans 1 an 63 | let dateExpiration = new Date(); 64 | dateExpiration.setTime(dateExpiration.getTime() + (365 * 24 * 60 * 60 * 1000)); 65 | document.cookie = "prompt=" + encodeURIComponent(document.getElementById("prompt").value) + ";expires=" + dateExpiration.toUTCString(); 66 | }); 67 | }); 68 | 69 | 70 | 71 | 72 | chrome.runtime.sendMessage({ //au chargement, on essaie de récupérer la sélection 73 | Phase : 1, 74 | Message : "getSelection" 75 | }) 76 | var selection = ""; 77 | chrome.runtime.onMessage.addListener((request) => { 78 | //console.log (request.Message); 79 | switch (request.Phase) { 80 | case 2: 81 | selection = request.Message; 82 | document.getElementById("selectionInput").value = selection; 83 | console.log("Sélection :", selection); 84 | break; 85 | case 4: // on a reçu la réponse de chatgpt, on l'affiche 86 | let outputGPT = document.getElementById("outputGPT"); 87 | if (!onAfficheReponse) { // se produit dans le cas où on reçoit une réponse de ChatGPT, alors qu'on n'a pas cliqué sur le bouton. cela se produit quand on a quitté la popup sans avoir attendu la fin du prompt : ici on récupère notre affichage 88 | afficherReponse(); 89 | onAfficheReponse = true; 90 | onEcrit(true); 91 | } 92 | reponseGPT = request.Message; 93 | outputGPT.value = request.Message + waitChar; 94 | outputGPT.scrollTop = outputGPT.scrollHeight; 95 | break; 96 | case 5: 97 | onEcrit(false); 98 | if (!onAfficheReponse) afficherReponse(); // c'est le cas où on recevrait un message 5 alors qu'on n'est pas en train d'afficher la réponse. Ce cas correspond au msg 5 qui arriverait alors qu'on vient d'ouvrir la popup, sur une requête précédente 99 | document.getElementById("outputGPT").value = request.Message; 100 | document.getElementById("fermer").style="display:inline"; 101 | //document.getElementById("info").innerHTML = "Réponse terminée !"; 102 | break; 103 | } 104 | }); 105 | --------------------------------------------------------------------------------