175 lines
6.3 KiB
Python
175 lines
6.3 KiB
Python
# gui/handle/login_fb.py
|
||
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
|
||
from PyQt5.QtGui import QImage, QPixmap
|
||
|
||
from services.action_service import ActionService
|
||
from services.detect_service import DetectService
|
||
from config import TEMPLATE_DIR
|
||
|
||
|
||
class LoginFB(QMainWindow):
|
||
def __init__(self, account=None, delay=0.1):
|
||
super().__init__()
|
||
self.account = account or {}
|
||
self.template_dir = os.path.abspath(TEMPLATE_DIR)
|
||
self.delay = delay
|
||
|
||
# --- Detect services ---
|
||
self.detector = DetectService(
|
||
template_dir=TEMPLATE_DIR,
|
||
target_labels=["username", "password", "buttons/login"]
|
||
)
|
||
|
||
# --- Detect login fail templates ---
|
||
self.fail_detector = DetectService(
|
||
template_dir=os.path.join(TEMPLATE_DIR, "login_fail")
|
||
)
|
||
|
||
# --- UI ---
|
||
self.setWindowTitle("FB Auto Vision Login")
|
||
self.setFixedSize(480, 680)
|
||
|
||
self.web = QWebEngineView()
|
||
self.web.setUrl(QUrl("https://facebook.com"))
|
||
self.web.setFixedSize(480, 480)
|
||
|
||
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)
|
||
|
||
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)
|
||
|
||
self.action = ActionService(webview=self.web, delay=self.delay)
|
||
self.login_clicked = False
|
||
|
||
# Giữ reference popup (nếu cần) – hiện tại không dùng
|
||
# self.login_fail_popup = None
|
||
|
||
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):
|
||
"""Called every time page load finished"""
|
||
if not ok:
|
||
self.log("[ERROR] Page failed to load")
|
||
self.status.setText("Status: Page load failed")
|
||
return
|
||
|
||
screen = self.capture_webview()
|
||
if screen is None:
|
||
self.status.setText("Status: Unable to capture webview")
|
||
self.log("Status: Unable to capture webview")
|
||
return
|
||
|
||
# Nếu đang sau nhấn login, check login failure
|
||
if self.login_clicked:
|
||
self.log("[INFO] Page loaded after login click. Detecting login failure...")
|
||
fail_regions = self.fail_detector.detect(screen)
|
||
|
||
if fail_regions:
|
||
self.log(f"[FAIL] Login failed detected via template ({len(fail_regions)} regions):")
|
||
for folder_name, filename, top_left, bottom_right, score in fail_regions:
|
||
self.log(f" - {filename} @ {top_left}-{bottom_right} (score: {score:.2f})")
|
||
|
||
self.status.setText("Status: Login failed")
|
||
else:
|
||
self.log("[SUCCESS] Login seems successful!")
|
||
self.status.setText("Status: Login successful")
|
||
|
||
self.login_clicked = False
|
||
return
|
||
|
||
# Nếu là initial page load
|
||
self.log("[INFO] Page loaded. Starting template detection...")
|
||
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", "")
|
||
|
||
for folder_name, filename, top_left, bottom_right, score in regions:
|
||
self.log(f"[ACTION] {folder_name}: {filename} ({score:.2f})")
|
||
label = folder_name.lower()
|
||
|
||
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)
|
||
|
||
elif "button" in label or "login" in label:
|
||
self.log("[DO] Clicking login button...")
|
||
self.login_clicked = True
|
||
self.action.click_center_of_region(top_left, bottom_right)
|
||
|
||
# ----------------------------------------------------
|
||
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)
|
||
win.show()
|
||
sys.exit(app.exec_())
|