import re
import inspect
import requests
import re
import asyncio
from typing import Any, Callable, Coroutine
def get_socials():
return [
{
"id": "instagram",
"name": "Instagram",
"img": "https://cdn.jsdelivr.net/npm/simple-icons@v6/icons/instagram.svg",
"validate": is_valid_instagram_username,
"resolve": resolve_instagram_username,
"message": lambda username: (
f'username "{username}" is ✅ Available.'
' However, usernames from disabled or deleted accounts may also appear'
' available but you can\'t choose them, eg: usernames like "we", "us", and "the".'
' Go to accounts center'
" and try changing the username of an existing account and see if it it's available")
},
# {
# id: "x",
# name: "X (formerly Twitter)",
# img: "https://cdn.jsdelivr.net/npm/simple-icons@v6/icons/twitter.svg"
# },
# {
# id: "linkedin-user",
# name: "LinkedIn User",
# img: "https://cdn.jsdelivr.net/npm/simple-icons@v6/icons/linkedin.svg",
# message = (
# f'username "{username}" is ✅ Available.'
# ' However, usernames from private or deleted accounts will appear'
# " available, login into LinkedIn and go to"
# f" \"https://www.linkedin.com/in/{username}\" and see if it it's available"))
# },
# {
# id: "linkedin-page",
# name: "LinkedIn Company",
# img: "https://cdn.jsdelivr.net/npm/simple-icons@v6/icons/linkedin.svg"
# }
]
def is_valid_instagram_username(username):
"""
Validates an Instagram username based on their username rules:
- 1 to 30 characters long
- Can contain letters (a-z), numbers (0-9), and periods/underscores
- Cannot start or end with a period
- Cannot have consecutive periods
- Cannot have periods next to underscores
- Can start or end with underscore
"""
# Regex pattern for Instagram username validation
pattern = r'^(?!.*\.\.)(?!.*\._)(?!.*_\.)[a-zA-Z0-9._][a-zA-Z0-9._]{0,28}[a-zA-Z0-9._]$'
# Additional length check since regex alone might not handle it perfectly
if len(username) < 1 or len(username) > 30:
return False
return re.match(pattern, username) is not None
def get_logger() -> tuple[list[dict[str, str]], Callable[[str, str], None]]:
logs = []
return logs, lambda key, value: logs.append({ "key": key, "value": value })
def get_json_value(page_source, key, value_pattern):
pattern = rf'[\'"]?{key}[\'"]?\s*:\s*[\'"]?({value_pattern})[\'"]?'
match = re.search(pattern, page_source, flags=re.IGNORECASE)
return match.group(1) if match else None
async def availability_response(
resolve: Callable[[str], Coroutine[Any, Any, bool]],
logger: Callable[[str, str], None],
message: str = None):
try:
username_is_available_uri: tuple[str, bool, str] = await resolve()\
if inspect.iscoroutinefunction(resolve) or inspect.isawaitable(resolve)\
else await asyncio.to_thread(resolve)
logger("username_is_available_uri", username_is_available_uri)
username, is_available, uri = username_is_available_uri
if is_available == True:
return {
'available': False,
'message': f"{username}: ❌ Taken",
'url': uri
}
if message:
return {
'available': True,
'message': message,
'url': uri
}
else:
return {
'available': True,
'message': f"{username}: ✅ Available",
'url': uri
}
except Exception as e:
logger(f"{availability_response.__name__}:Exception", str(e))
return { 'message': f"❌ {str(e)}" }
def resolve_instagram_username(
username: str, logger: Callable[[str, str], None]) -> tuple[str, bool, str]:
def resolve() -> bool:
profile_uri = f"https://www.instagram.com/{username}/"
profile_response = requests.get(profile_uri, allow_redirects = False)
logger(
"profile_response_status",
f"{profile_response.status_code} {profile_response.headers.get('Location')}" \
if profile_response.status_code in [301, 302] \
else profile_response.status_code)
profile_response_username = get_json_value(profile_response.text, "username", "\w+") or ""
logger("profile_response_username", profile_response_username)
_return_result = lambda is_available: (username, is_available, profile_uri)
# if there is a username in the page, then this is likely an existing account
if profile_response_username.lower().strip() == username.lower().strip():
return _return_result(True)
x_ig_app_id = get_json_value(profile_response.text, "X-IG-App-ID", "\d+")
web_profile_response = requests.get(
url=f"https://www.instagram.com/api/v1/users/web_profile_info/?username={username}",
headers={
"x-ig-app-id": x_ig_app_id,
},
allow_redirects = False)
logger("web_profile_response.status_code", web_profile_response.status_code)
# if status is 404, then the account doesnt exist!
is_html = re.match(r'.*(\w+)/html', web_profile_response.headers.get("Content-Type"))
if web_profile_response.status_code == 404 and is_html:
return _return_result(False)
# if status is 200, check status of the json
is_json = re.match(r'.*(\w+)/json', web_profile_response.headers.get("Content-Type"))
json_status = (web_profile_response.json() or {}).get('status') == 'ok' if is_json else False
return _return_result(web_profile_response.status_code == 200 and json_status)
return resolve