5
5
from datetime import datetime , timedelta
6
6
from typing import Dict , List , Optional , Union , Tuple
7
7
from urllib .parse import urlsplit , parse_qs
8
+ from random import choices
9
+ import string
8
10
9
11
from aiohttp import ClientSession , ClientResponse
10
12
from aiohttp .client_exceptions import ClientError , ClientResponseError
@@ -37,11 +39,17 @@ class API: # pylint: disable=too-many-instance-attributes
37
39
"""Define a class for interacting with the MyQ iOS App API."""
38
40
39
41
def __init__ (
40
- self , username : str , password : str , websession : ClientSession = None
42
+ self ,
43
+ username : str ,
44
+ password : str ,
45
+ websession : ClientSession = None ,
46
+ useragent : Optional [str ] = None ,
41
47
) -> None :
42
48
"""Initialize."""
43
49
self .__credentials = {"username" : username , "password" : password }
44
- self ._myqrequests = MyQRequest (websession or ClientSession ())
50
+ self ._myqrequests = MyQRequest (
51
+ websession or ClientSession (), useragent = useragent
52
+ )
45
53
self ._authentication_task = None # type:Optional[asyncio.Task]
46
54
self ._codeverifier = None # type: Optional[str]
47
55
self ._invalid_credentials = False # type: bool
@@ -383,7 +391,6 @@ async def _oauth_authenticate(self) -> Tuple[str, int]:
383
391
websession = session ,
384
392
headers = {
385
393
"Cookie" : resp .cookies .output (attrs = []),
386
- "User-Agent" : "null" ,
387
394
},
388
395
allow_redirects = False ,
389
396
login_request = True ,
@@ -400,7 +407,6 @@ async def _oauth_authenticate(self) -> Tuple[str, int]:
400
407
websession = session ,
401
408
headers = {
402
409
"Content-Type" : "application/x-www-form-urlencoded" ,
403
- "User-Agent" : "null" ,
404
410
},
405
411
data = {
406
412
"client_id" : OAUTH_CLIENT_ID ,
@@ -565,8 +571,48 @@ async def update_device_info(self) -> None:
565
571
async def login (username : str , password : str , websession : ClientSession = None ) -> API :
566
572
"""Log in to the API."""
567
573
574
+ # Retrieve user agent from GitHub if not provided for login.
575
+ _LOGGER .debug ("No user agent provided, trying to retrieve from GitHub." )
576
+ url = f"https://raw.githubusercontent.com/arraylabs/pymyq/master/.USER_AGENT"
577
+
578
+ try :
579
+ async with ClientSession () as session :
580
+ async with session .get (url ) as resp :
581
+ useragent = await resp .text ()
582
+ resp .raise_for_status ()
583
+ _LOGGER .debug (f"Retrieved user agent { useragent } from GitHub." )
584
+
585
+ except ClientError as exc :
586
+ # Default user agent to random string with length of 5 if failure to retrieve it from GitHub.
587
+ useragent = "#RANDOM:5"
588
+ _LOGGER .warning (
589
+ f"Failed retrieving user agent from GitHub, will use randomized user agent "
590
+ f"instead: { str (exc )} "
591
+ )
592
+
593
+ # Check if value for useragent is to create a random user agent.
594
+ useragent_list = useragent .split (":" )
595
+ if useragent_list [0 ] == "#RANDOM" :
596
+ # Create a random string, check if length is provided for the random string, if not then default is 5.
597
+ try :
598
+ randomlength = int (useragent_list [1 ]) if len (useragent_list ) == 2 else 5
599
+ except ValueError :
600
+ _LOGGER .debug (
601
+ f"Random length value { useragent_list [1 ]} in user agent { useragent } is not an integer. "
602
+ f"Setting to 5 instead."
603
+ )
604
+ randomlength = 5
605
+
606
+ # Create the random user agent.
607
+ useragent = "" .join (
608
+ choices (string .ascii_letters + string .digits , k = randomlength )
609
+ )
610
+ _LOGGER .debug (f"User agent set to randomized value: { useragent } ." )
611
+
568
612
# Set the user agent in the headers.
569
- api = API (username = username , password = password , websession = websession )
613
+ api = API (
614
+ username = username , password = password , websession = websession , useragent = useragent
615
+ )
570
616
_LOGGER .debug ("Performing initial authentication into MyQ" )
571
617
try :
572
618
await api .authenticate (wait = True )
0 commit comments