KudoAI / chatgpt.js

🤖 A powerful, open source client-side JavaScript library for ChatGPT
https://chatgpt.js.org
MIT License
1.91k stars 142 forks source link

sendInNewChat not working #38

Closed Quadragintillion closed 1 year ago

Quadragintillion commented 1 year ago

Uncaught TypeError: Cannot set properties of null (setting 'value')

adamlui commented 1 year ago

@DraginWasTaken can you link to your code?

Quadragintillion commented 1 year ago

I literally just have chatgpt.sendInNewChat("say hi"); in an async function. It may have something to do with that I put the actual file in my directory instead of added a requirement for it (as it was giving an error when I imported it from the site)

adamlui commented 1 year ago

@DraginWasTaken I would love to test all the code you have for context, if you create a repo ( could be private ) that would be cool

adamlui commented 1 year ago

@DraginWasTaken ( I know it should "just work" but if you can do this , you would be really helping to troubleshoot what should be a simple issue )

Quadragintillion commented 1 year ago

const delay = ms => new Promise(res => setTimeout(res, ms));

var page = document.getElementById("main"); var button = document.createElement("button"); button.addEventListener("click", hiIAreAFunction); button.style = "margin: 0px; padding: 5px; border-radius: 0.25rem; border-width: 1px; width: auto; cursor: pointer;"; button.appendChild(document.createTextNode("just a button lol")); page.appendChild(button);

async function hiIAreAFunction() { let userInput = prompt('Please input prompt:'); console.log(userInput); chatgpt.sendInNewChat("say hi"); // test prompt await delay(3000); alert(chatgpt.getResponse(0)); }

adamlui commented 1 year ago

@DraginWasTaken is it a userscript, also are you appending the button to chat.openai.com (and does it appear)

adamlui commented 1 year ago

@DraginWasTaken can you install this userscript and confirm it works for you too?

// ==UserScript==
// @name         New Userscript
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @match        https://chat.openai.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=openai.com
// @grant        none
// ==/UserScript==

(function() {

    // Import condensed chatgpt.js to test

    var chatgpt = {

        send: function(msg) {
            var textArea = document.querySelector('form textarea');
            textArea.value = msg;
            textArea.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 13, bubbles: true }));
        },

        sendInNewChat: function(msg) {
            document.querySelector('nav > a').click();
            setTimeout(function() { chatgpt.send(msg); }, 500);
        },

        getResponse: function(pos) {
            var responseDivSelector = 'main > div > div > div > div > div[class*=group]';
            var strPos = pos.toString().toLowerCase();
            if (/last|final/.test(strPos)) { // get last response
                var responseDivs = document.querySelectorAll(responseDivSelector);
                if (responseDivs.length < 2) return ''; // if no responses, return empty string
                return responseDivs[responseDivs.length - 1].textContent;
            } else { // get nth response
                var nthOfResponse = (

                    // Calculate base number
                    Number.isInteger(pos) ? pos : // do nothing for integers
                    strPos.match(/^\d+/) ? strPos.match(/^\d+/)[0] : // extract first digits for strings w/ them
                    ( // convert words to integers for digitless strings
                        /^(1|one|fir)(st)?$/.test(strPos) ? 1
                        : /^(2|tw(o|en|el(ve|f))|seco)(nd|t[yi])?(e?th)?$/.test(strPos) ? 2
                        : /^(3|th(ree|ir?))(rd|teen|t[yi])?(e?th)?$/.test(strPos) ? 3
                        : /^(4|fou?r)(teen|t[yi])?(e?th)?$/.test(strPos) ? 4
                        : /^(5|fi(ve|f))(teen|t[yi])?(e?th)?$/.test(strPos) ? 5
                        : /^(6|six)(teen|t[yi])?(e?th)?$/.test(strPos) ? 6
                        : /^(7|seven)(teen|t[yi])?(e?th)?$/.test(strPos) ? 7
                        : /^(8|eight?)(teen|t[yi])?(e?th)?$/.test(strPos) ? 8
                        : /^(9|nine?)(teen|t[yi])?(e?th)?$/.test(strPos) ? 9
                        : /^(10|ten)(th)?$/.test(strPos) ? 10 : 1 )

                    // Transform base number if suffixed
                    * ( /ty|ieth$/.test(strPos) ? 10 : 1 ) // x 10 if -ty/ieth
                    + ( /teen(th)?$/.test(strPos) ? 10 : 0 ) // + 10 if -teen/teenth

                ) * 2; // factor for own msg's

                var responseDiv = document.querySelector(`${responseDivSelector}:nth-of-type(${nthOfResponse}) p`);
                return responseDiv ? responseDiv.textContent : '';
            }
        }
    }

    // Functions

    function insertButton() {
        var sidebar = document.querySelector('nav');
        if (!sidebar.contains(button)) { // check if button exists first
            sidebar.appendChild(button);
    }}

    const delay = ms => new Promise(res => setTimeout(res, ms));
    async function hiIAreAFunction() {
        let userInput = prompt('Please input prompt:');
        console.log(userInput);
        chatgpt.sendInNewChat("say hi"); // test prompt
        await delay(3000);
        alert(chatgpt.getResponse(1));
    }

    // Main routine

    // Create button, add listener/style/txt
    var button = document.createElement("button");
    button.addEventListener("click", hiIAreAFunction);
    button.style = "margin: 0px; padding: 5px; border-radius: 0.25rem; border-width: 1px; width: auto; cursor: pointer;";
    button.appendChild(document.createTextNode("just a button lol"));

    // Insert button on page load + during navigation
    insertButton()
    var navObserver = new MutationObserver(function (mutations) {
        mutations.forEach(function (mutation) {
            if (mutation.type === 'childList' && mutation.addedNodes.length) {
                insertButton()
    }})})
    navObserver.observe(document.documentElement, { childList: true, subtree: true })

})();

