webscout 8.3.7__py3-none-any.whl โ 2025.10.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of webscout might be problematic. Click here for more details.
- webscout/AIauto.py +250 -250
- webscout/AIbase.py +379 -379
- webscout/AIutel.py +60 -60
- webscout/Bard.py +1012 -1012
- webscout/Bing_search.py +417 -417
- webscout/DWEBS.py +529 -529
- webscout/Extra/Act.md +309 -309
- webscout/Extra/GitToolkit/__init__.py +10 -10
- webscout/Extra/GitToolkit/gitapi/README.md +110 -110
- webscout/Extra/GitToolkit/gitapi/__init__.py +11 -11
- webscout/Extra/GitToolkit/gitapi/repository.py +195 -195
- webscout/Extra/GitToolkit/gitapi/user.py +96 -96
- webscout/Extra/GitToolkit/gitapi/utils.py +61 -61
- webscout/Extra/YTToolkit/README.md +375 -375
- webscout/Extra/YTToolkit/YTdownloader.py +956 -956
- webscout/Extra/YTToolkit/__init__.py +2 -2
- webscout/Extra/YTToolkit/transcriber.py +475 -475
- webscout/Extra/YTToolkit/ytapi/README.md +44 -44
- webscout/Extra/YTToolkit/ytapi/__init__.py +6 -6
- webscout/Extra/YTToolkit/ytapi/channel.py +307 -307
- webscout/Extra/YTToolkit/ytapi/errors.py +13 -13
- webscout/Extra/YTToolkit/ytapi/extras.py +118 -118
- webscout/Extra/YTToolkit/ytapi/https.py +88 -88
- webscout/Extra/YTToolkit/ytapi/patterns.py +61 -61
- webscout/Extra/YTToolkit/ytapi/playlist.py +58 -58
- webscout/Extra/YTToolkit/ytapi/pool.py +7 -7
- webscout/Extra/YTToolkit/ytapi/query.py +39 -39
- webscout/Extra/YTToolkit/ytapi/stream.py +62 -62
- webscout/Extra/YTToolkit/ytapi/utils.py +62 -62
- webscout/Extra/YTToolkit/ytapi/video.py +232 -232
- webscout/Extra/autocoder/__init__.py +9 -9
- webscout/Extra/autocoder/autocoder.py +1105 -1105
- webscout/Extra/autocoder/autocoder_utiles.py +332 -332
- webscout/Extra/gguf.md +429 -429
- webscout/Extra/gguf.py +1213 -1213
- webscout/Extra/tempmail/README.md +487 -487
- webscout/Extra/tempmail/__init__.py +27 -27
- webscout/Extra/tempmail/async_utils.py +140 -140
- webscout/Extra/tempmail/base.py +160 -160
- webscout/Extra/tempmail/cli.py +186 -186
- webscout/Extra/tempmail/emailnator.py +84 -84
- webscout/Extra/tempmail/mail_tm.py +360 -360
- webscout/Extra/tempmail/temp_mail_io.py +291 -291
- webscout/Extra/weather.md +281 -281
- webscout/Extra/weather.py +193 -193
- webscout/Litlogger/README.md +10 -10
- webscout/Litlogger/__init__.py +15 -15
- webscout/Litlogger/formats.py +13 -13
- webscout/Litlogger/handlers.py +121 -121
- webscout/Litlogger/levels.py +13 -13
- webscout/Litlogger/logger.py +134 -134
- webscout/Provider/AISEARCH/Perplexity.py +332 -332
- webscout/Provider/AISEARCH/README.md +279 -279
- webscout/Provider/AISEARCH/__init__.py +16 -1
- webscout/Provider/AISEARCH/felo_search.py +206 -206
- webscout/Provider/AISEARCH/genspark_search.py +323 -323
- webscout/Provider/AISEARCH/hika_search.py +185 -185
- webscout/Provider/AISEARCH/iask_search.py +410 -410
- webscout/Provider/AISEARCH/monica_search.py +219 -219
- webscout/Provider/AISEARCH/scira_search.py +316 -316
- webscout/Provider/AISEARCH/stellar_search.py +177 -177
- webscout/Provider/AISEARCH/webpilotai_search.py +255 -255
- webscout/Provider/Aitopia.py +314 -314
- webscout/Provider/Apriel.py +306 -0
- webscout/Provider/ChatGPTClone.py +236 -236
- webscout/Provider/ChatSandbox.py +343 -343
- webscout/Provider/Cloudflare.py +324 -324
- webscout/Provider/Cohere.py +208 -208
- webscout/Provider/Deepinfra.py +370 -366
- webscout/Provider/ExaAI.py +260 -260
- webscout/Provider/ExaChat.py +308 -308
- webscout/Provider/Flowith.py +221 -221
- webscout/Provider/GMI.py +293 -0
- webscout/Provider/Gemini.py +164 -164
- webscout/Provider/GeminiProxy.py +167 -167
- webscout/Provider/GithubChat.py +371 -372
- webscout/Provider/Groq.py +800 -800
- webscout/Provider/HeckAI.py +383 -383
- webscout/Provider/Jadve.py +282 -282
- webscout/Provider/K2Think.py +307 -307
- webscout/Provider/Koboldai.py +205 -205
- webscout/Provider/LambdaChat.py +423 -423
- webscout/Provider/Nemotron.py +244 -244
- webscout/Provider/Netwrck.py +248 -248
- webscout/Provider/OLLAMA.py +395 -395
- webscout/Provider/OPENAI/Cloudflare.py +393 -393
- webscout/Provider/OPENAI/FalconH1.py +451 -451
- webscout/Provider/OPENAI/FreeGemini.py +296 -296
- webscout/Provider/OPENAI/K2Think.py +431 -431
- webscout/Provider/OPENAI/NEMOTRON.py +240 -240
- webscout/Provider/OPENAI/PI.py +427 -427
- webscout/Provider/OPENAI/README.md +959 -959
- webscout/Provider/OPENAI/TogetherAI.py +345 -345
- webscout/Provider/OPENAI/TwoAI.py +465 -465
- webscout/Provider/OPENAI/__init__.py +33 -18
- webscout/Provider/OPENAI/base.py +248 -248
- webscout/Provider/OPENAI/chatglm.py +528 -0
- webscout/Provider/OPENAI/chatgpt.py +592 -592
- webscout/Provider/OPENAI/chatgptclone.py +521 -521
- webscout/Provider/OPENAI/chatsandbox.py +202 -202
- webscout/Provider/OPENAI/deepinfra.py +318 -314
- webscout/Provider/OPENAI/e2b.py +1665 -1665
- webscout/Provider/OPENAI/exaai.py +420 -420
- webscout/Provider/OPENAI/exachat.py +452 -452
- webscout/Provider/OPENAI/friendli.py +232 -232
- webscout/Provider/OPENAI/{refact.py โ gmi.py} +324 -274
- webscout/Provider/OPENAI/groq.py +364 -364
- webscout/Provider/OPENAI/heckai.py +314 -314
- webscout/Provider/OPENAI/llmchatco.py +337 -337
- webscout/Provider/OPENAI/netwrck.py +355 -355
- webscout/Provider/OPENAI/oivscode.py +290 -290
- webscout/Provider/OPENAI/opkfc.py +518 -518
- webscout/Provider/OPENAI/pydantic_imports.py +1 -1
- webscout/Provider/OPENAI/scirachat.py +535 -535
- webscout/Provider/OPENAI/sonus.py +308 -308
- webscout/Provider/OPENAI/standardinput.py +442 -442
- webscout/Provider/OPENAI/textpollinations.py +340 -340
- webscout/Provider/OPENAI/toolbaz.py +419 -416
- webscout/Provider/OPENAI/typefully.py +362 -362
- webscout/Provider/OPENAI/utils.py +295 -295
- webscout/Provider/OPENAI/venice.py +436 -436
- webscout/Provider/OPENAI/wisecat.py +387 -387
- webscout/Provider/OPENAI/writecream.py +166 -166
- webscout/Provider/OPENAI/x0gpt.py +378 -378
- webscout/Provider/OPENAI/yep.py +389 -389
- webscout/Provider/OpenGPT.py +230 -230
- webscout/Provider/Openai.py +243 -243
- webscout/Provider/PI.py +405 -405
- webscout/Provider/Perplexitylabs.py +430 -430
- webscout/Provider/QwenLM.py +272 -272
- webscout/Provider/STT/__init__.py +16 -1
- webscout/Provider/Sambanova.py +257 -257
- webscout/Provider/StandardInput.py +309 -309
- webscout/Provider/TTI/README.md +82 -82
- webscout/Provider/TTI/__init__.py +33 -18
- webscout/Provider/TTI/aiarta.py +413 -413
- webscout/Provider/TTI/base.py +136 -136
- webscout/Provider/TTI/bing.py +243 -243
- webscout/Provider/TTI/gpt1image.py +149 -149
- webscout/Provider/TTI/imagen.py +196 -196
- webscout/Provider/TTI/infip.py +211 -211
- webscout/Provider/TTI/magicstudio.py +232 -232
- webscout/Provider/TTI/monochat.py +219 -219
- webscout/Provider/TTI/piclumen.py +214 -214
- webscout/Provider/TTI/pixelmuse.py +232 -232
- webscout/Provider/TTI/pollinations.py +232 -232
- webscout/Provider/TTI/together.py +288 -288
- webscout/Provider/TTI/utils.py +12 -12
- webscout/Provider/TTI/venice.py +367 -367
- webscout/Provider/TTS/README.md +192 -192
- webscout/Provider/TTS/__init__.py +33 -18
- webscout/Provider/TTS/parler.py +110 -110
- webscout/Provider/TTS/streamElements.py +333 -333
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TeachAnything.py +237 -237
- webscout/Provider/TextPollinationsAI.py +310 -310
- webscout/Provider/TogetherAI.py +356 -356
- webscout/Provider/TwoAI.py +312 -312
- webscout/Provider/TypliAI.py +311 -311
- webscout/Provider/UNFINISHED/ChatHub.py +208 -208
- webscout/Provider/UNFINISHED/ChutesAI.py +313 -313
- webscout/Provider/UNFINISHED/GizAI.py +294 -294
- webscout/Provider/UNFINISHED/Marcus.py +198 -198
- webscout/Provider/UNFINISHED/Qodo.py +477 -477
- webscout/Provider/UNFINISHED/VercelAIGateway.py +338 -338
- webscout/Provider/UNFINISHED/XenAI.py +324 -324
- webscout/Provider/UNFINISHED/Youchat.py +330 -330
- webscout/Provider/UNFINISHED/liner.py +334 -0
- webscout/Provider/UNFINISHED/liner_api_request.py +262 -262
- webscout/Provider/UNFINISHED/puterjs.py +634 -634
- webscout/Provider/UNFINISHED/samurai.py +223 -223
- webscout/Provider/UNFINISHED/test_lmarena.py +119 -119
- webscout/Provider/Venice.py +250 -250
- webscout/Provider/VercelAI.py +256 -256
- webscout/Provider/WiseCat.py +231 -231
- webscout/Provider/WrDoChat.py +366 -366
- webscout/Provider/__init__.py +33 -18
- webscout/Provider/ai4chat.py +174 -174
- webscout/Provider/akashgpt.py +331 -331
- webscout/Provider/cerebras.py +446 -446
- webscout/Provider/chatglm.py +394 -301
- webscout/Provider/cleeai.py +211 -211
- webscout/Provider/elmo.py +282 -282
- webscout/Provider/geminiapi.py +208 -208
- webscout/Provider/granite.py +261 -261
- webscout/Provider/hermes.py +263 -263
- webscout/Provider/julius.py +223 -223
- webscout/Provider/learnfastai.py +309 -309
- webscout/Provider/llama3mitril.py +214 -214
- webscout/Provider/llmchat.py +243 -243
- webscout/Provider/llmchatco.py +290 -290
- webscout/Provider/meta.py +801 -801
- webscout/Provider/oivscode.py +309 -309
- webscout/Provider/scira_chat.py +383 -383
- webscout/Provider/searchchat.py +292 -292
- webscout/Provider/sonus.py +258 -258
- webscout/Provider/toolbaz.py +370 -367
- webscout/Provider/turboseek.py +273 -273
- webscout/Provider/typefully.py +207 -207
- webscout/Provider/yep.py +372 -372
- webscout/__init__.py +30 -31
- webscout/__main__.py +5 -5
- webscout/auth/api_key_manager.py +189 -189
- webscout/auth/config.py +175 -175
- webscout/auth/models.py +185 -185
- webscout/auth/routes.py +664 -664
- webscout/auth/simple_logger.py +236 -236
- webscout/cli.py +523 -523
- webscout/conversation.py +438 -438
- webscout/exceptions.py +361 -361
- webscout/litagent/Readme.md +298 -298
- webscout/litagent/__init__.py +28 -28
- webscout/litagent/agent.py +581 -581
- webscout/litagent/constants.py +59 -59
- webscout/litprinter/__init__.py +58 -58
- webscout/models.py +181 -181
- webscout/optimizers.py +419 -419
- webscout/prompt_manager.py +288 -288
- webscout/sanitize.py +1078 -1078
- webscout/scout/README.md +401 -401
- webscout/scout/__init__.py +8 -8
- webscout/scout/core/__init__.py +6 -6
- webscout/scout/core/crawler.py +297 -297
- webscout/scout/core/scout.py +706 -706
- webscout/scout/core/search_result.py +95 -95
- webscout/scout/core/text_analyzer.py +62 -62
- webscout/scout/core/text_utils.py +277 -277
- webscout/scout/core/web_analyzer.py +51 -51
- webscout/scout/element.py +599 -599
- webscout/scout/parsers/__init__.py +69 -69
- webscout/scout/parsers/html5lib_parser.py +172 -172
- webscout/scout/parsers/html_parser.py +236 -236
- webscout/scout/parsers/lxml_parser.py +178 -178
- webscout/scout/utils.py +37 -37
- webscout/swiftcli/Readme.md +323 -323
- webscout/swiftcli/__init__.py +95 -95
- webscout/swiftcli/core/__init__.py +7 -7
- webscout/swiftcli/core/cli.py +308 -308
- webscout/swiftcli/core/context.py +104 -104
- webscout/swiftcli/core/group.py +241 -241
- webscout/swiftcli/decorators/__init__.py +28 -28
- webscout/swiftcli/decorators/command.py +221 -221
- webscout/swiftcli/decorators/options.py +220 -220
- webscout/swiftcli/decorators/output.py +302 -302
- webscout/swiftcli/exceptions.py +21 -21
- webscout/swiftcli/plugins/__init__.py +9 -9
- webscout/swiftcli/plugins/base.py +135 -135
- webscout/swiftcli/plugins/manager.py +269 -269
- webscout/swiftcli/utils/__init__.py +59 -59
- webscout/swiftcli/utils/formatting.py +252 -252
- webscout/swiftcli/utils/parsing.py +267 -267
- webscout/update_checker.py +117 -117
- webscout/version.py +1 -1
- webscout/webscout_search.py +1183 -1183
- webscout/webscout_search_async.py +649 -649
- webscout/yep_search.py +346 -346
- webscout/zeroart/README.md +89 -89
- webscout/zeroart/__init__.py +134 -134
- webscout/zeroart/base.py +66 -66
- webscout/zeroart/effects.py +100 -100
- webscout/zeroart/fonts.py +1238 -1238
- {webscout-8.3.7.dist-info โ webscout-2025.10.11.dist-info}/METADATA +937 -937
- webscout-2025.10.11.dist-info/RECORD +300 -0
- webscout/Provider/AISEARCH/DeepFind.py +0 -254
- webscout/Provider/OPENAI/Qwen3.py +0 -303
- webscout/Provider/OPENAI/qodo.py +0 -630
- webscout/Provider/OPENAI/xenai.py +0 -514
- webscout/tempid.py +0 -134
- webscout-8.3.7.dist-info/RECORD +0 -301
- {webscout-8.3.7.dist-info โ webscout-2025.10.11.dist-info}/WHEEL +0 -0
- {webscout-8.3.7.dist-info โ webscout-2025.10.11.dist-info}/entry_points.txt +0 -0
- {webscout-8.3.7.dist-info โ webscout-2025.10.11.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.3.7.dist-info โ webscout-2025.10.11.dist-info}/top_level.txt +0 -0
webscout/litagent/agent.py
CHANGED
|
@@ -1,582 +1,582 @@
|
|
|
1
|
-
"""Main LitAgent implementation."""
|
|
2
|
-
|
|
3
|
-
import random
|
|
4
|
-
import threading
|
|
5
|
-
from typing import Any, Dict, List, Optional
|
|
6
|
-
|
|
7
|
-
from webscout.litagent.constants import BROWSERS, DEVICES, FINGERPRINTS, OS_VERSIONS
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class LitAgent:
|
|
11
|
-
"""A lit user agent generator that keeps it fresh! ๐"""
|
|
12
|
-
|
|
13
|
-
def __init__(self, thread_safe: bool = False):
|
|
14
|
-
"""Initialize LitAgent with style! ๐ซ
|
|
15
|
-
|
|
16
|
-
Args:
|
|
17
|
-
thread_safe (bool): Enable thread-safety for multi-threaded applications
|
|
18
|
-
"""
|
|
19
|
-
self.agents = self._generate_agents(100) # Keep 100 agents in memory
|
|
20
|
-
self.thread_safe = thread_safe
|
|
21
|
-
self.lock = threading.RLock() if thread_safe else None
|
|
22
|
-
self.ip_pool = self._generate_ip_pool(20)
|
|
23
|
-
self._ip_index = 0
|
|
24
|
-
self._refresh_timer = None
|
|
25
|
-
self._stats = {
|
|
26
|
-
"total_generated": 100,
|
|
27
|
-
"requests_served": 0,
|
|
28
|
-
"browser_usage": {browser: 0 for browser in BROWSERS.keys()},
|
|
29
|
-
"device_usage": {device: 0 for device in DEVICES.keys()}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
def _generate_agents(self, count: int) -> List[str]:
|
|
33
|
-
"""Generate some lit user agents! ๐ ๏ธ"""
|
|
34
|
-
agents = []
|
|
35
|
-
for _ in range(count):
|
|
36
|
-
browser = random.choice(list(BROWSERS.keys()))
|
|
37
|
-
version = random.randint(*BROWSERS[browser])
|
|
38
|
-
|
|
39
|
-
if browser in ['chrome', 'firefox', 'edge', 'opera', 'brave', 'vivaldi']:
|
|
40
|
-
os_type = random.choice(['windows', 'mac', 'linux'])
|
|
41
|
-
os_ver = random.choice(OS_VERSIONS[os_type])
|
|
42
|
-
|
|
43
|
-
if os_type == 'windows':
|
|
44
|
-
platform = f"Windows NT {os_ver}"
|
|
45
|
-
elif os_type == 'mac':
|
|
46
|
-
platform = f"Macintosh; Intel Mac OS X {os_ver}"
|
|
47
|
-
else:
|
|
48
|
-
platform = f"X11; Linux {os_ver}"
|
|
49
|
-
|
|
50
|
-
agent = f"Mozilla/5.0 ({platform}) AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
51
|
-
if browser == 'chrome':
|
|
52
|
-
agent += f"Chrome/{version}.0.0.0 Safari/537.36"
|
|
53
|
-
elif browser == 'firefox':
|
|
54
|
-
agent += f"Firefox/{version}.0"
|
|
55
|
-
elif browser == 'edge':
|
|
56
|
-
agent += f"Edge/{version}.0.0.0"
|
|
57
|
-
elif browser == 'opera':
|
|
58
|
-
agent += f"OPR/{version}.0.0.0"
|
|
59
|
-
elif browser == 'brave':
|
|
60
|
-
agent += f"Chrome/{version}.0.0.0 Safari/537.36 Brave/{version}.0.0.0"
|
|
61
|
-
elif browser == 'vivaldi':
|
|
62
|
-
agent += f"Chrome/{version}.0.0.0 Safari/537.36 Vivaldi/{version}.0.{random.randint(1000, 9999)}"
|
|
63
|
-
|
|
64
|
-
elif browser == 'safari':
|
|
65
|
-
device = random.choice(['mac', 'ios'])
|
|
66
|
-
if device == 'mac':
|
|
67
|
-
ver = random.choice(OS_VERSIONS['mac'])
|
|
68
|
-
agent = f"Mozilla/5.0 (Macintosh; Intel Mac OS X {ver}) "
|
|
69
|
-
else:
|
|
70
|
-
ver = random.choice(OS_VERSIONS['ios'])
|
|
71
|
-
device = random.choice(['iPhone', 'iPad'])
|
|
72
|
-
agent = f"Mozilla/5.0 ({device}; CPU OS {ver} like Mac OS X) "
|
|
73
|
-
agent += f"AppleWebKit/{version}.1.15 (KHTML, like Gecko) Version/{version//100}.0 Safari/{version}.1.15"
|
|
74
|
-
|
|
75
|
-
agents.append(agent)
|
|
76
|
-
|
|
77
|
-
return list(set(agents)) # Remove any duplicates
|
|
78
|
-
|
|
79
|
-
def _update_stats(self, browser_type=None, device_type=None):
|
|
80
|
-
"""Update usage statistics."""
|
|
81
|
-
if self.thread_safe and self.lock:
|
|
82
|
-
with self.lock:
|
|
83
|
-
self._stats["requests_served"] += 1
|
|
84
|
-
if browser_type:
|
|
85
|
-
self._stats["browser_usage"][browser_type] = self._stats["browser_usage"].get(browser_type, 0) + 1
|
|
86
|
-
if device_type:
|
|
87
|
-
self._stats["device_usage"][device_type] = self._stats["device_usage"].get(device_type, 0) + 1
|
|
88
|
-
else:
|
|
89
|
-
self._stats["requests_served"] += 1
|
|
90
|
-
if browser_type:
|
|
91
|
-
self._stats["browser_usage"][browser_type] = self._stats["browser_usage"].get(browser_type, 0) + 1
|
|
92
|
-
if device_type:
|
|
93
|
-
self._stats["device_usage"][device_type] = self._stats["device_usage"].get(device_type, 0) + 1
|
|
94
|
-
|
|
95
|
-
def random(self) -> str:
|
|
96
|
-
"""Get a random user agent! ๐ฒ (with blacklist/whitelist support)"""
|
|
97
|
-
if hasattr(self, '_whitelist') and self._whitelist:
|
|
98
|
-
pool = list(self._whitelist)
|
|
99
|
-
else:
|
|
100
|
-
pool = [a for a in self.agents if not hasattr(self, '_blacklist') or a not in self._blacklist]
|
|
101
|
-
if not pool:
|
|
102
|
-
pool = self.agents
|
|
103
|
-
if self.thread_safe and self.lock:
|
|
104
|
-
with self.lock:
|
|
105
|
-
agent = random.choice(pool)
|
|
106
|
-
self._update_stats()
|
|
107
|
-
self._add_to_history(agent)
|
|
108
|
-
return agent
|
|
109
|
-
else:
|
|
110
|
-
agent = random.choice(pool)
|
|
111
|
-
self._update_stats()
|
|
112
|
-
self._add_to_history(agent)
|
|
113
|
-
return agent
|
|
114
|
-
|
|
115
|
-
def browser(self, name: str) -> str:
|
|
116
|
-
"""Get a browser-specific agent! ๐"""
|
|
117
|
-
name = name.lower()
|
|
118
|
-
if name not in BROWSERS:
|
|
119
|
-
return self.random()
|
|
120
|
-
|
|
121
|
-
if self.thread_safe and self.lock:
|
|
122
|
-
with self.lock:
|
|
123
|
-
agents = [a for a in self.agents if name in a.lower()]
|
|
124
|
-
agent = random.choice(agents) if agents else self.random()
|
|
125
|
-
self._update_stats(browser_type=name)
|
|
126
|
-
return agent
|
|
127
|
-
else:
|
|
128
|
-
agents = [a for a in self.agents if name in a.lower()]
|
|
129
|
-
agent = random.choice(agents) if agents else self.random()
|
|
130
|
-
self._update_stats(browser_type=name)
|
|
131
|
-
return agent
|
|
132
|
-
|
|
133
|
-
def mobile(self) -> str:
|
|
134
|
-
"""Get a mobile device agent! ๐ฑ"""
|
|
135
|
-
if self.thread_safe and self.lock:
|
|
136
|
-
with self.lock:
|
|
137
|
-
agents = [a for a in self.agents if any(d in a for d in DEVICES['mobile'])]
|
|
138
|
-
agent = random.choice(agents) if agents else self.random()
|
|
139
|
-
self._update_stats(device_type="mobile")
|
|
140
|
-
return agent
|
|
141
|
-
else:
|
|
142
|
-
agents = [a for a in self.agents if any(d in a for d in DEVICES['mobile'])]
|
|
143
|
-
agent = random.choice(agents) if agents else self.random()
|
|
144
|
-
self._update_stats(device_type="mobile")
|
|
145
|
-
return agent
|
|
146
|
-
|
|
147
|
-
def desktop(self) -> str:
|
|
148
|
-
"""Get a desktop agent! ๐ป"""
|
|
149
|
-
if self.thread_safe and self.lock:
|
|
150
|
-
with self.lock:
|
|
151
|
-
agents = [a for a in self.agents if 'Windows' in a or 'Macintosh' in a or 'Linux' in a]
|
|
152
|
-
agent = random.choice(agents) if agents else self.random()
|
|
153
|
-
self._update_stats(device_type="desktop")
|
|
154
|
-
return agent
|
|
155
|
-
else:
|
|
156
|
-
agents = [a for a in self.agents if 'Windows' in a or 'Macintosh' in a or 'Linux' in a]
|
|
157
|
-
agent = random.choice(agents) if agents else self.random()
|
|
158
|
-
self._update_stats(device_type="desktop")
|
|
159
|
-
return agent
|
|
160
|
-
|
|
161
|
-
def tablet(self) -> str:
|
|
162
|
-
"""Get a tablet agent! ๐ฑ"""
|
|
163
|
-
if self.thread_safe and self.lock:
|
|
164
|
-
with self.lock:
|
|
165
|
-
# Focus on iPad and Android tablets
|
|
166
|
-
agents = [a for a in self.agents if 'iPad' in a or 'Android' in a and 'Mobile' not in a]
|
|
167
|
-
agent = random.choice(agents) if agents else self.random()
|
|
168
|
-
self._update_stats(device_type="tablet")
|
|
169
|
-
return agent
|
|
170
|
-
else:
|
|
171
|
-
agents = [a for a in self.agents if 'iPad' in a or 'Android' in a and 'Mobile' not in a]
|
|
172
|
-
agent = random.choice(agents) if agents else self.random()
|
|
173
|
-
self._update_stats(device_type="tablet")
|
|
174
|
-
return agent
|
|
175
|
-
|
|
176
|
-
def smart_tv(self) -> str:
|
|
177
|
-
"""Get a Smart TV agent! ๐บ"""
|
|
178
|
-
# Create a TV-specific agent since they may not be in our standard pool
|
|
179
|
-
tv_type = random.choice(DEVICES['tv'])
|
|
180
|
-
if 'Samsung' in tv_type:
|
|
181
|
-
agent = f"Mozilla/5.0 (SMART-TV; SAMSUNG; {tv_type}; Tizen 5.5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.38 Safari/537.36"
|
|
182
|
-
elif 'LG' in tv_type:
|
|
183
|
-
agent = f"Mozilla/5.0 (Web0S; {tv_type}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36"
|
|
184
|
-
elif 'Android' in tv_type:
|
|
185
|
-
agent = f"Mozilla/5.0 (Linux; Android 9; {tv_type}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36"
|
|
186
|
-
elif 'Apple' in tv_type:
|
|
187
|
-
agent = "Mozilla/5.0 (AppleTV; CPU like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148"
|
|
188
|
-
else:
|
|
189
|
-
agent = f"Mozilla/5.0 (Linux; {tv_type}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36"
|
|
190
|
-
|
|
191
|
-
self._update_stats(device_type="tv")
|
|
192
|
-
return agent
|
|
193
|
-
|
|
194
|
-
def gaming(self) -> str:
|
|
195
|
-
"""Get a gaming console agent! ๐ฎ"""
|
|
196
|
-
console_type = random.choice(DEVICES['console'])
|
|
197
|
-
if 'PlayStation' in console_type:
|
|
198
|
-
agent = f"Mozilla/5.0 ({console_type}/5.0) AppleWebKit/601.2 (KHTML, like Gecko)"
|
|
199
|
-
elif 'Xbox' in console_type:
|
|
200
|
-
agent = f"Mozilla/5.0 ({console_type}; Xbox; Xbox One) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19041"
|
|
201
|
-
elif 'Nintendo' in console_type:
|
|
202
|
-
agent = f"Mozilla/5.0 (Nintendo Switch; {console_type}) AppleWebKit/601.6 (KHTML, like Gecko) NintendoBrowser/5.1.0.13343"
|
|
203
|
-
else:
|
|
204
|
-
agent = self.random()
|
|
205
|
-
|
|
206
|
-
self._update_stats(device_type="console")
|
|
207
|
-
return agent
|
|
208
|
-
|
|
209
|
-
def chrome(self) -> str:
|
|
210
|
-
"""Get a Chrome agent! ๐"""
|
|
211
|
-
return self.browser('chrome')
|
|
212
|
-
|
|
213
|
-
def firefox(self) -> str:
|
|
214
|
-
"""Get a Firefox agent! ๐ฆ"""
|
|
215
|
-
return self.browser('firefox')
|
|
216
|
-
|
|
217
|
-
def safari(self) -> str:
|
|
218
|
-
"""Get a Safari agent! ๐งญ"""
|
|
219
|
-
return self.browser('safari')
|
|
220
|
-
|
|
221
|
-
def edge(self) -> str:
|
|
222
|
-
"""Get an Edge agent! ๐"""
|
|
223
|
-
return self.browser('edge')
|
|
224
|
-
|
|
225
|
-
def opera(self) -> str:
|
|
226
|
-
"""Get an Opera agent! ๐ญ"""
|
|
227
|
-
return self.browser('opera')
|
|
228
|
-
|
|
229
|
-
def brave(self) -> str:
|
|
230
|
-
"""Get a Brave agent! ๐ฆ"""
|
|
231
|
-
return self.browser('brave')
|
|
232
|
-
|
|
233
|
-
def vivaldi(self) -> str:
|
|
234
|
-
"""Get a Vivaldi agent! ๐จ"""
|
|
235
|
-
return self.browser('vivaldi')
|
|
236
|
-
|
|
237
|
-
# OS-specific agents
|
|
238
|
-
def windows(self) -> str:
|
|
239
|
-
"""Get a Windows agent! ๐ช"""
|
|
240
|
-
agents = [a for a in self.agents if 'Windows' in a]
|
|
241
|
-
agent = random.choice(agents) if agents else self.random()
|
|
242
|
-
self._update_stats()
|
|
243
|
-
return agent
|
|
244
|
-
|
|
245
|
-
def macos(self) -> str:
|
|
246
|
-
"""Get a macOS agent! ๐"""
|
|
247
|
-
agents = [a for a in self.agents if 'Macintosh' in a]
|
|
248
|
-
agent = random.choice(agents) if agents else self.random()
|
|
249
|
-
self._update_stats()
|
|
250
|
-
return agent
|
|
251
|
-
|
|
252
|
-
def linux(self) -> str:
|
|
253
|
-
"""Get a Linux agent! ๐ง"""
|
|
254
|
-
agents = [a for a in self.agents if 'Linux' in a and 'Android' not in a]
|
|
255
|
-
agent = random.choice(agents) if agents else self.random()
|
|
256
|
-
self._update_stats()
|
|
257
|
-
return agent
|
|
258
|
-
|
|
259
|
-
def android(self) -> str:
|
|
260
|
-
"""Get an Android agent! ๐ค"""
|
|
261
|
-
agents = [a for a in self.agents if 'Android' in a]
|
|
262
|
-
agent = random.choice(agents) if agents else self.random()
|
|
263
|
-
self._update_stats()
|
|
264
|
-
return agent
|
|
265
|
-
|
|
266
|
-
def ios(self) -> str:
|
|
267
|
-
"""Get an iOS agent! ๐ฑ"""
|
|
268
|
-
agents = [a for a in self.agents if 'iPhone' in a or 'iPad' in a]
|
|
269
|
-
agent = random.choice(agents) if agents else self.random()
|
|
270
|
-
self._update_stats()
|
|
271
|
-
return agent
|
|
272
|
-
|
|
273
|
-
def custom(self, browser: str, version: Optional[str] = None,
|
|
274
|
-
os: Optional[str] = None, os_version: Optional[str] = None,
|
|
275
|
-
device_type: Optional[str] = None) -> str:
|
|
276
|
-
"""Generate a custom user agent with specified parameters! ๐ ๏ธ
|
|
277
|
-
|
|
278
|
-
Args:
|
|
279
|
-
browser: Browser name (chrome, firefox, safari, edge, opera)
|
|
280
|
-
version: Browser version (optional)
|
|
281
|
-
os: Operating system (windows, mac, linux, android, ios)
|
|
282
|
-
os_version: OS version (optional)
|
|
283
|
-
device_type: Device type (desktop, mobile, tablet)
|
|
284
|
-
Returns:
|
|
285
|
-
Customized user agent string
|
|
286
|
-
"""
|
|
287
|
-
browser = browser.lower() if browser else 'chrome'
|
|
288
|
-
if browser not in BROWSERS:
|
|
289
|
-
browser = 'chrome'
|
|
290
|
-
|
|
291
|
-
if version:
|
|
292
|
-
try:
|
|
293
|
-
version_num = int(version.split('.')[0])
|
|
294
|
-
except (ValueError, IndexError):
|
|
295
|
-
version_num = random.randint(*BROWSERS[browser])
|
|
296
|
-
else:
|
|
297
|
-
version_num = random.randint(*BROWSERS[browser])
|
|
298
|
-
|
|
299
|
-
os = os.lower() if os else random.choice(['windows', 'mac', 'linux'])
|
|
300
|
-
if os not in OS_VERSIONS:
|
|
301
|
-
os = 'windows'
|
|
302
|
-
|
|
303
|
-
os_ver = os_version or random.choice(OS_VERSIONS[os])
|
|
304
|
-
|
|
305
|
-
device_type = device_type.lower() if device_type else 'desktop'
|
|
306
|
-
|
|
307
|
-
# Build the user agent
|
|
308
|
-
if os == 'windows':
|
|
309
|
-
platform = f"Windows NT {os_ver}"
|
|
310
|
-
elif os == 'mac':
|
|
311
|
-
platform = f"Macintosh; Intel Mac OS X {os_ver}"
|
|
312
|
-
elif os == 'linux':
|
|
313
|
-
platform = f"X11; Linux {OS_VERSIONS['linux'][0]}"
|
|
314
|
-
elif os == 'android':
|
|
315
|
-
platform = f"Linux; Android {os_ver}; {random.choice(DEVICES['mobile'])}"
|
|
316
|
-
elif os == 'ios':
|
|
317
|
-
device = 'iPhone' if device_type == 'mobile' else 'iPad'
|
|
318
|
-
platform = f"{device}; CPU OS {os_ver} like Mac OS X"
|
|
319
|
-
else:
|
|
320
|
-
platform = "Windows NT 10.0" # Default fallback
|
|
321
|
-
|
|
322
|
-
agent = f"Mozilla/5.0 ({platform}) AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
323
|
-
|
|
324
|
-
if browser == 'chrome':
|
|
325
|
-
agent += f"Chrome/{version_num}.0.0.0 Safari/537.36"
|
|
326
|
-
elif browser == 'firefox':
|
|
327
|
-
agent += f"Firefox/{version_num}.0"
|
|
328
|
-
elif browser == 'safari':
|
|
329
|
-
safari_ver = random.randint(*BROWSERS['safari'])
|
|
330
|
-
agent += f"Version/{version_num}.0 Safari/{safari_ver}.0"
|
|
331
|
-
elif browser == 'edge':
|
|
332
|
-
agent += f"Chrome/{version_num}.0.0.0 Safari/537.36 Edg/{version_num}.0.0.0"
|
|
333
|
-
elif browser == 'opera':
|
|
334
|
-
agent += f"Chrome/{version_num}.0.0.0 Safari/537.36 OPR/{version_num}.0.0.0"
|
|
335
|
-
elif browser == 'brave':
|
|
336
|
-
agent += f"Chrome/{version_num}.0.0.0 Safari/537.36 Brave/{version_num}.1.0"
|
|
337
|
-
|
|
338
|
-
self._update_stats(browser_type=browser, device_type=device_type)
|
|
339
|
-
return agent
|
|
340
|
-
|
|
341
|
-
@staticmethod
|
|
342
|
-
def generate_fingerprint(browser: Optional[str] = None) -> Dict[str, str]:
|
|
343
|
-
"""
|
|
344
|
-
Generate a consistent browser fingerprint for anti-fingerprinting purposes.
|
|
345
|
-
|
|
346
|
-
This method creates a dictionary of HTTP headers and related values that simulate
|
|
347
|
-
a realistic browser fingerprint, including user agent, accept headers, platform,
|
|
348
|
-
sec-ch-ua, and various IP-related headers. Optionally, a specific browser type
|
|
349
|
-
can be requested.
|
|
350
|
-
|
|
351
|
-
Args:
|
|
352
|
-
browser (Optional[str]): The browser name to generate the fingerprint for.
|
|
353
|
-
If not specified, a random browser is used.
|
|
354
|
-
|
|
355
|
-
Returns:
|
|
356
|
-
Dict[str, str]: A dictionary containing fingerprinting headers and values.
|
|
357
|
-
"""
|
|
358
|
-
# Get a random user agent using the random() method
|
|
359
|
-
agent = LitAgent()
|
|
360
|
-
user_agent = agent.random()
|
|
361
|
-
|
|
362
|
-
# If browser is specified, try to get a matching one
|
|
363
|
-
if browser:
|
|
364
|
-
browser = browser.lower()
|
|
365
|
-
if browser in BROWSERS:
|
|
366
|
-
user_agent = agent.browser(browser)
|
|
367
|
-
|
|
368
|
-
accept_language = random.choice(FINGERPRINTS["accept_language"])
|
|
369
|
-
accept = random.choice(FINGERPRINTS["accept"])
|
|
370
|
-
platform = random.choice(FINGERPRINTS["platforms"])
|
|
371
|
-
|
|
372
|
-
# Generate sec-ch-ua based on the user agent
|
|
373
|
-
sec_ch_ua = ""
|
|
374
|
-
for browser_name in FINGERPRINTS["sec_ch_ua"]:
|
|
375
|
-
if browser_name in user_agent.lower():
|
|
376
|
-
version = random.randint(*BROWSERS[browser_name])
|
|
377
|
-
sec_ch_ua = FINGERPRINTS["sec_ch_ua"][browser_name].format(version, version)
|
|
378
|
-
break
|
|
379
|
-
|
|
380
|
-
ip = agent.rotate_ip()
|
|
381
|
-
fingerprint = {
|
|
382
|
-
"user_agent": user_agent,
|
|
383
|
-
"accept_language": accept_language,
|
|
384
|
-
"accept": accept,
|
|
385
|
-
"sec_ch_ua": sec_ch_ua,
|
|
386
|
-
"platform": platform,
|
|
387
|
-
"x-forwarded-for": ip,
|
|
388
|
-
"x-real-ip": ip,
|
|
389
|
-
"x-client-ip": ip,
|
|
390
|
-
"forwarded": f"for={ip};proto=https",
|
|
391
|
-
"x-forwarded-proto": "https",
|
|
392
|
-
"x-request-id": agent.random_id(8) if hasattr(agent, 'random_id') else ''.join(random.choices('0123456789abcdef', k=8)),
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
agent._update_stats(browser_type=browser)
|
|
396
|
-
return fingerprint
|
|
397
|
-
|
|
398
|
-
def refresh(self) -> None:
|
|
399
|
-
"""Refresh the agents with new ones! ๐"""
|
|
400
|
-
if self.thread_safe and self.lock:
|
|
401
|
-
with self.lock:
|
|
402
|
-
self.agents = self._generate_agents(100)
|
|
403
|
-
self._stats["total_generated"] += 100
|
|
404
|
-
else:
|
|
405
|
-
self.agents = self._generate_agents(100)
|
|
406
|
-
self._stats["total_generated"] += 100
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
def auto_refresh(self, interval_minutes: int = 30) -> None:
|
|
410
|
-
"""Set up automatic refreshing of agents pool! โฑ๏ธ
|
|
411
|
-
|
|
412
|
-
Args:
|
|
413
|
-
interval_minutes: Minutes between refreshes
|
|
414
|
-
"""
|
|
415
|
-
if self._refresh_timer:
|
|
416
|
-
self._refresh_timer.cancel()
|
|
417
|
-
|
|
418
|
-
def _refresh_task():
|
|
419
|
-
self.refresh()
|
|
420
|
-
self._refresh_timer = threading.Timer(interval_minutes * 60, _refresh_task)
|
|
421
|
-
self._refresh_timer.daemon = True
|
|
422
|
-
self._refresh_timer.start()
|
|
423
|
-
|
|
424
|
-
self._refresh_timer = threading.Timer(interval_minutes * 60, _refresh_task)
|
|
425
|
-
self._refresh_timer.daemon = True
|
|
426
|
-
self._refresh_timer.start()
|
|
427
|
-
|
|
428
|
-
def get_stats(self) -> Dict[str, Any]:
|
|
429
|
-
"""Get statistics about agent usage! ๐
|
|
430
|
-
|
|
431
|
-
Returns:
|
|
432
|
-
Dictionary with usage statistics
|
|
433
|
-
"""
|
|
434
|
-
stats_copy = self._stats.copy()
|
|
435
|
-
# Calculate top browser
|
|
436
|
-
top_browser = max(stats_copy["browser_usage"].items(), key=lambda x: x[1])[0] if stats_copy["browser_usage"] else None
|
|
437
|
-
stats_copy["top_browser"] = top_browser
|
|
438
|
-
|
|
439
|
-
# Calculate fake detection avoidance rate (just for fun)
|
|
440
|
-
stats_copy["avoidance_rate"] = min(99.9, 90 + (stats_copy["total_generated"] / 1000))
|
|
441
|
-
|
|
442
|
-
return stats_copy
|
|
443
|
-
|
|
444
|
-
def export_stats(self, filename: str) -> bool:
|
|
445
|
-
"""Export usage statistics to a file! ๐พ
|
|
446
|
-
Args:
|
|
447
|
-
filename: Path to export the stats
|
|
448
|
-
Returns:
|
|
449
|
-
True if export was successful, False otherwise
|
|
450
|
-
"""
|
|
451
|
-
try:
|
|
452
|
-
import json
|
|
453
|
-
with open(filename, 'w') as f:
|
|
454
|
-
json.dump(self.get_stats(), f, indent=2)
|
|
455
|
-
return True
|
|
456
|
-
except Exception:
|
|
457
|
-
return False
|
|
458
|
-
|
|
459
|
-
def random_crypto_ip(self) -> str:
|
|
460
|
-
"""Generate a random IP address for cryptography purposes."""
|
|
461
|
-
return ".".join(str(random.randint(0, 255)) for _ in range(4))
|
|
462
|
-
|
|
463
|
-
def _generate_ip_pool(self, count: int = 20) -> List[str]:
|
|
464
|
-
"""Generate a pool of random IP addresses."""
|
|
465
|
-
return [self.random_crypto_ip() for _ in range(count)]
|
|
466
|
-
|
|
467
|
-
def rotate_ip(self) -> str:
|
|
468
|
-
"""Rotate through the IP pool and return the next IP."""
|
|
469
|
-
if not self.ip_pool:
|
|
470
|
-
self.ip_pool = self._generate_ip_pool(20)
|
|
471
|
-
self._ip_index = 0
|
|
472
|
-
|
|
473
|
-
ip = self.ip_pool[self._ip_index]
|
|
474
|
-
self._ip_index = (self._ip_index + 1) % len(self.ip_pool)
|
|
475
|
-
return ip
|
|
476
|
-
|
|
477
|
-
# Backwards compatibility for older versions expecting _random_ip
|
|
478
|
-
def _random_ip(self) -> str:
|
|
479
|
-
return self.rotate_ip()
|
|
480
|
-
|
|
481
|
-
def random_id(self, length: int = 16) -> str:
|
|
482
|
-
"""Generate a random identifier string."""
|
|
483
|
-
return ''.join(random.choices('0123456789abcdef', k=length)).lower()
|
|
484
|
-
|
|
485
|
-
def wearable(self) -> str:
|
|
486
|
-
"""Get a wearable device agent! โ"""
|
|
487
|
-
wearable_type = random.choice(DEVICES['wearable'])
|
|
488
|
-
# Example user agent for wearables (simplified)
|
|
489
|
-
if 'Apple Watch' in wearable_type:
|
|
490
|
-
agent = f"Mozilla/5.0 (AppleWatch; CPU WatchOS like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/9.0 Mobile/13S344 Safari/602.1"
|
|
491
|
-
elif 'Samsung' in wearable_type:
|
|
492
|
-
agent = f"Mozilla/5.0 (Linux; Tizen 3.0; {wearable_type}) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/1.0"
|
|
493
|
-
elif 'Fitbit' in wearable_type:
|
|
494
|
-
agent = f"Mozilla/5.0 (Linux; {wearable_type}) AppleWebKit/537.36 (KHTML, like Gecko)"
|
|
495
|
-
elif 'Garmin' in wearable_type:
|
|
496
|
-
agent = f"Mozilla/5.0 (Linux; {wearable_type}) AppleWebKit/537.36 (KHTML, like Gecko)"
|
|
497
|
-
else:
|
|
498
|
-
agent = self.random()
|
|
499
|
-
self._update_stats(device_type="wearable")
|
|
500
|
-
return agent
|
|
501
|
-
|
|
502
|
-
def set_proxy_pool(self, proxies: List[str]):
|
|
503
|
-
"""Set a pool of proxies for rotation."""
|
|
504
|
-
self._proxy_pool = proxies
|
|
505
|
-
self._proxy_index = 0
|
|
506
|
-
|
|
507
|
-
def rotate_proxy(self) -> Optional[str]:
|
|
508
|
-
"""Rotate through the proxy pool and return the next proxy."""
|
|
509
|
-
if not hasattr(self, '_proxy_pool') or not self._proxy_pool:
|
|
510
|
-
return None
|
|
511
|
-
proxy = self._proxy_pool[self._proxy_index]
|
|
512
|
-
self._proxy_index = (self._proxy_index + 1) % len(self._proxy_pool)
|
|
513
|
-
return proxy
|
|
514
|
-
|
|
515
|
-
def add_to_blacklist(self, agent: str):
|
|
516
|
-
"""Add a user agent to the blacklist."""
|
|
517
|
-
if not hasattr(self, '_blacklist'):
|
|
518
|
-
self._blacklist = set()
|
|
519
|
-
self._blacklist.add(agent)
|
|
520
|
-
|
|
521
|
-
def add_to_whitelist(self, agent: str):
|
|
522
|
-
"""Add a user agent to the whitelist."""
|
|
523
|
-
if not hasattr(self, '_whitelist'):
|
|
524
|
-
self._whitelist = set()
|
|
525
|
-
self._whitelist.add(agent)
|
|
526
|
-
|
|
527
|
-
def _add_to_history(self, agent: str):
|
|
528
|
-
if not hasattr(self, '_history'):
|
|
529
|
-
self._history = []
|
|
530
|
-
self._history.append(agent)
|
|
531
|
-
if len(self._history) > 50:
|
|
532
|
-
self._history.pop(0)
|
|
533
|
-
|
|
534
|
-
def get_history(self) -> List[str]:
|
|
535
|
-
"""Get the last 50 user agents served."""
|
|
536
|
-
return getattr(self, '_history', [])
|
|
537
|
-
|
|
538
|
-
def validate_agent(self, agent: str) -> bool:
|
|
539
|
-
"""Validate if a user agent string is realistic (basic check)."""
|
|
540
|
-
return agent.startswith("Mozilla/5.0") and any(b in agent for b in BROWSERS.keys())
|
|
541
|
-
|
|
542
|
-
if __name__ == "__main__":
|
|
543
|
-
# Test it out! ๐งช
|
|
544
|
-
agent = LitAgent()
|
|
545
|
-
print("Random:", agent.random())
|
|
546
|
-
print("Chrome:", agent.chrome())
|
|
547
|
-
print("Firefox:", agent.firefox())
|
|
548
|
-
print("Safari:", agent.safari())
|
|
549
|
-
print("Mobile:", agent.mobile())
|
|
550
|
-
print("Desktop:", agent.desktop())
|
|
551
|
-
print("Tablet:", agent.tablet())
|
|
552
|
-
print("Smart TV:", agent.smart_tv())
|
|
553
|
-
print("Gaming:", agent.gaming())
|
|
554
|
-
print("Wearable:", agent.wearable())
|
|
555
|
-
|
|
556
|
-
# Test custom agent
|
|
557
|
-
print("Custom:", agent.custom(browser="chrome", os="windows", os_version="10.0"))
|
|
558
|
-
|
|
559
|
-
# Test fingerprinting
|
|
560
|
-
print("Fingerprint:", agent.generate_fingerprint("chrome"))
|
|
561
|
-
|
|
562
|
-
# Test proxy rotation
|
|
563
|
-
agent.set_proxy_pool(["http://proxy1.com", "http://proxy2.com"])
|
|
564
|
-
print("Proxy 1:", agent.rotate_proxy())
|
|
565
|
-
print("Proxy 2:", agent.rotate_proxy())
|
|
566
|
-
|
|
567
|
-
# Test blacklist/whitelist
|
|
568
|
-
agent.add_to_blacklist("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36")
|
|
569
|
-
agent.add_to_whitelist("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
|
|
570
|
-
print("Blacklisted:", agent.random())
|
|
571
|
-
print("Whitelisted:", agent.random())
|
|
572
|
-
|
|
573
|
-
# Test agent history
|
|
574
|
-
for _ in range(55):
|
|
575
|
-
agent.random()
|
|
576
|
-
print("History:", agent.get_history())
|
|
577
|
-
|
|
578
|
-
# Test agent validation
|
|
579
|
-
print("Valid agent:", agent.validate_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"))
|
|
580
|
-
print("Invalid agent:", agent.validate_agent("InvalidUserAgentString"))
|
|
581
|
-
ip = agent.rotate_ip()
|
|
1
|
+
"""Main LitAgent implementation."""
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
import threading
|
|
5
|
+
from typing import Any, Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
from webscout.litagent.constants import BROWSERS, DEVICES, FINGERPRINTS, OS_VERSIONS
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class LitAgent:
|
|
11
|
+
"""A lit user agent generator that keeps it fresh! ๐"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, thread_safe: bool = False):
|
|
14
|
+
"""Initialize LitAgent with style! ๐ซ
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
thread_safe (bool): Enable thread-safety for multi-threaded applications
|
|
18
|
+
"""
|
|
19
|
+
self.agents = self._generate_agents(100) # Keep 100 agents in memory
|
|
20
|
+
self.thread_safe = thread_safe
|
|
21
|
+
self.lock = threading.RLock() if thread_safe else None
|
|
22
|
+
self.ip_pool = self._generate_ip_pool(20)
|
|
23
|
+
self._ip_index = 0
|
|
24
|
+
self._refresh_timer = None
|
|
25
|
+
self._stats = {
|
|
26
|
+
"total_generated": 100,
|
|
27
|
+
"requests_served": 0,
|
|
28
|
+
"browser_usage": {browser: 0 for browser in BROWSERS.keys()},
|
|
29
|
+
"device_usage": {device: 0 for device in DEVICES.keys()}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
def _generate_agents(self, count: int) -> List[str]:
|
|
33
|
+
"""Generate some lit user agents! ๐ ๏ธ"""
|
|
34
|
+
agents = []
|
|
35
|
+
for _ in range(count):
|
|
36
|
+
browser = random.choice(list(BROWSERS.keys()))
|
|
37
|
+
version = random.randint(*BROWSERS[browser])
|
|
38
|
+
|
|
39
|
+
if browser in ['chrome', 'firefox', 'edge', 'opera', 'brave', 'vivaldi']:
|
|
40
|
+
os_type = random.choice(['windows', 'mac', 'linux'])
|
|
41
|
+
os_ver = random.choice(OS_VERSIONS[os_type])
|
|
42
|
+
|
|
43
|
+
if os_type == 'windows':
|
|
44
|
+
platform = f"Windows NT {os_ver}"
|
|
45
|
+
elif os_type == 'mac':
|
|
46
|
+
platform = f"Macintosh; Intel Mac OS X {os_ver}"
|
|
47
|
+
else:
|
|
48
|
+
platform = f"X11; Linux {os_ver}"
|
|
49
|
+
|
|
50
|
+
agent = f"Mozilla/5.0 ({platform}) AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
51
|
+
if browser == 'chrome':
|
|
52
|
+
agent += f"Chrome/{version}.0.0.0 Safari/537.36"
|
|
53
|
+
elif browser == 'firefox':
|
|
54
|
+
agent += f"Firefox/{version}.0"
|
|
55
|
+
elif browser == 'edge':
|
|
56
|
+
agent += f"Edge/{version}.0.0.0"
|
|
57
|
+
elif browser == 'opera':
|
|
58
|
+
agent += f"OPR/{version}.0.0.0"
|
|
59
|
+
elif browser == 'brave':
|
|
60
|
+
agent += f"Chrome/{version}.0.0.0 Safari/537.36 Brave/{version}.0.0.0"
|
|
61
|
+
elif browser == 'vivaldi':
|
|
62
|
+
agent += f"Chrome/{version}.0.0.0 Safari/537.36 Vivaldi/{version}.0.{random.randint(1000, 9999)}"
|
|
63
|
+
|
|
64
|
+
elif browser == 'safari':
|
|
65
|
+
device = random.choice(['mac', 'ios'])
|
|
66
|
+
if device == 'mac':
|
|
67
|
+
ver = random.choice(OS_VERSIONS['mac'])
|
|
68
|
+
agent = f"Mozilla/5.0 (Macintosh; Intel Mac OS X {ver}) "
|
|
69
|
+
else:
|
|
70
|
+
ver = random.choice(OS_VERSIONS['ios'])
|
|
71
|
+
device = random.choice(['iPhone', 'iPad'])
|
|
72
|
+
agent = f"Mozilla/5.0 ({device}; CPU OS {ver} like Mac OS X) "
|
|
73
|
+
agent += f"AppleWebKit/{version}.1.15 (KHTML, like Gecko) Version/{version//100}.0 Safari/{version}.1.15"
|
|
74
|
+
|
|
75
|
+
agents.append(agent)
|
|
76
|
+
|
|
77
|
+
return list(set(agents)) # Remove any duplicates
|
|
78
|
+
|
|
79
|
+
def _update_stats(self, browser_type=None, device_type=None):
|
|
80
|
+
"""Update usage statistics."""
|
|
81
|
+
if self.thread_safe and self.lock:
|
|
82
|
+
with self.lock:
|
|
83
|
+
self._stats["requests_served"] += 1
|
|
84
|
+
if browser_type:
|
|
85
|
+
self._stats["browser_usage"][browser_type] = self._stats["browser_usage"].get(browser_type, 0) + 1
|
|
86
|
+
if device_type:
|
|
87
|
+
self._stats["device_usage"][device_type] = self._stats["device_usage"].get(device_type, 0) + 1
|
|
88
|
+
else:
|
|
89
|
+
self._stats["requests_served"] += 1
|
|
90
|
+
if browser_type:
|
|
91
|
+
self._stats["browser_usage"][browser_type] = self._stats["browser_usage"].get(browser_type, 0) + 1
|
|
92
|
+
if device_type:
|
|
93
|
+
self._stats["device_usage"][device_type] = self._stats["device_usage"].get(device_type, 0) + 1
|
|
94
|
+
|
|
95
|
+
def random(self) -> str:
|
|
96
|
+
"""Get a random user agent! ๐ฒ (with blacklist/whitelist support)"""
|
|
97
|
+
if hasattr(self, '_whitelist') and self._whitelist:
|
|
98
|
+
pool = list(self._whitelist)
|
|
99
|
+
else:
|
|
100
|
+
pool = [a for a in self.agents if not hasattr(self, '_blacklist') or a not in self._blacklist]
|
|
101
|
+
if not pool:
|
|
102
|
+
pool = self.agents
|
|
103
|
+
if self.thread_safe and self.lock:
|
|
104
|
+
with self.lock:
|
|
105
|
+
agent = random.choice(pool)
|
|
106
|
+
self._update_stats()
|
|
107
|
+
self._add_to_history(agent)
|
|
108
|
+
return agent
|
|
109
|
+
else:
|
|
110
|
+
agent = random.choice(pool)
|
|
111
|
+
self._update_stats()
|
|
112
|
+
self._add_to_history(agent)
|
|
113
|
+
return agent
|
|
114
|
+
|
|
115
|
+
def browser(self, name: str) -> str:
|
|
116
|
+
"""Get a browser-specific agent! ๐"""
|
|
117
|
+
name = name.lower()
|
|
118
|
+
if name not in BROWSERS:
|
|
119
|
+
return self.random()
|
|
120
|
+
|
|
121
|
+
if self.thread_safe and self.lock:
|
|
122
|
+
with self.lock:
|
|
123
|
+
agents = [a for a in self.agents if name in a.lower()]
|
|
124
|
+
agent = random.choice(agents) if agents else self.random()
|
|
125
|
+
self._update_stats(browser_type=name)
|
|
126
|
+
return agent
|
|
127
|
+
else:
|
|
128
|
+
agents = [a for a in self.agents if name in a.lower()]
|
|
129
|
+
agent = random.choice(agents) if agents else self.random()
|
|
130
|
+
self._update_stats(browser_type=name)
|
|
131
|
+
return agent
|
|
132
|
+
|
|
133
|
+
def mobile(self) -> str:
|
|
134
|
+
"""Get a mobile device agent! ๐ฑ"""
|
|
135
|
+
if self.thread_safe and self.lock:
|
|
136
|
+
with self.lock:
|
|
137
|
+
agents = [a for a in self.agents if any(d in a for d in DEVICES['mobile'])]
|
|
138
|
+
agent = random.choice(agents) if agents else self.random()
|
|
139
|
+
self._update_stats(device_type="mobile")
|
|
140
|
+
return agent
|
|
141
|
+
else:
|
|
142
|
+
agents = [a for a in self.agents if any(d in a for d in DEVICES['mobile'])]
|
|
143
|
+
agent = random.choice(agents) if agents else self.random()
|
|
144
|
+
self._update_stats(device_type="mobile")
|
|
145
|
+
return agent
|
|
146
|
+
|
|
147
|
+
def desktop(self) -> str:
|
|
148
|
+
"""Get a desktop agent! ๐ป"""
|
|
149
|
+
if self.thread_safe and self.lock:
|
|
150
|
+
with self.lock:
|
|
151
|
+
agents = [a for a in self.agents if 'Windows' in a or 'Macintosh' in a or 'Linux' in a]
|
|
152
|
+
agent = random.choice(agents) if agents else self.random()
|
|
153
|
+
self._update_stats(device_type="desktop")
|
|
154
|
+
return agent
|
|
155
|
+
else:
|
|
156
|
+
agents = [a for a in self.agents if 'Windows' in a or 'Macintosh' in a or 'Linux' in a]
|
|
157
|
+
agent = random.choice(agents) if agents else self.random()
|
|
158
|
+
self._update_stats(device_type="desktop")
|
|
159
|
+
return agent
|
|
160
|
+
|
|
161
|
+
def tablet(self) -> str:
|
|
162
|
+
"""Get a tablet agent! ๐ฑ"""
|
|
163
|
+
if self.thread_safe and self.lock:
|
|
164
|
+
with self.lock:
|
|
165
|
+
# Focus on iPad and Android tablets
|
|
166
|
+
agents = [a for a in self.agents if 'iPad' in a or 'Android' in a and 'Mobile' not in a]
|
|
167
|
+
agent = random.choice(agents) if agents else self.random()
|
|
168
|
+
self._update_stats(device_type="tablet")
|
|
169
|
+
return agent
|
|
170
|
+
else:
|
|
171
|
+
agents = [a for a in self.agents if 'iPad' in a or 'Android' in a and 'Mobile' not in a]
|
|
172
|
+
agent = random.choice(agents) if agents else self.random()
|
|
173
|
+
self._update_stats(device_type="tablet")
|
|
174
|
+
return agent
|
|
175
|
+
|
|
176
|
+
def smart_tv(self) -> str:
|
|
177
|
+
"""Get a Smart TV agent! ๐บ"""
|
|
178
|
+
# Create a TV-specific agent since they may not be in our standard pool
|
|
179
|
+
tv_type = random.choice(DEVICES['tv'])
|
|
180
|
+
if 'Samsung' in tv_type:
|
|
181
|
+
agent = f"Mozilla/5.0 (SMART-TV; SAMSUNG; {tv_type}; Tizen 5.5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.38 Safari/537.36"
|
|
182
|
+
elif 'LG' in tv_type:
|
|
183
|
+
agent = f"Mozilla/5.0 (Web0S; {tv_type}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36"
|
|
184
|
+
elif 'Android' in tv_type:
|
|
185
|
+
agent = f"Mozilla/5.0 (Linux; Android 9; {tv_type}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36"
|
|
186
|
+
elif 'Apple' in tv_type:
|
|
187
|
+
agent = "Mozilla/5.0 (AppleTV; CPU like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148"
|
|
188
|
+
else:
|
|
189
|
+
agent = f"Mozilla/5.0 (Linux; {tv_type}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36"
|
|
190
|
+
|
|
191
|
+
self._update_stats(device_type="tv")
|
|
192
|
+
return agent
|
|
193
|
+
|
|
194
|
+
def gaming(self) -> str:
|
|
195
|
+
"""Get a gaming console agent! ๐ฎ"""
|
|
196
|
+
console_type = random.choice(DEVICES['console'])
|
|
197
|
+
if 'PlayStation' in console_type:
|
|
198
|
+
agent = f"Mozilla/5.0 ({console_type}/5.0) AppleWebKit/601.2 (KHTML, like Gecko)"
|
|
199
|
+
elif 'Xbox' in console_type:
|
|
200
|
+
agent = f"Mozilla/5.0 ({console_type}; Xbox; Xbox One) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19041"
|
|
201
|
+
elif 'Nintendo' in console_type:
|
|
202
|
+
agent = f"Mozilla/5.0 (Nintendo Switch; {console_type}) AppleWebKit/601.6 (KHTML, like Gecko) NintendoBrowser/5.1.0.13343"
|
|
203
|
+
else:
|
|
204
|
+
agent = self.random()
|
|
205
|
+
|
|
206
|
+
self._update_stats(device_type="console")
|
|
207
|
+
return agent
|
|
208
|
+
|
|
209
|
+
def chrome(self) -> str:
|
|
210
|
+
"""Get a Chrome agent! ๐"""
|
|
211
|
+
return self.browser('chrome')
|
|
212
|
+
|
|
213
|
+
def firefox(self) -> str:
|
|
214
|
+
"""Get a Firefox agent! ๐ฆ"""
|
|
215
|
+
return self.browser('firefox')
|
|
216
|
+
|
|
217
|
+
def safari(self) -> str:
|
|
218
|
+
"""Get a Safari agent! ๐งญ"""
|
|
219
|
+
return self.browser('safari')
|
|
220
|
+
|
|
221
|
+
def edge(self) -> str:
|
|
222
|
+
"""Get an Edge agent! ๐"""
|
|
223
|
+
return self.browser('edge')
|
|
224
|
+
|
|
225
|
+
def opera(self) -> str:
|
|
226
|
+
"""Get an Opera agent! ๐ญ"""
|
|
227
|
+
return self.browser('opera')
|
|
228
|
+
|
|
229
|
+
def brave(self) -> str:
|
|
230
|
+
"""Get a Brave agent! ๐ฆ"""
|
|
231
|
+
return self.browser('brave')
|
|
232
|
+
|
|
233
|
+
def vivaldi(self) -> str:
|
|
234
|
+
"""Get a Vivaldi agent! ๐จ"""
|
|
235
|
+
return self.browser('vivaldi')
|
|
236
|
+
|
|
237
|
+
# OS-specific agents
|
|
238
|
+
def windows(self) -> str:
|
|
239
|
+
"""Get a Windows agent! ๐ช"""
|
|
240
|
+
agents = [a for a in self.agents if 'Windows' in a]
|
|
241
|
+
agent = random.choice(agents) if agents else self.random()
|
|
242
|
+
self._update_stats()
|
|
243
|
+
return agent
|
|
244
|
+
|
|
245
|
+
def macos(self) -> str:
|
|
246
|
+
"""Get a macOS agent! ๐"""
|
|
247
|
+
agents = [a for a in self.agents if 'Macintosh' in a]
|
|
248
|
+
agent = random.choice(agents) if agents else self.random()
|
|
249
|
+
self._update_stats()
|
|
250
|
+
return agent
|
|
251
|
+
|
|
252
|
+
def linux(self) -> str:
|
|
253
|
+
"""Get a Linux agent! ๐ง"""
|
|
254
|
+
agents = [a for a in self.agents if 'Linux' in a and 'Android' not in a]
|
|
255
|
+
agent = random.choice(agents) if agents else self.random()
|
|
256
|
+
self._update_stats()
|
|
257
|
+
return agent
|
|
258
|
+
|
|
259
|
+
def android(self) -> str:
|
|
260
|
+
"""Get an Android agent! ๐ค"""
|
|
261
|
+
agents = [a for a in self.agents if 'Android' in a]
|
|
262
|
+
agent = random.choice(agents) if agents else self.random()
|
|
263
|
+
self._update_stats()
|
|
264
|
+
return agent
|
|
265
|
+
|
|
266
|
+
def ios(self) -> str:
|
|
267
|
+
"""Get an iOS agent! ๐ฑ"""
|
|
268
|
+
agents = [a for a in self.agents if 'iPhone' in a or 'iPad' in a]
|
|
269
|
+
agent = random.choice(agents) if agents else self.random()
|
|
270
|
+
self._update_stats()
|
|
271
|
+
return agent
|
|
272
|
+
|
|
273
|
+
def custom(self, browser: str, version: Optional[str] = None,
|
|
274
|
+
os: Optional[str] = None, os_version: Optional[str] = None,
|
|
275
|
+
device_type: Optional[str] = None) -> str:
|
|
276
|
+
"""Generate a custom user agent with specified parameters! ๐ ๏ธ
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
browser: Browser name (chrome, firefox, safari, edge, opera)
|
|
280
|
+
version: Browser version (optional)
|
|
281
|
+
os: Operating system (windows, mac, linux, android, ios)
|
|
282
|
+
os_version: OS version (optional)
|
|
283
|
+
device_type: Device type (desktop, mobile, tablet)
|
|
284
|
+
Returns:
|
|
285
|
+
Customized user agent string
|
|
286
|
+
"""
|
|
287
|
+
browser = browser.lower() if browser else 'chrome'
|
|
288
|
+
if browser not in BROWSERS:
|
|
289
|
+
browser = 'chrome'
|
|
290
|
+
|
|
291
|
+
if version:
|
|
292
|
+
try:
|
|
293
|
+
version_num = int(version.split('.')[0])
|
|
294
|
+
except (ValueError, IndexError):
|
|
295
|
+
version_num = random.randint(*BROWSERS[browser])
|
|
296
|
+
else:
|
|
297
|
+
version_num = random.randint(*BROWSERS[browser])
|
|
298
|
+
|
|
299
|
+
os = os.lower() if os else random.choice(['windows', 'mac', 'linux'])
|
|
300
|
+
if os not in OS_VERSIONS:
|
|
301
|
+
os = 'windows'
|
|
302
|
+
|
|
303
|
+
os_ver = os_version or random.choice(OS_VERSIONS[os])
|
|
304
|
+
|
|
305
|
+
device_type = device_type.lower() if device_type else 'desktop'
|
|
306
|
+
|
|
307
|
+
# Build the user agent
|
|
308
|
+
if os == 'windows':
|
|
309
|
+
platform = f"Windows NT {os_ver}"
|
|
310
|
+
elif os == 'mac':
|
|
311
|
+
platform = f"Macintosh; Intel Mac OS X {os_ver}"
|
|
312
|
+
elif os == 'linux':
|
|
313
|
+
platform = f"X11; Linux {OS_VERSIONS['linux'][0]}"
|
|
314
|
+
elif os == 'android':
|
|
315
|
+
platform = f"Linux; Android {os_ver}; {random.choice(DEVICES['mobile'])}"
|
|
316
|
+
elif os == 'ios':
|
|
317
|
+
device = 'iPhone' if device_type == 'mobile' else 'iPad'
|
|
318
|
+
platform = f"{device}; CPU OS {os_ver} like Mac OS X"
|
|
319
|
+
else:
|
|
320
|
+
platform = "Windows NT 10.0" # Default fallback
|
|
321
|
+
|
|
322
|
+
agent = f"Mozilla/5.0 ({platform}) AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
323
|
+
|
|
324
|
+
if browser == 'chrome':
|
|
325
|
+
agent += f"Chrome/{version_num}.0.0.0 Safari/537.36"
|
|
326
|
+
elif browser == 'firefox':
|
|
327
|
+
agent += f"Firefox/{version_num}.0"
|
|
328
|
+
elif browser == 'safari':
|
|
329
|
+
safari_ver = random.randint(*BROWSERS['safari'])
|
|
330
|
+
agent += f"Version/{version_num}.0 Safari/{safari_ver}.0"
|
|
331
|
+
elif browser == 'edge':
|
|
332
|
+
agent += f"Chrome/{version_num}.0.0.0 Safari/537.36 Edg/{version_num}.0.0.0"
|
|
333
|
+
elif browser == 'opera':
|
|
334
|
+
agent += f"Chrome/{version_num}.0.0.0 Safari/537.36 OPR/{version_num}.0.0.0"
|
|
335
|
+
elif browser == 'brave':
|
|
336
|
+
agent += f"Chrome/{version_num}.0.0.0 Safari/537.36 Brave/{version_num}.1.0"
|
|
337
|
+
|
|
338
|
+
self._update_stats(browser_type=browser, device_type=device_type)
|
|
339
|
+
return agent
|
|
340
|
+
|
|
341
|
+
@staticmethod
|
|
342
|
+
def generate_fingerprint(browser: Optional[str] = None) -> Dict[str, str]:
|
|
343
|
+
"""
|
|
344
|
+
Generate a consistent browser fingerprint for anti-fingerprinting purposes.
|
|
345
|
+
|
|
346
|
+
This method creates a dictionary of HTTP headers and related values that simulate
|
|
347
|
+
a realistic browser fingerprint, including user agent, accept headers, platform,
|
|
348
|
+
sec-ch-ua, and various IP-related headers. Optionally, a specific browser type
|
|
349
|
+
can be requested.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
browser (Optional[str]): The browser name to generate the fingerprint for.
|
|
353
|
+
If not specified, a random browser is used.
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
Dict[str, str]: A dictionary containing fingerprinting headers and values.
|
|
357
|
+
"""
|
|
358
|
+
# Get a random user agent using the random() method
|
|
359
|
+
agent = LitAgent()
|
|
360
|
+
user_agent = agent.random()
|
|
361
|
+
|
|
362
|
+
# If browser is specified, try to get a matching one
|
|
363
|
+
if browser:
|
|
364
|
+
browser = browser.lower()
|
|
365
|
+
if browser in BROWSERS:
|
|
366
|
+
user_agent = agent.browser(browser)
|
|
367
|
+
|
|
368
|
+
accept_language = random.choice(FINGERPRINTS["accept_language"])
|
|
369
|
+
accept = random.choice(FINGERPRINTS["accept"])
|
|
370
|
+
platform = random.choice(FINGERPRINTS["platforms"])
|
|
371
|
+
|
|
372
|
+
# Generate sec-ch-ua based on the user agent
|
|
373
|
+
sec_ch_ua = ""
|
|
374
|
+
for browser_name in FINGERPRINTS["sec_ch_ua"]:
|
|
375
|
+
if browser_name in user_agent.lower():
|
|
376
|
+
version = random.randint(*BROWSERS[browser_name])
|
|
377
|
+
sec_ch_ua = FINGERPRINTS["sec_ch_ua"][browser_name].format(version, version)
|
|
378
|
+
break
|
|
379
|
+
|
|
380
|
+
ip = agent.rotate_ip()
|
|
381
|
+
fingerprint = {
|
|
382
|
+
"user_agent": user_agent,
|
|
383
|
+
"accept_language": accept_language,
|
|
384
|
+
"accept": accept,
|
|
385
|
+
"sec_ch_ua": sec_ch_ua,
|
|
386
|
+
"platform": platform,
|
|
387
|
+
"x-forwarded-for": ip,
|
|
388
|
+
"x-real-ip": ip,
|
|
389
|
+
"x-client-ip": ip,
|
|
390
|
+
"forwarded": f"for={ip};proto=https",
|
|
391
|
+
"x-forwarded-proto": "https",
|
|
392
|
+
"x-request-id": agent.random_id(8) if hasattr(agent, 'random_id') else ''.join(random.choices('0123456789abcdef', k=8)),
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
agent._update_stats(browser_type=browser)
|
|
396
|
+
return fingerprint
|
|
397
|
+
|
|
398
|
+
def refresh(self) -> None:
|
|
399
|
+
"""Refresh the agents with new ones! ๐"""
|
|
400
|
+
if self.thread_safe and self.lock:
|
|
401
|
+
with self.lock:
|
|
402
|
+
self.agents = self._generate_agents(100)
|
|
403
|
+
self._stats["total_generated"] += 100
|
|
404
|
+
else:
|
|
405
|
+
self.agents = self._generate_agents(100)
|
|
406
|
+
self._stats["total_generated"] += 100
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def auto_refresh(self, interval_minutes: int = 30) -> None:
|
|
410
|
+
"""Set up automatic refreshing of agents pool! โฑ๏ธ
|
|
411
|
+
|
|
412
|
+
Args:
|
|
413
|
+
interval_minutes: Minutes between refreshes
|
|
414
|
+
"""
|
|
415
|
+
if self._refresh_timer:
|
|
416
|
+
self._refresh_timer.cancel()
|
|
417
|
+
|
|
418
|
+
def _refresh_task():
|
|
419
|
+
self.refresh()
|
|
420
|
+
self._refresh_timer = threading.Timer(interval_minutes * 60, _refresh_task)
|
|
421
|
+
self._refresh_timer.daemon = True
|
|
422
|
+
self._refresh_timer.start()
|
|
423
|
+
|
|
424
|
+
self._refresh_timer = threading.Timer(interval_minutes * 60, _refresh_task)
|
|
425
|
+
self._refresh_timer.daemon = True
|
|
426
|
+
self._refresh_timer.start()
|
|
427
|
+
|
|
428
|
+
def get_stats(self) -> Dict[str, Any]:
|
|
429
|
+
"""Get statistics about agent usage! ๐
|
|
430
|
+
|
|
431
|
+
Returns:
|
|
432
|
+
Dictionary with usage statistics
|
|
433
|
+
"""
|
|
434
|
+
stats_copy = self._stats.copy()
|
|
435
|
+
# Calculate top browser
|
|
436
|
+
top_browser = max(stats_copy["browser_usage"].items(), key=lambda x: x[1])[0] if stats_copy["browser_usage"] else None
|
|
437
|
+
stats_copy["top_browser"] = top_browser
|
|
438
|
+
|
|
439
|
+
# Calculate fake detection avoidance rate (just for fun)
|
|
440
|
+
stats_copy["avoidance_rate"] = min(99.9, 90 + (stats_copy["total_generated"] / 1000))
|
|
441
|
+
|
|
442
|
+
return stats_copy
|
|
443
|
+
|
|
444
|
+
def export_stats(self, filename: str) -> bool:
|
|
445
|
+
"""Export usage statistics to a file! ๐พ
|
|
446
|
+
Args:
|
|
447
|
+
filename: Path to export the stats
|
|
448
|
+
Returns:
|
|
449
|
+
True if export was successful, False otherwise
|
|
450
|
+
"""
|
|
451
|
+
try:
|
|
452
|
+
import json
|
|
453
|
+
with open(filename, 'w') as f:
|
|
454
|
+
json.dump(self.get_stats(), f, indent=2)
|
|
455
|
+
return True
|
|
456
|
+
except Exception:
|
|
457
|
+
return False
|
|
458
|
+
|
|
459
|
+
def random_crypto_ip(self) -> str:
|
|
460
|
+
"""Generate a random IP address for cryptography purposes."""
|
|
461
|
+
return ".".join(str(random.randint(0, 255)) for _ in range(4))
|
|
462
|
+
|
|
463
|
+
def _generate_ip_pool(self, count: int = 20) -> List[str]:
|
|
464
|
+
"""Generate a pool of random IP addresses."""
|
|
465
|
+
return [self.random_crypto_ip() for _ in range(count)]
|
|
466
|
+
|
|
467
|
+
def rotate_ip(self) -> str:
|
|
468
|
+
"""Rotate through the IP pool and return the next IP."""
|
|
469
|
+
if not self.ip_pool:
|
|
470
|
+
self.ip_pool = self._generate_ip_pool(20)
|
|
471
|
+
self._ip_index = 0
|
|
472
|
+
|
|
473
|
+
ip = self.ip_pool[self._ip_index]
|
|
474
|
+
self._ip_index = (self._ip_index + 1) % len(self.ip_pool)
|
|
475
|
+
return ip
|
|
476
|
+
|
|
477
|
+
# Backwards compatibility for older versions expecting _random_ip
|
|
478
|
+
def _random_ip(self) -> str:
|
|
479
|
+
return self.rotate_ip()
|
|
480
|
+
|
|
481
|
+
def random_id(self, length: int = 16) -> str:
|
|
482
|
+
"""Generate a random identifier string."""
|
|
483
|
+
return ''.join(random.choices('0123456789abcdef', k=length)).lower()
|
|
484
|
+
|
|
485
|
+
def wearable(self) -> str:
|
|
486
|
+
"""Get a wearable device agent! โ"""
|
|
487
|
+
wearable_type = random.choice(DEVICES['wearable'])
|
|
488
|
+
# Example user agent for wearables (simplified)
|
|
489
|
+
if 'Apple Watch' in wearable_type:
|
|
490
|
+
agent = f"Mozilla/5.0 (AppleWatch; CPU WatchOS like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/9.0 Mobile/13S344 Safari/602.1"
|
|
491
|
+
elif 'Samsung' in wearable_type:
|
|
492
|
+
agent = f"Mozilla/5.0 (Linux; Tizen 3.0; {wearable_type}) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/1.0"
|
|
493
|
+
elif 'Fitbit' in wearable_type:
|
|
494
|
+
agent = f"Mozilla/5.0 (Linux; {wearable_type}) AppleWebKit/537.36 (KHTML, like Gecko)"
|
|
495
|
+
elif 'Garmin' in wearable_type:
|
|
496
|
+
agent = f"Mozilla/5.0 (Linux; {wearable_type}) AppleWebKit/537.36 (KHTML, like Gecko)"
|
|
497
|
+
else:
|
|
498
|
+
agent = self.random()
|
|
499
|
+
self._update_stats(device_type="wearable")
|
|
500
|
+
return agent
|
|
501
|
+
|
|
502
|
+
def set_proxy_pool(self, proxies: List[str]):
|
|
503
|
+
"""Set a pool of proxies for rotation."""
|
|
504
|
+
self._proxy_pool = proxies
|
|
505
|
+
self._proxy_index = 0
|
|
506
|
+
|
|
507
|
+
def rotate_proxy(self) -> Optional[str]:
|
|
508
|
+
"""Rotate through the proxy pool and return the next proxy."""
|
|
509
|
+
if not hasattr(self, '_proxy_pool') or not self._proxy_pool:
|
|
510
|
+
return None
|
|
511
|
+
proxy = self._proxy_pool[self._proxy_index]
|
|
512
|
+
self._proxy_index = (self._proxy_index + 1) % len(self._proxy_pool)
|
|
513
|
+
return proxy
|
|
514
|
+
|
|
515
|
+
def add_to_blacklist(self, agent: str):
|
|
516
|
+
"""Add a user agent to the blacklist."""
|
|
517
|
+
if not hasattr(self, '_blacklist'):
|
|
518
|
+
self._blacklist = set()
|
|
519
|
+
self._blacklist.add(agent)
|
|
520
|
+
|
|
521
|
+
def add_to_whitelist(self, agent: str):
|
|
522
|
+
"""Add a user agent to the whitelist."""
|
|
523
|
+
if not hasattr(self, '_whitelist'):
|
|
524
|
+
self._whitelist = set()
|
|
525
|
+
self._whitelist.add(agent)
|
|
526
|
+
|
|
527
|
+
def _add_to_history(self, agent: str):
|
|
528
|
+
if not hasattr(self, '_history'):
|
|
529
|
+
self._history = []
|
|
530
|
+
self._history.append(agent)
|
|
531
|
+
if len(self._history) > 50:
|
|
532
|
+
self._history.pop(0)
|
|
533
|
+
|
|
534
|
+
def get_history(self) -> List[str]:
|
|
535
|
+
"""Get the last 50 user agents served."""
|
|
536
|
+
return getattr(self, '_history', [])
|
|
537
|
+
|
|
538
|
+
def validate_agent(self, agent: str) -> bool:
|
|
539
|
+
"""Validate if a user agent string is realistic (basic check)."""
|
|
540
|
+
return agent.startswith("Mozilla/5.0") and any(b in agent for b in BROWSERS.keys())
|
|
541
|
+
|
|
542
|
+
if __name__ == "__main__":
|
|
543
|
+
# Test it out! ๐งช
|
|
544
|
+
agent = LitAgent()
|
|
545
|
+
print("Random:", agent.random())
|
|
546
|
+
print("Chrome:", agent.chrome())
|
|
547
|
+
print("Firefox:", agent.firefox())
|
|
548
|
+
print("Safari:", agent.safari())
|
|
549
|
+
print("Mobile:", agent.mobile())
|
|
550
|
+
print("Desktop:", agent.desktop())
|
|
551
|
+
print("Tablet:", agent.tablet())
|
|
552
|
+
print("Smart TV:", agent.smart_tv())
|
|
553
|
+
print("Gaming:", agent.gaming())
|
|
554
|
+
print("Wearable:", agent.wearable())
|
|
555
|
+
|
|
556
|
+
# Test custom agent
|
|
557
|
+
print("Custom:", agent.custom(browser="chrome", os="windows", os_version="10.0"))
|
|
558
|
+
|
|
559
|
+
# Test fingerprinting
|
|
560
|
+
print("Fingerprint:", agent.generate_fingerprint("chrome"))
|
|
561
|
+
|
|
562
|
+
# Test proxy rotation
|
|
563
|
+
agent.set_proxy_pool(["http://proxy1.com", "http://proxy2.com"])
|
|
564
|
+
print("Proxy 1:", agent.rotate_proxy())
|
|
565
|
+
print("Proxy 2:", agent.rotate_proxy())
|
|
566
|
+
|
|
567
|
+
# Test blacklist/whitelist
|
|
568
|
+
agent.add_to_blacklist("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36")
|
|
569
|
+
agent.add_to_whitelist("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
|
|
570
|
+
print("Blacklisted:", agent.random())
|
|
571
|
+
print("Whitelisted:", agent.random())
|
|
572
|
+
|
|
573
|
+
# Test agent history
|
|
574
|
+
for _ in range(55):
|
|
575
|
+
agent.random()
|
|
576
|
+
print("History:", agent.get_history())
|
|
577
|
+
|
|
578
|
+
# Test agent validation
|
|
579
|
+
print("Valid agent:", agent.validate_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"))
|
|
580
|
+
print("Invalid agent:", agent.validate_agent("InvalidUserAgentString"))
|
|
581
|
+
ip = agent.rotate_ip()
|
|
582
582
|
print(ip) # 192.168.1.10 (example)
|