├── .gitignore ├── README.md ├── requirements.txt └── server.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 30 | __pypackages__/ 31 | 32 | # Environments 33 | .env 34 | .venv 35 | .idea 36 | env/ 37 | venv/ 38 | pyenv/ 39 | ENV/ 40 | env.bak/ 41 | venv.bak/ 42 | 43 | # pytype static type analyzer 44 | .pytype/ 45 | 46 | # Cython debug symbols 47 | cython_debug/ 48 | 49 | #playwright 50 | playwright/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChatGPT api for everyone 2 | 3 | * Modified code from unofficial chatgpt to make it work with latest ui 4 | * It uses playwright and chromium to open browser and parse html. 5 | * It is an unoffical api for development purpose only. 6 | 7 | 8 | # How to install 9 | 10 | * Make sure that python and virual environment is installed. 11 | 12 | * Create a new virtual environment 13 | 14 | ```python 15 | For Linux: 16 | 17 | # one time 18 | virtualenv -p $(which python3) env 19 | 20 | # everytime you want to run the server 21 | source env/bin/activate 22 | ``` 23 | 24 | ```python 25 | For Windows: 26 | 27 | # one time 28 | python -m venv env 29 | 30 | # everytime you want to run the server 31 | env/Scripts/activate 32 | ``` 33 | 34 | * If you get error for windows while activating environment: 35 | ```shell 36 | if you get error: 37 | cannot be loaded because running scripts is disabled on this system. 38 | 39 | Solution: 40 | 1. Type Powershell and hit Enter. This will open the PowerShell interface. 41 | 2. Now, to change the Execution Policy, type Set-ExecutionPolicy RemoteSigned -Scope CurrentUser and hit Enter. This will allow scripts downloaded from the internet which are signed by a trusted publisher to be run. 42 | 3. PowerShell will ask for your confirmation. Type Y and hit Enter to confirm the policy change. 43 | 4. Try running your script again after this. It should work if the error was related to execution policy. 44 | ``` 45 | 46 | * Now install the requirements 47 | 48 | ``` 49 | pip install -r requirements.txt 50 | ``` 51 | 52 | * If you are installing playwright for the first time, it will ask you to run this command for one time only. 53 | 54 | ``` 55 | playwright install 56 | ``` 57 | 58 | * Now run the server, login with details, press enter on terminal. 59 | 60 | ``` 61 | python server.py 62 | ``` 63 | 64 | * once your login details is cached locally, you can change headless=True, to run it in headlesss mode. 65 | 66 | * The server runs at port `5001`. If you want to change, you can change it in server.py 67 | 68 | 69 | # Api Documentation 70 | 71 | * There is a single end point only which is working right now. It is available at `/chat` 72 | 73 | ```shell 74 | For Windows (Powershell), the command will be: 75 | 76 | Invoke-RestMethod -Uri "http://localhost:5001/chat" -Method Post -Body '{"message":"Hello GPT"}' -ContentType 'application/json' 77 | ``` 78 | ```shell 79 | For Linux, the command will be: 80 | 81 | curl -X POST -H "Content-Type: application/json" -d '{"message":"Hello GPT"}' "http://localhost:5001/chat" 82 | ``` 83 | 84 | 85 | # Credit 86 | 87 | * I modified script to work with latest Openai UI & windows, added documentation for running on windows, originally from [Daniel Gross's whatsapp gpt](https://github.com/danielgross/whatsapp-gpt) package which was documented by [Taranjeet ](https://github.com/taranjeet/unofficial-chatgpt-api/) for easy use. 88 | 89 | # Disclaimer 90 | 91 | Please note that this project is a personal undertaking and not an official OpenAI product. It is not affiliated with OpenAI in any way, and should not be mistaken as such. 92 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | playwright -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- 1 | """Make some requests to OpenAI's chatbot""" 2 | 3 | import time 4 | import os 5 | import flask 6 | from flask import g 7 | from flask import request 8 | 9 | from playwright.sync_api import sync_playwright 10 | 11 | 12 | def create_dir(): 13 | # Get the current directory 14 | current_dir = os.getcwd() 15 | 16 | # Create the playwright directory if not exist 17 | playwright_dir = os.path.join(current_dir, 'playwright') 18 | if not os.path.exists(playwright_dir): 19 | os.makedirs(playwright_dir) 20 | 21 | return playwright_dir 22 | 23 | playwright_dir = create_dir() 24 | 25 | APP = flask.Flask(__name__) 26 | PLAY = sync_playwright().start() 27 | BROWSER = PLAY.chromium.launch_persistent_context( 28 | user_data_dir=playwright_dir, 29 | headless=False, 30 | ) 31 | PAGE = BROWSER.new_page() 32 | 33 | def get_input_box(): 34 | """Get the child textarea of `PromptTextarea__TextareaWrapper`""" 35 | return PAGE.query_selector("textarea") 36 | 37 | def is_logged_in(): 38 | # See if we have a textarea with data-id="root" 39 | try: 40 | return get_input_box() is not None 41 | except AttributeError: 42 | return False 43 | 44 | def is_button_visible_and_correct(): 45 | """Check if a button with a specific class""" 46 | button = PAGE.query_selector(".btn.relative.btn-neutral.whitespace-nowrap.-z-0.border-0.md\\:border") 47 | 48 | # Check visibility and text 49 | if button and button.is_visible(): # and button.inner_text() == "Regenerate": 50 | return True 51 | 52 | return False 53 | 54 | def is_loading_response() -> bool: 55 | """See if the regenerate button is visible, if it does, we're not loading""" 56 | return not is_button_visible_and_correct() 57 | 58 | def send_message(message): 59 | # Send the message 60 | box = get_input_box() 61 | box.click() 62 | box.fill(message) 63 | box.press("Enter") 64 | 65 | def get_last_message(): 66 | """Get the latest message""" 67 | while is_loading_response(): 68 | time.sleep(0.25) 69 | print('wait for response over') 70 | page_elements = PAGE.query_selector_all(".markdown.prose.w-full.break-words.dark\\:prose-invert.light") 71 | if page_elements: # Check if any elements were found 72 | last_element = page_elements[-1] # -1 returns the last element in the list, last message 73 | return last_element.inner_text() 74 | else: 75 | return None 76 | 77 | def regenerate_response(): 78 | """Clicks on the Try again button. 79 | Returns None if there is no button""" 80 | try_again_button = PAGE.query_selector("button:has-text('Regenerate')") 81 | if try_again_button is not None: 82 | try_again_button.click() 83 | return try_again_button 84 | 85 | def get_reset_button(): 86 | """Returns the reset thread button (it is an a tag not a button)""" 87 | return PAGE.query_selector("a:has-text('Reset thread')") 88 | 89 | @APP.route("/chat", methods=["POST"]) 90 | def chat(): 91 | message = request.json['message'] 92 | print("Sending message: ", message) 93 | send_message(message) 94 | response = get_last_message() 95 | print("Response: ", response) 96 | return response 97 | 98 | # create a route for regenerating the response 99 | @APP.route("/regenerate", methods=["POST"]) 100 | def regenerate(): 101 | print("Regenerating response") 102 | if regenerate_response() is None: 103 | return "No response to regenerate" 104 | response = get_last_message() 105 | print("Response: ", response) 106 | return response 107 | 108 | @APP.route("/reset", methods=["POST"]) 109 | def reset(): 110 | print("Resetting chat") 111 | get_reset_button().click() 112 | return "Chat thread reset" 113 | 114 | @APP.route("/restart", methods=["POST"]) 115 | def restart(): 116 | global PAGE,BROWSER,PLAY 117 | PAGE.close() 118 | BROWSER.close() 119 | PLAY.stop() 120 | time.sleep(0.25) 121 | PLAY = sync_playwright().start() 122 | BROWSER = PLAY.chromium.launch_persistent_context( 123 | user_data_dir=playwright_dir, 124 | headless=False, 125 | ) 126 | PAGE = BROWSER.new_page() 127 | PAGE.goto("https://chat.openai.com/") 128 | return "API restart!" 129 | 130 | 131 | def start_browser(): 132 | PAGE.goto("https://chat.openai.com/") 133 | APP.run(port=5001, threaded=False) 134 | if not is_logged_in(): 135 | print("Please log in to OpenAI Chat") 136 | print("Press enter when you're done") 137 | input() 138 | else: 139 | print("Logged in") 140 | 141 | 142 | if __name__ == "__main__": 143 | start_browser() 144 | --------------------------------------------------------------------------------