├── .gitignore ├── LICENSE ├── README.md ├── chatgpt_selenium_automation ├── __init__.py └── handler.py ├── requirements └── setup.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 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # Text file generated 156 | *.txt 157 | 158 | # Remote profile folder 159 | remote-profile/ 160 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Michelangelo27 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chatgpt_selenium_automation 2 | 3 | ChatGPT Automation is a Python project that aims to automate interactions with OpenAI's ChatGPT using Selenium WebDriver. Currently, it requires human interaction for log-in and human verification. It handles launching Chrome, connecting to ChatGPT, sending prompts, and retrieving responses. This tool can be useful for experimenting with ChatGPT or building similar web automation tools. 4 | 5 | 6 | ## Prerequisites 7 | 8 | 1. Install the library: pip install git+https://github.com/Michelangelo27/chatgpt_selenium_automation.git 9 | 2. Download the appropriate version of `chromedriver.exe` and save it to a known location on your system. 10 | 11 | 12 | ## Example Usage 13 | 14 | ```python 15 | from chatgpt_selenium_automation.handler import ChatGPTAutomation 16 | 17 | # Define the path where the chrome driver is installed on your computer 18 | chrome_driver_path = r"C:\Users\user\Desktop\chromedriver.exe" 19 | 20 | # the sintax r'"..."' is required because the space in "Program Files" in the chrome path 21 | chrome_path = r'"C:\Program Files\Google\Chrome\Application\chrome.exe"' 22 | 23 | # Create an instance 24 | chatgpt = ChatGPTAutomation(chrome_path, chrome_driver_path) 25 | 26 | # Define a prompt and send it to chatgpt 27 | prompt = "What are the benefits of exercise?" 28 | chatgpt.send_prompt_to_chatgpt(prompt) 29 | 30 | # Retrieve the last response from ChatGPT 31 | response = chatgpt.return_last_response() 32 | print(response) 33 | 34 | # Save the conversation to a text file 35 | file_name = "conversation.txt" 36 | chatgpt.save_conversation(file_name) 37 | 38 | # Close the browser and terminate the WebDriver session 39 | chatgpt.quit() 40 | ``` 41 | 42 | 43 | ## Note 44 | 45 | After instantiating the ChatGPTAutomation class, chrome will open up to chatgpt page, it will require you to manually complete the register/ log-in / Human-verification. After that, you must tell the program to continue, in the console type 'y'. After Those steps, the program will be able to continue autonomously. 46 | 47 | ## Note on Changing Tabs or Conversations 48 | 49 | Please be aware that changing tabs or switching to another conversation while the script is running might cause errors or lead to the methods being applied to unintended chats. For optimal results and to avoid unintended consequences, it is recommended to avoid to manually interact with the browser (after the log-in/human verification) while the automation script is running. 50 | 51 | 52 | 53 | ## Note on Errors and Warnings 54 | 55 | While running the script, you may see some error messages or warnings in the console, such as: 56 | - DevTools listening on ws://... 57 | - INFO: Created TensorFlow Lite XNNPACK delegate for CPU. 58 | - ERROR: Couldn't read tbsCertificate as SEQUENCE 59 | - ERROR: Failed parsing Certificate 60 | 61 | 62 | These messages are related to the underlying libraries or the browser, and you can safely ignore them if the script works as expected. If you encounter any issues with the script, please ensure that you have installed the correct dependencies and are using the correct ChromeDriver version compatible with your Chrome browser. 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /chatgpt_selenium_automation/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /chatgpt_selenium_automation/handler.py: -------------------------------------------------------------------------------- 1 | import os 2 | import socket 3 | import threading 4 | import time 5 | 6 | from selenium import webdriver 7 | from selenium.webdriver.common.by import By 8 | from selenium.webdriver.common.keys import Keys 9 | 10 | 11 | class ChatGPTAutomation: 12 | 13 | def __init__(self, chrome_path, chrome_driver_path): 14 | """ 15 | This constructor automates the following steps: 16 | 1. Open a Chrome browser with remote debugging enabled at a specified URL. 17 | 2. Prompt the user to complete the log-in/registration/human verification, if required. 18 | 3. Connect a Selenium WebDriver to the browser instance after human verification is completed. 19 | 20 | :param chrome_path: file path to chrome.exe (ex. C:\\Users\\User\\...\\chromedriver.exe) 21 | :param chrome_driver_path: file path to chromedriver.exe (ex. C:\\Users\\User\\...\\chromedriver.exe) 22 | """ 23 | 24 | self.chrome_path = chrome_path 25 | self.chrome_driver_path = chrome_driver_path 26 | 27 | url = r"https://chat.openai.com" 28 | free_port = self.find_available_port() 29 | self.launch_chrome_with_remote_debugging(free_port, url) 30 | self.wait_for_human_verification() 31 | self.driver = self.setup_webdriver(free_port) 32 | self.cookie = self.get_cookie() 33 | 34 | @staticmethod 35 | def find_available_port(): 36 | """ This function finds and returns an available port number on the local machine by creating a temporary 37 | socket, binding it to an ephemeral port, and then closing the socket. """ 38 | 39 | with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: 40 | s.bind(('', 0)) 41 | s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 42 | return s.getsockname()[1] 43 | 44 | def launch_chrome_with_remote_debugging(self, port, url): 45 | """ Launches a new Chrome instance with remote debugging enabled on the specified port and navigates to the 46 | provided url """ 47 | 48 | def open_chrome(): 49 | chrome_cmd = f"{self.chrome_path} --remote-debugging-port={port} --user-data-dir=remote-profile {url}" 50 | os.system(chrome_cmd) 51 | 52 | chrome_thread = threading.Thread(target=open_chrome) 53 | chrome_thread.start() 54 | 55 | def setup_webdriver(self, port): 56 | """ Initializes a Selenium WebDriver instance, connected to an existing Chrome browser 57 | with remote debugging enabled on the specified port""" 58 | 59 | chrome_options = webdriver.ChromeOptions() 60 | chrome_options.binary_location = self.chrome_driver_path 61 | chrome_options.add_experimental_option("debuggerAddress", f"127.0.0.1:{port}") 62 | driver = webdriver.Chrome(options=chrome_options) 63 | return driver 64 | 65 | def get_cookie(self): 66 | """ 67 | Get chat.openai.com cookie from the running chrome instance. 68 | """ 69 | cookies = self.driver.get_cookies() 70 | cookie = [elem for elem in cookies if elem["name"] == '__Secure-next-auth.session-token'][0]['value'] 71 | return cookie 72 | 73 | def send_prompt_to_chatgpt(self, prompt): 74 | """ Sends a message to ChatGPT and waits for 20 seconds for the response """ 75 | 76 | input_box = self.driver.find_element(by=By.XPATH, value='//textarea[contains(@id, "prompt-textarea")]') 77 | self.driver.execute_script(f"arguments[0].value = '{prompt}';", input_box) 78 | input_box.send_keys(Keys.RETURN) 79 | input_box.submit() 80 | self.check_response_ended() 81 | 82 | def check_response_ended(self): 83 | """ Checks if ChatGPT response ended """ 84 | start_time = time.time() 85 | while len(self.driver.find_elements(by=By.CSS_SELECTOR, value='div.text-base')[-1].find_elements( 86 | by=By.CSS_SELECTOR, value='button.text-token-text-tertiary')) < 1: 87 | time.sleep(0.5) 88 | # Exit the while loop after 60 seconds anyway 89 | if time.time() - start_time > 60: 90 | break 91 | time.sleep(1) # the length should be =4, so it's better to wait a moment to be sure it's really finished 92 | 93 | def return_chatgpt_conversation(self): 94 | """ 95 | :return: returns a list of items, even items are the submitted questions (prompts) and odd items are chatgpt response 96 | """ 97 | 98 | return self.driver.find_elements(by=By.CSS_SELECTOR, value='div.text-base') 99 | 100 | def save_conversation(self, file_name): 101 | """ 102 | It saves the full chatgpt conversation of the tab open in chrome into a text file, with the following format: 103 | prompt: ... 104 | response: ... 105 | delimiter 106 | prompt: ... 107 | response: ... 108 | 109 | :param file_name: name of the file where you want to save 110 | """ 111 | 112 | directory_name = "conversations" 113 | if not os.path.exists(directory_name): 114 | os.makedirs(directory_name) 115 | 116 | delimiter = "|^_^|" 117 | chatgpt_conversation = self.return_chatgpt_conversation() 118 | with open(os.path.join(directory_name, file_name), "a") as file: 119 | for i in range(0, len(chatgpt_conversation), 2): 120 | file.write( 121 | f"prompt: {chatgpt_conversation[i].text}\nresponse: {chatgpt_conversation[i + 1].text}\n\n{delimiter}\n\n") 122 | 123 | def return_last_response(self): 124 | """ :return: the text of the last chatgpt response """ 125 | 126 | response_elements = self.driver.find_elements(by=By.CSS_SELECTOR, value='div.text-base') 127 | return response_elements[-1].text 128 | 129 | @staticmethod 130 | def wait_for_human_verification(): 131 | print("You need to manually complete the log-in or the human verification if required.") 132 | 133 | while True: 134 | user_input = input( 135 | "Enter 'y' if you have completed the log-in or the human verification, or 'n' to check again: ").lower().strip() 136 | 137 | if user_input == 'y': 138 | print("Continuing with the automation process...") 139 | break 140 | elif user_input == 'n': 141 | print("Waiting for you to complete the human verification...") 142 | time.sleep(5) # You can adjust the waiting time as needed 143 | else: 144 | print("Invalid input. Please enter 'y' or 'n'.") 145 | 146 | def quit(self): 147 | """ Closes the browser and terminates the WebDriver session.""" 148 | print("Closing the browser...") 149 | self.driver.close() 150 | self.driver.quit() 151 | -------------------------------------------------------------------------------- /requirements: -------------------------------------------------------------------------------- 1 | selenium==4.9.0 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='chatgpt_selenium_automation', 5 | version='1.1', 6 | packages=find_packages(), 7 | install_requires=[ 8 | 'selenium>=4.9.0', 9 | ], 10 | ) --------------------------------------------------------------------------------