user-scanner 1.1.0__py3-none-any.whl → 1.1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"auto_update_status": true}
@@ -1,4 +1,5 @@
1
1
  from enum import Enum
2
+
2
3
  from colorama import Fore, Style
3
4
 
4
5
  DEBUG_MSG = """Result {{
@@ -53,7 +54,7 @@ class Result:
53
54
  self.username = None
54
55
  self.site_name = None
55
56
  self.category = None
56
- self.is_email = False
57
+ self.is_email = False
57
58
  self.update(**kwargs)
58
59
 
59
60
  def update(self, **kwargs):
@@ -62,12 +63,12 @@ class Result:
62
63
  setattr(self, field, kwargs[field])
63
64
 
64
65
  @classmethod
65
- def taken(cls, **kwargs):
66
- return cls(Status.TAKEN, **kwargs)
66
+ def taken(cls, reason: str | Exception | None = None, **kwargs):
67
+ return cls(Status.TAKEN, reason, **kwargs)
67
68
 
68
69
  @classmethod
69
- def available(cls, **kwargs):
70
- return cls(Status.AVAILABLE, **kwargs)
70
+ def available(cls, reason: str | Exception | None = None, **kwargs):
71
+ return cls(Status.AVAILABLE, reason, **kwargs)
71
72
 
72
73
  @classmethod
73
74
  def error(cls, reason: str | Exception | None = None, **kwargs):
@@ -79,7 +80,7 @@ class Result:
79
80
  status = Status(i)
80
81
  except ValueError:
81
82
  return cls(Status.ERROR, "Invalid status. Please contact maintainers.")
82
- return cls(status, reason if status == Status.ERROR else None)
83
+ return cls(status, reason)
83
84
 
84
85
  def to_number(self) -> int:
85
86
  return self.status.value
@@ -102,7 +103,7 @@ class Result:
102
103
  "username": self.username,
103
104
  "site_name": self.site_name,
104
105
  "category": self.category,
105
- "is_email": self.is_email
106
+ "is_email": self.is_email,
106
107
  }
107
108
 
108
109
  def debug(self) -> str:
@@ -111,7 +112,7 @@ class Result:
111
112
  def to_json(self) -> str:
112
113
  msg = JSON_TEMPLATE.format(**self.as_dict())
113
114
  if self.is_email:
114
- msg = msg.replace("\t\"username\":", "\t\"email\":")
115
+ msg = msg.replace('\t"username":', '\t"email":')
115
116
  return msg
116
117
 
117
118
  def to_csv(self) -> str:
@@ -129,22 +130,29 @@ class Result:
129
130
  return self.to_number() == other
130
131
  return NotImplemented
131
132
 
133
+ def get_output_color(self) -> str:
134
+ if self == Status.ERROR:
135
+ return Fore.YELLOW
136
+ elif self.is_email:
137
+ return Fore.GREEN if self == Status.TAKEN else Fore.RED
138
+ else:
139
+ return Fore.GREEN if self == Status.AVAILABLE else Fore.RED
140
+
141
+ def get_output_icon(self) -> str:
142
+ if self == Status.ERROR:
143
+ return "[!]"
144
+ elif self.is_email:
145
+ return "[✔]" if self == Status.TAKEN else "[✘]"
146
+ else:
147
+ return "[✔]" if self == Status.AVAILABLE else "[✘]"
148
+
132
149
  def get_console_output(self) -> str:
133
150
  site_name = self.site_name
134
151
  username = self.username
135
152
  status_text = self.status.to_label(self.is_email)
136
153
 
137
- if self.is_email:
138
- color = Fore.GREEN if self == Status.TAKEN else Fore.RED
139
- icon = "[✔]" if self == Status.TAKEN else "[✘]"
140
- else:
141
- color = Fore.GREEN if self == Status.AVAILABLE else Fore.RED
142
- icon = "[✔]" if self == Status.AVAILABLE else "[✘]"
143
-
144
- if self == Status.AVAILABLE or self == Status.TAKEN:
145
- return f" {color}{icon} {site_name} ({username}): {status_text}{Style.RESET_ALL}"
146
- elif self == Status.ERROR:
147
- reason = f" ({self.get_reason()})" if self.has_reason() else ""
148
- return f" {Fore.YELLOW}[!] {site_name} ({username}): {status_text}{reason}{Style.RESET_ALL}"
154
+ color = self.get_output_color()
155
+ icon = self.get_output_icon()
149
156
 
150
- return ""
157
+ reason = f" ({self.get_reason()})" if self.has_reason() else ""
158
+ return f" {color}{icon} {site_name} ({username}): {status_text}{reason}{Style.RESET_ALL}"
@@ -14,7 +14,7 @@ async def _check(email: str) -> Result:
14
14
  "content-type": "application/x-www-form-urlencoded; charset=UTF-8"
15
15
  }
16
16
 
17
- async with httpx.AsyncClient(http2=True, follow_redirects=True, timeout=10) as client:
17
+ async with httpx.AsyncClient(http2=True, follow_redirects=True, timeout=3) as client:
18
18
  try:
19
19
  landing_resp = await client.get(base_url, headers=headers)
20
20
  token_match = re.search(r'var\s+token\s*=\s*"([^"]+)"', landing_resp.text)
File without changes
@@ -0,0 +1,40 @@
1
+ import httpx
2
+ from user_scanner.core.result import Result
3
+
4
+ async def _check(email: str) -> Result:
5
+ async with httpx.AsyncClient(http2=False, follow_redirects=True) as client:
6
+ try:
7
+ url = "https://stackoverflow.com/users/login"
8
+
9
+ payload = {
10
+ 'ssrc': "login",
11
+ 'email': email,
12
+ 'password': "Password109-grt",
13
+ 'oauth_version': "",
14
+ 'oauth_server': ""
15
+ }
16
+
17
+ headers = {
18
+ 'User-Agent': "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36",
19
+ 'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
20
+ 'Accept-Encoding': "identity",
21
+ 'sec-ch-ua-platform': '"Linux"',
22
+ 'origin': "https://stackoverflow.com",
23
+ 'referer': "https://stackoverflow.com/users/login"
24
+ }
25
+
26
+ response = await client.post(url, data=payload, headers=headers)
27
+ body = response.text
28
+
29
+ if "No user found with matching email" in body:
30
+ return Result.available()
31
+ elif "The email or password is incorrect" in body:
32
+ return Result.taken()
33
+ else:
34
+ return Result.error("Unexpected response body")
35
+
36
+ except Exception as e:
37
+ return Result.error(f"unexpected exception: {e}")
38
+
39
+ async def validate_stackoverflow(email: str) -> Result:
40
+ return await _check(email)
File without changes
@@ -0,0 +1,82 @@
1
+ import httpx
2
+ import re
3
+ from user_scanner.core.result import Result
4
+
5
+
6
+ async def _check(email: str) -> Result:
7
+ async with httpx.AsyncClient(http2=False, follow_redirects=True) as client:
8
+ try:
9
+ url1 = "https://gumroad.com/users/forgot_password/new"
10
+ headers1 = {
11
+ 'User-Agent': "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36",
12
+ 'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
13
+ 'Accept-Encoding': "gzip, deflate, br, zstd",
14
+ 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"',
15
+ 'sec-ch-ua-mobile': "?0",
16
+ 'sec-ch-ua-platform': '"Linux"',
17
+ 'upgrade-insecure-requests': "1",
18
+ 'referer': "https://www.google.com/",
19
+ 'accept-language': "en-US,en;q=0.9"
20
+ }
21
+
22
+ res1 = await client.get(url1, headers=headers1)
23
+ html = res1.text
24
+
25
+ csrf_match = re.search(
26
+ r'authenticity_token":"([^&]+)"', html)
27
+ if not csrf_match:
28
+ csrf_match = re.search(
29
+ r'name="csrf-token" content="([^"]+)"', html)
30
+
31
+ if not csrf_match:
32
+ return Result.error("Failed to extract CSRF token")
33
+
34
+ csrf_token = csrf_match.group(1)
35
+
36
+ url2 = "https://gumroad.com/users/forgot_password"
37
+
38
+ payload = {
39
+ "user": {
40
+ "email": email
41
+ }
42
+ }
43
+
44
+ headers2 = {
45
+ 'User-Agent': "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36",
46
+ 'Accept': "text/html, application/xhtml+xml",
47
+ 'Accept-Encoding': "gzip, deflate, br, zstd",
48
+ 'Content-Type': "application/json",
49
+ 'sec-ch-ua-platform': '"Linux"',
50
+ 'x-csrf-token': csrf_token,
51
+ 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"',
52
+ 'x-inertia': "true",
53
+ 'sec-ch-ua-mobile': "?0",
54
+ 'x-requested-with': "XMLHttpRequest",
55
+ 'origin': "https://gumroad.com",
56
+ 'sec-fetch-site': "same-origin",
57
+ 'sec-fetch-mode': "cors",
58
+ 'sec-fetch-dest': "empty",
59
+ 'referer': "https://gumroad.com/users/forgot_password/new",
60
+ 'accept-language': "en-US,en;q=0.9",
61
+ 'priority': "u=1, i"
62
+ }
63
+
64
+ response = await client.post(url2, json=payload, headers=headers2)
65
+
66
+ data = response.json()
67
+ flash_msg = data.get("props", {}).get(
68
+ "flash", {}).get("message", "")
69
+
70
+ if "An account does not exist" in flash_msg:
71
+ return Result.available()
72
+ elif "An account does not exist" not in flash_msg:
73
+ return Result.taken()
74
+ else:
75
+ return Result.error(f"Unexpected status: {response.status_code}")
76
+
77
+ except Exception as e:
78
+ return Result.error(f"unexpected exception: {e}")
79
+
80
+
81
+ async def validate_gumroad(email: str) -> Result:
82
+ return await _check(email)
@@ -0,0 +1,58 @@
1
+ import httpx
2
+ from user_scanner.core.result import Result
3
+
4
+
5
+ async def _check(email: str) -> Result:
6
+ async with httpx.AsyncClient(http2=False) as client:
7
+ try:
8
+ url = "https://www.patreon.com/api/auth"
9
+
10
+ params = {
11
+ 'include': "user.null",
12
+ 'fields[user]': "[]",
13
+ 'json-api-version': "1.0",
14
+ 'json-api-use-default-includes': "false"
15
+ }
16
+
17
+ payload = "{\"data\":{\"type\":\"genericPatreonApi\",\"attributes\":{\"patreon_auth\":{\"email\":\"" + email + \
18
+ "\",\"allow_account_creation\":false},\"auth_context\":\"auth\",\"ru\":\"https://www.patreon.com/home\"},\"relationships\":{}}}"
19
+
20
+ headers = {
21
+ 'User-Agent': "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Mobile Safari/537.36",
22
+ 'Accept-Encoding': "identity",
23
+ 'content-type': "application/vnd.api+json",
24
+ 'origin': "https://www.patreon.com"
25
+ }
26
+
27
+ response = await client.post(
28
+ url,
29
+ params=params,
30
+ content=payload,
31
+ headers=headers
32
+ )
33
+
34
+ if response.status_code != 200:
35
+ return Result.error(f"Status {response.status_code}")
36
+
37
+ data = response.json()
38
+ next_step = data.get("data", {}).get(
39
+ "attributes", {}).get("next_auth_step")
40
+
41
+ if next_step == "password":
42
+ return Result.taken()
43
+ elif next_step == "signup":
44
+ return Result.available()
45
+ else:
46
+ return Result.error("Unexpected auth step")
47
+
48
+ except Exception as e:
49
+ return Result.error(f"unexpected exception: {e}")
50
+
51
+
52
+ async def validate_patreon(email: str) -> Result:
53
+ return await _check(email)
54
+
55
+
56
+
57
+
58
+
File without changes
@@ -0,0 +1,47 @@
1
+ import httpx
2
+ from user_scanner.core.result import Result
3
+
4
+ async def _check(email: str) -> Result:
5
+ async with httpx.AsyncClient(http2=True) as client:
6
+ try:
7
+ url = "https://www.chess.com/rpc/chesscom.authentication.v1.EmailValidationService/Validate"
8
+
9
+ payload = {
10
+ "email": email
11
+ }
12
+
13
+ headers = {
14
+ 'User-Agent': "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Mobile Safari/537.36",
15
+ 'Accept': "application/json, text/plain, */*",
16
+ 'Accept-Encoding': "identity",
17
+ 'Content-Type': "application/json",
18
+ 'sec-ch-ua-platform': '"Android"',
19
+ 'accept-language': "en_US",
20
+ 'connect-protocol-version': "1",
21
+ 'origin': "https://www.chess.com",
22
+ 'sec-fetch-site': "same-origin",
23
+ 'sec-fetch-mode': "cors",
24
+ 'referer': "https://www.chess.com/register",
25
+ 'priority': "u=1, i"
26
+ }
27
+
28
+ response = await client.post(url, json=payload, headers=headers)
29
+
30
+ if response.status_code != 200:
31
+ return Result.error(f"Status {response.status_code}, report is via GitHub issues")
32
+
33
+ data = response.json()
34
+ status = data.get("status")
35
+
36
+ if status == "EMAIL_STATUS_TAKEN":
37
+ return Result.taken()
38
+ elif status == "EMAIL_STATUS_AVAILABLE":
39
+ return Result.available()
40
+ else:
41
+ return Result.error(f"Unknown status: {status}, report is via GitHub issues")
42
+
43
+ except Exception as e:
44
+ return Result.error(f"unexpected exception: {e}")
45
+
46
+ async def validate_chess_com(email: str) -> Result:
47
+ return await _check(email)
user_scanner/version.json CHANGED
@@ -1,4 +1,4 @@
1
1
  {
2
- "version": "1.1.0",
2
+ "version": "1.1.0.1",
3
3
  "version_type": "pypi"
4
4
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: user-scanner
3
- Version: 1.1.0
3
+ Version: 1.1.0.1
4
4
  Summary: Check username availability across multiple popular platforms
5
5
  Keywords: username,checker,availability,social,tech,python,user-scanner
6
6
  Author-email: Kaif <kafcodec@gmail.com>
@@ -16,7 +16,7 @@ Project-URL: Homepage, https://github.com/kaifcodec/user-scanner
16
16
 
17
17
  ![User Scanner Logo](https://github.com/user-attachments/assets/49ec8d24-665b-4115-8525-01a8d0ca2ef4)
18
18
  <p align="center">
19
- <img src="https://img.shields.io/badge/Version-1.0.10.1-blueviolet?style=for-the-badge&logo=github" />
19
+ <img src="https://img.shields.io/badge/Version-1.1.0.1-blueviolet?style=for-the-badge&logo=github" />
20
20
  <img src="https://img.shields.io/github/issues/kaifcodec/user-scanner?style=for-the-badge&logo=github" />
21
21
  <img src="https://img.shields.io/badge/Tested%20on-Termux-black?style=for-the-badge&logo=termux" />
22
22
  <img src="https://img.shields.io/badge/Tested%20on-Windows-cyan?style=for-the-badge&logo=Windows" />
@@ -28,7 +28,7 @@ Project-URL: Homepage, https://github.com/kaifcodec/user-scanner
28
28
 
29
29
  ### ⚠️ Email OSINT mode had not been implemented yet, still in progress
30
30
 
31
- A powerful *Email OSINT tool* that checks if a specific email is registered on various sites, combined with *username scanning* — 2-in-1 solution.
31
+ A powerful *Email OSINT tool* that checks if a specific email is registered on various sites, combined with *username scanning* for branding or OSINT — 2-in-1 tool.
32
32
 
33
33
  Perfect for fast, accurate and lightweight email OSINT
34
34
 
@@ -1,6 +1,7 @@
1
1
  user_scanner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  user_scanner/__main__.py,sha256=upuAt-VZO08YzaA4paD7ArUy2S2K12wdTqlNxn107eY,10762
3
- user_scanner/version.json,sha256=oTUn4qlwwbH4RAqsIExmaGV6igJwBW8xmvRpphKh6Bc,47
3
+ user_scanner/config.json,sha256=WtVnrpPxhGUBmx_dBShO3R0NnipVBVz3BfzlEPO5Amc,28
4
+ user_scanner/version.json,sha256=qPO6w95jVdDx0X8YsZd9W4mBunJRM3ygchXw7ESU4VY,49
4
5
  user_scanner/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
6
  user_scanner/cli/banner.py,sha256=3b4PIggnJrmxF4DfbuPMqSavpwNl0m5uedaOL2SXN3o,766
6
7
  user_scanner/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -8,22 +9,25 @@ user_scanner/core/email_orchestrator.py,sha256=EMQGqvk4GqaXQqCSTOYdveRuu63bLf71r
8
9
  user_scanner/core/formatter.py,sha256=CMwyR6PuP15HqyS6oWe40ye_4dne14lrIj7_5T7rla4,725
9
10
  user_scanner/core/helpers.py,sha256=g_WMbEJvT98TA4Db9c-LPIDl6ExjLQ1dSE3cIyKRUos,6406
10
11
  user_scanner/core/orchestrator.py,sha256=OLC4JmBJm1ZzwsDUt6Kqtcc2BENs3fP0iqbTT1nS3KU,4460
11
- user_scanner/core/result.py,sha256=HxzsaQrfvZ-PxDJknAENIaF-BekfyAAV2Mf8J382zjg,4597
12
+ user_scanner/core/result.py,sha256=3jd3CNciuJCtdh9P3SL7WW3x7cuDoyJOlq7WhgRivJA,4751
12
13
  user_scanner/core/version.py,sha256=k1_KTZdRLKBAxp8_PtOhTAtj8mBO_AUnUGdqI4epypY,855
13
14
  user_scanner/email_scan/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
15
  user_scanner/email_scan/adult/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- user_scanner/email_scan/adult/pornhub.py,sha256=cc_G5bclFl8QWU43ohM7SgilWk0RYmpUC7NHsw9Z0Bs,2135
16
+ user_scanner/email_scan/adult/pornhub.py,sha256=8OF-0h_FntUv1Pi0eT801Bhn6Uhd1dh_fD4lfbKV6OY,2134
16
17
  user_scanner/email_scan/adult/xnxx.py,sha256=WiKn4Vkc5FC1HXry9N8SN-dsTdm1qq0NS6KUcPRmMMY,1728
17
18
  user_scanner/email_scan/adult/xvideos.py,sha256=tx0PZOZ66KHDmrnvd1RABCnqCUIdKfCPgAbogBtX69A,1769
19
+ user_scanner/email_scan/community/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ user_scanner/email_scan/community/stackoverflow.py,sha256=Eq3kTXohmp2XodVLaR8ujWWCTjkTm1HXy-Yew4LzOGA,1608
21
+ user_scanner/email_scan/creator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ user_scanner/email_scan/creator/gumroad.py,sha256=wJfY_CPT2aiGShuL73XYPw_Oni7sEYn8nQ7avc0mrJg,3354
23
+ user_scanner/email_scan/creator/patreon.py,sha256=1A3tPNAyQ6WDLU_XY6PJUYe4FIVWzlALyEgOWzlZ-bE,1904
18
24
  user_scanner/email_scan/dev/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
25
  user_scanner/email_scan/dev/bitbucket.py,sha256=oR_fSug_Aft4fy8Lu8r0VO737b8ZlfcC_D3GMbcUF0k,1352
20
26
  user_scanner/email_scan/dev/github.py,sha256=JBSSN9kv3IQxKG0tmemUdSHwfQDu53dHC4i9oCwDqow,3160
21
27
  user_scanner/email_scan/dev/huggingface.py,sha256=GjFNkuVZ_8eFgs9OrFakhiEb8pVRwEJO-tyx32IQMns,1229
22
- user_scanner/email_scan/dev/.ruff_cache/.gitignore,sha256=njpg8ebsSuYCFcEdVLFxOSdF7CXp3e1DPVvZITY68xY,35
23
- user_scanner/email_scan/dev/.ruff_cache/CACHEDIR.TAG,sha256=WVMVbX4MVkpCclExbq8m-IcOZIOuIZf5FrYw5Pk-Ma4,43
24
- user_scanner/email_scan/dev/.ruff_cache/0.14.10/10328336453267387919,sha256=LEcpRIkQ-C6pcMhwLtJIv41263y2EjXHzr0r530xCtc,250
28
+ user_scanner/email_scan/gaming/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
+ user_scanner/email_scan/gaming/chess_com.py,sha256=EJF3OTxYtzDC-F4OAVfT0mRWUopmNZdwdYChw97VpE4,1841
25
30
  user_scanner/email_scan/shopping/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
- user_scanner/email_scan/shopping/ebay.py.lock,sha256=UTvqqwdIPPp3Pt8NCwNwHC3xNghexQc1HcNLXf9s4wQ,4326
27
31
  user_scanner/email_scan/shopping/flipkart.py,sha256=wMQJ1VIawhM6W0UQCThcIUtaYN7QeexvJSRSeXS4l04,1879
28
32
  user_scanner/email_scan/social/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
33
  user_scanner/email_scan/social/facebook.py,sha256=dnCDZfqRYLXDT7GjJQ-bSqGfC-rO8G9O7pu8dUse7Nw,4475
@@ -87,8 +91,8 @@ user_scanner/user_scan/social/youtube.py,sha256=UPu584teg75P7FT05RFG3nobbHgPmzjr
87
91
  user_scanner/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
92
  user_scanner/utils/update.py,sha256=Rj3kLuUrQ-LlKGB7bkndqVjj0IUqugbDSj2SUrPRidE,936
89
93
  user_scanner/utils/updater_logic.py,sha256=tl6kbKL02DrP-R1dkQWhHr12juVDgkJZZvKAfbI1ruU,2381
90
- user_scanner-1.1.0.dist-info/entry_points.txt,sha256=XqU3kssYZ0vXaPy5qYUOTCu4u-48Xie7QWFpBCYc7Nc,59
91
- user_scanner-1.1.0.dist-info/licenses/LICENSE,sha256=XH1QyQG68zo1opDIZHTHcTAbe9XMzewvTaFTukcN9vc,1061
92
- user_scanner-1.1.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
93
- user_scanner-1.1.0.dist-info/METADATA,sha256=eHPKnr6zczLO7pagkzEm0LFxyveyfSR3a9RVbIULioA,8601
94
- user_scanner-1.1.0.dist-info/RECORD,,
94
+ user_scanner-1.1.0.1.dist-info/entry_points.txt,sha256=XqU3kssYZ0vXaPy5qYUOTCu4u-48Xie7QWFpBCYc7Nc,59
95
+ user_scanner-1.1.0.1.dist-info/licenses/LICENSE,sha256=XH1QyQG68zo1opDIZHTHcTAbe9XMzewvTaFTukcN9vc,1061
96
+ user_scanner-1.1.0.1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
97
+ user_scanner-1.1.0.1.dist-info/METADATA,sha256=UhTN17ewVKkC7qZMXLMfR25uw5dGe1padSXC3BX3zcg,8620
98
+ user_scanner-1.1.0.1.dist-info/RECORD,,
@@ -1,2 +0,0 @@
1
- # Automatically created by ruff.
2
- *
@@ -1 +0,0 @@
1
- Signature: 8a477f597d28d172789f06886806bc55
@@ -1,97 +0,0 @@
1
- import httpx
2
- import re
3
- import asyncio
4
- from user_scanner.core.result import Result
5
-
6
- async def _check(email: str) -> Result:
7
- async with httpx.AsyncClient(http2=True, follow_redirects=False) as client:
8
- try:
9
- # --- Step 1: GET landing page ---
10
- url1 = "https://signin.ebay.com/signin/"
11
- headers1 = {
12
- 'host': 'signin.ebay.com',
13
- 'sec-ch-ua': '"Google Chrome";v="143", "Chromium";v="143", "Not A(Brand";v="24"',
14
- 'sec-ch-ua-mobile': '?1',
15
- 'sec-ch-ua-platform': '"Android"',
16
- 'upgrade-insecure-requests': '1',
17
- 'user-agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Mobile Safari/537.36',
18
- 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
19
- 'sec-fetch-site': 'cross-site',
20
- 'sec-fetch-mode': 'navigate',
21
- 'sec-fetch-user': '?1',
22
- 'sec-fetch-dest': 'document',
23
- 'referer': 'https://www.google.com/',
24
- 'accept-encoding': 'gzip, deflate, br, zstd',
25
- 'accept-language': 'en-US,en;q=0.9',
26
- 'priority': 'u=0, i'
27
- }
28
-
29
- res1 = await client.get(url1, headers=headers1)
30
- html = res1.text
31
-
32
- # Type-safe extraction for mypy
33
- srt_match = re.search(r'name=srt\s+id=srt\s+value=([^\s>]+)', html)
34
- if not srt_match:
35
- # Quoted fallback
36
- srt_match = re.search(r'name="srt".*?value="([^"]+)"', html)
37
-
38
- if not srt_match:
39
- return Result.error("Failed to extract srt")
40
-
41
- srt = srt_match.group(1)
42
-
43
- # --- Step 2: Identification POST ---
44
- url2 = "https://signin.ebay.com/signin/srv/identifer"
45
-
46
- payload = {
47
- 'identifier': email,
48
- 'srt': srt,
49
- 'webAuthNOptedInKeys': "",
50
- 'webAuthNCredCheck': "true",
51
- 'useCase': "AUTH",
52
- 'flowType': "PSI"
53
- }
54
-
55
- headers2 = {
56
- 'User-Agent': "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Mobile Safari/537.36",
57
- 'Accept-Encoding': "gzip, deflate, br, zstd",
58
- 'x-ebay-requested-with': "XMLHttpRequest",
59
- 'sec-ch-ua-platform': '"Android"',
60
- 'x-ebay-c-trackable-id': "01KFK31DZY0KKB2Y0HA1B00DWH",
61
- 'sec-ch-ua': '"Google Chrome";v="143", "Chromium";v="143", "Not A(Brand";v="24"',
62
- 'sec-ch-ua-model': '"I2404"',
63
- 'sec-ch-ua-mobile': "?1",
64
- 'x-requested-with': "XMLHttpRequest",
65
- 'sec-ch-ua-full-version': '"143.0.7499.192"',
66
- 'sec-ch-ua-platform-version': '"15.0.0"',
67
- 'origin': "https://signin.ebay.com",
68
- 'sec-fetch-site': "same-origin",
69
- 'sec-fetch-mode': "cors",
70
- 'sec-fetch-dest': "empty",
71
- 'referer': "https://signin.ebay.com/signin/",
72
- 'accept-language': "en-US,en;q=0.9",
73
- 'priority': "u=1, i"
74
- }
75
-
76
- # Optional: Short sleep to mimic human timing before POST
77
- await asyncio.sleep(1)
78
-
79
- response = await client.post(url2, data=payload, headers=headers2)
80
-
81
- if response.status_code == 483:
82
- return Result.error("Security challenge (483)")
83
-
84
- data = response.json()
85
-
86
- if data.get("signinErrorUpdate") is True and "couldn't find this eBay account" in str(data.get("errorMsg", "")):
87
- return Result.available()
88
- elif data.get("signinErrorUpdate") is False or "publicUserId" in data:
89
- return Result.taken()
90
- else:
91
- return Result.error("unexpected.... Report it via github issues...")
92
-
93
- except Exception as e:
94
- return Result.error(f"unexpected exception: {e}")
95
-
96
- async def validate_ebay(email: str) -> Result:
97
- return await _check(email)