Skip to content

Commit df36aa0

Browse files
committed
Iterate down possible URLs when they return 3030 errors
1 parent cee119e commit df36aa0

File tree

2 files changed

+85
-8
lines changed

2 files changed

+85
-8
lines changed

roborock/web_api.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import string
1010
import time
1111
from dataclasses import dataclass
12+
from typing import List
1213

1314
import aiohttp
1415
from aiohttp import ContentTypeError, FormData
@@ -74,11 +75,11 @@ def __init__(
7475
self._device_identifier = secrets.token_urlsafe(16)
7576
self.session = session
7677
self._iot_login_info: IotLoginInfo | None = None
78+
self._base_urls: List[str] = BASE_URLS if base_url is None else [base_url]
7779

7880
async def _get_iot_login_info(self) -> IotLoginInfo:
7981
if self._iot_login_info is None:
80-
valid_urls = BASE_URLS if self._base_url is None else [self._base_url]
81-
for iot_url in valid_urls:
82+
for iot_url in self._base_urls:
8283
url_request = PreparedRequest(iot_url, self.session)
8384
response = await url_request.request(
8485
"post",
@@ -95,9 +96,6 @@ async def _get_iot_login_info(self) -> IotLoginInfo:
9596
raise RoborockMissingParameters(
9697
"You are missing parameters for this request, are you sure you entered your username?"
9798
)
98-
elif response_code == 3030:
99-
_LOGGER.warning("Found account in deletion in %s (code %s)", country, country_code)
100-
continue
10199
else:
102100
raise RoborockException(f"{response.get('msg')} - response code: {response_code}")
103101
country_code = response["data"]["countrycode"]
@@ -272,6 +270,10 @@ async def request_code_v4(self) -> None:
272270
raise RoborockAccountDoesNotExist("Account does not exist - check your login and try again.")
273271
elif response_code == 9002:
274272
raise RoborockTooFrequentCodeRequests("You have attempted to request too many codes. Try again later")
273+
elif response_code == 3030 and len(self._base_urls) > 1:
274+
self._base_urls = self._base_urls[1:]
275+
self._iot_login_info = None
276+
return await self.request_code_v4()
275277
else:
276278
raise RoborockException(f"{code_response.get('msg')} - response code: {code_response.get('code')}")
277279

tests/test_web_api.py

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,18 +87,17 @@ async def test_code_login_v4_flow(mock_rest) -> None:
8787
ud = await api.code_login_v4(4123, "US", 1)
8888
assert ud == UserData.from_dict(USER_DATA)
8989

90-
9190
async def test_url_cycling(mock_rest) -> None:
9291
"""Test that we cycle through the URLs correctly."""
9392
# Clear mock rest so that we can override the patches.
9493
mock_rest.clear()
9594

96-
# 1. Mock US URL to return the account in deletion code
95+
# 2. Mock US URL to return valid status but None for countrycode
9796
mock_rest.post(
9897
re.compile("https://usiot.roborock.com/api/v1/getUrlByEmail.*"),
9998
status=200,
10099
payload={
101-
"code": 3030,
100+
"code": 200,
102101
"data": {"url": "https://usiot.roborock.com", "country": None, "countrycode": None},
103102
"msg": "Account in deletion",
104103
},
@@ -183,6 +182,82 @@ async def test_url_cycling(mock_rest) -> None:
183182
# Make sure we just have the three we tested for above.
184183
assert len(mock_rest.requests) == 3
185184

185+
async def test_thirty_thirty_cycling(mock_rest) -> None:
186+
"""Test that we cycle through the URLs correctly when users have deleted accounts in higher prio regions."""
187+
# Clear mock rest so that we can override the patches.
188+
mock_rest.clear()
189+
190+
mock_rest.post(
191+
re.compile("https://usiot.roborock.com/api/v1/getUrlByEmail.*"),
192+
status=200,
193+
payload={
194+
"code": 200,
195+
"data": {"url": "https://usiot.roborock.com", "country": "US", "countrycode": 1},
196+
"msg": "Account in deletion",
197+
},
198+
)
199+
200+
mock_rest.post(
201+
re.compile("https://euiot.roborock.com/api/v1/getUrlByEmail.*"),
202+
status=200,
203+
payload={
204+
"code": 200,
205+
"data": {"url": "https://euiot.roborock.com", "country": "EU", "countrycode": 49},
206+
"msg": "Success",
207+
},
208+
)
209+
210+
mock_rest.post(
211+
re.compile("https://usiot.roborock.com/api/v4/email/code/send.*"),
212+
status=200,
213+
payload={
214+
"code": 3030,
215+
},
216+
)
217+
mock_rest.post(
218+
re.compile("https://euiot.roborock.com/api/v4/email/code/send.*"),
219+
status=200,
220+
payload={
221+
"code": 200,
222+
},
223+
)
224+
225+
mock_rest.post(re.compile("https://ruiot.roborock.com/api/v1/getUrlByEmail.*"), status=500)
226+
mock_rest.post(re.compile("https://cniot.roborock.com/api/v1/getUrlByEmail.*"), status=500)
227+
228+
client = RoborockApiClient("test@example.com")
229+
await client.request_code_v4()
230+
231+
print(mock_rest.requests)
232+
assert (
233+
len(
234+
mock_rest.requests[
235+
(
236+
"post",
237+
normalize_url(
238+
"https://euiot.roborock.com/api/v4/email/code/send"
239+
),
240+
)
241+
]
242+
)
243+
== 1
244+
)
245+
assert (
246+
len(
247+
mock_rest.requests[
248+
(
249+
"post",
250+
normalize_url(
251+
"https://usiot.roborock.com/api/v4/email/code/send"
252+
),
253+
)
254+
]
255+
)
256+
== 1
257+
)
258+
# Assert that we didn't try on the Russian or Chinese regions
259+
assert('https://ruiot.roborock.com/api/v4/email/code/send' not in mock_rest.requests)
260+
assert('https://cniot.roborock.com/api/v4/email/code/send' not in mock_rest.requests)
186261

187262
async def test_missing_country_login(mock_rest) -> None:
188263
"""Test that we cycle through the URLs correctly."""

0 commit comments

Comments
 (0)