from fastapi import FastAPI, HTTPException from pydantic import BaseModel, Field from automation import ShopeeBot from typing import Optional import logging import os import sys import urllib.request import uuid import threading from collections import defaultdict from facebook_bot import FacebookBot # Quản lý hàng đợi (Lock) cho từng thiết bị device_locks = defaultdict(threading.Lock) # Cấu hình logging: ghi log ra cả màn hình (console) và file (shopee_bot.log) logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler("shopee_bot.log", encoding="utf-8"), logging.StreamHandler(sys.stdout) ] ) app = FastAPI(title="n8n Shopee Bridge API") class CommissionRequest(BaseModel): link: str # Cho phép truyền IP linh hoạt từ n8n. device_ip: Optional[str] = Field(default=None, description="IP:Port của điện thoại, ví dụ 192.168.1.100:5555 (WiFi) hoặc 10.8.0.5:5555 (VPN)") from typing import Optional, Any class FacebookCommentRequest(BaseModel): device_ip: Optional[str] = Field(default=None, description="IP:Port của điện thoại (Cấu hình trên n8n)") group_id: Any content: str image_url: Optional[str] = None image_base64: Optional[str] = None @app.post("/get_commission") def get_commission_endpoint(req: CommissionRequest): if not req.link: raise HTTPException(status_code=400, detail="Thiếu link sản phẩm") try: # Lấy IP theo độ ưu tiên raw_ip = req.device_ip or os.environ.get("DEVICE_IP") or "" lock_key = raw_ip if raw_ip else "default_device" # Dùng Lock để xếp hàng: Nếu điện thoại đang bận, request sau phải chờ with device_locks[lock_key]: logging.info(f"Đã khóa thiết bị {lock_key} để chạy tác vụ Shopee.") bot = ShopeeBot(device_ip=raw_ip) result = bot.get_commission_rate(req.link) # Nếu đã có status thì gộp thẳng các thuộc tính của result vào JSON trả về if isinstance(result, dict) and "status" in result: return result return { "status": "success", "commission": result } except Exception as e: logging.error(f"Lỗi API: {str(e)}") raise HTTPException(status_code=500, detail=str(e)) @app.post("/post_facebook_comment") def post_facebook_comment(req: FacebookCommentRequest): try: raw_ip = req.device_ip or os.environ.get("DEVICE_IP") or "" lock_key = raw_ip if raw_ip else "default_device" with device_locks[lock_key]: logging.info(f"Đã khóa thiết bị {lock_key} để chạy tác vụ Facebook.") bot = FacebookBot(device_ip=raw_ip) local_image_path = None if req.image_base64: import base64 # Bỏ phần header data:image/jpeg;base64, nếu có b64_data = req.image_base64 if "," in b64_data: b64_data = b64_data.split(",", 1)[1] local_image_path = f"temp_fb_{uuid.uuid4().hex}.jpg" with open(local_image_path, "wb") as f: f.write(base64.b64decode(b64_data)) logging.info(f"Đã tạo ảnh từ chuỗi Base64: {local_image_path}") elif req.image_url: import re url = str(req.image_url) # Kiểm tra xem url có phải là một đường dẫn file nội bộ trên máy hay không if os.path.exists(url): local_image_path = url logging.info(f"Sử dụng file ảnh nội bộ có sẵn: {local_image_path}") else: # Tự động chuyển link Google Drive sang link tải trực tiếp if "drive.google.com" in url: file_id_match = re.search(r"/d/([a-zA-Z0-9_-]+)", url) if file_id_match: file_id = file_id_match.group(1) url = f"https://drive.google.com/uc?export=download&id={file_id}" logging.info(f"Đã convert link Google Drive thành direct link: {url}") # Tải ảnh về máy chủ Python tạm thời local_image_path = f"temp_fb_{uuid.uuid4().hex}.jpg" urllib.request.urlretrieve(url, local_image_path) logging.info(f"Đã tải ảnh tạm thời từ internet: {local_image_path}") result = bot.post_comment(str(req.group_id), str(req.content), local_image_path) # Dọn dẹp ảnh tạm sau khi xong if local_image_path and os.path.exists(local_image_path): if "temp_fb_" in local_image_path: os.remove(local_image_path) logging.info(f"Đã dọn dẹp file tạm: {local_image_path}") if isinstance(result, dict) and "status" in result: # Trả về luôn JSON kết quả, n8n sẽ nhận HTTP 200 và tiếp tục vòng lặp # Ngay cả khi status == "error", ta vẫn muốn n8n chạy tiếp nhóm khác return result return {"status": "success", "message": "Hoàn tất"} except Exception as e: logging.error(f"Lỗi API Facebook: {str(e)}") raise HTTPException(status_code=500, detail=str(e)) if __name__ == "__main__": import uvicorn # Chạy server API ở cổng 8000 uvicorn.run(app, host="0.0.0.0", port=8000)