user-scanner 1.1.0.1__tar.gz → 1.1.0.3__tar.gz
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.
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/PKG-INFO +33 -7
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/README.md +32 -6
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/pyproject.toml +1 -1
- user_scanner-1.1.0.3/user_scanner/.ruff_cache/.gitignore +2 -0
- user_scanner-1.1.0.3/user_scanner/.ruff_cache/0.14.10/12603567674167520802 +0 -0
- user_scanner-1.1.0.3/user_scanner/.ruff_cache/0.14.10/12688104075811904861 +0 -0
- user_scanner-1.1.0.3/user_scanner/.ruff_cache/0.14.10/15694509833576274574 +0 -0
- user_scanner-1.1.0.3/user_scanner/.ruff_cache/0.14.10/301008395129872757 +0 -0
- user_scanner-1.1.0.3/user_scanner/.ruff_cache/0.14.10/4908403194175060721 +0 -0
- user_scanner-1.1.0.3/user_scanner/.ruff_cache/0.14.10/572980360641624523 +0 -0
- user_scanner-1.1.0.3/user_scanner/.ruff_cache/0.14.10/5935143038219898089 +0 -0
- user_scanner-1.1.0.3/user_scanner/.ruff_cache/CACHEDIR.TAG +1 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/__main__.py +2 -2
- user_scanner-1.1.0.3/user_scanner/config.json +3 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/core/email_orchestrator.py +17 -17
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/core/orchestrator.py +6 -3
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/core/result.py +10 -2
- user_scanner-1.1.0.3/user_scanner/email_scan/adult/sexvid.py +43 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/community/.ruff_cache/.gitignore +2 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/community/.ruff_cache/0.14.10/3933329632096143294 +0 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/community/.ruff_cache/CACHEDIR.TAG +1 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/community/quora.py +112 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/dev/.ruff_cache/.gitignore +2 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/dev/.ruff_cache/0.14.10/10328336453267387919 +0 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/dev/.ruff_cache/CACHEDIR.TAG +1 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/dev/__init__.py +1 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/email_scan/dev/bitbucket.py +2 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/dev/codecademy.py +49 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/dev/codepen.py +54 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/dev/devrant.py +49 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/email_scan/dev/github.py +2 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/dev/replit.py +51 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/dev/wordpress.py +52 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/hosting/.ruff_cache/.gitignore +2 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/hosting/.ruff_cache/0.14.10/6358275293999347997 +0 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/hosting/.ruff_cache/CACHEDIR.TAG +1 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/hosting/render.py +60 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/music/.ruff_cache/.gitignore +2 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/music/.ruff_cache/0.14.10/14677874048998292530 +0 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/music/.ruff_cache/0.14.10/7544735312652879689 +0 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/music/.ruff_cache/CACHEDIR.TAG +1 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/music/lastfm.py +60 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/music/spotify.py +87 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/shopping/envato.py +41 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/shopping/naturabuy.py +46 -0
- user_scanner-1.1.0.3/user_scanner/email_scan/shopping/vivino.py +49 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/email_scan/social/mastodon.py +5 -3
- user_scanner-1.1.0.3/user_scanner/user_scan/gaming/battlenet.py +65 -0
- user_scanner-1.1.0.3/user_scanner/user_scan/shopping/__init__.py +0 -0
- user_scanner-1.1.0.3/user_scanner/user_scan/shopping/vinted.py +42 -0
- user_scanner-1.1.0.3/user_scanner/utils/__init__.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/version.json +1 -1
- user_scanner-1.1.0.1/user_scanner/config.json +0 -1
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/LICENSE +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/__init__.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/cli/__init__.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/cli/banner.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/core/__init__.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/core/formatter.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/core/helpers.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/core/version.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/email_scan/__init__.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/email_scan/adult/__init__.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/email_scan/adult/pornhub.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/email_scan/adult/xnxx.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/email_scan/adult/xvideos.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/email_scan/community/__init__.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/email_scan/community/stackoverflow.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/email_scan/creator/__init__.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/email_scan/creator/gumroad.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/email_scan/creator/patreon.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/email_scan/dev/huggingface.py +0 -0
- {user_scanner-1.1.0.1/user_scanner/email_scan/dev → user_scanner-1.1.0.3/user_scanner/email_scan/gaming}/__init__.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/email_scan/gaming/chess_com.py +0 -0
- {user_scanner-1.1.0.1/user_scanner/email_scan/gaming → user_scanner-1.1.0.3/user_scanner/email_scan/hosting}/__init__.py +0 -0
- {user_scanner-1.1.0.1/user_scanner/email_scan/shopping → user_scanner-1.1.0.3/user_scanner/email_scan/music}/__init__.py +0 -0
- {user_scanner-1.1.0.1/user_scanner/email_scan/social → user_scanner-1.1.0.3/user_scanner/email_scan/shopping}/__init__.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/email_scan/shopping/flipkart.py +0 -0
- {user_scanner-1.1.0.1/user_scanner/user_scan/creator → user_scanner-1.1.0.3/user_scanner/email_scan/social}/__init__.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/email_scan/social/facebook.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/email_scan/social/instagram.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/email_scan/social/x.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/community/__init__.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/community/coderlegion.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/community/hackernews.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/community/lemmy.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/community/stackoverflow.py +0 -0
- {user_scanner-1.1.0.1/user_scanner/user_scan/donation → user_scanner-1.1.0.3/user_scanner/user_scan/creator}/__init__.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/creator/devto.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/creator/gumroad.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/creator/hashnode.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/creator/itch_io.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/creator/kaggle.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/creator/medium.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/creator/patreon.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/creator/producthunt.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/creator/substack.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/creator/twitch.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/dev/__init__.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/dev/bitbucket.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/dev/codeberg.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/dev/cratesio.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/dev/dockerhub.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/dev/github.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/dev/gitlab.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/dev/huggingface.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/dev/launchpad.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/dev/leetcode.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/dev/npmjs.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/dev/replit.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/dev/sourceforge.py +0 -0
- {user_scanner-1.1.0.1/user_scanner/user_scan/gaming → user_scanner-1.1.0.3/user_scanner/user_scan/donation}/__init__.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/donation/buymeacoffee.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/donation/liberapay.py +0 -0
- {user_scanner-1.1.0.1/user_scanner/utils → user_scanner-1.1.0.3/user_scanner/user_scan/gaming}/__init__.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/gaming/chess_com.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/gaming/lichess.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/gaming/minecraft.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/gaming/monkeytype.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/gaming/osu.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/gaming/roblox.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/gaming/steam.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/social/__init__.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/social/bluesky.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/social/discord.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/social/instagram.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/social/mastodon.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/social/pinterest.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/social/reddit.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/social/snapchat.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/social/soundcloud.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/social/telegram.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/social/threads.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/social/tiktok.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/social/x.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/user_scan/social/youtube.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/utils/update.py +0 -0
- {user_scanner-1.1.0.1 → user_scanner-1.1.0.3}/user_scanner/utils/updater_logic.py +0 -0
|
@@ -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.3
|
|
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
|

