From 5fadc8c981bfdb8e4434217cbd787bf8da8be577 Mon Sep 17 00:00:00 2001 From: dmitry pervushin Date: Sat, 4 Mar 2023 19:54:57 +0100 Subject: [PATCH 1/2] Two attempts to get csrf token --- pysuez/client.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/pysuez/client.py b/pysuez/client.py index 9273e69..aeedd4d 100644 --- a/pysuez/client.py +++ b/pysuez/client.py @@ -31,6 +31,20 @@ def __init__(self, username, password, counter_id, provider, session=None, timeo self._timeout = timeout self.state = 0 + def _get_token_1(self, content): + phrase = re.compile('csrf_token(.*)') + result = phrase.search(content) + if result is None: + return None + return result.group(1) + + def _get_token_2(self, content): + phrase = re.compile('csrfToken\\\\u0022\\\\u003A\\\\u0022(.*)\\\\u0022,\\\\u0022') + result = phrase.search(content) + if result is None: + return None + return result.group(1).encode().decode('unicode_escape') + def _get_token(self): """Get the token""" headers = { @@ -55,10 +69,11 @@ def _get_token(self): headers['Cookie'] += "; " headers['Cookie'] += key + "=" + response.cookies[key] - phrase = re.compile('_csrf_token" value="(.*)"/>') - result = phrase.search(response.content.decode('utf-8')) - self._token = result.group(1) self._headers = headers + decoded_content = response.content.decode('utf-8') + self._token = self._get_token_1(decoded_content) or self._get_token_2(decoded_content) + if self._token is None: + raise PySuezError("Can't get token.") def _get_cookie(self): """Connect and get the cookie""" From 83862247c59f34e9b641dc5efca905ee21983ae5 Mon Sep 17 00:00:00 2001 From: dmitry pervushin Date: Sat, 4 Mar 2023 21:30:21 +0100 Subject: [PATCH 2/2] Use aiohttp instead of requests --- pysuez/client.py | 85 ++++++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/pysuez/client.py b/pysuez/client.py index aeedd4d..fe6d799 100644 --- a/pysuez/client.py +++ b/pysuez/client.py @@ -1,6 +1,7 @@ -import requests import re import datetime +import asyncio +import aiohttp @@ -16,7 +17,7 @@ class PySuezError(Exception): class SuezClient(): """Global variables.""" - def __init__(self, username, password, counter_id, provider, session=None, timeout=None): + def __init__(self, username, password, counter_id, provider=None, timeout=None): """Initialize the client object.""" self._username = username self._password = password @@ -27,7 +28,7 @@ def __init__(self, username, password, counter_id, provider, session=None, timeo self.data = {} self.attributes = {} self.success = False - self._session = session + self._session = None self._timeout = timeout self.state = 0 @@ -45,7 +46,7 @@ def _get_token_2(self, content): return None return result.group(1).encode().decode('unicode_escape') - def _get_token(self): + async def _get_token(self): """Get the token""" headers = { 'Accept': "application/json, text/javascript, */*; q=0.01", @@ -61,26 +62,19 @@ def _get_token(self): BASE_URI = 'https://www.eau-olivet.fr' url = BASE_URI+API_ENDPOINT_LOGIN - response = requests.get(url, headers=headers, timeout=self._timeout) - - headers['Cookie'] = "" - for key in response.cookies.get_dict(): - if headers['Cookie']: - headers['Cookie'] += "; " - headers['Cookie'] += key + "=" + response.cookies[key] + response = await self._session.get(url, headers=headers, timeout=self._timeout) + headers['Cookie'] = "; ".join([f"{key}={value}" for (key, value) in response.cookies.items()]) self._headers = headers - decoded_content = response.content.decode('utf-8') + decoded_content = (await response.read()).decode('utf-8') self._token = self._get_token_1(decoded_content) or self._get_token_2(decoded_content) if self._token is None: raise PySuezError("Can't get token.") - def _get_cookie(self): + async def _get_cookie(self): """Connect and get the cookie""" - if self._session is None: - self._session = requests.Session() - self._get_token() + await self._get_token() data = { '_username': self._username, '_password': self._password, @@ -92,7 +86,7 @@ def _get_cookie(self): } url = BASE_URI+API_ENDPOINT_LOGIN try: - self._session.post(url, + response = await self._session.post(url, headers=self._headers, data=data, allow_redirects=False, @@ -100,15 +94,15 @@ def _get_cookie(self): except OSError: raise PySuezError("Can not submit login form.") - if not 'eZSESSID' in self._session.cookies.get_dict(): + if not 'eZSESSID' in response.cookies.keys(): raise PySuezError("Login error: Please check your username/password.") self._headers['Cookie'] = '' - self._headers['Cookie'] = 'eZSESSID='+self._session.cookies.get("eZSESSID") + self._headers['Cookie'] = f"eZSESSID={response.cookies['eZSESSID']}" return True - def _fetch_data(self): + async def _fetch_data(self): """Fetch latest data from Suez.""" now = datetime.datetime.now() today_year = now.strftime("%Y") @@ -123,12 +117,12 @@ def _fetch_data(self): yesterday_month, self._counter_id ) - self._get_cookie() - - data = requests.get(url, headers=self._headers) + await self._get_cookie() + data = await self._session.get(url, headers=self._headers) + json = await data.json() try: - self.state = int(float(data.json()[int( + self.state = int(float(json[int( yesterday_day)-1][1])*1000) self.success = True self.attributes['attribution'] = "Data provided by toutsurmoneau.fr" @@ -144,10 +138,10 @@ def _fetch_data(self): today_year, today_month, self._counter_id ) - data = requests.get(url, headers=self._headers) + data = await self._session.get(url, headers=self._headers) self.attributes['thisMonthConsumption'] = {} - for item in data.json(): + for item in json: self.attributes['thisMonthConsumption'][item[0]] = int( float(item[1])*1000) @@ -169,10 +163,11 @@ def _fetch_data(self): self._counter_id ) - data = requests.get(url, headers=self._headers) + data = await self._session.get(url, headers=self._headers) + json = await data.json() self.attributes['previousMonthConsumption'] = {} - for item in data.json(): + for item in json: self.attributes['previousMonthConsumption'][item[0]] = int( float(item[1])*1000) @@ -184,8 +179,8 @@ def _fetch_data(self): url = BASE_URI+API_ENDPOINT_HISTORY url += '{}'.format(self._counter_id) - data = requests.get(url, headers=self._headers) - fetched_data = data.json() + data = await self._session.get(url, headers=self._headers) + fetched_data = await data.json() self.attributes['highestMonthlyConsumption'] = int( float(fetched_data[-1])*1000) fetched_data.pop() @@ -205,11 +200,8 @@ def _fetch_data(self): raise PySuezError("Issue with history data") pass - def check_credentials(self): - if self._session is None: - self._session = requests.Session() - - self._get_token() + async def _check_credentials(self): + await self._get_token() data = { '_username': self._username, '_password': self._password, @@ -222,7 +214,7 @@ def check_credentials(self): url = BASE_URI+API_ENDPOINT_LOGIN try: - self._session.post(url, + await self._session.post(url, headers=self._headers, data=data, allow_redirects=False, @@ -249,9 +241,9 @@ def check_credentials(self): #else: # return False - def update(self): + async def _update(self): """Return the latest collected data from Linky.""" - self._fetch_data() + await self._fetch_data() if not self.success: return return self.attributes @@ -261,4 +253,21 @@ def close_session(self): self._session.close() self._session = None + async def update_async(self): + """Asynchronous update""" + async with aiohttp.ClientSession() as self._session: + return await self._update() + + async def check_credentials_async(self): + """Asynchronous check_credential""" + async with aiohttp.ClientSession() as self._session: + return await self._check_credentials() + + def update(self): + """Synchronous update""" + return asyncio.run(self.update_async()) + + def check_credentials(self): + """Asynchronous check_credential""" + return asyncio.run(self.check_credentials())