facebook-tool/gui/handle/login_fb.py

206 lines
6.9 KiB
Python

import os
import sys
import cv2
import numpy as np
from PyQt6.QtCore import Qt, QUrl, QTimer
from PyQt6.QtWidgets import (
QApplication,
QMainWindow,
QVBoxLayout,
QWidget,
QLabel,
QTextEdit,
QPushButton,
)
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWebEngineCore import QWebEngineProfile
from PyQt6.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):
"""Cửa sổ tự động đăng nhập Facebook bằng nhận diện hình ảnh."""
def __init__(self, account=None, delay=0.3):
super().__init__()
self.account = account or {}
self.delay = delay
self.template_dir = os.path.abspath(TEMPLATE_DIR)
# ✅ 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 setup ---
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.AlignmentFlag.AlignLeft)
self.status.setFixedHeight(20)
# Log area
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;
"""
)
# Refresh button
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)
os.makedirs(profile_path, exist_ok=True)
profile = self.web.page().profile()
profile.setPersistentCookiesPolicy(
QWebEngineProfile.PersistentCookiesPolicy.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):
"""Ghi log vào vùng log và console."""
self.log_area.append(message)
print(message)
# ----------------------------------------------------
def capture_webview(self):
"""Chụp hình ảnh nội dung webview dưới dạng numpy array (BGR)."""
pixmap = self.web.grab()
if pixmap.isNull():
return None
qimg = pixmap.toImage().convertToFormat(QImage.Format.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):
"""Khi trang web load xong."""
if not ok:
self.log("[ERROR] Page failed to load")
self.status.setText("Status: Page load failed")
return
self.log("[INFO] Page loaded successfully")
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("[WARN] 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")
# Chờ 500ms trước khi tự điền form
QTimer.singleShot(500, lambda: self.autofill_by_detection(regions))
# ----------------------------------------------------
def autofill_by_detection(self, regions):
"""Tự động điền email và password dựa vào vùng phát hiện."""
email = self.account.get("email", "")
password = self.account.get("password", "")
# sắp xếp: 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):
"""Tải lại trang."""
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())