For the button insertion, it must occur w/ each node mutation or else it can disappear cuz ChatGPT DOM constantly refreshes (so I added a mutation observer)

For getResponse() OpenAI changed their HTML structure so it didn't work for you, but the function in this script has been updated and will be added to latest release of lib & works as intended/indicated by alert at end of demo:

https://user-images.githubusercontent.com/10906554/235551575-0e0531c4-458b-4c5f-993e-ca085f7534e7.mp4

adamlui commented 1 year ago

@DraginWasTaken I didn't update the URL https://code.chatgptjs.org/chatgpt-latest.min.js in a while but now it should point to the latest release

You can use in async IIFE like:

(async function() {
    const { chatgpt } = await import('https://code.chatgptjs.org/chatgpt-latest.min.js')
    // Your code here
})()

..and in userscript if publishing to repo, a hash is required so it is

...
@require https://cdn.jsdelivr.net/gh/chatgptjs/chatgpt.js@abc1534f6d97601f944c171518bd249056a752ba/dist/chatgpt-1.6.3.min.js
// ==/UserScript==
// Your code here
Quadragintillion commented 1 year ago

@adamlui Refused to load the script 'https://code.chatgptjs.org/chatgpt-latest.min.js' because it violates the following Content Security Policy directive: "script-src 'self' 'wasm-unsafe-eval' 'inline-speculation-rules' http://localhost: http://127.0.0.1:". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.

adamlui commented 1 year ago

@DraginWasTaken it is being injected to chat.openai.com? Also can you try the updated code in the repo in a local dir again?

Quadragintillion commented 1 year ago

I just reran it and noticed a 2nd error: Uncaught (in promise) TypeError: Failed to fetch dynamically imported module: https://code.chatgptjs.org/chatgpt-latest.min.js

Also, I probably should have said this before, but I'm making a Chrome extension. I don't know if that changes anything.

adamlui commented 1 year ago

Thanks for the info, for Chrome extensions they don't work well with remote-imported libs for some reason (I need to updated readme with Chrome instructions) but the way it works for me is, I include the library in lib/, then, at the top of content or background script, I write:

/* NOTE: This script uses code from the powerful chatgpt.js library @ https://chatgpt.js.org */
/* (c) 2023 Adam Lui, chatgpt.js & contributors under the MIT license. */

(async function() {

    // Import libs
    var { chatgpt } = await import(chrome.runtime.getURL('lib/chatgpt.js'))

    // your code here

})()

(also the extension should be run at chat.openai.com as the lib interacts w/ the DOM there)

adamlui commented 1 year ago

@DraginWasTaken when you finish the extension I'd love to add it to showcase section of readme (especially if it's epic!)

Quadragintillion commented 1 year ago

It's giving yet another error... Uncaught (in promise) TypeError: Failed to fetch dynamically imported module: chrome-extension://#########/lib/chatgpt.js

(btw the id is censored just bc i havent made a chrome extension before and idk if that just lets ppl access it, prob not but its just a safety precaution)

adamlui commented 1 year ago

@DraginWasTaken I have to rest now because of COVID my heart is in pain, but I promise you I will try to solve your problem to the fullest when I awaken (I realized I had modified chatgpt.js for Chrome by adding export { chatgpt } as last line to make it work + some manifest.json lines, but I would prefer the export to work in one single file so users don't have to edit based on environment)

Quadragintillion commented 1 year ago

I'm using manifest v3, is that any different? Also, I hope you get better soon!

adamlui commented 1 year ago

Hey @DraginWasTaken thanks for recovery wishes, I added Chrome instructions here finally: https://chatgpt.js.org/#/?id=chrome

Quadragintillion commented 1 year ago

Thanks so much! It finally imports! However, I'm having another issue. I have this code:

chatgpt.sendInNewChat("say hi");
await delay(3000);
alert(chatgpt.getResponse());

It gives this error when run: Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'toString') at Object.getResponse (chatgpt.js:226:26) at HTMLButtonElement.hiIAreAFunction

Quadragintillion commented 1 year ago

Wait nevermind, that was on me, I put getResponse instead of getLastResponse. But now it's returning blank when it's called. (Same code but getLastResponse)

adamlui commented 1 year ago

@DraginWasTaken can you paste the getLastResponse function from lib/chatgpt.js?

Quadragintillion commented 1 year ago

getLastResponse: function() { var lastResponseDiv = chatgpt.getLastResponseDiv(); return lastResponseDiv ? lastResponseDiv.textContent : ''; },

adamlui commented 1 year ago

@DraginWasTaken I put your code as lines 19-21 of content.js in this demo extension.zip, and it works in Chrome for me:

https://user-images.githubusercontent.com/10906554/236592862-77060e92-6833-440f-af00-e3b201965820.mp4

(Also you can use await chatgpt.isIdle() to ensure response is received first)

Quadragintillion commented 1 year ago

Wait, I think I'm using this library totally wrong. The extension I'm making isn't for the actual OpenAI website, I've been using it to try to connect to ChatGPT from another website.

Quadragintillion commented 1 year ago

Am I using it correctly?

adamlui commented 1 year ago

Ohh @Quadragintillion it works only if you are making it for the actual OpenAI website. But you can look at https://github.com/kudoai/bravegpt and https://github.com/kudoai/duckduckgpt to see how I connect from other sites (not using lib)