diff --git a/make_post_sell/lib/crypto_clients.py b/make_post_sell/lib/crypto_clients.py index 6fe9f14..217e20b 100644 --- a/make_post_sell/lib/crypto_clients.py +++ b/make_post_sell/lib/crypto_clients.py @@ -6,6 +6,14 @@ import urllib.request import urllib.error import base64 +# Try to import requests for better Digest auth support +try: + import requests + from requests.auth import HTTPDigestAuth + HAS_REQUESTS = True +except ImportError: + HAS_REQUESTS = False + class MoneroClient: """ @@ -38,48 +46,29 @@ class MoneroClient: } if params is not None: payload["params"] = params - data = json.dumps(payload).encode() + + # Use requests library for all HTTP calls - much better Digest auth support + if not HAS_REQUESTS: + raise RuntimeError("requests library required for Monero RPC. Install with: pip install requests") - # Monero uses Digest authentication, not Basic - # We need to handle this differently from the headers approach - if self.rpc_user and self.rpc_pass: - # Create a password manager for Digest auth - password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm() - # Add password for both the full URL and just the base URL - password_mgr.add_password(None, self.rpc_url, self.rpc_user, self.rpc_pass) - from urllib.parse import urlparse - parsed = urlparse(self.rpc_url) - base_url = f"{parsed.scheme}://{parsed.netloc}" - password_mgr.add_password(None, base_url, self.rpc_user, self.rpc_pass) + auth = HTTPDigestAuth(self.rpc_user, self.rpc_pass) if self.rpc_user and self.rpc_pass else None + + try: + response = requests.post( + self.rpc_url, + json=payload, + auth=auth, + timeout=self.timeout + ) + response.raise_for_status() - # Create the Digest auth handler - auth_handler = urllib.request.HTTPDigestAuthHandler(password_mgr) - opener = urllib.request.build_opener(auth_handler) + obj = response.json() + if "error" in obj and obj["error"]: + raise RuntimeError(obj["error"]) # bubble up rpc error + return obj.get("result") - # Make the request with Digest auth - req = urllib.request.Request(self.rpc_url, data=data, - headers={"Content-Type": "application/json"}) - try: - with opener.open(req, timeout=self.timeout) as resp: - body = resp.read() - obj = json.loads(body) - if "error" in obj and obj["error"]: - raise RuntimeError(obj["error"]) # bubble up rpc error - return obj.get("result") - except urllib.error.URLError as e: - raise RuntimeError(f"Monero RPC connection error: {e}") - else: - # No auth needed - req = urllib.request.Request(self.rpc_url, data=data, headers=self._headers()) - try: - with urllib.request.urlopen(req, timeout=self.timeout) as resp: - body = resp.read() - obj = json.loads(body) - if "error" in obj and obj["error"]: - raise RuntimeError(obj["error"]) # bubble up rpc error - return obj.get("result") - except urllib.error.URLError as e: - raise RuntimeError(f"Monero RPC connection error: {e}") + except requests.exceptions.RequestException as e: + raise RuntimeError(f"Monero RPC connection error: {e}") # High-level helpers