webscout 7.5__py3-none-any.whl โ 7.7__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 +5 -53
- webscout/AIutel.py +8 -318
- webscout/DWEBS.py +460 -489
- webscout/Extra/YTToolkit/YTdownloader.py +14 -53
- webscout/Extra/YTToolkit/transcriber.py +12 -13
- webscout/Extra/YTToolkit/ytapi/video.py +0 -1
- webscout/Extra/__init__.py +0 -1
- webscout/Extra/autocoder/__init__.py +9 -9
- webscout/Extra/autocoder/autocoder_utiles.py +193 -199
- webscout/Extra/autocoder/rawdog.py +789 -677
- webscout/Extra/gguf.py +682 -428
- webscout/Extra/weather.py +178 -156
- webscout/Extra/weather_ascii.py +70 -17
- webscout/Litlogger/core/logger.py +1 -2
- webscout/Litlogger/handlers/file.py +1 -1
- webscout/Litlogger/styles/formats.py +0 -2
- webscout/Litlogger/utils/detectors.py +0 -1
- webscout/Provider/AISEARCH/DeepFind.py +0 -1
- webscout/Provider/AISEARCH/ISou.py +1 -22
- webscout/Provider/AISEARCH/felo_search.py +0 -1
- webscout/Provider/AllenAI.py +28 -30
- webscout/Provider/C4ai.py +29 -11
- webscout/Provider/ChatGPTClone.py +226 -0
- webscout/Provider/ChatGPTGratis.py +24 -56
- webscout/Provider/DeepSeek.py +25 -17
- webscout/Provider/Deepinfra.py +115 -48
- webscout/Provider/Gemini.py +1 -1
- webscout/Provider/Glider.py +33 -12
- webscout/Provider/HF_space/qwen_qwen2.py +2 -2
- webscout/Provider/HeckAI.py +23 -7
- webscout/Provider/Hunyuan.py +272 -0
- webscout/Provider/Jadve.py +20 -5
- webscout/Provider/LambdaChat.py +391 -0
- webscout/Provider/Netwrck.py +42 -19
- webscout/Provider/OLLAMA.py +256 -32
- webscout/Provider/PI.py +4 -2
- webscout/Provider/Perplexitylabs.py +26 -6
- webscout/Provider/PizzaGPT.py +10 -51
- webscout/Provider/TTI/AiForce/async_aiforce.py +4 -37
- webscout/Provider/TTI/AiForce/sync_aiforce.py +41 -38
- webscout/Provider/TTI/FreeAIPlayground/__init__.py +9 -9
- webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +179 -206
- webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +180 -192
- webscout/Provider/TTI/MagicStudio/__init__.py +2 -0
- webscout/Provider/TTI/MagicStudio/async_magicstudio.py +111 -0
- webscout/Provider/TTI/MagicStudio/sync_magicstudio.py +109 -0
- webscout/Provider/TTI/PollinationsAI/async_pollinations.py +5 -24
- webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +2 -22
- webscout/Provider/TTI/__init__.py +2 -3
- webscout/Provider/TTI/aiarta/async_aiarta.py +14 -14
- webscout/Provider/TTI/aiarta/sync_aiarta.py +52 -21
- webscout/Provider/TTI/artbit/async_artbit.py +3 -32
- webscout/Provider/TTI/artbit/sync_artbit.py +3 -31
- webscout/Provider/TTI/fastflux/__init__.py +22 -0
- webscout/Provider/TTI/fastflux/async_fastflux.py +261 -0
- webscout/Provider/TTI/fastflux/sync_fastflux.py +252 -0
- webscout/Provider/TTI/piclumen/__init__.py +22 -22
- webscout/Provider/TTI/piclumen/sync_piclumen.py +232 -232
- webscout/Provider/TTS/__init__.py +2 -2
- webscout/Provider/TTS/deepgram.py +12 -39
- webscout/Provider/TTS/elevenlabs.py +14 -40
- webscout/Provider/TTS/gesserit.py +11 -35
- webscout/Provider/TTS/murfai.py +13 -39
- webscout/Provider/TTS/parler.py +17 -40
- webscout/Provider/TTS/speechma.py +180 -0
- webscout/Provider/TTS/streamElements.py +17 -44
- webscout/Provider/TextPollinationsAI.py +39 -59
- webscout/Provider/Venice.py +25 -8
- webscout/Provider/WebSim.py +227 -0
- webscout/Provider/WiseCat.py +27 -5
- webscout/Provider/Youchat.py +64 -37
- webscout/Provider/__init__.py +12 -7
- webscout/Provider/akashgpt.py +20 -5
- webscout/Provider/flowith.py +33 -7
- webscout/Provider/freeaichat.py +32 -45
- webscout/Provider/koala.py +20 -5
- webscout/Provider/labyrinth.py +239 -0
- webscout/Provider/learnfastai.py +28 -15
- webscout/Provider/llamatutor.py +1 -1
- webscout/Provider/llmchat.py +30 -8
- webscout/Provider/multichat.py +65 -9
- webscout/Provider/sonus.py +208 -0
- webscout/Provider/talkai.py +1 -0
- webscout/Provider/turboseek.py +3 -0
- webscout/Provider/tutorai.py +2 -0
- webscout/Provider/typegpt.py +155 -65
- webscout/Provider/uncovr.py +297 -0
- webscout/Provider/x0gpt.py +3 -1
- webscout/Provider/yep.py +102 -20
- webscout/__init__.py +3 -0
- webscout/cli.py +53 -40
- webscout/conversation.py +1 -10
- webscout/litagent/__init__.py +2 -2
- webscout/litagent/agent.py +356 -20
- webscout/litagent/constants.py +34 -5
- webscout/litprinter/__init__.py +0 -3
- webscout/models.py +181 -0
- webscout/optimizers.py +1 -1
- webscout/prompt_manager.py +2 -8
- webscout/scout/core/scout.py +1 -4
- webscout/scout/core/search_result.py +1 -1
- webscout/scout/core/text_utils.py +1 -1
- webscout/scout/core.py +2 -5
- webscout/scout/element.py +1 -1
- webscout/scout/parsers/html_parser.py +1 -1
- webscout/scout/utils.py +0 -1
- webscout/swiftcli/__init__.py +1 -3
- webscout/tempid.py +1 -1
- webscout/update_checker.py +1 -3
- webscout/version.py +1 -1
- webscout/webscout_search_async.py +1 -2
- webscout/yep_search.py +297 -297
- {webscout-7.5.dist-info โ webscout-7.7.dist-info}/LICENSE.md +4 -4
- {webscout-7.5.dist-info โ webscout-7.7.dist-info}/METADATA +127 -405
- {webscout-7.5.dist-info โ webscout-7.7.dist-info}/RECORD +118 -117
- webscout/Extra/autollama.py +0 -231
- webscout/Provider/Amigo.py +0 -274
- webscout/Provider/Bing.py +0 -243
- webscout/Provider/DiscordRocks.py +0 -253
- webscout/Provider/TTI/blackbox/__init__.py +0 -4
- webscout/Provider/TTI/blackbox/async_blackbox.py +0 -212
- webscout/Provider/TTI/blackbox/sync_blackbox.py +0 -199
- webscout/Provider/TTI/deepinfra/__init__.py +0 -4
- webscout/Provider/TTI/deepinfra/async_deepinfra.py +0 -227
- webscout/Provider/TTI/deepinfra/sync_deepinfra.py +0 -199
- webscout/Provider/TTI/imgninza/__init__.py +0 -4
- webscout/Provider/TTI/imgninza/async_ninza.py +0 -214
- webscout/Provider/TTI/imgninza/sync_ninza.py +0 -209
- webscout/Provider/TTS/voicepod.py +0 -117
- {webscout-7.5.dist-info โ webscout-7.7.dist-info}/WHEEL +0 -0
- {webscout-7.5.dist-info โ webscout-7.7.dist-info}/entry_points.txt +0 -0
- {webscout-7.5.dist-info โ webscout-7.7.dist-info}/top_level.txt +0 -0
webscout/cli.py
CHANGED
|
@@ -1,34 +1,12 @@
|
|
|
1
|
-
import os
|
|
2
1
|
import sys
|
|
3
|
-
from
|
|
4
|
-
from datetime import datetime
|
|
5
|
-
from urllib.parse import unquote
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from .swiftcli import CLI, option, argument, group
|
|
8
|
-
from curl_cffi import requests
|
|
9
|
-
import pyreqwest_impersonate as pri
|
|
2
|
+
from .swiftcli import CLI, option
|
|
10
3
|
from .webscout_search import WEBS
|
|
11
|
-
from .utils import json_dumps, json_loads
|
|
12
4
|
from .version import __version__
|
|
13
|
-
from .Litlogger import LitLogger, LogFormat, ColorScheme
|
|
14
5
|
from rich.console import Console
|
|
15
6
|
from rich.panel import Panel
|
|
16
|
-
from rich.markdown import Markdown
|
|
17
7
|
from rich.table import Table
|
|
18
|
-
from rich.style import Style
|
|
19
8
|
from rich.text import Text
|
|
20
|
-
from rich.align import Align
|
|
21
|
-
from rich.progress import track, Progress
|
|
22
|
-
from rich.prompt import Prompt, Confirm
|
|
23
|
-
from rich.columns import Columns
|
|
24
|
-
from pyfiglet import figlet_format
|
|
25
9
|
|
|
26
|
-
logger = LitLogger(
|
|
27
|
-
name="webscout",
|
|
28
|
-
format=LogFormat.MODERN_EMOJI,
|
|
29
|
-
color_scheme=ColorScheme.CYBERPUNK,
|
|
30
|
-
console_output=True
|
|
31
|
-
)
|
|
32
10
|
|
|
33
11
|
COLORS = {
|
|
34
12
|
0: "black",
|
|
@@ -71,6 +49,42 @@ def _print_data(data):
|
|
|
71
49
|
console.print(Panel(table, title=f"Result {i}", expand=False, style="green on black"))
|
|
72
50
|
console.print("\n")
|
|
73
51
|
|
|
52
|
+
def _print_weather(data):
|
|
53
|
+
"""Prints weather data in a clean, focused format."""
|
|
54
|
+
console = Console()
|
|
55
|
+
|
|
56
|
+
# Current weather panel
|
|
57
|
+
current = data["current"]
|
|
58
|
+
current_table = Table(show_header=False, show_lines=True, expand=True, box=None)
|
|
59
|
+
current_table.add_column("Metric", style="cyan", no_wrap=True, width=15)
|
|
60
|
+
current_table.add_column("Value", style="white")
|
|
61
|
+
|
|
62
|
+
current_table.add_row("Temperature", f"{current['temperature_c']}ยฐC")
|
|
63
|
+
current_table.add_row("Feels Like", f"{current['feels_like_c']}ยฐC")
|
|
64
|
+
current_table.add_row("Humidity", f"{current['humidity']}%")
|
|
65
|
+
current_table.add_row("Wind", f"{current['wind_speed_ms']} m/s")
|
|
66
|
+
current_table.add_row("Direction", f"{current['wind_direction']}ยฐ")
|
|
67
|
+
|
|
68
|
+
console.print(Panel(current_table, title=f"Current Weather in {data['location']}", expand=False, style="green on black"))
|
|
69
|
+
console.print("\n")
|
|
70
|
+
|
|
71
|
+
# Daily forecast panel
|
|
72
|
+
daily_table = Table(show_header=True, show_lines=True, expand=True, box=None)
|
|
73
|
+
daily_table.add_column("Date", style="cyan")
|
|
74
|
+
daily_table.add_column("Condition", style="white")
|
|
75
|
+
daily_table.add_column("High", style="red")
|
|
76
|
+
daily_table.add_column("Low", style="blue")
|
|
77
|
+
|
|
78
|
+
for day in data["daily_forecast"][:5]: # Show next 5 days
|
|
79
|
+
daily_table.add_row(
|
|
80
|
+
day["date"],
|
|
81
|
+
day["condition"],
|
|
82
|
+
f"{day['max_temp_c']}ยฐC",
|
|
83
|
+
f"{day['min_temp_c']}ยฐC"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
console.print(Panel(daily_table, title="5-Day Forecast", expand=False, style="green on black"))
|
|
87
|
+
|
|
74
88
|
# Initialize CLI app
|
|
75
89
|
app = CLI(name="webscout", help="Search the web with a rich UI", version=__version__)
|
|
76
90
|
|
|
@@ -89,7 +103,7 @@ def chat(proxy: str = None, model: str = "gpt-4o-mini"):
|
|
|
89
103
|
console = Console()
|
|
90
104
|
|
|
91
105
|
# Display header
|
|
92
|
-
console.print(f"[bold blue]{figlet_format('Webscout Chat')}[/]\n", justify="center")
|
|
106
|
+
# console.print(f"[bold blue]{figlet_format('Webscout Chat')}[/]\n", justify="center")
|
|
93
107
|
console.print(f"[bold green]Using model:[/] {model}\n")
|
|
94
108
|
console.print("[cyan]Type your message and press Enter. Press Ctrl+C or type 'exit' to quit.[/]\n")
|
|
95
109
|
|
|
@@ -97,23 +111,18 @@ def chat(proxy: str = None, model: str = "gpt-4o-mini"):
|
|
|
97
111
|
try:
|
|
98
112
|
while True:
|
|
99
113
|
try:
|
|
100
|
-
user_input = input("
|
|
114
|
+
user_input = input(">>> ").strip()
|
|
101
115
|
if not user_input or user_input.lower() in ['exit', 'quit']:
|
|
102
116
|
break
|
|
103
117
|
|
|
104
|
-
logger.info(f"Sending message to {model}")
|
|
105
118
|
response = webs.chat(keywords=user_input, model=model)
|
|
106
119
|
console.print(f"\nAI: {response}\n")
|
|
107
|
-
logger.success("Received response from AI")
|
|
108
120
|
|
|
109
121
|
except Exception as e:
|
|
110
|
-
logger.error(f"Error in chat: {str(e)}")
|
|
111
122
|
console.print(f"[bold red]Error:[/] {str(e)}\n")
|
|
112
123
|
|
|
113
124
|
except KeyboardInterrupt:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
console.print("\n[cyan]Chat session ended. Goodbye![/]")
|
|
125
|
+
console.print("\n[bold red]Chat session interrupted. Exiting...[/]")
|
|
117
126
|
|
|
118
127
|
@app.command()
|
|
119
128
|
@option("--keywords", "-k", help="Search keywords", required=True)
|
|
@@ -130,7 +139,6 @@ def text(keywords: str, region: str, safesearch: str, timelimit: str, backend: s
|
|
|
130
139
|
results = webs.text(keywords, region, safesearch, timelimit, backend, max_results)
|
|
131
140
|
_print_data(results)
|
|
132
141
|
except Exception as e:
|
|
133
|
-
logger.error(f"Error in text search: {e}")
|
|
134
142
|
raise e
|
|
135
143
|
|
|
136
144
|
@app.command()
|
|
@@ -143,7 +151,6 @@ def answers(keywords: str, proxy: str = None):
|
|
|
143
151
|
results = webs.answers(keywords)
|
|
144
152
|
_print_data(results)
|
|
145
153
|
except Exception as e:
|
|
146
|
-
logger.error(f"Error in answers search: {e}")
|
|
147
154
|
raise e
|
|
148
155
|
|
|
149
156
|
@app.command()
|
|
@@ -177,7 +184,6 @@ def images(
|
|
|
177
184
|
results = webs.images(keywords, region, safesearch, timelimit, size, color, type, layout, license, max_results)
|
|
178
185
|
_print_data(results)
|
|
179
186
|
except Exception as e:
|
|
180
|
-
logger.error(f"Error in images search: {e}")
|
|
181
187
|
raise e
|
|
182
188
|
|
|
183
189
|
@app.command()
|
|
@@ -207,7 +213,6 @@ def videos(
|
|
|
207
213
|
results = webs.videos(keywords, region, safesearch, timelimit, resolution, duration, license, max_results)
|
|
208
214
|
_print_data(results)
|
|
209
215
|
except Exception as e:
|
|
210
|
-
logger.error(f"Error in videos search: {e}")
|
|
211
216
|
raise e
|
|
212
217
|
|
|
213
218
|
@app.command()
|
|
@@ -224,7 +229,6 @@ def news(keywords: str, region: str, safesearch: str, timelimit: str, max_result
|
|
|
224
229
|
results = webs.news(keywords, region, safesearch, timelimit, max_results)
|
|
225
230
|
_print_data(results)
|
|
226
231
|
except Exception as e:
|
|
227
|
-
logger.error(f"Error in news search: {e}")
|
|
228
232
|
raise e
|
|
229
233
|
|
|
230
234
|
@app.command()
|
|
@@ -275,7 +279,6 @@ def maps(
|
|
|
275
279
|
)
|
|
276
280
|
_print_data(results)
|
|
277
281
|
except Exception as e:
|
|
278
|
-
logger.error(f"Error in maps search: {e}")
|
|
279
282
|
raise e
|
|
280
283
|
|
|
281
284
|
@app.command()
|
|
@@ -290,7 +293,6 @@ def translate(keywords: str, from_: str, to: str, proxy: str = None):
|
|
|
290
293
|
results = webs.translate(keywords, from_, to)
|
|
291
294
|
_print_data(results)
|
|
292
295
|
except Exception as e:
|
|
293
|
-
logger.error(f"Error in translation: {e}")
|
|
294
296
|
raise e
|
|
295
297
|
|
|
296
298
|
@app.command()
|
|
@@ -304,7 +306,19 @@ def suggestions(keywords: str, region: str, proxy: str = None):
|
|
|
304
306
|
results = webs.suggestions(keywords, region)
|
|
305
307
|
_print_data(results)
|
|
306
308
|
except Exception as e:
|
|
307
|
-
|
|
309
|
+
raise e
|
|
310
|
+
|
|
311
|
+
@app.command()
|
|
312
|
+
@option("--location", "-l", help="Location to get weather for", required=True)
|
|
313
|
+
@option("--language", "-lang", help="Language code (e.g. 'en', 'es')", default="en")
|
|
314
|
+
@option("--proxy", "-p", help="Proxy URL to use for requests")
|
|
315
|
+
def weather(location: str, language: str, proxy: str = None):
|
|
316
|
+
"""Get weather information for a location from DuckDuckGo."""
|
|
317
|
+
webs = WEBS(proxy=proxy)
|
|
318
|
+
try:
|
|
319
|
+
results = webs.weather(location, language)
|
|
320
|
+
_print_weather(results)
|
|
321
|
+
except Exception as e:
|
|
308
322
|
raise e
|
|
309
323
|
|
|
310
324
|
def main():
|
|
@@ -312,7 +326,6 @@ def main():
|
|
|
312
326
|
try:
|
|
313
327
|
app.run()
|
|
314
328
|
except Exception as e:
|
|
315
|
-
logger.error(f"Error: {e}")
|
|
316
329
|
sys.exit(1)
|
|
317
330
|
|
|
318
331
|
if __name__ == "__main__":
|
webscout/conversation.py
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
|
-
import logging
|
|
3
2
|
from typing import Optional
|
|
4
|
-
from .Litlogger import Logger, LogFormat
|
|
5
3
|
|
|
6
|
-
# Create a logger instance for this module
|
|
7
|
-
logger = Logger(
|
|
8
|
-
name="Conversation",
|
|
9
|
-
format=LogFormat.MODERN_EMOJI,
|
|
10
|
-
)
|
|
11
4
|
|
|
12
5
|
class Conversation:
|
|
13
6
|
"""Handles prompt generation based on history and maintains chat context.
|
|
@@ -79,11 +72,9 @@ class Conversation:
|
|
|
79
72
|
), f"File '{filepath}' does not exist"
|
|
80
73
|
|
|
81
74
|
if not os.path.isfile(filepath):
|
|
82
|
-
logging.debug(f"Creating new chat-history file - '{filepath}'")
|
|
83
75
|
with open(filepath, "w", encoding="utf-8") as fh:
|
|
84
76
|
fh.write(self.intro)
|
|
85
77
|
else:
|
|
86
|
-
logging.debug(f"Loading conversation from '{filepath}'")
|
|
87
78
|
with open(filepath, encoding="utf-8") as fh:
|
|
88
79
|
file_contents = fh.readlines()
|
|
89
80
|
if file_contents:
|
|
@@ -221,7 +212,7 @@ class Conversation:
|
|
|
221
212
|
if role in role_formats:
|
|
222
213
|
self.chat_history += f"\n{role_formats[role]} : {content}"
|
|
223
214
|
else:
|
|
224
|
-
|
|
215
|
+
raise ValueError(f"Invalid role: {role}. Must be one of {list(role_formats.keys())}")
|
|
225
216
|
|
|
226
217
|
# # Enhanced logging for message addition
|
|
227
218
|
# logger.info(f"Added message from {role}: {content}")
|
webscout/litagent/__init__.py
CHANGED
|
@@ -22,8 +22,8 @@ Examples:
|
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
24
|
from .agent import LitAgent
|
|
25
|
-
from .constants import BROWSERS, OS_VERSIONS, DEVICES
|
|
25
|
+
from .constants import BROWSERS, OS_VERSIONS, DEVICES, FINGERPRINTS
|
|
26
26
|
|
|
27
27
|
agent = LitAgent()
|
|
28
28
|
|
|
29
|
-
__all__ = ['LitAgent', 'agent', 'BROWSERS', 'OS_VERSIONS', 'DEVICES']
|
|
29
|
+
__all__ = ['LitAgent', 'agent', 'BROWSERS', 'OS_VERSIONS', 'DEVICES', 'FINGERPRINTS']
|
webscout/litagent/agent.py
CHANGED
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
"""Main LitAgent implementation."""
|
|
2
2
|
|
|
3
3
|
import random
|
|
4
|
-
|
|
5
|
-
from
|
|
6
|
-
|
|
4
|
+
import threading
|
|
5
|
+
from typing import List, Dict, Any, Optional
|
|
6
|
+
|
|
7
|
+
from webscout.litagent.constants import BROWSERS, OS_VERSIONS, DEVICES, FINGERPRINTS
|
|
7
8
|
|
|
8
|
-
logger = Logger(
|
|
9
|
-
name="LitAgent",
|
|
10
|
-
format=LogFormat.MODERN_EMOJI,
|
|
11
|
-
)
|
|
12
9
|
|
|
13
10
|
class LitAgent:
|
|
14
11
|
"""A lit user agent generator that keeps it fresh! ๐"""
|
|
15
12
|
|
|
16
|
-
def __init__(self):
|
|
17
|
-
"""Initialize LitAgent with style! ๐ซ
|
|
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
|
+
"""
|
|
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._refresh_timer = None
|
|
23
|
+
self._stats = {
|
|
24
|
+
"total_generated": 100,
|
|
25
|
+
"requests_served": 0,
|
|
26
|
+
"browser_usage": {browser: 0 for browser in BROWSERS.keys()},
|
|
27
|
+
"device_usage": {device: 0 for device in DEVICES.keys()}
|
|
28
|
+
}
|
|
19
29
|
|
|
20
30
|
def _generate_agents(self, count: int) -> List[str]:
|
|
21
31
|
"""Generate some lit user agents! ๐ ๏ธ"""
|
|
@@ -24,7 +34,7 @@ class LitAgent:
|
|
|
24
34
|
browser = random.choice(list(BROWSERS.keys()))
|
|
25
35
|
version = random.randint(*BROWSERS[browser])
|
|
26
36
|
|
|
27
|
-
if browser in ['chrome', 'firefox', 'edge', 'opera']:
|
|
37
|
+
if browser in ['chrome', 'firefox', 'edge', 'opera', 'brave', 'vivaldi']:
|
|
28
38
|
os_type = random.choice(['windows', 'mac', 'linux'])
|
|
29
39
|
os_ver = random.choice(OS_VERSIONS[os_type])
|
|
30
40
|
|
|
@@ -44,6 +54,10 @@ class LitAgent:
|
|
|
44
54
|
agent += f"Edge/{version}.0.0.0"
|
|
45
55
|
elif browser == 'opera':
|
|
46
56
|
agent += f"OPR/{version}.0.0.0"
|
|
57
|
+
elif browser == 'brave':
|
|
58
|
+
agent += f"Chrome/{version}.0.0.0 Safari/537.36 Brave/{version}.0.0.0"
|
|
59
|
+
elif browser == 'vivaldi':
|
|
60
|
+
agent += f"Chrome/{version}.0.0.0 Safari/537.36 Vivaldi/{version}.0.{random.randint(1000, 9999)}"
|
|
47
61
|
|
|
48
62
|
elif browser == 'safari':
|
|
49
63
|
device = random.choice(['mac', 'ios'])
|
|
@@ -60,29 +74,127 @@ class LitAgent:
|
|
|
60
74
|
|
|
61
75
|
return list(set(agents)) # Remove any duplicates
|
|
62
76
|
|
|
77
|
+
def _update_stats(self, browser_type=None, device_type=None):
|
|
78
|
+
"""Update usage statistics."""
|
|
79
|
+
if self.thread_safe and self.lock:
|
|
80
|
+
with self.lock:
|
|
81
|
+
self._stats["requests_served"] += 1
|
|
82
|
+
if browser_type:
|
|
83
|
+
self._stats["browser_usage"][browser_type] = self._stats["browser_usage"].get(browser_type, 0) + 1
|
|
84
|
+
if device_type:
|
|
85
|
+
self._stats["device_usage"][device_type] = self._stats["device_usage"].get(device_type, 0) + 1
|
|
86
|
+
else:
|
|
87
|
+
self._stats["requests_served"] += 1
|
|
88
|
+
if browser_type:
|
|
89
|
+
self._stats["browser_usage"][browser_type] = self._stats["browser_usage"].get(browser_type, 0) + 1
|
|
90
|
+
if device_type:
|
|
91
|
+
self._stats["device_usage"][device_type] = self._stats["device_usage"].get(device_type, 0) + 1
|
|
92
|
+
|
|
63
93
|
def random(self) -> str:
|
|
64
94
|
"""Get a random user agent! ๐ฒ"""
|
|
65
|
-
|
|
95
|
+
if self.thread_safe and self.lock:
|
|
96
|
+
with self.lock:
|
|
97
|
+
agent = random.choice(self.agents)
|
|
98
|
+
self._update_stats()
|
|
99
|
+
return agent
|
|
100
|
+
else:
|
|
101
|
+
agent = random.choice(self.agents)
|
|
102
|
+
self._update_stats()
|
|
103
|
+
return agent
|
|
66
104
|
|
|
67
105
|
def browser(self, name: str) -> str:
|
|
68
106
|
"""Get a browser-specific agent! ๐"""
|
|
69
107
|
name = name.lower()
|
|
70
108
|
if name not in BROWSERS:
|
|
71
|
-
logger.warning(f"Unknown browser: {name} - Using random browser")
|
|
72
109
|
return self.random()
|
|
73
110
|
|
|
74
|
-
|
|
75
|
-
|
|
111
|
+
if self.thread_safe and self.lock:
|
|
112
|
+
with self.lock:
|
|
113
|
+
agents = [a for a in self.agents if name in a.lower()]
|
|
114
|
+
agent = random.choice(agents) if agents else self.random()
|
|
115
|
+
self._update_stats(browser_type=name)
|
|
116
|
+
return agent
|
|
117
|
+
else:
|
|
118
|
+
agents = [a for a in self.agents if name in a.lower()]
|
|
119
|
+
agent = random.choice(agents) if agents else self.random()
|
|
120
|
+
self._update_stats(browser_type=name)
|
|
121
|
+
return agent
|
|
76
122
|
|
|
77
123
|
def mobile(self) -> str:
|
|
78
124
|
"""Get a mobile device agent! ๐ฑ"""
|
|
79
|
-
|
|
80
|
-
|
|
125
|
+
if self.thread_safe and self.lock:
|
|
126
|
+
with self.lock:
|
|
127
|
+
agents = [a for a in self.agents if any(d in a for d in DEVICES['mobile'])]
|
|
128
|
+
agent = random.choice(agents) if agents else self.random()
|
|
129
|
+
self._update_stats(device_type="mobile")
|
|
130
|
+
return agent
|
|
131
|
+
else:
|
|
132
|
+
agents = [a for a in self.agents if any(d in a for d in DEVICES['mobile'])]
|
|
133
|
+
agent = random.choice(agents) if agents else self.random()
|
|
134
|
+
self._update_stats(device_type="mobile")
|
|
135
|
+
return agent
|
|
81
136
|
|
|
82
137
|
def desktop(self) -> str:
|
|
83
138
|
"""Get a desktop agent! ๐ป"""
|
|
84
|
-
|
|
85
|
-
|
|
139
|
+
if self.thread_safe and self.lock:
|
|
140
|
+
with self.lock:
|
|
141
|
+
agents = [a for a in self.agents if 'Windows' in a or 'Macintosh' in a or 'Linux' in a]
|
|
142
|
+
agent = random.choice(agents) if agents else self.random()
|
|
143
|
+
self._update_stats(device_type="desktop")
|
|
144
|
+
return agent
|
|
145
|
+
else:
|
|
146
|
+
agents = [a for a in self.agents if 'Windows' in a or 'Macintosh' in a or 'Linux' in a]
|
|
147
|
+
agent = random.choice(agents) if agents else self.random()
|
|
148
|
+
self._update_stats(device_type="desktop")
|
|
149
|
+
return agent
|
|
150
|
+
|
|
151
|
+
def tablet(self) -> str:
|
|
152
|
+
"""Get a tablet agent! ๐ฑ"""
|
|
153
|
+
if self.thread_safe and self.lock:
|
|
154
|
+
with self.lock:
|
|
155
|
+
# Focus on iPad and Android tablets
|
|
156
|
+
agents = [a for a in self.agents if 'iPad' in a or 'Android' in a and not 'Mobile' in a]
|
|
157
|
+
agent = random.choice(agents) if agents else self.random()
|
|
158
|
+
self._update_stats(device_type="tablet")
|
|
159
|
+
return agent
|
|
160
|
+
else:
|
|
161
|
+
agents = [a for a in self.agents if 'iPad' in a or 'Android' in a and not 'Mobile' in a]
|
|
162
|
+
agent = random.choice(agents) if agents else self.random()
|
|
163
|
+
self._update_stats(device_type="tablet")
|
|
164
|
+
return agent
|
|
165
|
+
|
|
166
|
+
def smart_tv(self) -> str:
|
|
167
|
+
"""Get a Smart TV agent! ๐บ"""
|
|
168
|
+
# Create a TV-specific agent since they may not be in our standard pool
|
|
169
|
+
tv_type = random.choice(DEVICES['tv'])
|
|
170
|
+
if 'Samsung' in tv_type:
|
|
171
|
+
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"
|
|
172
|
+
elif 'LG' in tv_type:
|
|
173
|
+
agent = f"Mozilla/5.0 (Web0S; {tv_type}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36"
|
|
174
|
+
elif 'Android' in tv_type:
|
|
175
|
+
agent = f"Mozilla/5.0 (Linux; Android 9; {tv_type}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36"
|
|
176
|
+
elif 'Apple' in tv_type:
|
|
177
|
+
agent = f"Mozilla/5.0 (AppleTV; CPU like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148"
|
|
178
|
+
else:
|
|
179
|
+
agent = f"Mozilla/5.0 (Linux; {tv_type}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36"
|
|
180
|
+
|
|
181
|
+
self._update_stats(device_type="tv")
|
|
182
|
+
return agent
|
|
183
|
+
|
|
184
|
+
def gaming(self) -> str:
|
|
185
|
+
"""Get a gaming console agent! ๐ฎ"""
|
|
186
|
+
console_type = random.choice(DEVICES['console'])
|
|
187
|
+
if 'PlayStation' in console_type:
|
|
188
|
+
agent = f"Mozilla/5.0 ({console_type}/5.0) AppleWebKit/601.2 (KHTML, like Gecko)"
|
|
189
|
+
elif 'Xbox' in console_type:
|
|
190
|
+
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"
|
|
191
|
+
elif 'Nintendo' in console_type:
|
|
192
|
+
agent = f"Mozilla/5.0 (Nintendo Switch; {console_type}) AppleWebKit/601.6 (KHTML, like Gecko) NintendoBrowser/5.1.0.13343"
|
|
193
|
+
else:
|
|
194
|
+
agent = self.random()
|
|
195
|
+
|
|
196
|
+
self._update_stats(device_type="console")
|
|
197
|
+
return agent
|
|
86
198
|
|
|
87
199
|
def chrome(self) -> str:
|
|
88
200
|
"""Get a Chrome agent! ๐"""
|
|
@@ -103,10 +215,225 @@ class LitAgent:
|
|
|
103
215
|
def opera(self) -> str:
|
|
104
216
|
"""Get an Opera agent! ๐ญ"""
|
|
105
217
|
return self.browser('opera')
|
|
218
|
+
|
|
219
|
+
def brave(self) -> str:
|
|
220
|
+
"""Get a Brave agent! ๐ฆ"""
|
|
221
|
+
return self.browser('brave')
|
|
222
|
+
|
|
223
|
+
def vivaldi(self) -> str:
|
|
224
|
+
"""Get a Vivaldi agent! ๐จ"""
|
|
225
|
+
return self.browser('vivaldi')
|
|
226
|
+
|
|
227
|
+
# OS-specific agents
|
|
228
|
+
def windows(self) -> str:
|
|
229
|
+
"""Get a Windows agent! ๐ช"""
|
|
230
|
+
agents = [a for a in self.agents if 'Windows' in a]
|
|
231
|
+
agent = random.choice(agents) if agents else self.random()
|
|
232
|
+
self._update_stats()
|
|
233
|
+
return agent
|
|
234
|
+
|
|
235
|
+
def macos(self) -> str:
|
|
236
|
+
"""Get a macOS agent! ๐"""
|
|
237
|
+
agents = [a for a in self.agents if 'Macintosh' in a]
|
|
238
|
+
agent = random.choice(agents) if agents else self.random()
|
|
239
|
+
self._update_stats()
|
|
240
|
+
return agent
|
|
241
|
+
|
|
242
|
+
def linux(self) -> str:
|
|
243
|
+
"""Get a Linux agent! ๐ง"""
|
|
244
|
+
agents = [a for a in self.agents if 'Linux' in a and 'Android' not in a]
|
|
245
|
+
agent = random.choice(agents) if agents else self.random()
|
|
246
|
+
self._update_stats()
|
|
247
|
+
return agent
|
|
248
|
+
|
|
249
|
+
def android(self) -> str:
|
|
250
|
+
"""Get an Android agent! ๐ค"""
|
|
251
|
+
agents = [a for a in self.agents if 'Android' in a]
|
|
252
|
+
agent = random.choice(agents) if agents else self.random()
|
|
253
|
+
self._update_stats()
|
|
254
|
+
return agent
|
|
255
|
+
|
|
256
|
+
def ios(self) -> str:
|
|
257
|
+
"""Get an iOS agent! ๐ฑ"""
|
|
258
|
+
agents = [a for a in self.agents if 'iPhone' in a or 'iPad' in a]
|
|
259
|
+
agent = random.choice(agents) if agents else self.random()
|
|
260
|
+
self._update_stats()
|
|
261
|
+
return agent
|
|
262
|
+
|
|
263
|
+
def custom(self, browser: str, version: Optional[str] = None,
|
|
264
|
+
os: Optional[str] = None, os_version: Optional[str] = None,
|
|
265
|
+
device_type: Optional[str] = None) -> str:
|
|
266
|
+
"""Generate a custom user agent with specified parameters! ๐ ๏ธ
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
browser: Browser name (chrome, firefox, safari, edge, opera)
|
|
270
|
+
version: Browser version (optional)
|
|
271
|
+
os: Operating system (windows, mac, linux, android, ios)
|
|
272
|
+
os_version: OS version (optional)
|
|
273
|
+
device_type: Device type (desktop, mobile, tablet)
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
Customized user agent string
|
|
277
|
+
"""
|
|
278
|
+
browser = browser.lower() if browser else 'chrome'
|
|
279
|
+
if browser not in BROWSERS:
|
|
280
|
+
browser = 'chrome'
|
|
281
|
+
|
|
282
|
+
if version:
|
|
283
|
+
try:
|
|
284
|
+
version_num = int(version.split('.')[0])
|
|
285
|
+
except (ValueError, IndexError):
|
|
286
|
+
version_num = random.randint(*BROWSERS[browser])
|
|
287
|
+
else:
|
|
288
|
+
version_num = random.randint(*BROWSERS[browser])
|
|
289
|
+
|
|
290
|
+
os = os.lower() if os else random.choice(['windows', 'mac', 'linux'])
|
|
291
|
+
if os not in OS_VERSIONS:
|
|
292
|
+
os = 'windows'
|
|
293
|
+
|
|
294
|
+
os_ver = os_version or random.choice(OS_VERSIONS[os])
|
|
295
|
+
|
|
296
|
+
device_type = device_type.lower() if device_type else 'desktop'
|
|
297
|
+
|
|
298
|
+
# Build the user agent
|
|
299
|
+
if os == 'windows':
|
|
300
|
+
platform = f"Windows NT {os_ver}"
|
|
301
|
+
elif os == 'mac':
|
|
302
|
+
platform = f"Macintosh; Intel Mac OS X {os_ver}"
|
|
303
|
+
elif os == 'linux':
|
|
304
|
+
platform = f"X11; Linux {OS_VERSIONS['linux'][0]}"
|
|
305
|
+
elif os == 'android':
|
|
306
|
+
platform = f"Linux; Android {os_ver}; {random.choice(DEVICES['mobile'])}"
|
|
307
|
+
elif os == 'ios':
|
|
308
|
+
device = 'iPhone' if device_type == 'mobile' else 'iPad'
|
|
309
|
+
platform = f"{device}; CPU OS {os_ver} like Mac OS X"
|
|
310
|
+
else:
|
|
311
|
+
platform = f"Windows NT 10.0" # Default fallback
|
|
312
|
+
|
|
313
|
+
agent = f"Mozilla/5.0 ({platform}) AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
314
|
+
|
|
315
|
+
if browser == 'chrome':
|
|
316
|
+
agent += f"Chrome/{version_num}.0.0.0 Safari/537.36"
|
|
317
|
+
elif browser == 'firefox':
|
|
318
|
+
agent += f"Firefox/{version_num}.0"
|
|
319
|
+
elif browser == 'safari':
|
|
320
|
+
safari_ver = random.randint(*BROWSERS['safari'])
|
|
321
|
+
agent += f"Version/{version_num}.0 Safari/{safari_ver}.0"
|
|
322
|
+
elif browser == 'edge':
|
|
323
|
+
agent += f"Chrome/{version_num}.0.0.0 Safari/537.36 Edg/{version_num}.0.0.0"
|
|
324
|
+
elif browser == 'opera':
|
|
325
|
+
agent += f"Chrome/{version_num}.0.0.0 Safari/537.36 OPR/{version_num}.0.0.0"
|
|
326
|
+
elif browser == 'brave':
|
|
327
|
+
agent += f"Chrome/{version_num}.0.0.0 Safari/537.36 Brave/{version_num}.1.0"
|
|
328
|
+
|
|
329
|
+
self._update_stats(browser_type=browser, device_type=device_type)
|
|
330
|
+
return agent
|
|
331
|
+
|
|
332
|
+
def generate_fingerprint(self, browser: Optional[str] = None) -> Dict[str, str]:
|
|
333
|
+
"""Generate a consistent browser fingerprint! ๐
|
|
334
|
+
|
|
335
|
+
This creates a coherent set of headers for anti-fingerprinting.
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
browser: Specific browser to generate fingerprint for
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
Dictionary with fingerprinting headers
|
|
342
|
+
"""
|
|
343
|
+
# Get a random user agent using the random() method
|
|
344
|
+
user_agent = self.random()
|
|
345
|
+
|
|
346
|
+
# If browser is specified, try to get a matching one
|
|
347
|
+
if browser:
|
|
348
|
+
browser = browser.lower()
|
|
349
|
+
if browser in BROWSERS:
|
|
350
|
+
user_agent = self.browser(browser)
|
|
351
|
+
|
|
352
|
+
accept_language = random.choice(FINGERPRINTS["accept_language"])
|
|
353
|
+
accept = random.choice(FINGERPRINTS["accept"])
|
|
354
|
+
platform = random.choice(FINGERPRINTS["platforms"])
|
|
355
|
+
|
|
356
|
+
# Generate sec-ch-ua based on the user agent
|
|
357
|
+
sec_ch_ua = ""
|
|
358
|
+
for browser_name in FINGERPRINTS["sec_ch_ua"]:
|
|
359
|
+
if browser_name in user_agent.lower():
|
|
360
|
+
version = random.randint(*BROWSERS[browser_name])
|
|
361
|
+
sec_ch_ua = FINGERPRINTS["sec_ch_ua"][browser_name].format(version, version)
|
|
362
|
+
break
|
|
363
|
+
|
|
364
|
+
fingerprint = {
|
|
365
|
+
"user_agent": user_agent,
|
|
366
|
+
"accept_language": accept_language,
|
|
367
|
+
"accept": accept,
|
|
368
|
+
"sec_ch_ua": sec_ch_ua,
|
|
369
|
+
"platform": platform
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
self._update_stats(browser_type=browser)
|
|
373
|
+
return fingerprint
|
|
106
374
|
|
|
107
375
|
def refresh(self) -> None:
|
|
108
376
|
"""Refresh the agents with new ones! ๐"""
|
|
109
|
-
self.
|
|
377
|
+
if self.thread_safe and self.lock:
|
|
378
|
+
with self.lock:
|
|
379
|
+
self.agents = self._generate_agents(100)
|
|
380
|
+
self._stats["total_generated"] += 100
|
|
381
|
+
else:
|
|
382
|
+
self.agents = self._generate_agents(100)
|
|
383
|
+
self._stats["total_generated"] += 100
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def auto_refresh(self, interval_minutes: int = 30) -> None:
|
|
387
|
+
"""Set up automatic refreshing of agents pool! โฑ๏ธ
|
|
388
|
+
|
|
389
|
+
Args:
|
|
390
|
+
interval_minutes: Minutes between refreshes
|
|
391
|
+
"""
|
|
392
|
+
if self._refresh_timer:
|
|
393
|
+
self._refresh_timer.cancel()
|
|
394
|
+
|
|
395
|
+
def _refresh_task():
|
|
396
|
+
self.refresh()
|
|
397
|
+
self._refresh_timer = threading.Timer(interval_minutes * 60, _refresh_task)
|
|
398
|
+
self._refresh_timer.daemon = True
|
|
399
|
+
self._refresh_timer.start()
|
|
400
|
+
|
|
401
|
+
self._refresh_timer = threading.Timer(interval_minutes * 60, _refresh_task)
|
|
402
|
+
self._refresh_timer.daemon = True
|
|
403
|
+
self._refresh_timer.start()
|
|
404
|
+
|
|
405
|
+
def get_stats(self) -> Dict[str, Any]:
|
|
406
|
+
"""Get statistics about agent usage! ๐
|
|
407
|
+
|
|
408
|
+
Returns:
|
|
409
|
+
Dictionary with usage statistics
|
|
410
|
+
"""
|
|
411
|
+
stats_copy = self._stats.copy()
|
|
412
|
+
# Calculate top browser
|
|
413
|
+
top_browser = max(stats_copy["browser_usage"].items(), key=lambda x: x[1])[0] if stats_copy["browser_usage"] else None
|
|
414
|
+
stats_copy["top_browser"] = top_browser
|
|
415
|
+
|
|
416
|
+
# Calculate fake detection avoidance rate (just for fun)
|
|
417
|
+
stats_copy["avoidance_rate"] = min(99.9, 90 + (stats_copy["total_generated"] / 1000))
|
|
418
|
+
|
|
419
|
+
return stats_copy
|
|
420
|
+
|
|
421
|
+
def export_stats(self, filename: str) -> bool:
|
|
422
|
+
"""Export usage statistics to a file! ๐พ
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
filename: Path to export the stats
|
|
426
|
+
|
|
427
|
+
Returns:
|
|
428
|
+
True if export was successful, False otherwise
|
|
429
|
+
"""
|
|
430
|
+
try:
|
|
431
|
+
import json
|
|
432
|
+
with open(filename, 'w') as f:
|
|
433
|
+
json.dump(self.get_stats(), f, indent=2)
|
|
434
|
+
return True
|
|
435
|
+
except Exception as e:
|
|
436
|
+
return False
|
|
110
437
|
|
|
111
438
|
if __name__ == "__main__":
|
|
112
439
|
# Test it out! ๐งช
|
|
@@ -116,4 +443,13 @@ if __name__ == "__main__":
|
|
|
116
443
|
print("Firefox:", agent.firefox())
|
|
117
444
|
print("Safari:", agent.safari())
|
|
118
445
|
print("Mobile:", agent.mobile())
|
|
119
|
-
print("Desktop:", agent.desktop())
|
|
446
|
+
print("Desktop:", agent.desktop())
|
|
447
|
+
print("Tablet:", agent.tablet())
|
|
448
|
+
print("Smart TV:", agent.smart_tv())
|
|
449
|
+
print("Gaming:", agent.gaming())
|
|
450
|
+
|
|
451
|
+
# Test custom agent
|
|
452
|
+
print("Custom:", agent.custom(browser="chrome", os="windows", os_version="10.0"))
|
|
453
|
+
|
|
454
|
+
# Test fingerprinting
|
|
455
|
+
print("Fingerprint:", agent.generate_fingerprint("chrome"))
|