zsodur / chatgpt-api-by-browser-script

This project runs on users' browsers through the Tampermonkey script and converts the web version of ChatGPT operations into an API interface. This way, you can use GPT-4 in chatgpt web pages to play things like Auto-GPT. / 该项目通过Tampermonkey脚本在用户的浏览器上运行,并将ChatGPT操作的Web版本转换为API接口。这样,您就可以使用chatgpt网页中的GPT-4来玩Auto-GPT等内容。
195 stars 40 forks source link

The script just doesnt load somehow #12

Open MoritzHinterleitner opened 8 months ago

MoritzHinterleitner commented 8 months ago

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

codisfy commented 7 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();
})();
yuligesec commented 6 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;
  }

  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.

codisfy commented 6 months ago

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.

yuligesec commented 6 months ago

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.

codisfy commented 6 months ago

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)

mzywl commented 4 months ago

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

SantoshSrinivas79 commented 3 months ago

Hi @codisfy do you have a new script for the latest chatgpt.com interface by any chance?