import o import io import json import urllib.parse import requests from fastapi import FastAPI, File, UploadFile, Form, Request, BackgroundTasks from fastapi.responses import HTMLResponse from PIL import Image import imagehash from playwright.sync_api import sync_playwright app = FastAPI() DATABASE_DIR = "scam_database" VOTES_FILE = "votes.json" # ฟังก์ชันโหลดข้อมูลโหวตจากไฟล์ JSON def load_votes(): if os.path.exists(VOTES_FILE): try: with open(VOTES_FILE, "r", encoding="utf-8") as f: return json.load(f) except Exception: return {} return {} # ฟังก์ชันบันทึกข้อมูลโหวตลงไฟล์ JSON def save_votes(votes_data): with open(VOTES_FILE, "w", encoding="utf-8") as f: json.dump(votes_data, f, ensure_ascii=False, indent=2) # ฟังก์ชันดึง Hash ของรูปทั้งหมดในฐานข้อมูล def get_database_hashes(): hashes = {} if not os.path.exists(DATABASE_DIR): os.makedirs(DATABASE_DIR) for filename in os.listdir(DATABASE_DIR): if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.webp')): filepath = os.path.join(DATABASE_DIR, filename) try: img = Image.open(filepath) h = str(imagehash.phash(img)) hashes[filename] = h except Exception as e: print(f"Error loading {filename}: {e}") return hashes # ฟังก์ชันดึง IP ของผู้ใช้ def get_client_ip(request: Request): forwarded_for = request.headers.get("x-forwarded-for") if forwarded_for: return forwarded_for.split(",")[0].strip() return request.client.host if request.client else "127.0.0.1" # --------------------------------------------------------- # ระบบบอทสแกนรูปภาพจาก TikTok (เพิ่มระบบกันตรวจจับบอท) # --------------------------------------------------------- def run_tiktok_bot_task(keyword: str): db_hashes = get_database_hashes() existing_hash_objs = [imagehash.hex_to_hash(h) for h in db_hashes.values()] MATCH_THRESHOLD = 12 encoded_keyword = urllib.parse.quote(keyword) search_url = f"https://www.tiktok.com/search?q={encoded_keyword}" try: with sync_playwright() as p: # เพิ่ม args ปลอมตัวไม่ให้ TikTok บล็อกบอท browser = p.chromium.launch( headless=True, args=[ "--no-sandbox", "--disable-setuid-sandbox", "--disable-blink-features=AutomationControlled", "--disable-dev-shm-usage" ] ) context = browser.new_context( user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", viewport={"width": 1280, "height": 800}, locale="th-TH" ) page = context.new_page() # ซ่อนสถานะ webdriver page.add_init_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})") print(f"[Bot] กำลังเปิด TikTok ค้นหาคำว่า: '{keyword}'") page.goto(search_url, wait_until="domcontentloaded", timeout=45000) page.wait_for_timeout(5000) # เลื่อนหน้าจอลงหลายๆ ครั้งกระตุ้นให้คลิปโหลด for _ in range(4): page.mouse.wheel(0, 1000) page.wait_for_timeout(2000) # ค้นหาลิงก์คลิปทั้งหมดในหน้าค้นหา video_links = set() links = page.query_selector_all("a") for a in links: href = a.get_attribute("href") if href and ("/video/" in href or "/photo/" in href): if href.startswith("/"): href = "https://www.tiktok.com" + href clean_url = href.split("?")[0] video_links.add(clean_url) video_list = list(video_links) print(f"[Bot] เจอวิดีโอจากหน้าค้นหาทั้งหมด {len(video_list)} คลิป") # สแกนทีละคลิป (สูงสุด 5 คลิปแรก) for v_idx, v_url in enumerate(video_list[:5]): try: print(f"[Bot] ({v_idx+1}/{min(len(video_list), 5)}) กำลังเข้าไปสแกนคอมเมนต์คลิป: {v_url}") page.goto(v_url, wait_until="domcontentloaded", timeout=30000) page.wait_for_timeout(4000) # สกรอลล์ลงไปส่วนคอมเมนต์ page.mouse.wheel(0, 1000) page.wait_for_timeout(2500) # ดึงเฉพาะรูปภาพที่แนบมาในคอมเมนต์ comment_imgs = page.query_selector_all( "[data-e2e='comment-list'] img, [class*='comment'] img, div[class*='Comment'] img" ) print(f"[Bot] พบรูปในคอมเมนต์จำนวน {len(comment_imgs)} รูป") for img_el in comment_imgs: src = img_el.get_attribute("src") if not src or not src.startswith("http"): continue try: resp = requests.get(src, timeout=5) if resp.status_code == 200: img = Image.open(io.BytesIO(resp.content)) # กรองรูปโปรไฟล์ขนาดเล็กออก (รูปในคอมเมนต์มักจะใหญ่กว่า 120px) if img.width < 120 or img.height < 120: continue new_hash_obj = imagehash.phash(img) is_dup = False for existing_h in existing_hash_objs: if (new_hash_obj - existing_h) <= MATCH_THRESHOLD: is_dup = True break if not is_dup: filename = f"bot_{str(new_hash_obj)}.jpg" filepath = os.path.join(DATABASE_DIR, filename) img.convert("RGB").save(filepath, "JPEG") existing_hash_objs.append(new_hash_obj) print(f"[Bot] ✅ บันทึกรูปใหม่จากคอมเมนต์: {filename}") else: print(f"[Bot] ❌ รูปในคอมเมนต์ซ้ำแล้ว - ข้าม") except Exception: continue except Exception as ve: print(f"[Bot Error] เข้าดูคลิปไม่ได้: {ve}") continue browser.close() print(f"[Bot] สแกนเสร็จสิ้นสำหรับคำว่า '{keyword}'") except Exception as e: print(f"[Bot Error] {e}") # Endpoint สำหรับสั่งให้บอทเริ่มทำงาน @app.post("/bot/run") async def trigger_bot( keyword: str = Form(...), background_tasks: BackgroundTasks = BackgroundTasks() ): background_tasks.add_task(run_tiktok_bot_task, keyword) return { "success": True, "message": f"บอทเริ่มค้นหาคำว่า '{keyword}' และสแกนรูปในคอมเมนต์เรียบร้อยแล้ว" } # --------------------------------------------------------- # Endpoint เดิม (100% ตามโค้ดเดิม) # --------------------------------------------------------- @app.get("/", response_class=HTMLResponse) def serve_index(): if os.path.exists("index.html"): with open("index.html", "r", encoding="utf-8") as f: return f.read() return "