summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Dockerfile3
-rw-r--r--README.md12
-rw-r--r--gpt4free/forefront/README.md13
-rw-r--r--gpt4free/forefront/__init__.py91
-rw-r--r--gpt4free/forefront/typing.py6
-rw-r--r--gpt4free/quora/api.py35
-rw-r--r--gpt4free/quora/tests/__init__.py0
-rw-r--r--gpt4free/quora/tests/test_api.py38
-rw-r--r--gpt4free/you/__init__.py61
-rw-r--r--requirements.txt3
10 files changed, 185 insertions, 77 deletions
diff --git a/Dockerfile b/Dockerfile
index 297ebbbf..0ac667fb 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -4,6 +4,9 @@ WORKDIR /usr/app
ENV PATH="/usr/app/venv/bin:$PATH"
#RUN apt-get update && apt-get install -y git
+RUN apt-get update
+RUN apt-get install ffmpeg -y #issue 445
+
RUN mkdir -p /usr/app
RUN python -m venv ./venv
diff --git a/README.md b/README.md
index 95172f98..16e7ea17 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,6 @@
-Due to legal and personal issues, the development speed of this Repository may slow down over the next one to two weeks. I apologize for any inconvenience this may cause. I have been putting a lot of effort into this small personal/educational project, and it is now on the verge of being taken down.
+**The development speed of this Repository may slow down over the next one to two weeks !!**
+
+-- Due to legal and personal issues. I apologize for any inconvenience this may cause. I have been putting a lot of effort into this small personal/educational project, and it is now on the verge of being taken down.
<p>You may join our discord: <a href="https://discord.com/invite/gpt4free">discord.gg/gpt4free<a> for further updates. <a href="https://discord.gg/gpt4free"><img align="center" alt="gpt4free Discord" width="22px" src="https://raw.githubusercontent.com/peterthehan/peterthehan/master/assets/discord.svg" /></a></p>
@@ -138,6 +140,8 @@ Download or clone this GitHub repo
install requirements with:
```sh
+python3 -m venv venv
+. venv/bin/activate
pip3 install -r requirements.txt
```
@@ -147,6 +151,12 @@ pip3 install -r requirements.txt
Move `streamlit_app.py` from `./gui` to the base folder then run:
`streamlit run streamlit_app.py` or `python3 -m streamlit run streamlit_app.py`
+```sh
+cp gui/streamlit_app.py .
+streamlit run streamlit_app.py
+```
+
+
## Docker <a name="docker-instructions"></a>
Build
diff --git a/gpt4free/forefront/README.md b/gpt4free/forefront/README.md
index 887097ec..7a59fe8e 100644
--- a/gpt4free/forefront/README.md
+++ b/gpt4free/forefront/README.md
@@ -2,15 +2,18 @@
```python
from gpt4free import forefront
+
+
# create an account
-token = forefront.Account.create(logging=False)
-print(token)
+account_data = forefront.Account.create(logging=False)
+
# get a response
for response in forefront.StreamingCompletion.create(
- token=token,
- prompt='hello world',
- model='gpt-4'
+ account_data=account_data,
+ prompt='hello world',
+ model='gpt-4'
):
print(response.choices[0].text, end='')
print("")
+
``` \ No newline at end of file
diff --git a/gpt4free/forefront/__init__.py b/gpt4free/forefront/__init__.py
index dbf1730b..240ee0a4 100644
--- a/gpt4free/forefront/__init__.py
+++ b/gpt4free/forefront/__init__.py
@@ -1,27 +1,31 @@
+import hashlib
+from base64 import b64encode
from json import loads
-from xtempmail import Email
from re import findall
-from typing import Optional, Generator
-from faker import Faker
from time import time, sleep
+from typing import Generator, Optional
from uuid import uuid4
+
+from Crypto.Cipher import AES
+from Crypto.Random import get_random_bytes
from fake_useragent import UserAgent
+from mailgw_temporary_email import Email
from requests import post
from tls_client import Session
-from .typing import ForeFrontResponse
+
+from .typing import ForeFrontResponse, AccountData
class Account:
@staticmethod
- def create(proxy: Optional[str] = None, logging: bool = False):
+ def create(proxy: Optional[str] = None, logging: bool = False) -> AccountData:
proxies = {'http': 'http://' + proxy, 'https': 'http://' + proxy} if proxy else False
- faker = Faker()
- name = (faker.name().replace(' ', '_')).lower()
start = time()
- mail_client = Email(name=name)
- mail_address = mail_client.email
+ mail_client = Email()
+ mail_client.register()
+ mail_address = mail_client.address
client = Session(client_identifier='chrome110')
client.proxies = proxies
@@ -40,7 +44,7 @@ class Account:
if logging:
print(trace_token)
except KeyError:
- return 'Failed to create account!'
+ raise RuntimeError('Failed to create account!')
response = client.post(
f'https://clerk.forefront.ai/v1/client/sign_ups/{trace_token}/prepare_verification?_clerk_js_version=4.38.4',
@@ -54,23 +58,26 @@ class Account:
print(response.text)
if 'sign_up_attempt' not in response.text:
- return 'Failed to create account!'
- verification_url = None
- new_message = mail_client.get_new_message(5)
- for msg in new_message:
- verification_url = findall(r'https:\/\/clerk\.forefront\.ai\/v1\/verify\?token=\w.+', msg.text)[0]
+ raise RuntimeError('Failed to create account!')
+
+ while True:
+ sleep(5)
+ message_id = mail_client.message_list()[0]['id']
+ message = mail_client.message(message_id)
+ verification_url = findall(r'https:\/\/clerk\.forefront\.ai\/v1\/verify\?token=\w.+', message["text"])[0]
if verification_url:
break
-
- if verification_url is None or not verification_url:
- raise RuntimeError('Error while obtaining verfication URL!')
+
if logging:
print(verification_url)
- response = client.get(verification_url)
+ client.get(verification_url)
- response = client.get('https://clerk.forefront.ai/v1/client?_clerk_js_version=4.38.4')
+ response = client.get('https://clerk.forefront.ai/v1/client?_clerk_js_version=4.38.4').json()
+ session_data = response['response']['sessions'][0]
- token = response.json()['response']['sessions'][0]['last_active_token']['jwt']
+ user_id = session_data['user']['id']
+ session_id = session_data['id']
+ token = session_data['last_active_token']['jwt']
with open('accounts.txt', 'a') as f:
f.write(f'{mail_address}:{token}\n')
@@ -78,32 +85,32 @@ class Account:
if logging:
print(time() - start)
- return token
+ return AccountData(token=token, user_id=user_id, session_id=session_id)
class StreamingCompletion:
@staticmethod
def create(
- token=None,
+ prompt: str,
+ account_data: AccountData,
chat_id=None,
- prompt='',
action_type='new',
default_persona='607e41fe-95be-497e-8e97-010a59b2e2c0', # default
model='gpt-4',
proxy=None
) -> Generator[ForeFrontResponse, None, None]:
- if not token:
- raise Exception('Token is required!')
+ token = account_data.token
if not chat_id:
chat_id = str(uuid4())
- proxies = { 'http': 'http://' + proxy, 'https': 'http://' + proxy } if proxy else None
+ proxies = {'http': 'http://' + proxy, 'https': 'http://' + proxy} if proxy else None
+ base64_data = b64encode((account_data.user_id + default_persona + chat_id).encode()).decode()
+ encrypted_signature = StreamingCompletion.__encrypt(base64_data, account_data.session_id)
headers = {
'authority': 'chat-server.tenant-forefront-default.knative.chi.coreweave.com',
'accept': '*/*',
'accept-language': 'en,fr-FR;q=0.9,fr;q=0.8,es-ES;q=0.7,es;q=0.6,en-US;q=0.5,am;q=0.4,de;q=0.3',
- 'authorization': 'Bearer ' + token,
'cache-control': 'no-cache',
'content-type': 'application/json',
'origin': 'https://chat.forefront.ai',
@@ -115,6 +122,8 @@ class StreamingCompletion:
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
+ 'authorization': f"Bearer {token}",
+ 'X-Signature': encrypted_signature,
'user-agent': UserAgent().random,
}
@@ -128,7 +137,7 @@ class StreamingCompletion:
}
for chunk in post(
- 'https://chat-server.tenant-forefront-default.knative.chi.coreweave.com/chat',
+ 'https://streaming.tenant-forefront-default.knative.chi.coreweave.com/chat',
headers=headers,
proxies=proxies,
json=json_data,
@@ -155,13 +164,28 @@ class StreamingCompletion:
}
)
+ @staticmethod
+ def __encrypt(data: str, key: str) -> str:
+ hash_key = hashlib.sha256(key.encode()).digest()
+ iv = get_random_bytes(16)
+ cipher = AES.new(hash_key, AES.MODE_CBC, iv)
+ encrypted_data = cipher.encrypt(StreamingCompletion.__pad_data(data.encode()))
+ return iv.hex() + encrypted_data.hex()
+
+ @staticmethod
+ def __pad_data(data: bytes) -> bytes:
+ block_size = AES.block_size
+ padding_size = block_size - len(data) % block_size
+ padding = bytes([padding_size] * padding_size)
+ return data + padding
+
class Completion:
@staticmethod
def create(
- token=None,
+ prompt: str,
+ account_data: AccountData,
chat_id=None,
- prompt='',
action_type='new',
default_persona='607e41fe-95be-497e-8e97-010a59b2e2c0', # default
model='gpt-4',
@@ -170,7 +194,7 @@ class Completion:
text = ''
final_response = None
for response in StreamingCompletion.create(
- token=token,
+ account_data=account_data,
chat_id=chat_id,
prompt=prompt,
action_type=action_type,
@@ -185,7 +209,6 @@ class Completion:
if final_response:
final_response.text = text
else:
- raise Exception('Unable to get the response, Please try again')
+ raise RuntimeError('Unable to get the response, Please try again')
return final_response
- \ No newline at end of file
diff --git a/gpt4free/forefront/typing.py b/gpt4free/forefront/typing.py
index 23e90903..b572e2c2 100644
--- a/gpt4free/forefront/typing.py
+++ b/gpt4free/forefront/typing.py
@@ -24,3 +24,9 @@ class ForeFrontResponse(BaseModel):
choices: List[Choice]
usage: Usage
text: str
+
+
+class AccountData(BaseModel):
+ token: str
+ user_id: str
+ session_id: str
diff --git a/gpt4free/quora/api.py b/gpt4free/quora/api.py
index d388baee..9e3c0b91 100644
--- a/gpt4free/quora/api.py
+++ b/gpt4free/quora/api.py
@@ -56,18 +56,25 @@ def generate_payload(query_name, variables):
return {"query": queries[query_name], "variables": variables}
-def request_with_retries(method, *args, **kwargs):
- attempts = kwargs.get("attempts") or 10
+def retry_request(method, *args, **kwargs):
+ """Retry a request with 10 attempts by default, delay increases exponentially"""
+ max_attempts: int = kwargs.pop("max_attempts", 10)
+ delay = kwargs.pop("delay", 1)
url = args[0]
- for i in range(attempts):
- r = method(*args, **kwargs)
- if r.status_code == 200:
- return r
- logger.warn(
- f"Server returned a status code of {r.status_code} while downloading {url}. Retrying ({i + 1}/{attempts})..."
- )
- raise RuntimeError(f"Failed to download {url} too many times.")
+ for attempt in range(1, max_attempts + 1):
+ try:
+ response = method(*args, **kwargs)
+ response.raise_for_status()
+ return response
+ except Exception as error:
+ logger.warning(
+ f"Attempt {attempt}/{max_attempts} failed with error: {error}. "
+ f"Retrying in {delay} seconds..."
+ )
+ time.sleep(delay)
+ delay *= 2
+ raise RuntimeError(f"Failed to download {url} after {max_attempts} attempts.")
class Client:
@@ -134,7 +141,7 @@ class Client:
def get_next_data(self, overwrite_vars=False):
logger.info("Downloading next_data...")
- r = request_with_retries(self.session.get, self.home_url)
+ r = retry_request(self.session.get, self.home_url)
json_regex = r'<script id="__NEXT_DATA__" type="application\/json">(.+?)</script>'
json_text = re.search(json_regex, r.text).group(1)
next_data = json.loads(json_text)
@@ -149,7 +156,7 @@ class Client:
def get_bot(self, display_name):
url = f'https://poe.com/_next/data/{self.next_data["buildId"]}/{display_name}.json'
- r = request_with_retries(self.session.get, url)
+ r = retry_request(self.session.get, url)
chat_data = r.json()["pageProps"]["payload"]["chatOfBotDisplayName"]
return chat_data
@@ -198,7 +205,7 @@ class Client:
def get_channel_data(self, channel=None):
logger.info("Downloading channel data...")
- r = request_with_retries(self.session.get, self.settings_url)
+ r = retry_request(self.session.get, self.settings_url)
data = r.json()
return data["tchannelData"]
@@ -222,7 +229,7 @@ class Client:
}
headers = {**self.gql_headers, **headers}
- r = request_with_retries(self.session.post, self.gql_url, data=payload, headers=headers)
+ r = retry_request(self.session.post, self.gql_url, data=payload, headers=headers)
data = r.json()
if data["data"] is None:
diff --git a/gpt4free/quora/tests/__init__.py b/gpt4free/quora/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/gpt4free/quora/tests/__init__.py
diff --git a/gpt4free/quora/tests/test_api.py b/gpt4free/quora/tests/test_api.py
new file mode 100644
index 00000000..2a4bb41b
--- /dev/null
+++ b/gpt4free/quora/tests/test_api.py
@@ -0,0 +1,38 @@
+import unittest
+import requests
+from unittest.mock import MagicMock
+from gpt4free.quora.api import retry_request
+
+
+class TestRetryRequest(unittest.TestCase):
+ def test_successful_request(self):
+ # Mock a successful request with a 200 status code
+ mock_response = MagicMock()
+ mock_response.status_code = 200
+ requests.get = MagicMock(return_value=mock_response)
+
+ # Call the function and assert that it returns the response
+ response = retry_request(requests.get, "http://example.com", max_attempts=3)
+ self.assertEqual(response.status_code, 200)
+
+ def test_exponential_backoff(self):
+ # Mock a failed request that succeeds after two retries
+ mock_response = MagicMock()
+ mock_response.status_code = 200
+ requests.get = MagicMock(side_effect=[requests.exceptions.RequestException] * 2 + [mock_response])
+
+ # Call the function and assert that it retries with exponential backoff
+ with self.assertLogs() as logs:
+ response = retry_request(requests.get, "http://example.com", max_attempts=3, delay=1)
+ self.assertEqual(response.status_code, 200)
+ self.assertGreaterEqual(len(logs.output), 2)
+ self.assertIn("Retrying in 1 seconds...", logs.output[0])
+ self.assertIn("Retrying in 2 seconds...", logs.output[1])
+
+ def test_too_many_attempts(self):
+ # Mock a failed request that never succeeds
+ requests.get = MagicMock(side_effect=requests.exceptions.RequestException)
+
+ # Call the function and assert that it raises an exception after the maximum number of attempts
+ with self.assertRaises(RuntimeError):
+ retry_request(requests.get, "http://example.com", max_attempts=3)
diff --git a/gpt4free/you/__init__.py b/gpt4free/you/__init__.py
index da22d05e..11847fb5 100644
--- a/gpt4free/you/__init__.py
+++ b/gpt4free/you/__init__.py
@@ -5,10 +5,13 @@ from uuid import uuid4
from fake_useragent import UserAgent
from pydantic import BaseModel
+from requests import RequestException
+from retrying import retry
from tls_client import Session
+from tls_client.response import Response
-class PoeResponse(BaseModel):
+class YouResponse(BaseModel):
text: Optional[str] = None
links: List[str] = []
extra: Dict[str, Any] = {}
@@ -31,7 +34,7 @@ class Completion:
detailed: bool = False,
debug: bool = False,
proxy: Optional[str] = None,
- ) -> PoeResponse:
+ ) -> YouResponse:
if chat is None:
chat = []
@@ -41,30 +44,29 @@ class Completion:
client.headers = Completion.__get_headers()
client.proxies = proxies
- response = client.get(
- f'https://you.com/api/streamingSearch',
- params={
- 'q': prompt,
- 'page': page,
- 'count': count,
- 'safeSearch': safe_search,
- 'onShoppingPage': on_shopping_page,
- 'mkt': mkt,
- 'responseFilter': response_filter,
- 'domain': domain,
- 'queryTraceId': str(uuid4()) if query_trace_id is None else query_trace_id,
- 'chat': str(chat), # {'question':'','answer':' ''}
- },
- )
+ params = {
+ 'q': prompt,
+ 'page': page,
+ 'count': count,
+ 'safeSearch': safe_search,
+ 'onShoppingPage': on_shopping_page,
+ 'mkt': mkt,
+ 'responseFilter': response_filter,
+ 'domain': domain,
+ 'queryTraceId': str(uuid4()) if query_trace_id is None else query_trace_id,
+ 'chat': str(chat), # {'question':'','answer':' ''}
+ }
+
+ try:
+ response = Completion.__make_request(client, params)
+ except Exception:
+ return Completion.__get_failure_response()
if debug:
print('\n\n------------------\n\n')
print(response.text)
print('\n\n------------------\n\n')
- if 'youChatToken' not in response.text:
- return Completion.__get_failure_response()
-
you_chat_serp_results = re.search(
r'(?<=event: youChatSerpResults\ndata:)(.*\n)*?(?=event: )', response.text
).group()
@@ -80,7 +82,7 @@ class Completion:
# 'slots' : loads(slots)
}
- response = PoeResponse(text=text.replace('\\n', '\n').replace('\\\\', '\\').replace('\\"', '"'))
+ response = YouResponse(text=text.replace('\\n', '\n').replace('\\\\', '\\').replace('\\"', '"'))
if include_links:
response.links = json.loads(third_party_search_results)['search']['third_party_search_results']
@@ -108,5 +110,18 @@ class Completion:
}
@staticmethod
- def __get_failure_response() -> PoeResponse:
- return PoeResponse(text='Unable to fetch the response, Please try again.')
+ def __get_failure_response() -> YouResponse:
+ return YouResponse(text='Unable to fetch the response, Please try again.')
+
+ @staticmethod
+ @retry(
+ wait_fixed=5000,
+ stop_max_attempt_number=5,
+ retry_on_exception=lambda e: isinstance(e, RequestException),
+ )
+ def __make_request(client: Session, params: dict) -> Response:
+ response = client.get(f'https://you.com/api/streamingSearch', params=params)
+ if 'youChatToken' not in response.text:
+ print('retry')
+ raise RequestException('Unable to get the response from server')
+ return response
diff --git a/requirements.txt b/requirements.txt
index bc46bc70..940ba42f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -13,3 +13,6 @@ https://github.com/AI-Yash/st-chat/archive/refs/pull/24/head.zip
pydantic
pymailtm
Levenshtein
+retrying
+mailgw_temporary_email
+pycryptodome