facebook-tool/services/profile_service.py

143 lines
5.6 KiB
Python

# services/profile_service.py
import os
import re
import shutil
import logging
from typing import Optional, List
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
def _sanitize_name(name: str) -> str:
"""
Chuyển tên (email/username) thành dạng an toàn cho filesystem.
Giữ chữ, số, dấu gạch ngang và gạch dưới.
Ví dụ: "user+test@example.com" -> "user-test_example-com"
"""
if not isinstance(name, str):
raise ValueError("Profile name must be a string")
name = name.strip().lower()
# Thay các kí tự '@' và '+' thành '-'
name = name.replace("@", "-at-").replace("+", "-plus-")
# Thay các kí tự không an toàn thành '-'
name = re.sub(r"[^a-z0-9._-]", "-", name)
# Compact nhiều dấu '-' liên tiếp
name = re.sub(r"-{2,}", "-", name)
# Trim dấu '-' đầu cuối
name = name.strip("-")
return name or "profile"
class ProfileService:
"""
Service để quản lý thư mục profiles.
Mặc định root folder là ./profiles (tương đối với working dir).
"""
def __init__(self, profiles_root: Optional[str] = None):
self.profiles_root = os.path.abspath(profiles_root or "profiles")
os.makedirs(self.profiles_root, exist_ok=True)
logger.info("Profiles root: %s", self.profiles_root)
def get_profile_dirname(self, name: str) -> str:
"""Tên folder đã sanitize (chỉ tên folder, không có path)"""
return _sanitize_name(name)
def get_profile_path(self, name: str) -> str:
"""Trả về path tuyệt đối tới folder profile"""
return os.path.join(self.profiles_root, self.get_profile_dirname(name))
def exists(self, name: str) -> bool:
"""Check folder có tồn tại không"""
return os.path.isdir(self.get_profile_path(name))
def create(self, name: str, copy_from: Optional[str] = None, exist_ok: bool = True) -> str:
"""
Tạo folder profile.
- name: email/username
- copy_from: nếu truyền path tới folder mẫu, sẽ copy nội dung từ đó
- exist_ok: nếu True và folder đã tồn tại thì không raise
Trả về path tới folder profile.
"""
path = self.get_profile_path(name)
if os.path.isdir(path):
if exist_ok:
logger.debug("Profile already exists: %s", path)
return path
raise FileExistsError(f"Profile already exists: {path}")
os.makedirs(path, exist_ok=True)
logger.info("Created profile dir: %s", path)
if copy_from:
copy_from = os.path.abspath(copy_from)
if os.path.isdir(copy_from):
# copy nội dung bên trong copy_from vào path
for item in os.listdir(copy_from):
s = os.path.join(copy_from, item)
d = os.path.join(path, item)
if os.path.isdir(s):
shutil.copytree(s, d, dirs_exist_ok=True)
else:
shutil.copy2(s, d)
logger.info("Copied profile template from %s to %s", copy_from, path)
else:
logger.warning("copy_from path not found or not a dir: %s", copy_from)
return path
def delete(self, name: str, ignore_errors: bool = False) -> None:
"""Xóa folder profile (recursive)."""
path = self.get_profile_path(name)
if not os.path.isdir(path):
raise FileNotFoundError(f"Profile not found: {path}")
shutil.rmtree(path, ignore_errors=ignore_errors)
logger.info("Deleted profile dir: %s", path)
def list_profiles(self) -> List[str]:
"""Trả về danh sách tên folder (dirnames) trong profiles_root."""
try:
return sorted(
[
d
for d in os.listdir(self.profiles_root)
if os.path.isdir(os.path.join(self.profiles_root, d))
]
)
except FileNotFoundError:
return []
# ----------------- Optional: QWebEngineProfile creator -----------------
def create_qwebengine_profile(self, name: str, parent=None, profile_id: Optional[str] = None):
"""
Tạo và trả về QWebEngineProfile đã cấu hình persistent storage (cookies, local storage, cache).
Yêu cầu PyQt5.QtWebEngineWidgets được cài.
- name: email/username để đặt thư mục lưu
- parent: parent QObject cho QWebEngineProfile (thường là self)
- profile_id: tên id cho profile (tùy chọn)
"""
try:
from PyQt5.QtWebEngineWidgets import QWebEngineProfile
except Exception as e:
raise RuntimeError(
"PyQt5.QtWebEngineWidgets không khả dụng. "
"Không thể tạo QWebEngineProfile."
) from e
profile_path = self.get_profile_path(name)
os.makedirs(profile_path, exist_ok=True)
profile_name = profile_id or self.get_profile_dirname(name)
profile = QWebEngineProfile(profile_name, parent)
profile.setPersistentStoragePath(profile_path)
profile.setCachePath(profile_path)
# Force lưu cookie persist
try:
profile.setPersistentCookiesPolicy(QWebEngineProfile.ForcePersistentCookies)
except Exception:
# Một vài phiên bản PyQt có thể khác tên hằng, bọc try/except để an toàn
pass
logger.info("Created QWebEngineProfile for %s -> %s", name, profile_path)
return profile