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