Skip to content

Commit

Permalink
feat: ssid refresher with crf bypass
Browse files Browse the repository at this point in the history
  • Loading branch information
SantiiRepair committed Jul 24, 2024
1 parent cc35099 commit 9982906
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 60 deletions.
11 changes: 10 additions & 1 deletion quotexpy/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from quotexpy.http.login import Login
from quotexpy.http.logout import Logout
from quotexpy.http.refresh import Refresh
from quotexpy.ws.channels.ssid import Ssid
from quotexpy.ws.channels.trade import Trade
from quotexpy.exceptions import QuotexTimeout
Expand Down Expand Up @@ -109,6 +110,14 @@ def websocket(self):
"""
return self.websocket_client.wss

@property
def refresh(self):
"""Property for Quotex http refresh ssid resource.
:returns: The instance of :class:`Refresh
<quotexpy.http.refresh.Refresh>`.
"""
return Refresh(self)

@property
def logout(self):
"""Property for get Quotex http login resource.
Expand Down Expand Up @@ -163,6 +172,7 @@ async def connect(self) -> bool:
if not self.SSID:
self.SSID, self.cookies = await self.get_ssid()
check_websocket = self.start_websocket()
print(self.refresh())
return check_websocket

async def get_ssid(self) -> typing.Tuple[str, str]:
Expand Down Expand Up @@ -193,7 +203,6 @@ def subscribe_realtime_candle(self, asset: str, period: int) -> None:
payload = {"asset": asset, "period": period}
data = f'42["instruments/update", {json.dumps(payload)}]'
self.send_websocket_request(data)
payload = {"asset": asset, "period": period}
data = f'42["depth/follow", "{asset}"]'
self.send_websocket_request(data)
payload = {"asset": asset, "version": "1.0.0"}
Expand Down
21 changes: 9 additions & 12 deletions quotexpy/http/logout.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
"""Module for Quotex http login resource."""
"""Module for Quotex http logout resource."""

from quotexpy.http.navigator import Navigator


class Logout(Navigator):
"""Class for Quotex login resource."""

base_url = "qxbroker.com"
https_base_url = f"https://{base_url}"

def _post(self, data=None, headers=None):
"""Send get request for Quotex API login http resource.
:returns: The instance of :class:`navigator.Session`.
"""
return self.send_request(method="POST", url=f"{self.https_base_url}/logout", data=data, headers=headers)
"""Class for Quotex logout resource."""

def __call__(self):
return self._post()
response = self.send_request(
method="GET",
url=f"{self.https_base_url}/logout",
headers={"User-Agent": self.api.user_agent, "Cookie": self.api.cookies},
)

return response
48 changes: 15 additions & 33 deletions quotexpy/http/navigator.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,28 @@
import random
import requests
from bs4 import BeautifulSoup
from urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter

from quotexpy.http.user_agents import agents

retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504, 104],
allowed_methods=["HEAD", "POST", "PUT", "GET", "OPTIONS"],
)

adapter = HTTPAdapter(max_retries=retry_strategy)
user_agent_list = agents.split("\n")


class Navigator(object):
def __init__(self, api):
"""
Tools for quotexpy navigation.
:param object api: The instance of :class:`quotexpy.api.QuotexAPI`.
"""
"""Tools for quotexpy navigation."""
self.base_url = "qxbroker.com"
self.https_base_url = f"https://{self.base_url}"

self.api = api
self.response = None
self.headers = self.get_headers()
self.session = requests.Session()
self.api.user_agent = self.headers["User-Agent"]
self.session.mount("http://", adapter)
self.session.mount("https://", adapter)

def get_headers(self):
self.headers = {
"User-Agent": user_agent_list[random.randint(0, len(user_agent_list) - 1)],
}
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504, 104],
allowed_methods=["HEAD", "POST", "PUT", "GET", "OPTIONS"],
)

return self.headers
adapter = HTTPAdapter(max_retries=retry_strategy)

def get_soup(self):
return BeautifulSoup(self.response.content, "html.parser")
self.session.mount("http://", adapter)
self.session.mount("https://", adapter)

def send_request(self, method, url, **kwargs):
self.response = self.session.request(method, url, headers=self.headers, **kwargs)
return self.response
def send_request(self, method: str, url: str, headers: dict, **kwargs):
return self.session.request(method, url, headers=headers, **kwargs)
10 changes: 4 additions & 6 deletions quotexpy/http/qxbroker.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,11 @@ def get_ssid_and_cookies(self) -> typing.Tuple[str, str]:
raise QuotexAuthError("incorrect username or password")

cookies = self.browser.get_cookies()
self.api.cookies = cookies
user_agent = self.browser.execute_script("return navigator.userAgent;")
self.api.user_agent = user_agent
self.api.user_agent = self.browser.execute_script("return navigator.userAgent;")

ssid = wsd.get("token")
cookiejar = requests.utils.cookiejar_from_dict({c["name"]: c["value"] for c in cookies})
cookie_string = "; ".join([f"{c.name}={c.value}" for c in cookiejar])
self.api.cookies = "; ".join([f"{c.name}={c.value}" for c in cookiejar])
output_file = Path(sessions_file_path)
output_file.parent.mkdir(exist_ok=True, parents=True)

Expand All @@ -90,11 +88,11 @@ def get_ssid_and_cookies(self) -> typing.Tuple[str, str]:
with output_file.open("rb") as file:
data = pickle.load(file)

data[self.email] = [{"cookies": cookie_string, "ssid": ssid, "user_agent": user_agent}]
data[self.email] = [{"cookies": self.api.cookies, "ssid": ssid, "user_agent": self.api.user_agent}]
with output_file.open("wb") as file:
pickle.dump(data, file)

return ssid, cookie_string
return ssid, self.api.cookies
except TypeError as exc:
raise SystemError("Chrome is not installed, did you forget?") from exc
finally:
Expand Down
27 changes: 27 additions & 0 deletions quotexpy/http/refresh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""Module for Quotex http refresh ssid resource."""

from quotexpy.http.navigator import Navigator


class Refresh(Navigator):
"""Class for Quotex refresh resource."""

def __call__(self):
try:
response = self.send_request(
method="GET",
url=f"{self.https_base_url}/api/v1/cabinets/digest",
headers={"User-Agent": self.api.user_agent, "Cookie": self.api.cookies},
)

if response.status_code == 200:
response_json: dict = response.json()
data: dict = response_json.get("data")
if data and data.get("token"):
return data.get("token")
else:
raise KeyError("token not found in response data")
else:
raise ValueError("error refreshing ssid, maybe your client started without credentials?")
except Exception as err:
raise RuntimeError(f"unhandled error: {str(err)}")
2 changes: 1 addition & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pytest==7.4.2
black==23.9.1
poetry==1.6.1
pylint==2.17.5
pytest==7.4.2
termcolor==2.3.0
13 changes: 6 additions & 7 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
psutil
beautifulsoup4==4.11.2
certifi>=2022.12.7
greenlet>=3.0.3
undetected-chromedriver>=3.5.5
pytz>=2023.3
requests>=2.32.3
requests-toolbelt>=1.0.0
urllib3>=2.2.2
websockets>=12
greenlet>=3.0.3
requests>=2.32.3
certifi>=2022.12.7
websocket-client>=1.8
websockets>=12
requests-toolbelt>=1.0.0
undetected-chromedriver>=3.5.5

0 comments on commit 9982906

Please sign in to comment.