|
|
18
18
|
<p align="center">
|
|
19
|
-
<img src="https://img.shields.io/badge/Version-1.1.0.
|
|
19
|
+
<img src="https://img.shields.io/badge/Version-1.1.0.3-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" />
|
|
@@ -26,8 +26,6 @@ Project-URL: Homepage, https://github.com/kaifcodec/user-scanner
|
|
|
26
26
|
|
|
27
27
|
---
|
|
28
28
|
|
|
29
|
-
### ⚠️ Email OSINT mode had not been implemented yet, still in progress
|
|
30
|
-
|
|
31
29
|
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
30
|
|
|
33
31
|
Perfect for fast, accurate and lightweight email OSINT
|
|
@@ -54,12 +52,28 @@ Perfect for finding a **unique username** across GitHub, Twitter, Reddit, Instag
|
|
|
54
52
|
- ✅ **Bulk email scanning** from file support for checking multiple emails at once
|
|
55
53
|
---
|
|
56
54
|
|
|
57
|
-
##
|
|
55
|
+
## Virtual Environment (optional but recommended)
|
|
58
56
|
|
|
59
57
|
```bash
|
|
60
|
-
|
|
58
|
+
# create venv
|
|
59
|
+
python -m venv .venv
|
|
60
|
+
````
|
|
61
|
+
## Activate venv
|
|
62
|
+
```bash
|
|
63
|
+
# Linux / macOS
|
|
64
|
+
source .venv/bin/activate
|
|
65
|
+
|
|
66
|
+
# Windows (PowerShell)
|
|
67
|
+
.venv\Scripts\Activate.ps1
|
|
61
68
|
```
|
|
69
|
+
## Installation
|
|
70
|
+
```bash
|
|
71
|
+
# upgrade pip
|
|
72
|
+
python -m pip install --upgrade pip
|
|
62
73
|
|
|
74
|
+
# install
|
|
75
|
+
pip install user-scanner
|
|
76
|
+
```
|
|
63
77
|
---
|
|
64
78
|
|
|
65
79
|
## Important Flags
|
|
@@ -169,9 +183,11 @@ user-scanner -U
|
|
|
169
183
|
|
|
170
184
|
- Note*: New modules are constantly getting added so this might have only limited, outdated output:
|
|
171
185
|
|
|
172
|
-
<img width="
|
|
186
|
+
<img width="1080" height="656" alt="1000146096" src="https://github.com/user-attachments/assets/1101e2f8-18ea-45a4-9492-92e237ecc670" />
|
|
173
187
|
|
|
188
|
+
---
|
|
174
189
|
|
|
190
|
+
<img width="1072" height="848" alt="user-scanner's main usage screenshot" src="https://github.com/user-attachments/assets/34e44ca6-e314-419e-9035-d951b493b47f" />
|
|
175
191
|
|
|
176
192
|
---
|
|
177
193
|
|
|
@@ -224,6 +240,16 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for examples.
|
|
|
224
240
|
|
|
225
241
|
This project is licensed under the **MIT License**. See [LICENSE](LICENSE) for details.
|
|
226
242
|
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## ⚠️ Disclaimer
|
|
246
|
+
|
|
247
|
+
This tool is provided for **educational purposes** and **authorized security research** only.
|
|
248
|
+
|
|
249
|
+
- **User Responsibility:** Users are solely responsible for ensuring their usage complies with all applicable laws and the Terms of Service (ToS) of any third-party providers.
|
|
250
|
+
- **Methodology:** The tool interacts only with **publicly accessible, unauthenticated web endpoints**. It does not bypass authentication, security controls, or access private user data.
|
|
251
|
+
- **No Profiling:** This software performs only basic **yes/no availability checks**. It does not collect, store, aggregate, or analyze user data, behavior, or identities.
|
|
252
|
+
- **Limitation of Liability:** The software is provided **“as is”**, without warranty of any kind. The developers assume no liability for misuse or any resulting damage or legal consequences.
|
|
227
253
|
|
|
228
254
|
---
|
|
229
255
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
<p align="center">
|
|
5
|
-
<img src="https://img.shields.io/badge/Version-1.1.0.
|
|
5
|
+
<img src="https://img.shields.io/badge/Version-1.1.0.3-blueviolet?style=for-the-badge&logo=github" />
|
|
6
6
|
<img src="https://img.shields.io/github/issues/kaifcodec/user-scanner?style=for-the-badge&logo=github" />
|
|
7
7
|
<img src="https://img.shields.io/badge/Tested%20on-Termux-black?style=for-the-badge&logo=termux" />
|
|
8
8
|
<img src="https://img.shields.io/badge/Tested%20on-Windows-cyan?style=for-the-badge&logo=Windows" />
|
|
@@ -12,8 +12,6 @@
|
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
-
### ⚠️ Email OSINT mode had not been implemented yet, still in progress
|
|
16
|
-
|
|
17
15
|
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.
|
|
18
16
|
|
|
19
17
|
Perfect for fast, accurate and lightweight email OSINT
|
|
@@ -40,12 +38,28 @@ Perfect for finding a **unique username** across GitHub, Twitter, Reddit, Instag
|
|
|
40
38
|
- ✅ **Bulk email scanning** from file support for checking multiple emails at once
|
|
41
39
|
---
|
|
42
40
|
|
|
43
|
-
##
|
|
41
|
+
## Virtual Environment (optional but recommended)
|
|
44
42
|
|
|
45
43
|
```bash
|
|
46
|
-
|
|
44
|
+
# create venv
|
|
45
|
+
python -m venv .venv
|
|
46
|
+
````
|
|
47
|
+
## Activate venv
|
|
48
|
+
```bash
|
|
49
|
+
# Linux / macOS
|
|
50
|
+
source .venv/bin/activate
|
|
51
|
+
|
|
52
|
+
# Windows (PowerShell)
|
|
53
|
+
.venv\Scripts\Activate.ps1
|
|
47
54
|
```
|
|
55
|
+
## Installation
|
|
56
|
+
```bash
|
|
57
|
+
# upgrade pip
|
|
58
|
+
python -m pip install --upgrade pip
|
|
48
59
|
|
|
60
|
+
# install
|
|
61
|
+
pip install user-scanner
|
|
62
|
+
```
|
|
49
63
|
---
|
|
50
64
|
|
|
51
65
|
## Important Flags
|
|
@@ -155,9 +169,11 @@ user-scanner -U
|
|
|
155
169
|
|
|
156
170
|
- Note*: New modules are constantly getting added so this might have only limited, outdated output:
|
|
157
171
|
|
|
158
|
-
<img width="
|
|
172
|
+
<img width="1080" height="656" alt="1000146096" src="https://github.com/user-attachments/assets/1101e2f8-18ea-45a4-9492-92e237ecc670" />
|
|
159
173
|
|
|
174
|
+
---
|
|
160
175
|
|
|
176
|
+
<img width="1072" height="848" alt="user-scanner's main usage screenshot" src="https://github.com/user-attachments/assets/34e44ca6-e314-419e-9035-d951b493b47f" />
|
|
161
177
|
|
|
162
178
|
---
|
|
163
179
|
|
|
@@ -210,6 +226,16 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for examples.
|
|
|
210
226
|
|
|
211
227
|
This project is licensed under the **MIT License**. See [LICENSE](LICENSE) for details.
|
|
212
228
|
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## ⚠️ Disclaimer
|
|
232
|
+
|
|
233
|
+
This tool is provided for **educational purposes** and **authorized security research** only.
|
|
234
|
+
|
|
235
|
+
- **User Responsibility:** Users are solely responsible for ensuring their usage complies with all applicable laws and the Terms of Service (ToS) of any third-party providers.
|
|
236
|
+
- **Methodology:** The tool interacts only with **publicly accessible, unauthenticated web endpoints**. It does not bypass authentication, security controls, or access private user data.
|
|
237
|
+
- **No Profiling:** This software performs only basic **yes/no availability checks**. It does not collect, store, aggregate, or analyze user data, behavior, or identities.
|
|
238
|
+
- **Limitation of Liability:** The software is provided **“as is”**, without warranty of any kind. The developers assume no liability for misuse or any resulting damage or legal consequences.
|
|
213
239
|
|
|
214
240
|
---
|
|
215
241
|
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Signature: 8a477f597d28d172789f06886806bc55
|
|
@@ -196,7 +196,7 @@ def main():
|
|
|
196
196
|
print(f"{R}[✘] Error: No valid emails found in {args.email_file}{X}")
|
|
197
197
|
sys.exit(1)
|
|
198
198
|
|
|
199
|
-
print(f"{C}[+] Loaded {len(valid_emails)} emails from {args.email_file}{X}")
|
|
199
|
+
print(f"{C}[+] Loaded {len(valid_emails)} {'email' if len(valid_emails) == 1 else 'emails'} from {args.email_file}{X}")
|
|
200
200
|
is_email = True
|
|
201
201
|
targets = valid_emails
|
|
202
202
|
except FileNotFoundError:
|
|
@@ -213,7 +213,7 @@ def main():
|
|
|
213
213
|
if not usernames:
|
|
214
214
|
print(f"{R}[✘] Error: No valid usernames found in {args.username_file}{X}")
|
|
215
215
|
sys.exit(1)
|
|
216
|
-
print(f"{C}[+] Loaded {len(usernames)} usernames from {args.username_file}{X}")
|
|
216
|
+
print(f"{C}[+] Loaded {len(usernames)} {'username' if len(usernames) == 1 else 'usernames'} from {args.username_file}{X}")
|
|
217
217
|
is_email = False
|
|
218
218
|
targets = usernames
|
|
219
219
|
except FileNotFoundError:
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from typing import List
|
|
4
3
|
from types import ModuleType
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
5
6
|
from colorama import Fore, Style
|
|
6
7
|
|
|
7
|
-
from user_scanner.core.helpers import load_categories, load_modules
|
|
8
|
+
from user_scanner.core.helpers import find_category, load_categories, load_modules
|
|
8
9
|
from user_scanner.core.result import Result
|
|
9
10
|
|
|
10
11
|
# Concurrency control
|
|
@@ -13,11 +14,21 @@ MAX_CONCURRENT_REQUESTS = 15
|
|
|
13
14
|
|
|
14
15
|
async def _async_worker(module: ModuleType, email: str, sem: asyncio.Semaphore) -> Result:
|
|
15
16
|
async with sem:
|
|
16
|
-
module_name = module.__name__.split(
|
|
17
|
+
module_name = module.__name__.split(".")[-1]
|
|
17
18
|
func_name = f"validate_{module_name}"
|
|
19
|
+
actual_cat = find_category(module) or "Email"
|
|
20
|
+
|
|
21
|
+
params = {
|
|
22
|
+
"site_name": module_name.capitalize(),
|
|
23
|
+
"username": email,
|
|
24
|
+
"category": actual_cat,
|
|
25
|
+
"is_email": True,
|
|
26
|
+
}
|
|
18
27
|
|
|
19
28
|
if not hasattr(module, func_name):
|
|
20
|
-
return
|
|
29
|
+
return (
|
|
30
|
+
Result.error(f"Function {func_name} not found").update(**params).show()
|
|
31
|
+
)
|
|
21
32
|
|
|
22
33
|
func = getattr(module, func_name)
|
|
23
34
|
|
|
@@ -27,21 +38,10 @@ async def _async_worker(module: ModuleType, email: str, sem: asyncio.Semaphore)
|
|
|
27
38
|
except Exception as e:
|
|
28
39
|
result = Result.error(e)
|
|
29
40
|
|
|
30
|
-
|
|
31
|
-
actual_cat = find_category(module) or "Email"
|
|
32
|
-
|
|
33
|
-
result.update(
|
|
34
|
-
site_name=module_name.capitalize(),
|
|
35
|
-
username=email,
|
|
36
|
-
category=actual_cat,
|
|
37
|
-
is_email=True
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
print(result.get_console_output())
|
|
41
|
-
return result
|
|
41
|
+
return result.update(**params).show()
|
|
42
42
|
|
|
43
43
|
|
|
44
|
-
async def _run_batch(modules: List[ModuleType], email:str) -> List[Result]:
|
|
44
|
+
async def _run_batch(modules: List[ModuleType], email: str) -> List[Result]:
|
|
45
45
|
sem = asyncio.Semaphore(MAX_CONCURRENT_REQUESTS)
|
|
46
46
|
tasks = []
|
|
47
47
|
for module in modules:
|
|
@@ -15,7 +15,11 @@ def _worker_single(module: ModuleType, username: str) -> Result:
|
|
|
15
15
|
site_name = get_site_name(module)
|
|
16
16
|
|
|
17
17
|
if not func:
|
|
18
|
-
return Result.error(
|
|
18
|
+
return Result.error(
|
|
19
|
+
f"{site_name} has no validate_ function",
|
|
20
|
+
site_name=site_name,
|
|
21
|
+
username=username,
|
|
22
|
+
)
|
|
19
23
|
|
|
20
24
|
try:
|
|
21
25
|
result: Result = func(username)
|
|
@@ -48,8 +52,7 @@ def run_user_category(category_path: Path, username: str) -> List[Result]:
|
|
|
48
52
|
for result in exec_map:
|
|
49
53
|
result.update(category=category_name)
|
|
50
54
|
results.append(result)
|
|
51
|
-
|
|
52
|
-
print(result.get_console_output())
|
|
55
|
+
result.show()
|
|
53
56
|
|
|
54
57
|
return results
|
|
55
58
|
|
|
@@ -61,6 +61,7 @@ class Result:
|
|
|
61
61
|
for field in ("username", "site_name", "category", "is_email"):
|
|
62
62
|
if field in kwargs and kwargs[field] is not None:
|
|
63
63
|
setattr(self, field, kwargs[field])
|
|
64
|
+
return self
|
|
64
65
|
|
|
65
66
|
@classmethod
|
|
66
67
|
def taken(cls, reason: str | Exception | None = None, **kwargs):
|
|
@@ -148,11 +149,18 @@ class Result:
|
|
|
148
149
|
|
|
149
150
|
def get_console_output(self) -> str:
|
|
150
151
|
site_name = self.site_name
|
|
151
|
-
username = self.username
|
|
152
152
|
status_text = self.status.to_label(self.is_email)
|
|
153
|
+
username = ""
|
|
154
|
+
if self.username:
|
|
155
|
+
username = f"({self.username})"
|
|
153
156
|
|
|
154
157
|
color = self.get_output_color()
|
|
155
158
|
icon = self.get_output_icon()
|
|
156
159
|
|
|
157
160
|
reason = f" ({self.get_reason()})" if self.has_reason() else ""
|
|
158
|
-
return f" {color}{icon} {site_name}
|
|
161
|
+
return f" {color}{icon} {site_name} {username}: {status_text}{reason}{Style.RESET_ALL}"
|
|
162
|
+
|
|
163
|
+
def show(self):
|
|
164
|
+
"""Prints the console output and returns itself for chaining"""
|
|
165
|
+
print(self.get_console_output())
|
|
166
|
+
return self
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
from user_scanner.core.result import Result
|
|
3
|
+
|
|
4
|
+
async def _check(email: str) -> Result:
|
|
5
|
+
url = "https://www.sexvid.pro/reset-password/"
|
|
6
|
+
|
|
7
|
+
payload = {
|
|
8
|
+
'action': "restore_password",
|
|
9
|
+
'mode': "async",
|
|
10
|
+
'format': "json",
|
|
11
|
+
'email_link': "https://www.sexvid.pro/signup.php",
|
|
12
|
+
'email': email,
|
|
13
|
+
'code': "xxxxx"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
headers = {
|
|
17
|
+
'User-Agent': "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Mobile Safari/537.36",
|
|
18
|
+
'Accept': "*/*",
|
|
19
|
+
'X-Requested-With': "XMLHttpRequest",
|
|
20
|
+
'Origin': "https://www.sexvid.pro",
|
|
21
|
+
'Referer': "https://www.sexvid.pro/reset-password/",
|
|
22
|
+
'Content-Type': "application/x-www-form-urlencoded"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async with httpx.AsyncClient(http2=False, timeout=5.0) as client:
|
|
26
|
+
try:
|
|
27
|
+
response = await client.post(url, data=payload, headers=headers)
|
|
28
|
+
res_text = response.text
|
|
29
|
+
|
|
30
|
+
if "doesnt_exist" in res_text or "No user with such email exists" in res_text:
|
|
31
|
+
return Result.available()
|
|
32
|
+
elif "A new generated password has been sent" in res_text or "status\":\"success" in res_text:
|
|
33
|
+
return Result.taken()
|
|
34
|
+
elif response.status_code == 429:
|
|
35
|
+
return Result.error("Rate-limited")
|
|
36
|
+
else:
|
|
37
|
+
return Result.error(f"[{response.status_code}] Unexpected response body, report it via GitHub issues")
|
|
38
|
+
|
|
39
|
+
except Exception as e:
|
|
40
|
+
return Result.error(e)
|
|
41
|
+
|
|
42
|
+
async def validate_sexvid(email: str) -> Result:
|
|
43
|
+
return await _check(email)
|
user_scanner-1.1.0.3/user_scanner/email_scan/community/.ruff_cache/0.14.10/3933329632096143294
ADDED
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Signature: 8a477f597d28d172789f06886806bc55
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import re
|
|
4
|
+
import os
|
|
5
|
+
import shutil
|
|
6
|
+
from user_scanner.core.result import Result
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def _check(email: str) -> Result:
|
|
10
|
+
if not shutil.which("curl"):
|
|
11
|
+
return Result.error("curl is not installed, install it to use Quora validation")
|
|
12
|
+
|
|
13
|
+
cookie_path = f"quora_cookie_{os.getpid()}.txt"
|
|
14
|
+
base_url = "https://www.quora.com/"
|
|
15
|
+
gql_url = "https://www.quora.com/graphql/gql_para_POST"
|
|
16
|
+
|
|
17
|
+
headers = {
|
|
18
|
+
'host': 'www.quora.com',
|
|
19
|
+
'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"',
|
|
20
|
+
'sec-ch-ua-mobile': '?1',
|
|
21
|
+
'sec-ch-ua-platform': '"Android"',
|
|
22
|
+
'upgrade-insecure-requests': '1',
|
|
23
|
+
'user-agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Mobile Safari/537.36',
|
|
24
|
+
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
|
|
25
|
+
'referer': 'https://www.google.com/',
|
|
26
|
+
'accept-language': 'en-US,en;q=0.9',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async def run_curl(url, current_headers, post_data=None, url_params=None):
|
|
30
|
+
cmd = ["curl", "-s", "-k", "-L", "--http2", "--max-time",
|
|
31
|
+
"5", "-c", cookie_path, "-b", cookie_path]
|
|
32
|
+
for k, v in current_headers.items():
|
|
33
|
+
cmd.extend(["-H", f"{k}: {v}"])
|
|
34
|
+
if url_params:
|
|
35
|
+
import urllib.parse
|
|
36
|
+
url += "?" + urllib.parse.urlencode(url_params)
|
|
37
|
+
if post_data:
|
|
38
|
+
cmd.extend(["-X", "POST", "--data-raw", json.dumps(post_data)])
|
|
39
|
+
cmd.append(url)
|
|
40
|
+
|
|
41
|
+
process = await asyncio.create_subprocess_exec(
|
|
42
|
+
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
|
43
|
+
)
|
|
44
|
+
try:
|
|
45
|
+
stdout, _ = await asyncio.wait_for(process.communicate(), timeout=5)
|
|
46
|
+
return stdout.decode('utf-8', errors='ignore')
|
|
47
|
+
except asyncio.TimeoutError:
|
|
48
|
+
try:
|
|
49
|
+
process.kill()
|
|
50
|
+
except Exception:
|
|
51
|
+
pass
|
|
52
|
+
return ""
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
html = await run_curl(base_url, headers)
|
|
56
|
+
if not html:
|
|
57
|
+
if os.path.exists(cookie_path):
|
|
58
|
+
os.remove(cookie_path)
|
|
59
|
+
return Result.error("Connection timed out")
|
|
60
|
+
|
|
61
|
+
formkey_match = re.search(r'\"formkey\":\s*\"([a-f0-9]{32})\"', html)
|
|
62
|
+
if not formkey_match:
|
|
63
|
+
if os.path.exists(cookie_path):
|
|
64
|
+
os.remove(cookie_path)
|
|
65
|
+
return Result.error("Quora blocked the request (Bot detection/403)")
|
|
66
|
+
|
|
67
|
+
formkey = formkey_match.group(1)
|
|
68
|
+
|
|
69
|
+
gql_headers = headers.copy()
|
|
70
|
+
gql_headers.update({
|
|
71
|
+
'content-type': 'application/json',
|
|
72
|
+
'quora-formkey': formkey,
|
|
73
|
+
'quora-canary-revision': 'false',
|
|
74
|
+
'origin': 'https://www.quora.com',
|
|
75
|
+
'referer': 'https://www.quora.com/',
|
|
76
|
+
'priority': 'u=1, i'
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
payload = {
|
|
80
|
+
"queryName": "SignupEmailForm_validateEmail_Query",
|
|
81
|
+
"variables": {"email": email},
|
|
82
|
+
"extensions": {
|
|
83
|
+
"hash": "1db80096407be846d5581fe1b42b12fd05e0b40a5d3095ed40a0b4bd28f49fe7"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
response_text = await run_curl(gql_url, gql_headers, post_data=payload, url_params={'q': "SignupEmailForm_validateEmail_Query"})
|
|
88
|
+
|
|
89
|
+
if os.path.exists(cookie_path):
|
|
90
|
+
os.remove(cookie_path)
|
|
91
|
+
|
|
92
|
+
if not response_text.strip():
|
|
93
|
+
return Result.error("Quora timed out or returned empty body")
|
|
94
|
+
|
|
95
|
+
data = json.loads(response_text)
|
|
96
|
+
status = data.get("data", {}).get("validateEmail")
|
|
97
|
+
|
|
98
|
+
if status == "IN_USE":
|
|
99
|
+
return Result.taken()
|
|
100
|
+
elif status == "OK":
|
|
101
|
+
return Result.available()
|
|
102
|
+
else:
|
|
103
|
+
return Result.error(f"Unexpected status: {status}")
|
|
104
|
+
|
|
105
|
+
except Exception as e:
|
|
106
|
+
if os.path.exists(cookie_path):
|
|
107
|
+
os.remove(cookie_path)
|
|
108
|
+
return Result.error(e)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
async def validate_quora(email: str) -> Result:
|
|
112
|
+
return await _check(email)
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Signature: 8a477f597d28d172789f06886806bc55
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import httpx
|
|
2
2
|
from user_scanner.core.result import Result
|
|
3
3
|
|
|
4
|
+
|
|
4
5
|
async def _check(email: str) -> Result:
|
|
5
6
|
async with httpx.AsyncClient(http2=True) as client:
|
|
6
7
|
try:
|
|
@@ -29,5 +30,6 @@ async def _check(email: str) -> Result:
|
|
|
29
30
|
except Exception as e:
|
|
30
31
|
return Result.error(f"Unexpected exception:{e}")
|
|
31
32
|
|
|
33
|
+
|
|
32
34
|
async def validate_bitbucket(email: str) -> Result:
|
|
33
35
|
return await _check(email)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
import re
|
|
3
|
+
from user_scanner.core.result import Result
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def _check(email: str) -> Result:
|
|
7
|
+
headers = {
|
|
8
|
+
'User-Agent': "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36",
|
|
9
|
+
'Accept': 'application/json',
|
|
10
|
+
'Accept-Language': 'en-US,en;q=0.9',
|
|
11
|
+
'Referer': 'https://www.codecademy.com/register?redirect=%2F',
|
|
12
|
+
'Content-Type': 'application/json',
|
|
13
|
+
'Origin': 'https://www.codecademy.com',
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
async with httpx.AsyncClient(timeout=4.0, follow_redirects=True) as client:
|
|
18
|
+
init_res = await client.get("https://www.codecademy.com/register", headers=headers)
|
|
19
|
+
|
|
20
|
+
csrf_match = re.search(
|
|
21
|
+
r'name="csrf-token" content="([^"]+)"', init_res.text)
|
|
22
|
+
if not csrf_match:
|
|
23
|
+
return Result.error("Could not find CSRF token")
|
|
24
|
+
|
|
25
|
+
headers["X-CSRF-Token"] = csrf_match.group(1)
|
|
26
|
+
|
|
27
|
+
payload = {"user": {"email": email}}
|
|
28
|
+
|
|
29
|
+
response = await client.post(
|
|
30
|
+
'https://www.codecademy.com/register/validate',
|
|
31
|
+
headers=headers,
|
|
32
|
+
json=payload
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
if response.status_code == 400 and 'has already been taken' in response.text:
|
|
36
|
+
return Result.taken()
|
|
37
|
+
elif response.status_code == 200:
|
|
38
|
+
return Result.available()
|
|
39
|
+
|
|
40
|
+
return Result.error(f"Unexpected response: {response.status_code}")
|
|
41
|
+
|
|
42
|
+
except httpx.TimeoutException:
|
|
43
|
+
return Result.error("Connection timed out")
|
|
44
|
+
except Exception as e:
|
|
45
|
+
return Result.error(str(e))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
async def validate_codecademy(email: str) -> Result:
|
|
49
|
+
return await _check(email)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
import re
|
|
3
|
+
from user_scanner.core.result import Result
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def _check(email: str) -> Result:
|
|
7
|
+
headers = {
|
|
8
|
+
'User-Agent': "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36",
|
|
9
|
+
'Accept': '*/*',
|
|
10
|
+
'Accept-Language': 'en-US,en;q=0.9',
|
|
11
|
+
'Referer': 'https://codepen.io/accounts/signup/user/free',
|
|
12
|
+
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
13
|
+
'X-Requested-With': 'XMLHttpRequest',
|
|
14
|
+
'Origin': 'https://codepen.io',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
async with httpx.AsyncClient(timeout=10.0, follow_redirects=True) as client:
|
|
19
|
+
init_res = await client.get("https://codepen.io/accounts/signup/user/free", headers=headers)
|
|
20
|
+
|
|
21
|
+
csrf_match = re.search(
|
|
22
|
+
r'name="csrf-token" content="([^"]+)"', init_res.text)
|
|
23
|
+
if not csrf_match:
|
|
24
|
+
return Result.error("Could not find CSRF token")
|
|
25
|
+
|
|
26
|
+
headers["X-CSRF-Token"] = csrf_match.group(1)
|
|
27
|
+
|
|
28
|
+
payload = {
|
|
29
|
+
'attribute': 'email',
|
|
30
|
+
'value': email,
|
|
31
|
+
'context': 'user'
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
response = await client.post(
|
|
35
|
+
'https://codepen.io/accounts/duplicate_check',
|
|
36
|
+
headers=headers,
|
|
37
|
+
data=payload
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if "That Email is already taken." in response.text:
|
|
41
|
+
return Result.taken()
|
|
42
|
+
elif response.status_code == 200:
|
|
43
|
+
return Result.available()
|
|
44
|
+
|
|
45
|
+
return Result.error(f"Unexpected response: {response.status_code}")
|
|
46
|
+
|
|
47
|
+
except httpx.TimeoutException:
|
|
48
|
+
return Result.error("Connection timed out")
|
|
49
|
+
except Exception as e:
|
|
50
|
+
return Result.error(str(e))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
async def validate_codepen(email: str) -> Result:
|
|
54
|
+
return await _check(email)
|