""" WA BLAST OTOMATIS - FIREFOX VERSION (FIXED) Deteksi login lebih akurat - Pengiriman pesan diperbaiki """ import os import sys import time import re import subprocess from datetime import datetime from typing import List, Tuple def install_packages(): packages = ["selenium", "webdriver-manager", "colorama"] for pkg in packages: try: __import__(pkg.replace("-", "_")) except ImportError: print(f"Installing {pkg}...") subprocess.check_call([sys.executable, "-m", "pip", "install", pkg, "--quiet"]) try: from selenium import webdriver from selenium.webdriver.firefox.service import Service from selenium.webdriver.firefox.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException, NoSuchElementException from webdriver_manager.firefox import GeckoDriverManager from colorama import init, Fore, Style init(autoreset=True) except ImportError: install_packages() from selenium import webdriver from selenium.webdriver.firefox.service import Service from selenium.webdriver.firefox.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from webdriver_manager.firefox import GeckoDriverManager from colorama import init, Fore, Style init(autoreset=True) OK = Fore.GREEN INFO = Fore.CYAN WARN = Fore.YELLOW ERR = Fore.RED RESET = Fore.RESET BOLD = Style.BRIGHT DELAY_ANTAR_KIRIM = 3 def bersihkan_nomor(nomor: str) -> str: nomor = re.sub(r'[\s\-\(\)\+]', '', nomor.strip()) if nomor.startswith('08'): nomor = '628' + nomor[2:] elif nomor.startswith('8') and len(nomor) >= 10: nomor = '62' + nomor elif nomor.startswith('+'): nomor = nomor[1:] if nomor.isdigit() and 10 <= len(nomor) <= 15: return nomor return None def baca_nomor_dari_file(file_path: str) -> List[str]: nomor_list = [] try: with open(file_path, 'r', encoding='utf-8') as f: for line in f: line = line.strip() if line and not line.startswith('#'): clean = bersihkan_nomor(line) if clean: nomor_list.append(clean) except FileNotFoundError: print(f"{ERR}❌ File {file_path} tidak ditemukan!{RESET}") return nomor_list def baca_pesan_dari_file(file_path: str) -> str: try: with open(file_path, 'r', encoding='utf-8') as f: pesan = f.read().strip() if not pesan: print(f"{ERR}❌ File {file_path} kosong!{RESET}") return None print(f"{OK}✅ Pesan berhasil dimuat: {len(pesan)} karakter{RESET}") return pesan except FileNotFoundError: print(f"{ERR}❌ File {file_path} tidak ditemukan!{RESET}") return None def create_firefox_driver(): print(f"{INFO}🔧 Menyiapkan Firefox...{RESET}") options = Options() options.add_argument('--no-sandbox') options.add_argument('--disable-dev-shm-usage') # Firefox specific settings options.set_preference("dom.webdriver.enabled", False) options.set_preference("useAutomationExtension", False) options.set_preference("general.useragent.override", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0") # Biarkan browser tetap terbuka options.set_preference("browser.link.open_newwindow", 3) driver = webdriver.Firefox(service=Service(GeckoDriverManager().install()), options=options) print(f"{OK}✅ Firefox siap{RESET}") return driver def login_whatsapp(driver, timeout=90): print(f"\n{INFO}📱 Membuka WhatsApp Web...{RESET}") driver.get("https://web.whatsapp.com") print(f"{WARN}{'='*50}{RESET}") print(f"{WARN}📱 CARA SCAN QR CODE:{RESET}") print(f" 1. Buka WhatsApp di HP{RESET}") print(f" 2. Klik titik tiga (⋮) → Perangkat Tertaut / Linked Devices{RESET}") print(f" 3. Klik TAUTKAN PERANGKAT / LINK A DEVICE{RESET}") print(f" 4. Scan QR Code yang muncul di layar ini{RESET}") print(f"{WARN}{'='*50}{RESET}") print(f"{INFO}⏳ Menunggu login... (maksimal {timeout} detik){RESET}") start_time = time.time() last_status = "" while time.time() - start_time < timeout: try: # Cek apakah URL berubah (sudah masuk ke chat) current_url = driver.current_url if "web.whatsapp.com" in current_url and "qrcode" not in current_url.lower(): # Cek apakah ada elemen chat chat_elements = driver.find_elements(By.XPATH, "//div[@data-testid='chat-list'] | //div[@role='row'] | //div[contains(@class,'_ak8q')]") if chat_elements: print(f"{OK}✅ Login BERHASIL! Ditemukan chat list.{RESET}") time.sleep(3) return True # Cek apakah QR masih ada qr_elements = driver.find_elements(By.XPATH, "//canvas[@aria-label='Scan me!'] | //div[contains(@class,'qrcode')]") if not qr_elements and "scan" not in driver.page_source.lower(): # Tidak ada QR, kemungkinan sudah login print(f"{OK}✅ Login BERHASIL! QR Code sudah hilang.{RESET}") time.sleep(3) return True # Status update setiap 10 detik elapsed = int(time.time() - start_time) if elapsed % 10 == 0 and elapsed != last_status: last_status = elapsed print(f"{INFO} ⏳ Masih menunggu... ({elapsed} detik){RESET}") except Exception as e: pass time.sleep(2) print(f"{ERR}❌ Timeout! Tidak bisa mendeteksi login.{RESET}") print(f"{WARN}💡 Pastikan:{RESET}") print(f" - QR Code sudah di scan dengan benar") print(f" - HP terhubung internet") print(f" - WhatsApp Web sudah muncul chat list") return False def kirim_pesan_wa(driver, nomor: str, pesan: str): url = f"https://web.whatsapp.com/send?phone={nomor}" print(f"{INFO} 📤 Membuka: {nomor}{RESET}") driver.get(url) time.sleep(5) # Tambah waktu load # Debug: cek judul halaman print(f"{INFO} 🔍 Memuat halaman...{RESET}") # Cari kotak pesan dengan berbagai selector (lebih lengkap) selectors = [ "div[contenteditable='true'][role='textbox']", "div[contenteditable='true']", "div[data-testid='conversation-compose-box-input']", "footer div[contenteditable='true']", "div[role='textbox']", "div[aria-label='Ketik pesan']", "div[aria-label='Type a message']", "div[aria-placeholder='Ketik pesan']", "div[aria-placeholder='Type a message']" ] message_box = None for selector in selectors: try: message_box = WebDriverWait(driver, 5).until( EC.presence_of_element_located((By.CSS_SELECTOR, selector)) ) if message_box and message_box.is_displayed(): print(f"{INFO} ✅ Kotak pesan ditemukan (selector: {selector[:30]}){RESET}") break else: message_box = None except: continue if not message_box: # Cek penyebab page_source = driver.page_source.lower() if "invalid" in page_source or "not on whatsapp" in page_source: return False, "Nomor tidak valid/tidak terdaftar" return False, "Kotak pesan tidak ditemukan" # Kirim pesan dengan metode yang lebih robust try: # Scroll ke kotak pesan driver.execute_script("arguments[0].scrollIntoView(true);", message_box) time.sleep(0.5) # Klik kotak pesan message_box.click() time.sleep(0.5) # Kosongkan dulu (jika ada) message_box.clear() time.sleep(0.3) # Ketik pesan per karakter (lebih natural) for char in pesan: message_box.send_keys(char) time.sleep(0.01) # sedikit delay antar karakter time.sleep(1) # Coba klik send button dengan berbagai cara send_btn = None # Method 1: Cari tombol send send_selectors = [ "button[data-testid='compose-btn-send']", "span[data-icon='send']", "button[aria-label='Kirim']", "button[aria-label='Send']" ] for s_selector in send_selectors: try: send_btn = driver.find_element(By.CSS_SELECTOR, s_selector) if send_btn and send_btn.is_displayed(): send_btn.click() print(f"{OK} ✅ Pesan terkirim (tombol send){RESET}") return True, "OK" except: continue # Method 2: Tekan Enter message_box.send_keys("\n") print(f"{OK} ✅ Pesan terkirim (Enter){RESET}") return True, "OK" except Exception as e: return False, f"Error: {str(e)[:40]}" def kirim_broadcast(driver, nomor_list, pesan): total = len(nomor_list) sukses = 0 print(f"\n{BOLD}{INFO}{'='*50}{RESET}") print(f"{BOLD}{INFO}🚀 BROADCAST DIMULAI{RESET}") print(f"{INFO}📊 Total: {total} nomor | ⏱️ Delay: {DELAY_ANTAR_KIRIM} detik{RESET}") print(f"{INFO}📝 Pesan: {pesan[:50]}{'...' if len(pesan) > 50 else ''}{RESET}") print(f"{INFO}{'='*50}{RESET}\n") for idx, nomor in enumerate(nomor_list, 1): print(f"{BOLD}[{idx}/{total}] {nomor}{RESET}") status, msg = kirim_pesan_wa(driver, nomor, pesan) if status: sukses += 1 print(f" {OK}✅ Berhasil!{RESET}") else: print(f" {ERR}❌ Gagal: {msg}{RESET}") print(f" 📊 Progress: {idx}/{total} | ✅ {sukses} | ❌ {idx-sukses}") if idx < total: print(f"{WARN} ⏳ Tunggu {DELAY_ANTAR_KIRIM} detik...{RESET}") time.sleep(DELAY_ANTAR_KIRIM) print() print(f"{BOLD}{OK}{'='*50}{RESET}") print(f"{BOLD}{OK}✅ SELESAI! Berhasil: {sukses}/{total}{RESET}") print(f"{BOLD}{OK}{'='*50}{RESET}") def main(): os.system('cls' if os.name == 'nt' else 'clear') print(f""" {BOLD}{OK}╔════════════════════════════════════════╗ ║ WA BLAST OTOMATIS (FIREFOX) ║ ║ Delay 3 detik - AMAN ║ ╚════════════════════════════════════════╝{RESET} """) driver = create_firefox_driver() if not login_whatsapp(driver): print(f"{ERR}Gagal login. Program akan keluar.{RESET}") input("Tekan Enter untuk keluar...") driver.quit() return print(f"{OK}✅ WhatsApp Web sudah siap!{RESET}") while True: print(f"\n{INFO}MENU:{RESET}") print(" 1. Kirim broadcast (dari file)") print(" 2. Test 1 nomor") print(" 3. Keluar") pilih = input("\nPilihan: ").strip() if pilih == "1": fn = input("File nomor (nomor.txt): ").strip() or "nomor.txt" fp = input("File pesan (pesan.txt): ").strip() or "pesan.txt" nomor_list = baca_nomor_dari_file(fn) if not nomor_list: print(f"{ERR}Tidak ada nomor valid!{RESET}") continue pesan = baca_pesan_dari_file(fp) if not pesan: print(f"{ERR}Pesan kosong atau file tidak ada!{RESET}") continue print(f"\n{WARN}⚠️ Akan kirim ke {len(nomor_list)} nomor{RESET}") print(f"{INFO}📝 Preview pesan:{RESET}") print(f"{WARN}{pesan[:200]}{'...' if len(pesan) > 200 else ''}{RESET}") if input("\nLanjut? (y/n): ").lower() == 'y': kirim_broadcast(driver, nomor_list, pesan) elif pilih == "2": nomor = input("Nomor: ").strip() nomor = bersihkan_nomor(nomor) if not nomor: print(f"{ERR}Nomor salah!{RESET}") continue pesan = input("Pesan: ").strip() if not pesan: print(f"{ERR}Pesan tidak boleh kosong!{RESET}") continue status, msg = kirim_pesan_wa(driver, nomor, pesan) if status: print(f"{OK}✅ Berhasil terkirim!{RESET}") else: print(f"{ERR}❌ Gagal: {msg}{RESET}") elif pilih == "3": break driver.quit() print(f"{OK}Terima kasih!{RESET}") if __name__ == "__main__": main()