Skip to content

Commit fd3f77e

Browse files
authored
feat: add Wisio email and username modules (#644)
* feat: add Wisio email and username modules * fix(user_scan): handle ambiguous Wisio profile 404s
1 parent b12f251 commit fd3f77e

2 files changed

Lines changed: 123 additions & 0 deletions

File tree

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import httpx
2+
3+
from user_scanner.core.result import Result
4+
5+
URL = "https://be-lb-app.scaleabout.com/api/v1/auth/email/is_registered"
6+
7+
8+
async def validate_wisio(email: str) -> Result:
9+
show_url = "https://www.wisio.com"
10+
try:
11+
async with httpx.AsyncClient(timeout=15.0) as client:
12+
response = await client.get(URL, params={"email": email})
13+
except httpx.HTTPError as exc:
14+
return Result.error(exc, url=show_url)
15+
16+
if response.status_code != 200:
17+
return Result.error(
18+
f"Unexpected Wisio response: {response.status_code}", url=show_url
19+
)
20+
if response.text.strip() == "true":
21+
return Result.taken(url=show_url)
22+
if response.text.strip() == "false":
23+
return Result.available(url=show_url)
24+
return Result.error("Unexpected Wisio response body", url=show_url)
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
from urllib.parse import quote
2+
3+
from user_scanner.core.orchestrator import generic_validate
4+
from user_scanner.core.result import Result
5+
6+
API_URL = "https://be-lb-app.scaleabout.com/api/v1"
7+
8+
9+
def validate_wisio(user: str) -> Result:
10+
show_url = f"https://www.wisio.com/{user}"
11+
12+
def process_availability(response):
13+
available = response.text.strip()
14+
if response.status_code != 200 or available not in {"true", "false"}:
15+
return Result.error(
16+
f"Unexpected Wisio availability response: {response.status_code}"
17+
)
18+
if available == "true":
19+
return Result.available()
20+
21+
def process_profile(profile_response):
22+
if profile_response.status_code == 404:
23+
return Result.error(
24+
"No public creator profile "
25+
"(private, reserved, and invalid usernames look the same)"
26+
)
27+
28+
if profile_response.status_code != 200:
29+
return Result.error(
30+
f"Unexpected Wisio profile response: {profile_response.status_code}"
31+
)
32+
profile = profile_response.json()
33+
34+
username = profile.get("username") if isinstance(profile, dict) else None
35+
if not isinstance(username, str) or username.casefold() != user.casefold():
36+
return Result.error(
37+
"Wisio profile did not match the requested username"
38+
)
39+
40+
skills = [
41+
skill["name"]
42+
for skill in profile.get("skills") or []
43+
if isinstance(skill, dict) and isinstance(skill.get("name"), str)
44+
]
45+
social_links = [
46+
channel.get("formatted_url") or channel.get("url")
47+
for channel in profile.get("social_channels") or []
48+
if isinstance(channel, dict)
49+
and isinstance(channel.get("formatted_url") or channel.get("url"), str)
50+
]
51+
external_links = [
52+
link["url"]
53+
for link in profile.get("links") or []
54+
if isinstance(link, dict) and isinstance(link.get("url"), str)
55+
]
56+
avatar = profile.get("avatar") or {}
57+
cover = profile.get("cover_image") or {}
58+
59+
return Result.taken(
60+
extra={
61+
"id": profile.get("id"),
62+
"first_name": profile.get("first_name"),
63+
"last_name": profile.get("last_name"),
64+
"display_name": profile.get("display_name"),
65+
"title": profile.get("title"),
66+
"location": profile.get("location"),
67+
"skills": skills or None,
68+
"social_links": social_links or None,
69+
"external_links": external_links or None,
70+
"reviews": profile.get("reviews_count"),
71+
"rating": profile.get("reviews_score"),
72+
"created_at": profile.get("created_at"),
73+
"updated_at": profile.get("updated_at"),
74+
"preferred_currency": profile.get("preferred_currency"),
75+
"seller_availability": profile.get("seller_availability"),
76+
"locale": profile.get("locale"),
77+
"vacation": profile.get("vacation"),
78+
},
79+
media={
80+
"avatar": (avatar.get("medium") or {}).get("url")
81+
if isinstance(avatar, dict)
82+
else None,
83+
"cover": (cover.get("big") or {}).get("url")
84+
if isinstance(cover, dict)
85+
else None,
86+
},
87+
)
88+
89+
return generic_validate(
90+
f"{API_URL}/users/{quote(user, safe='')}",
91+
process_profile,
92+
)
93+
94+
return generic_validate(
95+
f"{API_URL}/users/is_username_allowed",
96+
process_availability,
97+
params={"username": user},
98+
show_url=show_url,
99+
)

0 commit comments

Comments
 (0)