Skip to content
This repository has been archived by the owner on Dec 7, 2023. It is now read-only.

Commit

Permalink
Merge pull request #70 from terry3041/patch-pypasser
Browse files Browse the repository at this point in the history
Add PyPasser support
  • Loading branch information
terry3041 authored Dec 30, 2022
2 parents d71b308 + 590e480 commit 3622263
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 38 deletions.
27 changes: 16 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,30 @@ session_token = 'abc123' # `__Secure-next-auth.session-token` cookie from https
api = ChatGPT(session_token) # auth with session token
api = ChatGPT(session_token, conversation_id='some-random-uuid') # specify conversation id
api = ChatGPT(session_token, proxy='http://proxy.example.com:8080') # specify proxy
api = ChatGPT(auth_type='google', email='[email protected]', password='password') # auth with google login
api = ChatGPT(session_token, moderation=False) # disable moderation
api = ChatGPT(session_token, window_size=(1024, 768)) # specify window size
api = ChatGPT(session_token, verbose=True) # verbose mode (print debug messages)

# login with openai and solve simple reCAPTCHA automatically, but you should reCAPTCHA by yourself,
# it does not work on server without GUI.
api = ChatGPT(auth_type='openai', email='[email protected]', password='password', verbose=True)
# login with openai and solve simple reCAPTCHA automatically, while pass reCAPTCHA enterprise with 2captcha API,
# it works on server without GUI.
api = ChatGPT(auth_type='openai', email='[email protected]', password='password',
twocaptcha_apikey='2captcha_apikey',
verbose=True)
# auth with google login
api = ChatGPT(auth_type='google', email='[email protected]', password='password')
# auth with openai login (captcha solving using speech-to-text engine)
api = ChatGPT(auth_type='openai', email='[email protected]', password='password')
# auth with openai login (manual captcha solving)
api = ChatGPT(
auth_type='openai', captcha_solver='manual',
email='[email protected]', password='password'
)
# auth with openai login (2captcha for captcha solving)
api = ChatGPT(
auth_type='openai', captcha_solver='2captcha', solver_apikey='abc',
email='[email protected]', password='password'
)
# reuse cookies generated by successful login before login,
# if `login_cookies_path` does not exist, it will process logining with `auth_type`, and save cookies to `login_cookies_path`
# only works when `auth_type` is `openai` or `google`
api = ChatGPT(auth_type='openai', email='[email protected]', password='password',
login_cookies_path='your_cookies_path',
verbose=True)
login_cookies_path='your_cookies_path',
)

