File size: 11,559 Bytes
03c0888
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
import random
from typing import Optional, Literal, List, Dict, Tuple
import re


class UserAgentGenerator:
    """
    Generate random user agents with specified constraints.
    
    Attributes:
        desktop_platforms (dict): A dictionary of possible desktop platforms and their corresponding user agent strings.
        mobile_platforms (dict): A dictionary of possible mobile platforms and their corresponding user agent strings.
        browser_combinations (dict): A dictionary of possible browser combinations and their corresponding user agent strings.
        rendering_engines (dict): A dictionary of possible rendering engines and their corresponding user agent strings.
        chrome_versions (list): A list of possible Chrome browser versions.
        firefox_versions (list): A list of possible Firefox browser versions.
        edge_versions (list): A list of possible Edge browser versions.
        safari_versions (list): A list of possible Safari browser versions.
        ios_versions (list): A list of possible iOS browser versions.
        android_versions (list): A list of possible Android browser versions.
        
        Methods:
            generate_user_agent(
                platform: Literal["desktop", "mobile"] = "desktop",
                browser: str = "chrome",
                rendering_engine: str = "chrome_webkit",
                chrome_version: Optional[str] = None,
                firefox_version: Optional[str] = None,
                edge_version: Optional[str] = None,
                safari_version: Optional[str] = None,
                ios_version: Optional[str] = None,
                android_version: Optional[str] = None
            ): Generates a random user agent string based on the specified parameters.    
    """
    def __init__(self):
        # Previous platform definitions remain the same...
        self.desktop_platforms = {
            "windows": {
                "10_64": "(Windows NT 10.0; Win64; x64)",
                "10_32": "(Windows NT 10.0; WOW64)",
            },
            "macos": {
                "intel": "(Macintosh; Intel Mac OS X 10_15_7)",
                "newer": "(Macintosh; Intel Mac OS X 10.15; rv:109.0)",
            },
            "linux": {
                "generic": "(X11; Linux x86_64)",
                "ubuntu": "(X11; Ubuntu; Linux x86_64)",
                "chrome_os": "(X11; CrOS x86_64 14541.0.0)",
            }
        }

        self.mobile_platforms = {
            "android": {
                "samsung": "(Linux; Android 13; SM-S901B)",
                "pixel": "(Linux; Android 12; Pixel 6)",
                "oneplus": "(Linux; Android 13; OnePlus 9 Pro)",
                "xiaomi": "(Linux; Android 12; M2102J20SG)",
            },
            "ios": {
                "iphone": "(iPhone; CPU iPhone OS 16_5 like Mac OS X)",
                "ipad": "(iPad; CPU OS 16_5 like Mac OS X)",
            }
        }

        # Browser Combinations
        self.browser_combinations = {
            1: [
                ["chrome"],
                ["firefox"],
                ["safari"],
                ["edge"]
            ],
            2: [
                ["gecko", "firefox"],
                ["chrome", "safari"],
                ["webkit", "safari"]
            ],
            3: [
                ["chrome", "safari", "edge"],
                ["webkit", "chrome", "safari"]
            ]
        }

        # Rendering Engines with versions
        self.rendering_engines = {
            "chrome_webkit": "AppleWebKit/537.36",
            "safari_webkit": "AppleWebKit/605.1.15",
            "gecko": [  # Added Gecko versions
                "Gecko/20100101",
                "Gecko/20100101",  # Firefox usually uses this constant version
                "Gecko/2010010",
            ]
        }

        # Browser Versions
        self.chrome_versions = [
            "Chrome/119.0.6045.199",
            "Chrome/118.0.5993.117",
            "Chrome/117.0.5938.149",
            "Chrome/116.0.5845.187",
            "Chrome/115.0.5790.171",
        ]

        self.edge_versions = [
            "Edg/119.0.2151.97",
            "Edg/118.0.2088.76",
            "Edg/117.0.2045.47",
            "Edg/116.0.1938.81",
            "Edg/115.0.1901.203",
        ]

        self.safari_versions = [
            "Safari/537.36",  # For Chrome-based
            "Safari/605.1.15",
            "Safari/604.1",
            "Safari/602.1",
            "Safari/601.5.17",
        ]

        # Added Firefox versions
        self.firefox_versions = [
            "Firefox/119.0",
            "Firefox/118.0.2",
            "Firefox/117.0.1",
            "Firefox/116.0",
            "Firefox/115.0.3",
            "Firefox/114.0.2",
            "Firefox/113.0.1",
            "Firefox/112.0",
            "Firefox/111.0.1",
            "Firefox/110.0",
        ]

    def get_browser_stack(self, num_browsers: int = 1) -> List[str]:
        """
        Get a valid combination of browser versions.
        
        How it works:
        1. Check if the number of browsers is supported.
        2. Randomly choose a combination of browsers.
        3. Iterate through the combination and add browser versions.
        4. Return the browser stack.
        
        Args:
            num_browsers: Number of browser specifications (1-3)
            
        Returns:
            List[str]: A list of browser versions.
        """
        if num_browsers not in self.browser_combinations:
            raise ValueError(f"Unsupported number of browsers: {num_browsers}")
        
        combination = random.choice(self.browser_combinations[num_browsers])
        browser_stack = []
        
        for browser in combination:
            if browser == "chrome":
                browser_stack.append(random.choice(self.chrome_versions))
            elif browser == "firefox":
                browser_stack.append(random.choice(self.firefox_versions))
            elif browser == "safari":
                browser_stack.append(random.choice(self.safari_versions))
            elif browser == "edge":
                browser_stack.append(random.choice(self.edge_versions))
            elif browser == "gecko":
                browser_stack.append(random.choice(self.rendering_engines["gecko"]))
            elif browser == "webkit":
                browser_stack.append(self.rendering_engines["chrome_webkit"])
        
        return browser_stack

    def generate(self, 
                device_type: Optional[Literal['desktop', 'mobile']] = None,
                os_type: Optional[str] = None,
                device_brand: Optional[str] = None,
                browser_type: Optional[Literal['chrome', 'edge', 'safari', 'firefox']] = None,
                num_browsers: int = 3) -> str:
        """
        Generate a random user agent with specified constraints.
        
        Args:
            device_type: 'desktop' or 'mobile'
            os_type: 'windows', 'macos', 'linux', 'android', 'ios'
            device_brand: Specific device brand
            browser_type: 'chrome', 'edge', 'safari', or 'firefox'
            num_browsers: Number of browser specifications (1-3)
        """
        # Get platform string
        platform = self.get_random_platform(device_type, os_type, device_brand)
        
        # Start with Mozilla
        components = ["Mozilla/5.0", platform]
        
        # Add browser stack
        browser_stack = self.get_browser_stack(num_browsers)
        
        # Add appropriate legacy token based on browser stack
        if "Firefox" in str(browser_stack):
            components.append(random.choice(self.rendering_engines["gecko"]))
        elif "Chrome" in str(browser_stack) or "Safari" in str(browser_stack):
            components.append(self.rendering_engines["chrome_webkit"])
            components.append("(KHTML, like Gecko)")
        
        # Add browser versions
        components.extend(browser_stack)
        
        return " ".join(components)

    def generate_with_client_hints(self, **kwargs) -> Tuple[str, str]:
        """Generate both user agent and matching client hints"""
        user_agent = self.generate(**kwargs)
        client_hints = self.generate_client_hints(user_agent)
        return user_agent, client_hints

    def get_random_platform(self, device_type, os_type, device_brand):
        """Helper method to get random platform based on constraints"""
        platforms = self.desktop_platforms if device_type == 'desktop' else \
                   self.mobile_platforms if device_type == 'mobile' else \
                   {**self.desktop_platforms, **self.mobile_platforms}
        
        if os_type:
            for platform_group in [self.desktop_platforms, self.mobile_platforms]:
                if os_type in platform_group:
                    platforms = {os_type: platform_group[os_type]}
                    break
        
        os_key = random.choice(list(platforms.keys()))
        if device_brand and device_brand in platforms[os_key]:
            return platforms[os_key][device_brand]
        return random.choice(list(platforms[os_key].values()))

    def parse_user_agent(self, user_agent: str) -> Dict[str, str]:
        """Parse a user agent string to extract browser and version information"""
        browsers = {
            'chrome': r'Chrome/(\d+)',
            'edge': r'Edg/(\d+)',
            'safari': r'Version/(\d+)',
            'firefox': r'Firefox/(\d+)'
        }
        
        result = {}
        for browser, pattern in browsers.items():
            match = re.search(pattern, user_agent)
            if match:
                result[browser] = match.group(1)
        
        return result

    def generate_client_hints(self, user_agent: str) -> str:
        """Generate Sec-CH-UA header value based on user agent string"""
        browsers = self.parse_user_agent(user_agent)
        
        # Client hints components
        hints = []
        
        # Handle different browser combinations
        if 'chrome' in browsers:
            hints.append(f'"Chromium";v="{browsers["chrome"]}"')
            hints.append('"Not_A Brand";v="8"')
            
            if 'edge' in browsers:
                hints.append(f'"Microsoft Edge";v="{browsers["edge"]}"')
            else:
                hints.append(f'"Google Chrome";v="{browsers["chrome"]}"')
                
        elif 'firefox' in browsers:
            # Firefox doesn't typically send Sec-CH-UA
            return '""'
            
        elif 'safari' in browsers:
            # Safari's format for client hints
            hints.append(f'"Safari";v="{browsers["safari"]}"')
            hints.append('"Not_A Brand";v="8"')
        
        return ', '.join(hints)

# Example usage:
if __name__ == "__main__":
    generator = UserAgentGenerator()
    print(generator.generate())
    
    print("\nSingle browser (Chrome):")
    print(generator.generate(num_browsers=1, browser_type='chrome'))
    
    print("\nTwo browsers (Gecko/Firefox):")
    print(generator.generate(num_browsers=2))
    
    print("\nThree browsers (Chrome/Safari/Edge):")
    print(generator.generate(num_browsers=3))
    
    print("\nFirefox on Linux:")
    print(generator.generate(
        device_type='desktop',
        os_type='linux',
        browser_type='firefox',
        num_browsers=2
    ))
    
    print("\nChrome/Safari/Edge on Windows:")
    print(generator.generate(
        device_type='desktop',
        os_type='windows',
        num_browsers=3
    ))