Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions user_scanner/email_scan/shopping/hautesauce.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import httpx
from user_scanner.core.result import Result


async def _check(email: str) -> Result:
url = "https://www.buyhautesauce.com/api/2024-04/graphql.json"
show_url = "https://www.buyhautesauce.com"

payload = {
"query": "mutation customerCreate($input: CustomerCreateInput!) {\n customerCreate(input: $input) {\n customer {\n id\n firstName\n lastName\n acceptsMarketing\n email\n }\n customerUserErrors {\n field\n message\n code\n }\n }\n}",
"operationName": "customerCreate",
"variables": {
"input": {
"acceptsMarketing": False,
"email": email,
"password": "",
"firstName": "Lost",
"lastName": "Knight"
}
}
}

headers = {
'User-Agent': "okhttp/4.12.0",
'Accept': "application/graphql+json, application/json",
'Accept-Encoding': "gzip",
'Content-Type': "application/json",
'x-shopify-storefront-access-token': "7b89272b5ed5a3ff00ad881bf63b130a"
}

async with httpx.AsyncClient(http2=True) as client:
try:
response = await client.post(url, json=payload, headers=headers, timeout=6.0)

if response.status_code == 429:
return Result.error("Rate limited", url=show_url)

if response.status_code == 200:
data = response.json()
errors = data.get("data", {}).get("customerCreate", {}).get("customerUserErrors", [])

is_taken = False
is_available = False
extras = {}

for error in errors:
code = error.get("code", "")
msg = error.get("message", "")
field = error.get("field") or []

if code == "TAKEN" or "already been taken" in msg or "already exists" in msg:
is_taken = True
elif "verify your email address" in msg:
is_taken = True
extras["is_verified"] = "False"
elif code == "BLANK" or "Password" in msg or "password" in field:
is_available = True

if is_taken:
return Result.taken(url=show_url, extra=extras)
elif is_available:
return Result.available(url=show_url)

return Result.error("Unexpected response body, report it via GitHub issues", url=show_url)

return Result.error(f"Unexpected response status: {response.status_code}, report it via GitHub issues", url=show_url)

except Exception as e:
return Result.error(e, url=show_url)


async def validate_hautesauce(email: str) -> Result:
"""
Haute Sauce email validator. Checks Shopify storefront customerCreate mutation.
"""
return await _check(email)
118 changes: 118 additions & 0 deletions user_scanner/email_scan/shopping/rappi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import sys
import time
import secrets
import httpx
from user_scanner.core.result import Result


def _generate_device_id() -> str:
"""Generate a random 16-character hex string for deviceid."""
return secrets.token_hex(8)


def _generate_timestamp() -> str:
"""Generate current epoch timestamp in milliseconds."""
return str(int(time.time() * 1000))


async def _check(email: str) -> Result:
url_check = "https://services.rappi.com.ar/api/rocket/user/account/check-email"
url_login = "https://services.rappi.com.ar/api/rocket/login/email/application_user"
show_url = "https://www.rappi.com"

device_id = _generate_device_id()
ts = _generate_timestamp()

headers = {
'User-Agent': "Dalvik/2.1.0 (Linux; U; Android 13; Pixel 6 Build/TP1A.220624.021)",
'Accept': "application/json",
'Accept-Encoding': "gzip",
'user_id': "",
'custom_country_code': "AR",
'deviceid': device_id,
'app-version': "88721",
'app-version-name': "8.35.20260724-88721",
'store-platform': "google",
'amplitude-session-id': ts,
'timestamp': ts,
'request_timestamp': ts,
'accept-language': "en-US",
'language': "en",
'country-code': "AR",
'fp_dp_id': "3703578013",
'content-type': "application/json; charset=UTF-8"
}

payload_check = {
"email": email
}

async with httpx.AsyncClient(http2=True) as client:
try:
# Silent check-email endpoint
response = await client.post(url_check, json=payload_check, headers=headers, timeout=6.0)

if response.status_code == 429:
return Result.error("Rate limited, wait 30 to 60s and retry", url=show_url)

if response.status_code == 200:
data = response.json()
exists = data.get("exists")

if exists is True:
# Explicitly verified email exists
pass
elif exists is False:
return Result.available(url=show_url)
else:
return Result.error("Unexpected response body, report it via GitHub issues", url=show_url)

else:
return Result.error(f"Unexpected response status: {response.status_code}, report it via GitHub issues", url=show_url)

# If registered (exists is True), check if --allow-loud was passed
allow_loud = "--allow-loud" in sys.argv

if not allow_loud:
# Return TAKEN silently without hitting the loud login endpoint
return Result.taken(reason="Use flag '--allow-loud' to see the target's masked phone number", url=show_url)

# If --allow-loud is passed, proceed to Endpoint 2 to extract masked phone info
payload_login = {
"email": email,
"scope": "all"
}
new_ts = _generate_timestamp()
headers['timestamp'] = new_ts
headers['request_timestamp'] = new_ts

response_login = await client.post(url_login, json=payload_login, headers=headers, timeout=6.0)

extras = {}
if response_login.status_code in [200, 400, 401, 422]:
try:
data_login = response_login.json()
err = data_login.get("error", {})
if isinstance(err, dict):
phone = err.get("verification_value")
if phone:
extras["phone"] = phone
v_type = err.get("verification_type")
if v_type:
extras["verification_type"] = v_type
except Exception:
pass

return Result.taken(url=show_url, extra=extras)

except Exception as e:
return Result.error(e, url=show_url)


