feat: add FastAPI server and Facebook automation bot for device-based task execution

This commit is contained in:
Victor Phan
2026-07-23 14:50:39 +07:00
parent 384be28c2b
commit fea2789bd4
2 changed files with 54 additions and 13 deletions
+18 -7
View File
@@ -14,9 +14,15 @@ class FacebookBot:
logging.warning("Không có file ảnh cục bộ để đẩy lên điện thoại.") logging.warning("Không có file ảnh cục bộ để đẩy lên điện thoại.")
return False return False
import uuid
logging.info(f"Đang đẩy ảnh {local_image_path} vào điện thoại...") logging.info(f"Đang đẩy ảnh {local_image_path} vào điện thoại...")
# Đẩy ảnh vào thư mục Pictures # Tạo tên file độc nhất để đảm bảo luôn hiện lên ĐẦU thư viện ảnh
remote_path = "/sdcard/Pictures/n8n_fb_comment.jpg" random_name = f"n8n_fb_{uuid.uuid4().hex[:8]}.jpg"
remote_path = f"/sdcard/Pictures/{random_name}"
# Xóa bớt các file tạm cũ trước khi đẩy file mới (giữ điện thoại sạch sẽ)
self.d.shell("rm -f /sdcard/Pictures/n8n_fb_*.jpg")
self.d.push(local_image_path, remote_path) self.d.push(local_image_path, remote_path)
# Gọi Media Scanner để Thư viện ảnh nhận diện ngay lập tức # Gọi Media Scanner để Thư viện ảnh nhận diện ngay lập tức
@@ -111,11 +117,16 @@ class FacebookBot:
time.sleep(3) time.sleep(3)
self.dump_ui_for_debug("fb_step5_camera_opened.xml") self.dump_ui_for_debug("fb_step5_camera_opened.xml")
# Chọn ảnh đầu tiên trong thư viện (vừa mới push vào) # Facebook gallery mới dùng Button với description 'Ảnh, mục X...'
# Facebook gallery thường có checkbox hoặc chỉ cần click vào vùng ảnh gallery_images = self.d(descriptionContains="Ảnh, mục")
gallery_images = self.d(className="android.widget.ImageView") if not gallery_images.exists:
if gallery_images.count >= 2: gallery_images = self.d(descriptionContains="Photo")
gallery_images[1].click() # Bỏ qua icon đầu tiên thường là icon camera if not gallery_images.exists:
gallery_images = self.d(descriptionContains="Ảnh")
if gallery_images.exists:
# Bấm vào ảnh đầu tiên (ảnh vừa mới push vào điện thoại sẽ nằm ở đầu danh sách)
gallery_images[0].click()
time.sleep(1) time.sleep(1)
self.dump_ui_for_debug("fb_step6_image_selected.xml") self.dump_ui_for_debug("fb_step6_image_selected.xml")
+34 -4
View File
@@ -38,6 +38,7 @@ class FacebookCommentRequest(BaseModel):
group_id: Any group_id: Any
content: str content: str
image_url: Optional[str] = None image_url: Optional[str] = None
image_base64: Optional[str] = None
@app.post("/get_commission") @app.post("/get_commission")
def get_commission_endpoint(req: CommissionRequest): def get_commission_endpoint(req: CommissionRequest):
@@ -79,18 +80,47 @@ def post_facebook_comment(req: FacebookCommentRequest):
bot = FacebookBot(device_ip=raw_ip) bot = FacebookBot(device_ip=raw_ip)
local_image_path = None local_image_path = None
if req.image_url: 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 # Tải ảnh về máy chủ Python tạm thời
local_image_path = f"temp_fb_{uuid.uuid4().hex}.jpg" local_image_path = f"temp_fb_{uuid.uuid4().hex}.jpg"
urllib.request.urlretrieve(str(req.image_url), local_image_path) urllib.request.urlretrieve(url, local_image_path)
logging.info(f"Đã tải ảnh tạm thời: {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) result = bot.post_comment(str(req.group_id), str(req.content), local_image_path)
# Dọn dẹp ảnh tạm sau khi xong # Dọn dẹp ảnh tạm sau khi xong
if local_image_path and os.path.exists(local_image_path): if local_image_path and os.path.exists(local_image_path):
if "temp_fb_" in local_image_path:
os.remove(local_image_path) os.remove(local_image_path)
logging.info(f"Đã xóa ảnh tạm: {local_image_path}") logging.info(f"Đã dọn dẹp file tạm: {local_image_path}")
if isinstance(result, dict) and "status" in result: 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 # Trả về luôn JSON kết quả, n8n sẽ nhận HTTP 200 và tiếp tục vòng lặp