File size: 6,100 Bytes
261a211 e7f8ff5 261a211 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
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 <b>"{username}"</b> is β
Available.'
' However, usernames from disabled or deleted accounts may also appear'
' available but you can\'t choose them, eg: usernames like <b>"we"</b>, <b>"us"</b>, and <b>"the"</b>.'
' Go to <a target="_blank" href="https://accountscenter.instagram.com/">accounts center</a>'
" 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 <b>"{username}"</b> 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 |