返回提交历史
Modified
g4f/Provider/Aura.py
+4
-1
Modified
g4f/Provider/Bestim.py
+1
-14
Modified
g4f/Provider/Chatgpt4Online.py
+12
-18
Modified
g4f/Provider/PerplexityLabs.py
+1
-1
Modified
g4f/Provider/helper.py
+4
-4
Modified
g4f/Provider/selenium/AItianhuSpace.py
+2
-2
Modified
g4f/errors.py
+2
-2
Modified
g4f/requests.py
+22
-121
Added
g4f/requests_aiohttp.py
+39
-0
Added
g4f/requests_curl_cffi.py
+77
-0
Modified
g4f/webdriver.py
+22
-1
XFEstudio/gpt4free
Fix PerplexityLabs Provider, Improve bypass_cloudflare helper
47b50b48
代码差异
11 个文件
+186
-164
@@ -1,5 +1,7 @@
1
1
from __future__ import annotations
2
2
3
from aiohttp import ClientSession
4
3
5
from ..typing import AsyncResult, Messages
4
6
from .base_provider import AsyncGeneratorProvider
5
7
@@ -23,7 +25,7 @@ class Aura(AsyncGeneratorProvider):
23
25
"Content-Type": "application/json",
24
26
"Origin": f"{cls.url}",
25
27
"Referer": f"{cls.url}/",
26
"Sec-Ch-Ua": '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
28
"Sec-Ch-Ua": '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
27
29
"Sec-Ch-Ua-Mobile": "?0",
28
30
"Sec-Ch-Ua-Platform": '"Linux"',
29
31
"Sec-Fetch-Dest": "empty",
@@ -52,5 +54,6 @@ class Aura(AsyncGeneratorProvider):
52
54
"temperature": 0.5
53
55
}
54
56
async with session.post(f"{cls.url}/api/chat", json=data, proxy=proxy) as response:
57
response.raise_for_status()
55
58
async for chunk in response.content.iter_any():
56
59
yield chunk.decode()
@@ -4,7 +4,6 @@ from ..typing import Messages
4
4
from .base_provider import BaseProvider, CreateResult
5
5
from ..requests import get_session_from_browser
6
6
from uuid import uuid4
7
import requests
8
7
9
8
class Bestim(BaseProvider):
10
9
url = "https://chatgpt.bestim.org"
@@ -24,18 +23,7 @@ class Bestim(BaseProvider):
24
23
) -> CreateResult:
25
24
session = get_session_from_browser(cls.url, proxy=proxy)
26
25
headers = {
27
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
28
26
'Accept': 'application/json, text/event-stream',
29
'Accept-Language': 'en-US,en;q=0.5',
30
'Accept-Encoding': 'gzip, deflate, br',
31
'Referer': 'https://chatgpt.bestim.org/chat/',
32
'Origin': 'https://chatgpt.bestim.org',
33
'Alt-Used': 'chatgpt.bestim.org',
34
'Connection': 'keep-alive',
35
'Sec-Fetch-Dest': 'empty',
36
'Sec-Fetch-Mode': 'cors',
37
'Sec-Fetch-Site': 'same-origin',
38
'TE': 'trailers'
39
27
}
40
28
data = {
41
29
"messagesHistory": [{
@@ -47,9 +35,8 @@ class Bestim(BaseProvider):
47
35
}
48
36
response = session.post(
49
37
url="https://chatgpt.bestim.org/chat/send2/",
50
headers=headers,
51
38
json=data,
52
proxies={"https": proxy},
39
headers=headers,
53
40
stream=True
54
41
)
55
42
response.raise_for_status()
@@ -5,6 +5,7 @@ import json
5
5
from aiohttp import ClientSession
6
6
7
7
from ..typing import Messages, AsyncResult
8
from ..requests import get_args_from_browser
8
9
from .base_provider import AsyncGeneratorProvider
9
10
from .helper import get_random_string
10
11
@@ -12,8 +13,9 @@ class Chatgpt4Online(AsyncGeneratorProvider):
12
13
url = "https://chatgpt4online.org"
13
14
supports_message_history = True
14
15
supports_gpt_35_turbo = True
15
working = False # cloudfare block !
16
working = False
16
17
_wpnonce = None
18
_context_id = None
17
19
18
20
@classmethod
19
21
async def create_async_generator(
@@ -23,23 +25,10 @@ class Chatgpt4Online(AsyncGeneratorProvider):
23
25
proxy: str = None,
24
26
**kwargs
25
27
) -> AsyncResult:
26
headers = {
27
"accept": "*/*",
28
"accept-language": "en-US",
29
"content-type": "application/json",
30
"sec-ch-ua": "\"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Google Chrome\";v=\"120\"",
31
"sec-ch-ua-mobile": "?0",
32
"sec-ch-ua-platform": "\"Windows\"",
33
"sec-fetch-dest": "empty",
34
"sec-fetch-mode": "cors",
35
"sec-fetch-site": "same-origin",
36
"user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
37
"referer": "https://chatgpt4online.org/",
38
"referrer-policy": "strict-origin-when-cross-origin"
39
}
40
async with ClientSession(headers=headers) as session:
28
args = get_args_from_browser(f"{cls.url}/chat/", proxy=proxy)
29
async with ClientSession(**args) as session:
41
30
if not cls._wpnonce:
42
async with session.get(f"{cls.url}/", proxy=proxy) as response:
31
async with session.get(f"{cls.url}/chat/", proxy=proxy) as response:
43
32
response.raise_for_status()
44
33
response = await response.text()
45
34
result = re.search(r'restNonce":"(.*?)"', response)
@@ -47,12 +36,17 @@ class Chatgpt4Online(AsyncGeneratorProvider):
47
36
cls._wpnonce = result.group(1)
48
37
else:
49
38
raise RuntimeError("No nonce found")
39
result = re.search(r'contextId":(.*?),', response)
40
if result:
41
cls._context_id = result.group(1)
42
else:
43
raise RuntimeError("No contextId found")
50
44
data = {
51
45
"botId":"default",
52
46
"customId":None,
53
47
"session":"N/A",
54
48
"chatId":get_random_string(11),
55
"contextId":58,
49
"contextId":cls._context_id,
56
50
"messages":messages[:-1],
57
51
"newMessage":messages[-1]["content"],
58
52
"newImageId":None,
@@ -76,7 +76,7 @@ class PerplexityLabs(AsyncGeneratorProvider, ProviderModelMixin):
76
76
'model': cls.get_model(model),
77
77
'messages': messages
78
78
}
79
await ws.send_str('42' + json.dumps(['perplexity_playground', message_data]))
79
await ws.send_str('42' + json.dumps(['perplexity_labs', message_data]))
80
80
last_message = 0
81
81
while True:
82
82
message = await ws.receive_str()
@@ -22,11 +22,11 @@ except ImportError:
22
22
has_browser_cookie3 = False
23
23
24
24
from ..typing import Dict, Messages, Cookies, Optional
25
from ..errors import AiohttpSocksError, MissingRequirementsError
25
from ..errors import MissingAiohttpSocksError, MissingRequirementsError
26
26
from .. import debug
27
27
28
28
# Global variable to store cookies
29
_cookies: Dict[str, Dict[str, str]] = {}
29
_cookies: Dict[str, Cookies] = {}
30
30
31
31
if has_browser_cookie3 and os.environ.get('DBUS_SESSION_BUS_ADDRESS') == "/dev/null":
32
32
_LinuxPasswordManager.get_password = lambda a, b: b"secret"
@@ -54,7 +54,7 @@ def set_cookies(domain_name: str, cookies: Cookies = None) -> None:
54
54
else:
55
55
_cookies.pop(domain_name)
56
56
57
def load_cookies_from_browsers(domain_name: str, raise_requirements_error: bool = True) -> Dict[str, str]:
57
def load_cookies_from_browsers(domain_name: str, raise_requirements_error: bool = True) -> Cookies:
58
58
"""
59
59
Helper function to load cookies from various browsers.
60
60
@@ -149,5 +149,5 @@ def get_connector(connector: BaseConnector = None, proxy: str = None) -> Optiona
149
149
from aiohttp_socks import ProxyConnector
150
150
connector = ProxyConnector.from_url(proxy)
151
151
except ImportError:
152
raise AiohttpSocksError('Install "aiohttp_socks" package for proxy support')
152
raise MissingAiohttpSocksError('Install "aiohttp_socks" package for proxy support')
153
153
return connector
@@ -52,9 +52,9 @@ class AItianhuSpace(AbstractProvider):
52
52
wait.until(EC.visibility_of_element_located((By.ID, "sheet")))
53
53
driver.execute_script(f"""
54
54
document.getElementById('sheet').addEventListener('click', () => {{
55
window.open('{url}', '_blank');
55
window.open(arguments[0]);
56
56
}});
57
""")
57
""", url)
58
58
driver.find_element(By.ID, "sheet").click()
59
59
time.sleep(10)
60
60
@@ -31,10 +31,10 @@ class NestAsyncioError(Exception):
31
31
class ModelNotSupportedError(Exception):
32
32
pass
33
33
34
class MissingRequirementsError(ImportError):
34
class MissingRequirementsError(Exception):
35
35
pass
36
36
37
class AiohttpSocksError(MissingRequirementsError):
37
class MissingAiohttpSocksError(MissingRequirementsError):
38
38
pass
39
39
40
40
class MissingAccessToken(Exception):
@@ -1,130 +1,21 @@
1
1
from __future__ import annotations
2
2
3
import json
4
from functools import partialmethod
5
from typing import AsyncGenerator
6
3
from urllib.parse import urlparse
7
4
8
5
try:
9
from curl_cffi.requests import AsyncSession, Session, Response
6
from curl_cffi.requests import Session
7
from .requests_curl_cffi import StreamResponse, StreamSession
10
8
has_curl_cffi = True
11
9
except ImportError:
12
10
Session = type
11
from .requests_aiohttp import StreamResponse, StreamSession
13
12
has_curl_cffi = False
14
13
15
14
from .webdriver import WebDriver, WebDriverSession, bypass_cloudflare, get_driver_cookies
16
15
from .errors import MissingRequirementsError
17
16
18
if not has_curl_cffi:
19
from aiohttp import ClientSession, ClientResponse, ClientTimeout
20
from .Provider.helper import get_connector
21
22
class StreamResponse(ClientResponse):
23
async def iter_lines(self) -> iter[bytes, None]:
24
async for line in self.content:
25
yield line.rstrip(b"\r\n")
26
27
async def json(self):
28
return await super().json(content_type=None)
29
30
class StreamSession(ClientSession):
31
def __init__(self, headers: dict = {}, timeout: int = None, proxies: dict = {}, impersonate = None, **kwargs):
32
if impersonate:
33
headers = {
34
'Accept-Encoding': 'gzip, deflate, br',
35
'Accept-Language': 'en-US',
36
'Connection': 'keep-alive',
37
'Sec-Fetch-Dest': 'empty',
38
'Sec-Fetch-Mode': 'cors',
39
'Sec-Fetch-Site': 'same-site',
40
"User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36',
41
'Accept': '*/*',
42
'sec-ch-ua': '"Google Chrome";v="107", "Chromium";v="107", "Not?A_Brand";v="24"',
43
'sec-ch-ua-mobile': '?0',
44
'sec-ch-ua-platform': '"Windows"',
45
**headers
46
}
47
super().__init__(
48
**kwargs,
49
timeout=ClientTimeout(timeout) if timeout else None,
50
response_class=StreamResponse,
51
connector=get_connector(kwargs.get("connector"), proxies.get("https")),
52
headers=headers
53
)
54
else:
55
class StreamResponse:
56
"""
57
A wrapper class for handling asynchronous streaming responses.
58
59
Attributes:
60
inner (Response): The original Response object.
61
"""
62
63
def __init__(self, inner: Response) -> None:
64
"""Initialize the StreamResponse with the provided Response object."""
65
self.inner: Response = inner
66
67
async def text(self) -> str:
68
"""Asynchronously get the response text."""
69
return await self.inner.atext()
70
71
def raise_for_status(self) -> None:
72
"""Raise an HTTPError if one occurred."""
73
self.inner.raise_for_status()
74
75
async def json(self, **kwargs) -> dict:
76
"""Asynchronously parse the JSON response content."""
77
return json.loads(await self.inner.acontent(), **kwargs)
78
79
async def iter_lines(self) -> AsyncGenerator[bytes, None]:
80
"""Asynchronously iterate over the lines of the response."""
81
async for line in self.inner.aiter_lines():
82
yield line
83
84
async def iter_content(self) -> AsyncGenerator[bytes, None]:
85
"""Asynchronously iterate over the response content."""
86
async for chunk in self.inner.aiter_content():
87
yield chunk
88
89
async def __aenter__(self):
90
"""Asynchronously enter the runtime context for the response object."""
91
inner: Response = await self.inner
92
self.inner = inner
93
self.request = inner.request
94
self.status_code: int = inner.status_code
95
self.reason: str = inner.reason
96
self.ok: bool = inner.ok
97
self.headers = inner.headers
98
self.cookies = inner.cookies
99
return self
100
101
async def __aexit__(self, *args):
102
"""Asynchronously exit the runtime context for the response object."""
103
await self.inner.aclose()
104
105
class StreamSession(AsyncSession):
106
"""
107
An asynchronous session class for handling HTTP requests with streaming.
108
109
Inherits from AsyncSession.
110
"""
111
17
112
def request(
113
self, method: str, url: str, **kwargs
114
) -> StreamResponse:
115
"""Create and return a StreamResponse object for the given HTTP request."""
116
return StreamResponse(super().request(method, url, stream=True, **kwargs))
117
118
# Defining HTTP methods as partial methods of the request method.
119
head = partialmethod(request, "HEAD")
120
get = partialmethod(request, "GET")
121
post = partialmethod(request, "POST")
122
put = partialmethod(request, "PUT")
123
patch = partialmethod(request, "PATCH")
124
delete = partialmethod(request, "DELETE")
125
126
127
def get_session_from_browser(url: str, webdriver: WebDriver = None, proxy: str = None, timeout: int = 120) -> Session:
18
def get_args_from_browser(url: str, webdriver: WebDriver = None, proxy: str = None, timeout: int = 120) -> dict:
128
19
"""
129
20
Create a Session object using a WebDriver to handle cookies and headers.
130
21
@@ -137,26 +28,36 @@ def get_session_from_browser(url: str, webdriver: WebDriver = None, proxy: str =
137
28
Returns:
138
29
Session: A Session object configured with cookies and headers from the WebDriver.
139
30
"""
140
if not has_curl_cffi:
141
raise MissingRequirementsError('Install "curl_cffi" package')
142
with WebDriverSession(webdriver, "", proxy=proxy, virtual_display=True) as driver:
31
with WebDriverSession(webdriver, "", proxy=proxy, virtual_display=False) as driver:
143
32
bypass_cloudflare(driver, url, timeout)
144
33
cookies = get_driver_cookies(driver)
145
34
user_agent = driver.execute_script("return navigator.userAgent")
146
147
35
parse = urlparse(url)
148
return Session(
149
cookies=cookies,
150
headers={
36
return {
37
'cookies': cookies,
38
'headers': {
151
39
'accept': '*/*',
40
"accept-language": "en-US",
41
"accept-encoding": "gzip, deflate, br",
152
42
'authority': parse.netloc,
153
43
'origin': f'{parse.scheme}://{parse.netloc}',
154
44
'referer': url,
45
"sec-ch-ua": "\"Google Chrome\";v=\"121\", \"Not;A=Brand\";v=\"8\", \"Chromium\";v=\"121\"",
46
"sec-ch-ua-mobile": "?0",
47
"sec-ch-ua-platform": "Windows",
155
48
'sec-fetch-dest': 'empty',
156
49
'sec-fetch-mode': 'cors',
157
50
'sec-fetch-site': 'same-origin',
158
'user-agent': user_agent
51
'user-agent': user_agent,
159
52
},
53
}
54
55
def get_session_from_browser(url: str, webdriver: WebDriver = None, proxy: str = None, timeout: int = 120) -> Session:
56
if not has_curl_cffi:
57
raise MissingRequirementsError('Install "curl_cffi" package')
58
args = get_args_from_browser(url, webdriver, proxy, timeout)
59
return Session(
60
**args,
160
61
proxies={"https": proxy, "http": proxy},
161
62
timeout=timeout,
162
63
impersonate="chrome110"
@@ -0,0 +1,39 @@
1
from __future__ import annotations
2
3
from aiohttp import ClientSession, ClientResponse, ClientTimeout
4
from typing import AsyncGenerator, Any
5
6
from .Provider.helper import get_connector
7
8
class StreamResponse(ClientResponse):
9
async def iter_lines(self) -> AsyncGenerator[bytes, None]:
10
async for line in self.content:
11
yield line.rstrip(b"\r\n")
12
13
async def json(self) -> Any:
14
return await super().json(content_type=None)
15
16
class StreamSession(ClientSession):
17
def __init__(self, headers: dict = {}, timeout: int = None, proxies: dict = {}, impersonate = None, **kwargs):
18
if impersonate:
19
headers = {
20
'Accept-Encoding': 'gzip, deflate, br',
21
'Accept-Language': 'en-US',
22
'Connection': 'keep-alive',
23
'Sec-Fetch-Dest': 'empty',
24
'Sec-Fetch-Mode': 'cors',
25
'Sec-Fetch-Site': 'same-site',
26
"User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36',
27
'Accept': '*/*',
28
'sec-ch-ua': '"Google Chrome";v="107", "Chromium";v="107", "Not?A_Brand";v="24"',
29
'sec-ch-ua-mobile': '?0',
30
'sec-ch-ua-platform': '"Windows"',
31
**headers
32
}
33
super().__init__(
34
**kwargs,
35
timeout=ClientTimeout(timeout) if timeout else None,
36
response_class=StreamResponse,
37
connector=get_connector(kwargs.get("connector"), proxies.get("https")),
38
headers=headers
39
)
@@ -0,0 +1,77 @@
1
from __future__ import annotations
2
3
from curl_cffi.requests import AsyncSession, Response
4
from typing import AsyncGenerator, Any
5
from functools import partialmethod
6
import json
7
8
class StreamResponse:
9
"""
10
A wrapper class for handling asynchronous streaming responses.
11
12
Attributes:
13
inner (Response): The original Response object.
14
"""
15
16
def __init__(self, inner: Response) -> None:
17
"""Initialize the StreamResponse with the provided Response object."""
18
self.inner: Response = inner
19
20
async def text(self) -> str:
21
"""Asynchronously get the response text."""
22
return await self.inner.atext()
23
24
def raise_for_status(self) -> None:
25
"""Raise an HTTPError if one occurred."""
26
self.inner.raise_for_status()
27
28
async def json(self, **kwargs) -> Any:
29
"""Asynchronously parse the JSON response content."""
30
return json.loads(await self.inner.acontent(), **kwargs)
31
32
async def iter_lines(self) -> AsyncGenerator[bytes, None]:
33
"""Asynchronously iterate over the lines of the response."""
34
async for line in self.inner.aiter_lines():
35
yield line
36
37
async def iter_content(self) -> AsyncGenerator[bytes, None]:
38
"""Asynchronously iterate over the response content."""
39
async for chunk in self.inner.aiter_content():
40
yield chunk
41
42
async def __aenter__(self):
43
"""Asynchronously enter the runtime context for the response object."""
44
inner: Response = await self.inner
45
self.inner = inner
46
self.request = inner.request
47
self.status_code: int = inner.status_code
48
self.reason: str = inner.reason
49
self.ok: bool = inner.ok
50
self.headers = inner.headers
51
self.cookies = inner.cookies
52
return self
53
54
async def __aexit__(self, *args):
55
"""Asynchronously exit the runtime context for the response object."""
56
await self.inner.aclose()
57
58
class StreamSession(AsyncSession):
59
"""
60
An asynchronous session class for handling HTTP requests with streaming.
61
62
Inherits from AsyncSession.
63
"""
64
65
def request(
66
self, method: str, url: str, **kwargs
67
) -> StreamResponse:
68
"""Create and return a StreamResponse object for the given HTTP request."""
69
return StreamResponse(super().request(method, url, stream=True, **kwargs))
70
71
# Defining HTTP methods as partial methods of the request method.
72
head = partialmethod(request, "HEAD")
73
get = partialmethod(request, "GET")
74
post = partialmethod(request, "POST")
75
put = partialmethod(request, "PUT")
76
patch = partialmethod(request, "PATCH")
77
delete = partialmethod(request, "DELETE")
@@ -13,7 +13,8 @@ try:
13
13
except ImportError:
14
14
from typing import Type as WebDriver
15
15
has_requirements = False
16
16
17
import time
17
18
from os import path
18
19
from os import access, R_OK
19
20
from .errors import MissingRequirementsError
@@ -92,7 +93,27 @@ def bypass_cloudflare(driver: WebDriver, url: str, timeout: int) -> None:
92
93
if driver.find_element(By.TAG_NAME, "body").get_attribute("class") == "no-js":
93
94
if debug.logging:
94
95
print("Cloudflare protection detected:", url)
96
97
# Open website in a new tab
98
element = driver.find_element(By.ID, "challenge-body-text")
99
driver.execute_script(f"""
100
arguments[0].addEventListener('click', () => {{
101
window.open(arguments[1]);
102
}});
103
""", element, url)
104
element.click()
105
time.sleep(3)
106
107
# Switch to the new tab and close the old tab
108
original_window = driver.current_window_handle
109
for window_handle in driver.window_handles:
110
if window_handle != original_window:
111
driver.close()
112
driver.switch_to.window(window_handle)
113
break
114
95
115
try:
116
# Click on the challenge button in the iframe
96
117
driver.switch_to.frame(driver.find_element(By.CSS_SELECTOR, "#turnstile-wrapper iframe"))
97
118
WebDriverWait(driver, 5).until(
98
119
EC.presence_of_element_located((By.CSS_SELECTOR, "#challenge-stage input"))