async def validate_rappi(email: str) -> Result:
"""
Rappi email validator.
Silent by default (hits check-email endpoint).
When --allow-loud is passed, also hits the login endpoint to extract masked phone intelligence.
"""
return await _check(email)
62 changes: 62 additions & 0 deletions user_scanner/email_scan/women_health/femometer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import httpx
from user_scanner.core.result import Result


async def _check(email: str) -> Result:
url = "https://api.femometer.com/v1/user_location"
show_url = "https://www.femometer.com"

params = {
'country_code': "0",
'phone_no': "0",
'email': email,
'app_flag': "1",
'language': "2",
'localTimezone': "UTC",
'app_version': "5.43.5(4260)",
'platform_type': "1",
'isGooglePlay': "true"
}

headers = {
'Host': "api-us.femometer.com",
'User-Agent': "okhttp/4.12.0",
'Accept-Encoding': "gzip",
'content-type': "application/json; charset=utf-8"
}

async with httpx.AsyncClient(http2=True) as client:
try:
response = await client.get(url, params=params, headers=headers, timeout=6.0)

if response.status_code == 429:
return Result.error("Rate limited", url=show_url)

if response.status_code == 200:
data = response.json()
user_id = data.get("userId")
location = data.get("userLocation")

if user_id == 0 and location == 0:
return Result.available(url=show_url)
elif user_id and user_id != 0:
extras = {}
extras["user id"] = user_id
if data.get("url"):
extras["server location"] = data.get("url")

return Result.taken(url=show_url, extra=extras)

return Result.error("Unexpected response body, report it via GitHub issues", url=show_url)

return Result.error(f"Unexpected response status: {response.status_code}, report it via GitHub issues", url=show_url)

except Exception as e:
return Result.error(e, url=show_url)


async def validate_femometer(email: str) -> Result:
"""
Femometer email validator. Checks user location endpoint for account existence.
"""
return await _check(email)
55 changes: 55 additions & 0 deletions user_scanner/email_scan/women_health/iyoni.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import httpx
from user_scanner.core.result import Result


async def _check(email: str) -> Result:
url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword"
show_url = "https://play.google.com/store/apps/details?id=pl.lifebite.iyoni"

params = {
'key': "AIzaSyCBpmgku8MoHzusfTQlPwMcUObhPGUHc-w"
}

payload = {
"email": email,
"password": "generic_password_123",
"returnSecureToken": True,
"clientType": "CLIENT_TYPE_ANDROID"
}

headers = {
'User-Agent': "Dalvik/2.1.0 (Linux; U; Android 13; Pixel 6 Build/TP1A.220624.021)",
'Connection': "Keep-Alive",
'Accept-Encoding': "gzip",
'Content-Type': "application/json",
'X-Android-Package': "pl.lifebite.iyoni",
'Accept-Language': "en-US"
}

async with httpx.AsyncClient(http2=True) as client:
try:
response = await client.post(url, params=params, json=payload, headers=headers, timeout=6.0)

if response.status_code == 429:
return Result.error("Rate limited", url=show_url)

if response.status_code == 400:
data = response.json()
error_msg = data.get("error", {}).get("message", "")
if error_msg == "EMAIL_NOT_FOUND":
return Result.available(url=show_url)
elif error_msg == "INVALID_PASSWORD":
return Result.taken(url=show_url)
return Result.error("Unexpected response body, report it via GitHub issues", url=show_url)

return Result.error(f"Unexpected response status: {response.status_code}, report it via GitHub issues", url=show_url)

except Exception as e:
return Result.error(e, url=show_url)


async def validate_iyoni(email: str) -> Result:
"""
Iyoni email validator. Checks Firebase Auth verifyPassword endpoint.
"""
return await _check(email)
77 changes: 77 additions & 0 deletions user_scanner/email_scan/women_health/meetyou.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import time
import httpx
from user_scanner.core.result import Result


def _generate_timestamp() -> str:
"""Generate current epoch timestamp in milliseconds."""
return str(int(time.time() * 1000))


async def _check(email: str) -> Result:
url = "https://ad.meetyouintl.com/v2/email_check"
show_url = "https://www.meetyouintl.com"

params = {
'email': email,
'v': "5.0.1",
'platform': "android"
}

ts = _generate_timestamp()

headers = {
'Host': "users.meetyouintl.com",
'User-Agent': "com.meetyou.intl/5.0.1 MeetYouClient/2.0.0 (2930501020100000)",
'Accept-Encoding': "gzip, deflate",
'country': "US",
'open-person-ad': "1",
'bundleid': "201",
'platform': "android",
'mode': "3",
'syslang': "en-US",
'zone': "0",
'is-em': "0000300-NONE",
'client': "0",
'simcountry': "US",
'lang': "en-US",
'uregion': "singapore",
'version': "5.0.1",
'myclient': "2930501020100000",
'clang': "en-US",
'v': "5.0.1",
'channel_id': "201",
'syscountry': "US",
'session-id': ts
}

async with httpx.AsyncClient(http2=True) as client:
try:
response = await client.get(url, params=params, headers=headers, timeout=6.0)

if response.status_code == 429:
return Result.error("Rate limited", url=show_url)

if response.status_code == 200:
data = response.json()
code = data.get("code")
msg = str(data.get("message", "")).lower()

if code == 0 and "registered" not in msg:
return Result.available(url=show_url)
elif code == 11001130 or "registered" in msg:
return Result.taken(url=show_url)

return Result.error("Unexpected response body, report it via GitHub issues", url=show_url)

return Result.error(f"Unexpected response status: {response.status_code}, report it via GitHub issues", url=show_url)

except Exception as e:
return Result.error(e, url=show_url)


async def validate_meetyou(email: str) -> Result:
"""
MeetYou email validator. Checks email_check endpoint for account existence.
"""
return await _check(email)
Loading
Loading