resp = api.send_message('Hello, world!')
print(resp['message'])
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "pyChatGPT"
version = "0.4.0"
version = "0.4.1"
authors = [
{ name="terry3041", email="[email protected]" },
]
Expand All @@ -19,7 +19,7 @@ classifiers = [
dependencies = [
'undetected-chromedriver >= 3.1.7',
'PyVirtualDisplay >= 3.0',
'markdownify >= 0.11.6',
'markdownify >= 0.11.6'
]

[project.urls]
Expand Down
93 changes: 68 additions & 25 deletions src/pyChatGPT/pyChatGPT.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ def __init__(
moderation: bool = True,
verbose: bool = False,
window_size: tuple = (800, 600),
twocaptcha_apikey: str = '',
openai_auth_semi_automatic: bool = True,
captcha_solver: str = 'pypasser',
solver_apikey: str = '',
login_cookies_path: str = '',
) -> None:
'''
Expand All @@ -48,8 +48,8 @@ def __init__(
- moderation: (optional) Whether to enable message moderation. Default is `True`
- verbose: (optional) Whether to print debug messages
- window_size: (optional) window_size for web driver
- twocaptcha_apikey: (optional) 2captcha apikey, for solving reCAPTCHA. Use the apikey only for auth_type='openai'
- openai_auth_semi_automatic: (optional) allow solving reCAPTCHA by user when 2captcha method have failed.
- captcha_solver: (optional) captcha solving method. Can be `pypasser` or `2captcha` or `manual`
- solver_apikey: (optional) captcha solver apikey, for solving reCAPTCHA. Use the apikey only for captcha_solver='2captcha'
- login_cookies_path: (optional) cookies path to be saved or loaded.
'''
self.__verbose = verbose
Expand All @@ -65,8 +65,8 @@ def __init__(
self.__auth_type = auth_type
self.__window_size = window_size
self.__moderation = moderation
self.__twocaptcha_apikey = twocaptcha_apikey
self.__openai_auth_semi_automatic = openai_auth_semi_automatic
self.__captcha_solver = captcha_solver
self.__solver_apikey = solver_apikey
self.__login_cookies_path = login_cookies_path
if self.__auth_type not in [None, 'google', 'windowslive', 'openai']:
raise ValueError('Invalid authentication type')
Expand All @@ -77,6 +77,28 @@ def __init__(
raise ValueError(
'Please provide either a session token or login credentials'
)
if self.__auth_type == 'openai':
if self.__captcha_solver not in ['pypasser', '2captcha', 'manual']:
raise ValueError(
'Invalid captcha solving method. Can be pypasser, 2captcha or manual'
)
if self.__captcha_solver == '2captcha' and not self.__solver_apikey:
raise ValueError('Please provide a 2captcha apikey')
if self.__captcha_solver == 'pypasser':
try:
import ffmpeg_downloader as ffdl
except ModuleNotFoundError:
raise ValueError(
'Please install ffmpeg_downloader, PyPasser, and pocketsphinx by running `pip install ffmpeg_downloader PyPasser pocketsphinx`'
)

ffmpeg_installed = bool(ffdl.ffmpeg_version)
self.__verbose_print('[0] ffmpeg installed:', ffmpeg_installed)
if not ffmpeg_installed:
import subprocess as sp

sp.run(['ffdl', 'install'])
os.environ['PATH'] += os.pathsep + ffdl.ffmpeg_dir

self.__is_headless = (
platform.system() == 'Linux' and 'DISPLAY' not in os.environ
Expand Down Expand Up @@ -178,27 +200,30 @@ def __init_browser(self) -> None:
self.__check_and_dismiss_intro()

# Check if there is an alert
self.__verbose_print('[init] Check if there is alert')
self.__check_and_dismiss_alert()

def __check_and_dismiss_alert(self):
self.__verbose_print('[check_alert] Check if there is alert')
alerts = self.driver.find_elements(By.XPATH, '//div[@role="alert"]')
if alerts:
self.__verbose_print('Dismissing alert')
self.__verbose_print('[check_alert] Dismissing alert')
self.driver.execute_script(
"""
var element = document.querySelector('div[role="alert"]');
if (element)
element.parentNode.removeChild(element);
"""
)
else:
self.__verbose_print('[check_alert] Did not found one')

def __check_and_dismiss_intro(self):
self.__verbose_print('[check_intro] Check if there is intro')
try:
WebDriverWait(self.driver, 3).until(
EC.presence_of_element_located((By.ID, 'headlessui-portal-root'))
)
self.__verbose_print('Dismissing intro')
self.__verbose_print('[check_intro] Dismissing intro')
self.driver.execute_script(
"""
var element = document.getElementById('headlessui-portal-root');
Expand All @@ -207,21 +232,21 @@ def __check_and_dismiss_intro(self):
"""
)
except SeleniumExceptions.TimeoutException:
self.__verbose_print('[init] Did not found one')
self.__verbose_print('[check_intro] Did not found one')
pass

def __save_chat_gpt_cookies(self, path):
with open(path, 'w', encoding='utf-8') as f:
cookies_list = self.driver.execute_cdp_cmd(
"Network.getCookies", {"urls": ["https://chat.openai.com/chat"]}
)["cookies"]
'Network.getCookies', {'urls': ['https://chat.openai.com/chat']}
)['cookies']
json.dump(cookies_list, f, indent=2, ensure_ascii=False)

def __load_chat_gpt_cookies(self, path):
with open(path, 'r', encoding='utf-8') as f:
cookies_list = json.load(f)
for cookie in cookies_list:
if cookie["name"] == "__Secure-next-auth.session-token":
if cookie['name'] == '__Secure-next-auth.session-token':
self.driver.execute_cdp_cmd('Network.setCookie', cookie)

def __login(self) -> None:
Expand All @@ -244,6 +269,9 @@ def __login(self) -> None:
(By.XPATH, '//div[text()="Welcome to ChatGPT"]')
)
)
WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((By.XPATH, '//button[text()="Log in"]'))
)
self.driver.find_element(By.XPATH, '//button[text()="Log in"]').click()

