feat: implement FastAPI service with device-locked automation for Shopee commissions and Facebook commenting
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import logging
|
||||
import uiautomator2 as u2
|
||||
import time
|
||||
import os
|
||||
|
||||
class FacebookBot:
|
||||
def __init__(self, device_ip: str):
|
||||
logging.info(f"Đang kết nối tới thiết bị {device_ip} để chạy Facebook...")
|
||||
self.d = u2.connect(device_ip)
|
||||
logging.info("Kết nối thành công!")
|
||||
|
||||
def push_image_to_gallery(self, local_image_path: str):
|
||||
if not local_image_path or not os.path.exists(local_image_path):
|
||||
logging.warning("Không có file ảnh cục bộ để đẩy lên điện thoại.")
|
||||
return False
|
||||
|
||||
logging.info(f"Đang đẩy ảnh {local_image_path} vào điện thoại...")
|
||||
# Đẩy ảnh vào thư mục Pictures
|
||||
remote_path = "/sdcard/Pictures/n8n_fb_comment.jpg"
|
||||
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
|
||||
logging.info("Đang cập nhật thư viện ảnh...")
|
||||
self.d.shell(f'am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file://{remote_path}')
|
||||
time.sleep(2)
|
||||
return True
|
||||
|
||||
def post_comment(self, group_id: str, content: str, local_image_path: str = None) -> dict:
|
||||
try:
|
||||
self.d.screen_on()
|
||||
|
||||
# Đẩy ảnh vào điện thoại nếu có
|
||||
has_image = False
|
||||
if local_image_path:
|
||||
has_image = self.push_image_to_gallery(local_image_path)
|
||||
|
||||
logging.info(f"Khởi động Facebook và vào Group ID: {group_id}...")
|
||||
# Dùng lệnh ADB shell để mở thẳng Deep Link của Group
|
||||
self.d.shell(f'am start -a android.intent.action.VIEW -d "fb://group/{group_id}"')
|
||||
time.sleep(8) # Đợi load giao diện group
|
||||
|
||||
# Tìm nút "Bình luận" của bài viết đầu tiên hiển thị
|
||||
logging.info("Tìm nút Bình luận của bài viết đầu tiên...")
|
||||
# Facebook dùng description hoặc text, thường là "Bình luận" hoặc "Comment"
|
||||
comment_btn = self.d(descriptionContains="Bình luận")
|
||||
if not comment_btn.exists:
|
||||
comment_btn = self.d(descriptionContains="Comment")
|
||||
|
||||
if comment_btn.exists:
|
||||
comment_btn[0].click() # Click vào bài đầu tiên tìm thấy
|
||||
else:
|
||||
self.dump_ui_for_debug("error_fb_no_comment_btn.xml")
|
||||
return {"status": "error", "message": "Không tìm thấy nút Bình luận. Đã lưu dump UI."}
|
||||
|
||||
time.sleep(4)
|
||||
|
||||
# Gõ nội dung
|
||||
logging.info("Đang nhập nội dung comment...")
|
||||
edit_box = self.d(className="android.widget.EditText")
|
||||
if edit_box.exists:
|
||||
edit_box.set_text(content)
|
||||
time.sleep(1)
|
||||
else:
|
||||
self.dump_ui_for_debug("error_fb_no_edittext.xml")
|
||||
return {"status": "error", "message": "Không tìm thấy ô nhập chữ."}
|
||||
|
||||
# Chọn ảnh nếu có
|
||||
if has_image:
|
||||
logging.info("Đang chọn ảnh đính kèm...")
|
||||
# Nút chọn ảnh thường có chữ Camera hoặc Ảnh/Video
|
||||
photo_btn = self.d(descriptionContains="ảnh")
|
||||
if not photo_btn.exists:
|
||||
photo_btn = self.d(descriptionContains="photo")
|
||||
|
||||
if photo_btn.exists:
|
||||
photo_btn.click()
|
||||
time.sleep(3)
|
||||
|
||||
# Chọn ảnh đầu tiên trong thư viện (vừa mới push vào)
|
||||
# Facebook gallery thường có checkbox hoặc chỉ cần click vào vùng ảnh
|
||||
gallery_images = self.d(className="android.widget.ImageView")
|
||||
if gallery_images.count >= 2:
|
||||
gallery_images[1].click() # Bỏ qua icon đầu tiên thường là icon camera
|
||||
time.sleep(1)
|
||||
|
||||
# Nút Xong / Done
|
||||
done_btn = self.d(textContains="Xong")
|
||||
if not done_btn.exists:
|
||||
done_btn = self.d(textContains="Done")
|
||||
if done_btn.exists:
|
||||
done_btn.click()
|
||||
time.sleep(1)
|
||||
else:
|
||||
logging.warning("Không tìm thấy nút đính kèm ảnh, sẽ chỉ gửi text.")
|
||||
|
||||
# Bấm gửi
|
||||
logging.info("Bấm nút Gửi (Send)...")
|
||||
send_btn = self.d(descriptionContains="Gửi")
|
||||
if not send_btn.exists:
|
||||
send_btn = self.d(descriptionContains="Send")
|
||||
|
||||
if send_btn.exists:
|
||||
send_btn.click()
|
||||
time.sleep(3)
|
||||
return {"status": "success", "message": "Đã comment thành công!"}
|
||||
else:
|
||||
self.dump_ui_for_debug("error_fb_no_send_btn.xml")
|
||||
return {"status": "error", "message": "Không tìm thấy nút Gửi."}
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Lỗi Facebook Bot: {str(e)}")
|
||||
return {"status": "error", "message": f"Lỗi không xác định: {str(e)}"}
|
||||
|
||||
finally:
|
||||
logging.info("Thoát ra màn hình chính (Homescreen)...")
|
||||
self.d.press("home")
|
||||
|
||||
def dump_ui_for_debug(self, filename: str):
|
||||
xml_dump = self.d.dump_hierarchy()
|
||||
with open(filename, "w", encoding="utf-8") as f:
|
||||
f.write(xml_dump)
|
||||
logging.error(f"Đã lưu giao diện lỗi vào file {filename}")
|
||||
Reference in New Issue
Block a user