Skip to content

Commit 58c1e3e

Browse files
committed
feat: add Battle.net username validator
Add support for checking username availability on Battle.net via the Overwatch player search API. The endpoint returns a JSON array of matching players - empty array means available, non-empty means taken. Includes username pre-validation for BattleTag rules: - 3-12 characters - Must start with a letter - Only letters and numbers allowed - Strips #1234 discriminator if provided Note: This checks Overwatch profiles specifically. Some Battle.net accounts may not have Overwatch profiles. Closes #97
1 parent 955851a commit 58c1e3e

File tree

1 file changed

+65
-0
lines changed

1 file changed

+65
-0
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import re
2+
from user_scanner.core.orchestrator import generic_validate
3+
from user_scanner.core.result import Result
4+
5+
6+
def validate_battlenet(user: str) -> Result:
7+
"""
8+
Check username availability on Battle.net via Overwatch player search.
9+
10+
Battle.net uses BattleTags (Username#1234) but this validator checks
11+
if the username portion exists in the Overwatch player database.
12+
13+
Note: This checks Overwatch profiles specifically. A username may exist
14+
on Battle.net but not have an Overwatch profile, or vice versa.
15+
16+
API behavior:
17+
- Returns JSON array with player data if username exists
18+
- Returns empty array [] if username not found
19+
"""
20+
# BattleTag username rules: 3-12 chars, letters/numbers, one optional #
21+
# For this validator, we strip any #1234 discriminator if present
22+
username = user.split('#')[0]
23+
24+
if not (3 <= len(username) <= 12):
25+
return Result.error("Length must be 3-12 characters")
26+
27+
if not re.match(r'^[a-zA-Z][a-zA-Z0-9]*$', username):
28+
return Result.error("Must start with letter, only letters and numbers allowed")
29+
30+
url = f"https://overwatch.blizzard.com/en-us/search/account-by-name/{username}"
31+
32+
headers = {
33+
"User-Agent": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Mobile Safari/537.36",
34+
"Accept": "application/json",
35+
"Accept-Encoding": "gzip, deflate, br, zstd",
36+
}
37+
38+
def process(response):
39+
if response.status_code != 200:
40+
return Result.error(f"Unexpected status: {response.status_code}")
41+
42+
try:
43+
data = response.json()
44+
if isinstance(data, list) and len(data) == 0:
45+
return Result.available()
46+
elif isinstance(data, list) and len(data) > 0:
47+
return Result.taken()
48+
else:
49+
return Result.error("Unexpected response format")
50+
except Exception:
51+
return Result.error("Failed to parse response")
52+
53+
return generic_validate(url, process, headers=headers, timeout=15.0, follow_redirects=True)
54+
55+
56+
if __name__ == "__main__":
57+
user = input("Username?: ").strip()
58+
result = validate_battlenet(user)
59+
60+
if result == 1:
61+
print("Available!")
62+
elif result == 0:
63+
print("Unavailable!")
64+
else:
65+
print(f"Error occurred! Reason: {result.get_reason()}")

0 commit comments

Comments
 (0)