Skip to content

Commit 0dfdc2c

Browse files
authored
fix(user_scan): update Boot.dev module for public API (#631)
- Use the public user API with URL-encoded handles - Extract profile metadata and avatar from verified responses
1 parent 4db3972 commit 0dfdc2c

1 file changed

Lines changed: 43 additions & 18 deletions

File tree

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,52 @@
1-
from user_scanner.core.helpers import get_random_user_agent
1+
from urllib.parse import quote
2+
23
from user_scanner.core.orchestrator import generic_validate
34
from user_scanner.core.result import Result
45

5-
def validate_boot(user):
6-
url = f"https://www.boot.dev/u/{user}"
7-
show_url = f"https://boot.dev/u/{user}"
86

9-
headers = {
10-
"User-Agent": get_random_user_agent(),
11-
"Accept": "application/json, text/plain, */*",
12-
"X-Requested-With": "XMLHttpRequest",
13-
"Referer": "https://boot.dev/",
14-
}
7+
def validate_boot_dev(user: str) -> Result:
8+
url = f"https://api.boot.dev/v1/users/public/{quote(user.lower(), safe='')}"
9+
show_url = f"https://boot.dev/u/{user}"
1510

1611
def process(response):
17-
data = response.text
12+
data = response.json()
1813

19-
if response.status_code == 404:
14+
if response.status_code == 404 and data.get("error") == "User not found":
2015
return Result.available()
21-
if "User not found" in data:
22-
return Result.available()
23-
if response.status_code == 200 and "__NUXT__" in data and f"publicUser:{user}" in data:
24-
return Result.taken()
25-
return Result.error("Unexpected response format")
16+
profile = data.get("data", {})
17+
if (
18+
response.status_code == 200
19+
and data.get("status") == "success"
20+
and profile.get("handle", "").lower() == user.lower()
21+
):
22+
linkedin = profile.get("linkedinURL")
23+
if linkedin and linkedin.startswith(("linkedin.com/", "www.linkedin.com/")):
24+
linkedin = f"https://{linkedin}"
25+
26+
return Result.taken(
27+
extra={
28+
"first_name": profile.get("firstName"),
29+
"last_name": profile.get("lastName"),
30+
"handle": profile.get("handle"),
31+
"bio": profile.get("bio"),
32+
"location": profile.get("location"),
33+
"github": profile.get("githubHandle"),
34+
"twitter": profile.get("twitterHandle"),
35+
"linkedin": linkedin,
36+
"website": profile.get("websiteURL"),
37+
"user_id": profile.get("uuid"),
38+
"joined": profile.get("createdAt"),
39+
"updated": profile.get("updatedAt"),
40+
"level": profile.get("level"),
41+
"xp": profile.get("xp"),
42+
"role": profile.get("role"),
43+
"is_member": profile.get("isMember"),
44+
"gems": profile.get("gems"),
45+
"xp_for_level": profile.get("xpForLevel"),
46+
"xp_total_for_level": profile.get("xpTotalForLevel"),
47+
},
48+
media={"avatar": profile.get("profileImageURL")},
49+
)
50+
return Result.error(f"Unexpected response status: {response.status_code}")
2651

27-
return generic_validate(url, process, show_url=show_url, headers=headers)
52+
return generic_validate(url, process, show_url=show_url)

0 commit comments

Comments
 (0)