diff options
-rw-r--r-- | Dockerfile | 3 | ||||
-rw-r--r-- | README.md | 12 | ||||
-rw-r--r-- | gpt4free/forefront/README.md | 13 | ||||
-rw-r--r-- | gpt4free/forefront/__init__.py | 91 | ||||
-rw-r--r-- | gpt4free/forefront/typing.py | 6 | ||||
-rw-r--r-- | gpt4free/quora/api.py | 35 | ||||
-rw-r--r-- | gpt4free/quora/tests/__init__.py | 0 | ||||
-rw-r--r-- | gpt4free/quora/tests/test_api.py | 38 | ||||
-rw-r--r-- | gpt4free/you/__init__.py | 61 | ||||
-rw-r--r-- | requirements.txt | 3 |
10 files changed, 185 insertions, 77 deletions
@@ -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 @@ -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 |