Add Threading and Lock
This commit is contained in:
parent
a8749e3239
commit
1bf944a0cf
376
main.py
376
main.py
@ -3,9 +3,9 @@ import time
|
||||
import json
|
||||
import urllib.parse
|
||||
import traceback
|
||||
import subprocess # <--- اضافه شد
|
||||
import sys # <--- اضافه شد
|
||||
import threading # <--- اضافه شد
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
from flask import Flask, request, jsonify, send_file
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
@ -15,6 +15,10 @@ CHROMIUM_PATH = "/usr/bin/chromium-browser"
|
||||
if not os.path.exists(CHROMIUM_PATH):
|
||||
CHROMIUM_PATH = "/usr/bin/chromium"
|
||||
|
||||
# --- قفل برای صفبندی درخواستهای سنگین ---
|
||||
# این قفل باعث میشود لایک/فالو/کامنت یکییکی انجام شوند
|
||||
TASK_LOCK = threading.Lock()
|
||||
|
||||
# مرورگر اصلی (فقط یک بار اجرا میشود)
|
||||
playwright_instance = None
|
||||
browser = None
|
||||
@ -130,228 +134,237 @@ def inject_cookies(context, cookies_data):
|
||||
# 🚀 مسیرها (Routes)
|
||||
# =======================================================
|
||||
|
||||
@app.route('/')
|
||||
def health_check():
|
||||
"""این مسیر همیشه سریع جواب میدهد، حتی اگر ربات مشغول باشد"""
|
||||
return "OK - Server is Alive", 200
|
||||
|
||||
@app.route('/auto_like', methods=['POST'])
|
||||
def auto_like():
|
||||
context = None
|
||||
page = None
|
||||
try:
|
||||
# 1. دریافت دادهها
|
||||
data = request.json
|
||||
proxy_str = data.get('proxy') # <--- گرفتن پروکسی از ورودی
|
||||
cookies = data.get('cookies')
|
||||
post_url = data.get('post_url')
|
||||
|
||||
print(f"Request received for LIKE using proxy: {proxy_str}")
|
||||
|
||||
# 2. ساخت کانتکست اختصاصی
|
||||
context = create_context_with_proxy(proxy_str)
|
||||
page = context.new_page()
|
||||
|
||||
# 3. تزریق کوکی و رفتن به صفحه
|
||||
inject_cookies(context, cookies)
|
||||
|
||||
# استفاده از قفل: اگر کسی داخل باشد، بقیه منتظر میمانند (صف)
|
||||
with TASK_LOCK:
|
||||
context = None
|
||||
page = None
|
||||
try:
|
||||
page.goto(post_url, wait_until="networkidle", timeout=60000)
|
||||
except: pass
|
||||
# 1. دریافت دادهها
|
||||
data = request.json
|
||||
proxy_str = data.get('proxy')
|
||||
cookies = data.get('cookies')
|
||||
post_url = data.get('post_url')
|
||||
|
||||
# 4. لاگیک لایک (همان کد هوشمند قبلی)
|
||||
status = "Failed"
|
||||
target_btn = None
|
||||
print(f"Request received for LIKE using proxy: {proxy_str}")
|
||||
|
||||
# تشخیص وضعیت
|
||||
for _ in range(20):
|
||||
if page.locator('svg[aria-label="Unlike"]').count() > 0 or page.locator('svg[aria-label="نپسندیدن"]').count() > 0:
|
||||
status = "Already Liked"
|
||||
break
|
||||
# 2. ساخت کانتکست اختصاصی
|
||||
context = create_context_with_proxy(proxy_str)
|
||||
page = context.new_page()
|
||||
|
||||
likes = page.locator('svg[aria-label="Like"]')
|
||||
if likes.count() == 0: likes = page.locator('svg[aria-label="پسندیدن"]')
|
||||
# 3. تزریق کوکی و رفتن به صفحه
|
||||
inject_cookies(context, cookies)
|
||||
|
||||
if likes.count() > 0 and likes.first.is_visible():
|
||||
target_btn = likes.first
|
||||
break
|
||||
page.wait_for_timeout(1000)
|
||||
try:
|
||||
page.goto(post_url, wait_until="networkidle", timeout=60000)
|
||||
except: pass
|
||||
|
||||
if status == "Already Liked":
|
||||
pass
|
||||
elif target_btn:
|
||||
target_btn.click(force=True)
|
||||
page.wait_for_timeout(2000)
|
||||
if page.locator('svg[aria-label="Unlike"]').count() > 0:
|
||||
status = "Success: Liked [Verified]"
|
||||
# 4. لاگیک لایک
|
||||
status = "Failed"
|
||||
target_btn = None
|
||||
|
||||
# تشخیص وضعیت
|
||||
for _ in range(20):
|
||||
if page.locator('svg[aria-label="Unlike"]').count() > 0 or page.locator('svg[aria-label="نپسندیدن"]').count() > 0:
|
||||
status = "Already Liked"
|
||||
break
|
||||
|
||||
likes = page.locator('svg[aria-label="Like"]')
|
||||
if likes.count() == 0: likes = page.locator('svg[aria-label="پسندیدن"]')
|
||||
|
||||
if likes.count() > 0 and likes.first.is_visible():
|
||||
target_btn = likes.first
|
||||
break
|
||||
page.wait_for_timeout(1000)
|
||||
|
||||
if status == "Already Liked":
|
||||
pass
|
||||
elif target_btn:
|
||||
target_btn.click(force=True)
|
||||
page.wait_for_timeout(2000)
|
||||
if page.locator('svg[aria-label="Unlike"]').count() > 0:
|
||||
status = "Success: Liked [Verified]"
|
||||
else:
|
||||
status = "Success: Clicked (No Verify)"
|
||||
else:
|
||||
status = "Success: Clicked (No Verify)"
|
||||
else:
|
||||
page.mouse.dblclick(page.viewport_size['width']/2, page.viewport_size['height']/2)
|
||||
status = "Success: DoubleTap"
|
||||
page.mouse.dblclick(page.viewport_size['width']/2, page.viewport_size['height']/2)
|
||||
status = "Success: DoubleTap"
|
||||
|
||||
page.screenshot(path="current_view.png")
|
||||
return jsonify({"status": status, "proxy_used": proxy_str})
|
||||
page.screenshot(path="current_view.png")
|
||||
return jsonify({"status": status, "proxy_used": proxy_str})
|
||||
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
return jsonify({"error": str(e)}), 500
|
||||
finally:
|
||||
# بسیار مهم: بستن کانتکست برای آزاد کردن رم و آیپی
|
||||
if context: context.close()
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
return jsonify({"error": str(e)}), 500
|
||||
finally:
|
||||
if context: context.close()
|
||||
|
||||
|
||||
@app.route('/auto_comment', methods=['POST'])
|
||||
def auto_comment():
|
||||
context = None
|
||||
page = None
|
||||
try:
|
||||
data = request.json
|
||||
proxy_str = data.get('proxy')
|
||||
cookies = data.get('cookies')
|
||||
post_url = data.get('post_url')
|
||||
comment_text = data.get('comment_text')
|
||||
|
||||
print(f"Request received for COMMENT using proxy: {proxy_str}")
|
||||
|
||||
context = create_context_with_proxy(proxy_str)
|
||||
page = context.new_page()
|
||||
inject_cookies(context, cookies)
|
||||
|
||||
with TASK_LOCK:
|
||||
context = None
|
||||
page = None
|
||||
try:
|
||||
page.goto(post_url, wait_until="networkidle", timeout=60000)
|
||||
except: pass
|
||||
data = request.json
|
||||
proxy_str = data.get('proxy')
|
||||
cookies = data.get('cookies')
|
||||
post_url = data.get('post_url')
|
||||
comment_text = data.get('comment_text')
|
||||
|
||||
if page.locator("input[name='username']").count() > 0:
|
||||
return jsonify({"status": "Failed", "reason": "Login Redirect"}), 401
|
||||
print(f"Request received for COMMENT using proxy: {proxy_str}")
|
||||
|
||||
# لاگیک کامنت (همان کد نهایی و وریفای شده)
|
||||
bubbles = [page.locator(s) for s in ['svg[aria-label="Comment"]', 'svg[aria-label="محاوره"]', 'svg[class*="_ab6-"]']]
|
||||
bubbles.append(page.get_by_role("button").filter(has_text="Comment"))
|
||||
context = create_context_with_proxy(proxy_str)
|
||||
page = context.new_page()
|
||||
inject_cookies(context, cookies)
|
||||
|
||||
for b in bubbles:
|
||||
if b.count() > 0 and b.first.is_visible():
|
||||
b.first.click(force=True)
|
||||
break
|
||||
try:
|
||||
page.goto(post_url, wait_until="networkidle", timeout=60000)
|
||||
except: pass
|
||||
|
||||
input_ready = False
|
||||
for _ in range(30):
|
||||
placeholders = page.get_by_text("Add a comment...", exact=False)
|
||||
if placeholders.count() > 0 and placeholders.last.is_visible():
|
||||
placeholders.last.click(force=True)
|
||||
input_ready = True
|
||||
break
|
||||
forms = page.locator("form")
|
||||
if forms.count() > 0 and forms.last.is_visible():
|
||||
forms.last.click(force=True)
|
||||
input_ready = True
|
||||
break
|
||||
page.wait_for_timeout(1000)
|
||||
if page.locator("input[name='username']").count() > 0:
|
||||
return jsonify({"status": "Failed", "reason": "Login Redirect"}), 401
|
||||
|
||||
status = "Failed"
|
||||
if input_ready:
|
||||
page.wait_for_timeout(1000)
|
||||
candidates = [
|
||||
page.locator('div[contenteditable="true"][role="textbox"]').last,
|
||||
page.locator('form textarea').last
|
||||
]
|
||||
final_box = next((c for c in candidates if c.is_visible()), None)
|
||||
# لاگیک کامنت
|
||||
bubbles = [page.locator(s) for s in ['svg[aria-label="Comment"]', 'svg[aria-label="محاوره"]', 'svg[class*="_ab6-"]']]
|
||||
bubbles.append(page.get_by_role("button").filter(has_text="Comment"))
|
||||
|
||||
if final_box:
|
||||
final_box.click()
|
||||
final_box.type(comment_text, delay=100)
|
||||
page.wait_for_timeout(500)
|
||||
for b in bubbles:
|
||||
if b.count() > 0 and b.first.is_visible():
|
||||
b.first.click(force=True)
|
||||
break
|
||||
|
||||
post_btn = page.locator('div[role="button"]').filter(has_text="Post").last
|
||||
if not post_btn.is_visible(): post_btn = page.locator('div[role="button"]').filter(has_text="ارسال").last
|
||||
input_ready = False
|
||||
for _ in range(30):
|
||||
placeholders = page.get_by_text("Add a comment...", exact=False)
|
||||
if placeholders.count() > 0 and placeholders.last.is_visible():
|
||||
placeholders.last.click(force=True)
|
||||
input_ready = True
|
||||
break
|
||||
forms = page.locator("form")
|
||||
if forms.count() > 0 and forms.last.is_visible():
|
||||
forms.last.click(force=True)
|
||||
input_ready = True
|
||||
break
|
||||
page.wait_for_timeout(1000)
|
||||
|
||||
if post_btn.is_visible():
|
||||
post_btn.click()
|
||||
status = "Success: Posted [Verified]"
|
||||
else:
|
||||
page.keyboard.press("Enter")
|
||||
status = "Success: Enter [Verified]"
|
||||
status = "Failed"
|
||||
if input_ready:
|
||||
page.wait_for_timeout(1000)
|
||||
candidates = [
|
||||
page.locator('div[contenteditable="true"][role="textbox"]').last,
|
||||
page.locator('form textarea').last
|
||||
]
|
||||
final_box = next((c for c in candidates if c.is_visible()), None)
|
||||
|
||||
page.screenshot(path="current_view.png")
|
||||
return jsonify({"status": status, "proxy_used": proxy_str})
|
||||
if final_box:
|
||||
final_box.click()
|
||||
final_box.type(comment_text, delay=100)
|
||||
page.wait_for_timeout(500)
|
||||
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
return jsonify({"error": str(e)}), 500
|
||||
finally:
|
||||
if context: context.close()
|
||||
post_btn = page.locator('div[role="button"]').filter(has_text="Post").last
|
||||
if not post_btn.is_visible(): post_btn = page.locator('div[role="button"]').filter(has_text="ارسال").last
|
||||
|
||||
if post_btn.is_visible():
|
||||
post_btn.click()
|
||||
status = "Success: Posted [Verified]"
|
||||
else:
|
||||
page.keyboard.press("Enter")
|
||||
status = "Success: Enter [Verified]"
|
||||
|
||||
page.screenshot(path="current_view.png")
|
||||
return jsonify({"status": status, "proxy_used": proxy_str})
|
||||
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
return jsonify({"error": str(e)}), 500
|
||||
finally:
|
||||
if context: context.close()
|
||||
|
||||
|
||||
@app.route('/auto_follow', methods=['POST'])
|
||||
def auto_follow():
|
||||
context = None
|
||||
page = None
|
||||
try:
|
||||
data = request.json
|
||||
proxy_str = data.get('proxy')
|
||||
cookies = data.get('cookies')
|
||||
target_user = data.get('target_username')
|
||||
|
||||
print(f"Request received for FOLLOW using proxy: {proxy_str}")
|
||||
|
||||
context = create_context_with_proxy(proxy_str)
|
||||
page = context.new_page()
|
||||
inject_cookies(context, cookies)
|
||||
|
||||
with TASK_LOCK:
|
||||
context = None
|
||||
page = None
|
||||
try:
|
||||
page.goto(f"https://www.instagram.com/{target_user}/", wait_until="networkidle", timeout=60000)
|
||||
except: pass
|
||||
data = request.json
|
||||
proxy_str = data.get('proxy')
|
||||
cookies = data.get('cookies')
|
||||
target_user = data.get('target_username')
|
||||
|
||||
status = "Failed"
|
||||
target_btn = None
|
||||
done_keys = ["Following", "Requested", "دنبال شد", "درخواست شد"]
|
||||
follow_keys = ["Follow", "Follow Back", "دنبال کردن"]
|
||||
print(f"Request received for FOLLOW using proxy: {proxy_str}")
|
||||
|
||||
for _ in range(20):
|
||||
buttons = page.locator("header button")
|
||||
if buttons.count() == 0: buttons = page.locator("button")
|
||||
found = False
|
||||
for i in range(buttons.count()):
|
||||
btn = buttons.nth(i)
|
||||
if not btn.is_visible(): continue
|
||||
txt = btn.text_content().strip()
|
||||
context = create_context_with_proxy(proxy_str)
|
||||
page = context.new_page()
|
||||
inject_cookies(context, cookies)
|
||||
|
||||
if any(k in txt for k in done_keys):
|
||||
status = f"Already: {txt}"
|
||||
found = True; break
|
||||
try:
|
||||
page.goto(f"https://www.instagram.com/{target_user}/", wait_until="networkidle", timeout=60000)
|
||||
except: pass
|
||||
|
||||
if ("Message" in txt or "پیام" in txt) and page.get_by_text("Follow", exact=True).count() == 0:
|
||||
pass
|
||||
status = "Failed"
|
||||
target_btn = None
|
||||
done_keys = ["Following", "Requested", "دنبال شد", "درخواست شد"]
|
||||
follow_keys = ["Follow", "Follow Back", "دنبال کردن"]
|
||||
|
||||
if any(k == txt or k in txt for k in follow_keys) and "Following" not in txt:
|
||||
target_btn = btn
|
||||
found = True; break
|
||||
if found: break
|
||||
page.wait_for_timeout(1000)
|
||||
for _ in range(20):
|
||||
buttons = page.locator("header button")
|
||||
if buttons.count() == 0: buttons = page.locator("button")
|
||||
found = False
|
||||
for i in range(buttons.count()):
|
||||
btn = buttons.nth(i)
|
||||
if not btn.is_visible(): continue
|
||||
txt = btn.text_content().strip()
|
||||
|
||||
if "Already" in status:
|
||||
pass
|
||||
elif target_btn:
|
||||
target_btn.click(force=True)
|
||||
for _ in range(15):
|
||||
if any(k in txt for k in done_keys):
|
||||
status = f"Already: {txt}"
|
||||
found = True; break
|
||||
|
||||
if ("Message" in txt or "پیام" in txt) and page.get_by_text("Follow", exact=True).count() == 0:
|
||||
pass
|
||||
|
||||
if any(k == txt or k in txt for k in follow_keys) and "Following" not in txt:
|
||||
target_btn = btn
|
||||
found = True; break
|
||||
if found: break
|
||||
page.wait_for_timeout(1000)
|
||||
full_text = page.locator("header").text_content()
|
||||
if any(k in full_text for k in done_keys):
|
||||
status = "Success: Followed [Verified]"
|
||||
break
|
||||
if "Success" not in status: status = "Success: Clicked (Timeout)"
|
||||
|
||||
page.screenshot(path="current_view.png")
|
||||
return jsonify({"status": status, "target": target_user})
|
||||
if "Already" in status:
|
||||
pass
|
||||
elif target_btn:
|
||||
target_btn.click(force=True)
|
||||
for _ in range(15):
|
||||
page.wait_for_timeout(1000)
|
||||
full_text = page.locator("header").text_content()
|
||||
if any(k in full_text for k in done_keys):
|
||||
status = "Success: Followed [Verified]"
|
||||
break
|
||||
if "Success" not in status: status = "Success: Clicked (Timeout)"
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
finally:
|
||||
if context: context.close()
|
||||
page.screenshot(path="current_view.png")
|
||||
return jsonify({"status": status, "target": target_user})
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
finally:
|
||||
if context: context.close()
|
||||
|
||||
@app.route('/screenshot')
|
||||
def serve_screenshot():
|
||||
# اسکرین شات پشت قفل نمیماند
|
||||
if os.path.exists("current_view.png"):
|
||||
return send_file("current_view.png", mimetype='image/png')
|
||||
return "No image"
|
||||
|
||||
# =======================================================
|
||||
# 🔄 تابع آپدیت خودکار (اضافه شده)
|
||||
# 🔄 تابع آپدیت خودکار
|
||||
# =======================================================
|
||||
def auto_update_loop():
|
||||
"""هر 60 ثانیه چک میکند اگر آپدیت آمده بود، برنامه را ریستارت میکند"""
|
||||
@ -381,7 +394,6 @@ def auto_update_loop():
|
||||
print("[UPDATER] Restarting application...\n")
|
||||
|
||||
# 5. Restart
|
||||
# بستن مرورگر قبل از ریستارت (اختیاری ولی تمیزتر)
|
||||
if playwright_instance:
|
||||
playwright_instance.stop()
|
||||
|
||||
@ -389,7 +401,7 @@ def auto_update_loop():
|
||||
|
||||
except Exception as e:
|
||||
print(f"[UPDATER ERROR]: {e}")
|
||||
time.sleep(60) # اگر خطا داد، صبر کن و دوباره تلاش کن
|
||||
time.sleep(60)
|
||||
|
||||
if __name__ == '__main__':
|
||||
# --- شروع ترد آپدیت کننده در پسزمینه ---
|
||||
@ -397,6 +409,8 @@ if __name__ == '__main__':
|
||||
update_thread.start()
|
||||
print(">>> Auto-updater started in background...")
|
||||
|
||||
# حتما threaded=False باشد تا تداخل در Playwright پیش نیاید
|
||||
start_browser_engine() # استارت اولیه موتور
|
||||
app.run(host='0.0.0.0', port=5000, threaded=False)
|
||||
|
||||
# تغییر مهم: threaded=True فعال شد
|
||||
# درخواستهای همزمان پذیرفته میشوند، اما توابع سنگین با TASK_LOCK صفبندی میشوند
|
||||
app.run(host='0.0.0.0', port=5000, threaded=True)
|
||||
Loading…
x
Reference in New Issue
Block a user