import os import sys import cv2 import numpy as np from PyQt5.QtCore import Qt, QUrl, QTimer from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QLabel, QTextEdit, QPushButton from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile from PyQt5.QtGui import QImage from services.action_service import ActionService from services.detect_service import DetectService from services.profile_service import ProfileService from config import TEMPLATE_DIR class LoginFB(QMainWindow): def __init__(self, account=None, delay=0.3): super().__init__() self.account = account or {} self.template_dir = os.path.abspath(TEMPLATE_DIR) self.delay = delay # ✅ Lấy tên profile từ email hoặc username self.profile_name = self.account.get("email") or self.account.get("username") or "default" # --- Detect services --- self.detector = DetectService( template_dir=TEMPLATE_DIR, target_labels=["username", "password"] ) # --- UI cơ bản --- self.setWindowTitle(f"FB Auto Vision Login - {self.profile_name}") self.setFixedSize(480, 680) self.web = QWebEngineView() self.status = QLabel("Status: Ready") self.status.setAlignment(Qt.AlignLeft) self.status.setFixedHeight(20) self.log_area = QTextEdit() self.log_area.setReadOnly(True) self.log_area.setFixedHeight(120) self.log_area.setStyleSheet(""" background-color: #1e1e1e; color: #dcdcdc; font-size: 12px; font-family: Consolas, monospace; """) self.btn_refresh = QPushButton("Refresh") self.btn_refresh.setFixedHeight(30) self.btn_refresh.clicked.connect(self.refresh_page) # --- Profile --- self.profile_service = ProfileService() profile_path = self.profile_service.get_profile_path(self.profile_name) profile = self.web.page().profile() profile.setPersistentCookiesPolicy(QWebEngineProfile.ForcePersistentCookies) profile.setPersistentStoragePath(profile_path) self.log(f"[INFO] Profile applied at: {profile_path}") # --- Webview --- self.web.setUrl(QUrl("https://facebook.com")) self.web.setFixedSize(480, 480) # --- Layout --- layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.web) layout.addWidget(self.status) layout.addWidget(self.log_area) layout.addWidget(self.btn_refresh) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) # --- Services --- self.action = ActionService(webview=self.web, delay=self.delay) self.web.loadFinished.connect(self.on_web_loaded) # ---------------------------------------------------- def log(self, message: str): self.log_area.append(message) print(message) # ---------------------------------------------------- def capture_webview(self): pixmap = self.web.grab() if pixmap.isNull(): return None qimg = pixmap.toImage().convertToFormat(QImage.Format_RGBA8888) width, height = qimg.width(), qimg.height() ptr = qimg.bits() ptr.setsize(height * width * 4) arr = np.frombuffer(ptr, np.uint8).reshape((height, width, 4)) return cv2.cvtColor(arr, cv2.COLOR_RGBA2BGR) # ---------------------------------------------------- def on_web_loaded(self, ok=True): if not ok: self.log("[ERROR] Page failed to load") self.status.setText("Status: Page load failed") return self.log("[INFO] Page loaded") self.status.setText("Status: Page loaded") # ✅ Lưu profile khi load xong self.profile_service.save_profile(self.profile_name) self.log(f"[INFO] Profile saved for {self.profile_name}") # 🧠 Detect field screen = self.capture_webview() if screen is None: self.status.setText("Status: Unable to capture webview") self.log("Status: Unable to capture webview") return self.log("[INFO] Detecting email/password fields...") regions = self.detector.detect(screen) if not regions: self.status.setText("[WARN] No regions detected") self.log("[WARN] No regions detected") return self.status.setText(f"[INFO] Detected {len(regions)} valid regions") self.log(f"[INFO] Detected {len(regions)} valid regions") QTimer.singleShot(500, lambda: self.autofill_by_detection(regions)) # ---------------------------------------------------- def autofill_by_detection(self, regions): email = self.account.get("email", "") password = self.account.get("password", "") # sắp xếp để điền username trước, password sau ordered = sorted(regions, key=lambda r: ("pass" in r[0].lower(), "user" not in r[0].lower())) def do_action(i=0): if i >= len(ordered): return folder_name, filename, top_left, bottom_right, score = ordered[i] label = folder_name.lower() self.log(f"[ACTION] {folder_name}: {filename} ({score:.2f})") if ("user" in label or "email" in label) and email: self.log(f"[DO] Filling email: {email}") self.action.write_in_region(top_left, bottom_right, email) elif "pass" in label and password: self.log(f"[DO] Filling password: {'*' * len(password)}") self.action.write_in_region(top_left, bottom_right, password) QTimer.singleShot(int(self.delay * 1000), lambda: do_action(i + 1)) do_action() # ---------------------------------------------------- def refresh_page(self): self.log("[INFO] Refreshing page...") self.web.reload() if __name__ == "__main__": app = QApplication(sys.argv) fake_account = {"email": "test@example.com", "password": "123456"} win = LoginFB(account=fake_account, delay=0.5) win.show() sys.exit(app.exec_())