WebDriverWait(self.driver, 5).until(
Expand Down Expand Up @@ -281,16 +309,16 @@ def __login(self) -> None:
def __check_capacity(self, target_url):
while True:
try:
self.__verbose_print('Checking if ChatGPT is at capacity')
self.__verbose_print('[check_cap] Checking if ChatGPT is at capacity')
WebDriverWait(self.driver, 3).until(
EC.presence_of_element_located(
(By.XPATH, '//div[text()="ChatGPT is at capacity right now"]')
)
)
self.__verbose_print('ChatGPT is at capacity, retrying')
self.__verbose_print('[check_cap] ChatGPT is at capacity, retrying')
self.driver.get(target_url)
except SeleniumExceptions.TimeoutException:
self.__verbose_print('ChatGPT is not at capacity')
self.__verbose_print('[check_cap] ChatGPT is not at capacity')
break

def __google_login(self):
Expand Down Expand Up @@ -375,10 +403,15 @@ def __have_image_recaptcha(self):

def __2captcha_solve(self, enterprise=1, retry=8):
self.driver.switch_to.default_content()
import twocaptcha
try:
import twocaptcha
except ModuleNotFoundError:
raise ValueError(
'twocaptcha module not found, please install it using `pip install 2captcha-python`'
)

self.__verbose_print('[reCAPTCHA] trying twocaptcha max retry =', retry)
solver = twocaptcha.TwoCaptcha(self.__twocaptcha_apikey, pollingInterval=5)
solver = twocaptcha.TwoCaptcha(self.__solver_apikey, pollingInterval=5)
# get result using 2Captcha
el = self.driver.find_element(
By.XPATH, '//div[@data-recaptcha-provider="recaptcha_enterprise"]'
Expand All @@ -394,7 +427,7 @@ def __2captcha_solve(self, enterprise=1, retry=8):
enterprise=enterprise,
)
except Exception as e:
self.__verbose_print('twocaptcha solver error', e)
self.__verbose_print('[reCAPTCHA] twocaptcha solver error:', str(e))
if result is not None:
break
if result is None:
Expand Down Expand Up @@ -436,10 +469,11 @@ def __openai_login(self):
)
)
)
self.driver.find_element(
By.XPATH,
'//label[@class="rc-anchor-center-item rc-anchor-checkbox-label"]',
).click()
# Create a div and block pypasser for some reasons
# self.driver.find_element(
# By.XPATH,
# '//label[@class="rc-anchor-center-item rc-anchor-checkbox-label"]',
# ).click()
need_check_recaptcha_result = True
except SeleniumExceptions.NoSuchFrameException as e:
self.__verbose_print(e)
Expand All @@ -455,16 +489,25 @@ def __openai_login(self):
)
)
except SeleniumExceptions.TimeoutException:
if self.__twocaptcha_apikey:
if self.__captcha_solver == '2captcha':
self.__2captcha_solve()
elif self.__captcha_solver == 'pypasser':
from pypasser import reCaptchaV2

self.__verbose_print('[reCAPTCHA] trying pypasser max retry = 3')
try:
reCaptchaV2(self.driver)
except Exception as e:
self.__verbose_print('[reCAPTCHA] pypasser solver error:', str(e))

if need_check_recaptcha_result:
if self.__have_recaptcha_value():
self.__verbose_print('[login] Congrats, solved reCAPTCHA.')
elif self.__openai_auth_semi_automatic:
else:
self.__verbose_print(
'[login] Ops, you have to solve reCAPTCHA on browser.'
)
self.driver.get(self.driver.current_url)
while need_check_recaptcha_result:
# check image selection reCAPTCHA
# self.__have_image_recaptcha()
Expand Down

0 comments on commit 3622263

Please sign in to comment.