Open MoritzHinterleitner opened 8 months ago
You will need to 1 disable CSP - you can look for a CSP disabling extension on the chrome store.
Also the document selectors have changed since the UI on CGPT has changed quite a bit. I tweaked the tampermonkey script to work with the latest UI. I have tested with gpt 4 only
// ==UserScript==
// @name ChatGPT API By Browser Script
// @namespace http://tampermonkey.net/
// @version 1
// @match https://chat.openai.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=openai.com
// @grant GM_webRequest
// @license MIT
// ==/UserScript==
console.log('starting');
const WS_URL = `ws://localhost:8765`;
function getTextFromNode(node) {
let result = '';
if (!node) return result;
const childNodes = node.childNodes;
for (let i = 0; i < childNodes.length; i++) {
let childNode = childNodes[i];
if (childNode.nodeType === Node.TEXT_NODE) {
result += childNode.textContent;
} else if (childNode.nodeType === Node.ELEMENT_NODE) {
result += getTextFromNode(childNode);
}
}
return result;
}
function sleep(time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
// Main app class
class App {
constructor() {
this.socket = null;
this.observer = null;
this.stop = false;
this.dom = null;
this.queue = [];
this.isProcessing = false;
}
async start(data) {
return new Promise(async (resolve, reject) => {
const { text, model, newChat } = data;
this.stop = false
const textarea = document.querySelector('textarea');
textarea.value = text;
const event = new Event('input', { bubbles: true });
textarea.dispatchEvent(event);
await sleep(500);
const send = document.querySelectorAll('button[data-testid="send-button"]')[0];
if (send) {
send.click();
// wait for the message to be sent
await sleep(1000);
} else {
console.log('Send button not found');
}
this.observeMutations(resolve);
});
}
observeMutations(resolve) {
this.observer = new MutationObserver(async (mutations) => {
const list = [...document.querySelectorAll('div.agent-turn')];
const last = list[list.length - 1];
if (!last) {
console.log('No last message found');
return;
}
let codeNode = last.querySelector('div[data-message-author-role="assistant"] code');
let lastText = ''
if (codeNode) {
lastText = getTextFromNode(codeNode);
} else {
lastText = getTextFromNode(last.querySelector('div[data-message-author-role="assistant"]'));
}
console.log('send', {
text: lastText,
})
this.socket.send(
JSON.stringify({
type: 'answer',
text: lastText,
})
);
await sleep(1000);
const button = document.querySelector('button[aria-label="Stop generating"]');
if (button) return
if (
!button
) {
if (this.stop) return
this.stop = true
console.log('send', {
type: 'stop',
})
this.socket.send(
JSON.stringify({
type: 'stop',
})
);
this.observer.disconnect();
await sleep(1000);
resolve();
}
});
const observerConfig = { childList: true, subtree: true };
this.observer.observe(document.body, observerConfig);
}
sendHeartbeat() {
if (this.socket.readyState === WebSocket.OPEN) {
this.socket.send(JSON.stringify({ type: 'heartbeat' }));
}
}
connect() {
this.socket = new WebSocket(WS_URL);
this.socket.onopen = () => {
console.log('Server connected, can process requests now.');
this.dom.innerHTML = '<div style="color: green; ">API Connected !</div>';
}
this.socket.onclose = () => {
console.log('The server connection has been disconnected, the request cannot be processed.');
this.dom.innerHTML = '<div style="color: red; ">API Disconnected !</div>';
setTimeout(() => {
console.log('Attempting to reconnect...');
this.connect();
}, 2000);
}
this.socket.onerror = () => {
console.log('Server connection error, please check the server.');
this.dom.innerHTML = '<div style="color: red; ">API Error !</div>';
}
this.socket.onmessage = (event) => {
const data = JSON.parse(event.data)
console.log('params', data)
this.queue.push(data);
this.processQueue();
};
}
processQueue() {
console.log('Processing queue ', this.isProcessing);
if (!this.isProcessing && this.queue.length > 0) {
this.isProcessing = true;
const data = this.queue.shift();
this.start(data).then(() => {
this.isProcessing = false;
this.processQueue();
});
}
}
init() {
window.addEventListener('load', () => {
this.dom = document.createElement('div');
this.dom.style = 'position: fixed; top: 10px; right: 10px; z-index: 9999; display: flex; justify-content: center; align-items: center;';
document.body.appendChild(this.dom);
this.connect();
setInterval(() => this.sendHeartbeat(), 30000);
});
}
}
(function () {
'use strict';
const app = new App();
app.init();
})();
You will need to 1 disable CSP - you can look for a CSP disabling extension on the chrome store.
Also the document selectors have changed since the UI on CGPT has changed quite a bit. I tweaked the tampermonkey script to work with the latest UI. I have tested with gpt 4 only
// ==UserScript== // @name ChatGPT API By Browser Script // @namespace http://tampermonkey.net/ // @version 1 // @match https://chat.openai.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=openai.com // @grant GM_webRequest // @license MIT // ==/UserScript== console.log('starting'); const WS_URL = `ws://localhost:8765`; function getTextFromNode(node) { let result = ''; if (!node) return result; const childNodes = node.childNodes; for (let i = 0; i < childNodes.length; i++) { let childNode = childNodes[i]; if (childNode.nodeType === Node.TEXT_NODE) { result += childNode.textContent; } else if (childNode.nodeType === Node.ELEMENT_NODE) { result += getTextFromNode(childNode); } } return result; } function sleep(time) { return new Promise((resolve) => setTimeout(resolve, time)); } // Main app class class App { constructor() { this.socket = null; this.observer = null; this.stop = false; this.dom = null; } async start({ text, model, newChat }) { this.stop = false const textarea = document.querySelector('textarea'); textarea.value = text; const event = new Event('input', { bubbles: true }); textarea.dispatchEvent(event); await sleep(500); const send = document.querySelectorAll('button[data-testid="send-button"]')[0]; if (send) { send.click(); } else { console.log('Send button not found'); } this.observeMutations(); } observeMutations() { this.observer = new MutationObserver(async (mutations) => { const list = [...document.querySelectorAll('div.agent-turn')]; const last = list[list.length - 1]; if (!last) { console.log('No last message found'); return; } const lastText = getTextFromNode(last.querySelector('div[data-message-author-role="assistant"]')); console.log('send', { text: lastText, }) this.socket.send( JSON.stringify({ type: 'answer', text: lastText, }) ); await sleep(1000); const button = document.querySelector('button[aria-label="Stop generating"]'); if (button) return if ( !button ) { if (this.stop) return this.stop = true console.log('send', { type: 'stop', }) this.socket.send( JSON.stringify({ type: 'stop', }) ); this.observer.disconnect(); } }); const observerConfig = { childList: true, subtree: true }; this.observer.observe(document.body, observerConfig); } sendHeartbeat() { if (this.socket.readyState === WebSocket.OPEN) { this.socket.send(JSON.stringify({ type: 'heartbeat' })); } } connect() { this.socket = new WebSocket(WS_URL); this.socket.onopen = () => { console.log('Server connected, can process requests now.'); this.dom.innerHTML = '<div style="color: green; ">API Connected !</div>'; } this.socket.onclose = () => { console.log('The server connection has been disconnected, the request cannot be processed.'); this.dom.innerHTML = '<div style="color: red; ">API Disconnected !</div>'; setTimeout(() => { console.log('Attempting to reconnect...'); this.connect(); }, 2000); } this.socket.onerror = () => { console.log('Server connection error, please check the server.'); this.dom.innerHTML = '<div style="color: red; ">API Error !</div>'; } this.socket.onmessage = (event) => { const data = JSON.parse(event.data) console.log('params', data) this.start(data); }; } init() { window.addEventListener('load', () => { this.dom = document.createElement('div'); this.dom.style = 'position: fixed; top: 10px; right: 10px; z-index: 9999; display: flex; justify-content: center; align-items: center;'; document.body.appendChild(this.dom); this.connect(); setInterval(() => this.sendHeartbeat(), 30000); }); } } (function () { 'use strict'; const app = new App(); app.init(); })();
Utilizing your script, I can only retrieve a response to the previous message, not the one currently being sent.
I have made a few tweaks lately, mainly around queueing up multiple asynchronous calls and how it extracts Markdown. I have updated the original comment see if that works for you.
I have made a few tweaks lately, mainly around queueing up multiple asynchronous calls and how it extracts Markdown. I have updated the original comment see if that works for you.
good job! Thank you for your assistance. However, have you conducted tests under GPT-4? I have consistently been unable to receive user responses.
Yes - I use it almost daily. Make sure you see the "API Connected" message in green on top (I had to disable CSP for that)
Yes - I use it almost daily. Make sure you see the "API Connected" message in green on top (I had to disable CSP for that)
Is there an latest version can you share with me Thank you very much
Hi @codisfy do you have a new script for the latest chatgpt.com interface by any chance?
I have tried everything. changing the port, running as admin, disabling content security policy, using firefox edge chrome, etc.. but the script will not show any error in console, say anything in console or display any text. sometimes it randomly says error for a second or so but i cant find any pattern when that happens
edit: i found the error that sometimes pops up but only once then its gone for some time again doesnt matter what i do "Uncaught NS_ERROR_CONTENT_BLOCKED:"
edit2: the websocket works fine on other sites google etc just not on chatgpt
edit3: lol :) the problem seems to be that the script waits until the page is loaded but that never seems to happen according to the code to do that. i removed it and its kinda hacky and i dont get a response but the script loads in the first place