Skip to content

Commit 7bc431c

Browse files
committed
add: 2 new email_scan modules
1 parent 58cbdbd commit 7bc431c

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import httpx
2+
from user_scanner.core.result import Result
3+
4+
5+
async def _check(email: str) -> Result:
6+
url = "https://www.howtogeek.com/check-user-exists/"
7+
show_url = "https://www.howtogeek.com"
8+
9+
headers = {
10+
'User-Agent': "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Mobile Safari/537.36",
11+
'Accept': "application/json, text/plain, */*",
12+
'Accept-Encoding': "identity",
13+
'sec-ch-ua-platform': '"Android"',
14+
'Referer': "https://www.howtogeek.com/",
15+
'Priority': "u=1, i"
16+
}
17+
18+
params = {
19+
'email': email
20+
}
21+
22+
try:
23+
async with httpx.AsyncClient(timeout=10.0) as client:
24+
response = await client.get(url, params=params, headers=headers)
25+
26+
if response.status_code == 403:
27+
return Result.error("Caught by WAF or IP Block (403)")
28+
29+
if response.status_code == 429:
30+
return Result.error("Rate limited by How-To Geek (429)")
31+
32+
if response.status_code != 200:
33+
return Result.error(f"HTTP Error: {response.status_code}")
34+
35+
data = response.json()
36+
37+
# Use the userExists key for validation
38+
if data.get("userExists") is True:
39+
return Result.taken(url=show_url)
40+
41+
elif data.get("userExists") is False:
42+
return Result.available(url=show_url)
43+
44+
return Result.error("Unexpected response body structure")
45+
46+
except httpx.ConnectTimeout:
47+
return Result.error("Connection timed out! maybe region blocks")
48+
except httpx.ReadTimeout:
49+
return Result.error("Server took too long to respond (Read Timeout)")
50+
except Exception as e:
51+
return Result.error(e)
52+
53+
54+
async def validate_howtogeek(email: str) -> Result:
55+
return await _check(email)
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import httpx
2+
import json
3+
from user_scanner.core.result import Result
4+
5+
6+
async def _check(email: str) -> Result:
7+
csrf_url = "https://accounts.wondershare.com/api/v3/csrf-token"
8+
inspect_url = "https://accounts.wondershare.com/api/v3/account/inspect"
9+
show_url = "https://wondershare.com"
10+
11+
headers = {
12+
'User-Agent': "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Mobile Safari/537.36",
13+
'Accept': "application/json, text/plain, */*",
14+
'Accept-Encoding': "identity",
15+
'sec-ch-ua-platform': '"Android"',
16+
'x-lang': "en-us",
17+
'sec-ch-ua': '"Not:A-Brand";v="99", "Google Chrome";v="145", "Chromium";v="145"',
18+
'sec-ch-ua-mobile': "?1",
19+
'sec-fetch-site': "same-origin",
20+
'sec-fetch-mode': "cors",
21+
'sec-fetch-dest': "empty",
22+
'referer': "https://accounts.wondershare.com/m/login?source=&redirect_uri=https://www.wondershare.com/?source=&site=www.wondershare.com&verify=no",
23+
'accept-language': "en-US,en;q=0.9",
24+
'priority': "u=1, i"
25+
}
26+
27+
try:
28+
async with httpx.AsyncClient(timeout=15.0) as client:
29+
30+
r_csrf = await client.get(csrf_url, headers=headers)
31+
if r_csrf.status_code != 200:
32+
return Result.error(f"Handshake failed: {r_csrf.status_code}")
33+
34+
token = client.cookies.get("req_identity")
35+
36+
if not token:
37+
return Result.error("req_identity cookie missing from handshake")
38+
39+
inspect_headers = headers.copy()
40+
inspect_headers.update({
41+
'x-csrf-token': token,
42+
'Content-Type': "application/json",
43+
'origin': "https://accounts.wondershare.com",
44+
'referer': "https://accounts.wondershare.com/m/register"
45+
})
46+
47+
payload = {"email": email}
48+
49+
response = await client.put(
50+
inspect_url,
51+
content=json.dumps(payload),
52+
headers=inspect_headers
53+
)
54+
55+
if response.status_code == 403:
56+
return Result.error("Caught by WAF (403)")
57+
58+
if response.status_code != 200:
59+
return Result.error(f"HTTP Error: {response.status_code}")
60+
61+
data_final = response.json()
62+
status_obj = data_final.get("data")
63+
64+
if not isinstance(status_obj, dict):
65+
return Result.error("Invalid response data structure")
66+
67+
status_code = status_obj.get("exist")
68+
69+
# 1 = Registered, 2 = Not Registered
70+
if status_code == 1:
71+
return Result.taken(url=show_url)
72+
elif status_code == 2:
73+
return Result.available(url=show_url)
74+
75+
return Result.error(f"Unexpected exist value: {status_code}")
76+
77+
except Exception as e:
78+
return Result.error(e)
79+
80+
81+
async def validate_wondershare(email: str) -> Result:
82+
return await _check(email)

0 commit comments

Comments
 (0)