Opera8 commited on
Commit
66d9fd6
·
verified ·
1 Parent(s): 8bd13e5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +120 -132
app.py CHANGED
@@ -5,46 +5,19 @@ import uvicorn
5
  from fastapi import FastAPI, Form, File, UploadFile
6
  from fastapi.responses import HTMLResponse, StreamingResponse
7
  from playwright.async_api import async_playwright
8
- from contextlib import asynccontextmanager
9
 
 
10
  UPLOAD_DIR = "temp_uploads"
11
  os.makedirs(UPLOAD_DIR, exist_ok=True)
12
 
13
- global_browser = None
14
- pw_instance = None
15
-
16
- @asynccontextmanager
17
- async def lifespan(app: FastAPI):
18
- global global_browser, pw_instance
19
- print("⚡ در حال راه‌اندازی مرورگر سراسری کروم...")
20
- pw_instance = await async_playwright().start()
21
- global_browser = await pw_instance.chromium.launch(
22
- headless=True,
23
- args=[
24
- "--no-sandbox",
25
- "--disable-setuid-sandbox",
26
- "--disable-dev-shm-usage",
27
- "--disable-blink-features=AutomationControlled"
28
- ]
29
- )
30
- print("🚀 مرورگر سراسری با موفقیت لود شد.")
31
- yield
32
- print("🛑 در حال بستن مرورگر سراسری...")
33
- if global_browser:
34
- await global_browser.close()
35
- if pw_instance:
36
- await pw_instance.stop()
37
-
38
- app = FastAPI(lifespan=lifespan)
39
-
40
- # رابط کاربری با قابلیت خواندن استریم باینری پاسخ‌ها با استفاده از ReadableStream فرچ
41
  HTML_CHAT_INTERFACE = """
42
  <!DOCTYPE html>
43
  <html lang="fa" dir="rtl">
44
  <head>
45
  <meta charset="UTF-8">
46
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
47
- <title>Google AI Free Chatbot + Streaming</title>
48
  <style>
49
  :root {
50
  --bg-gradient: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
@@ -173,25 +146,20 @@ HTML_CHAT_INTERFACE = """
173
  formData.append('message', text || "این تصویر را تحلیل کن");
174
  if (file) formData.append('image', file);
175
 
176
- // ایجاد حباب خالی هوش مصنوعی برای آماده‌سازی فرآیند تایپ زنده
177
  const aiMessageDiv = document.createElement('div');
178
  aiMessageDiv.classList.add('message', 'ai');
179
  messagesBox.appendChild(aiMessageDiv);
180
 
181
  try {
182
  const response = await fetch('/api/chat', { method: 'POST', body: formData });
183
-
184
  if (!response.ok) {
185
- aiMessageDiv.innerText = "خطا در برقراری ارتباط با سرور گوگل.";
186
  setLoading(false);
187
  return;
188
  }
189
 
190
- // خواندن باینری استریم کلمه به کلمه خروجی پایتون
191
  const reader = response.body.getReader();
192
  const decoder = new TextDecoder("utf-8");
193
-
194
- // پنهان کردن انیمیشن لودینگ به محض شروع اولین کلمه دریافتی
195
  let firstChunk = true;
196
 
197
  while (true) {
@@ -201,7 +169,6 @@ HTML_CHAT_INTERFACE = """
201
  const chunk = decoder.decode(value, { stream: true });
202
  if (chunk) {
203
  if (firstChunk) {
204
- // حذف لودینگ سه نقطه‌ای
205
  const indicator = document.getElementById('typingIndicator');
206
  if (indicator) indicator.remove();
207
  firstChunk = false;
@@ -243,20 +210,23 @@ HTML_CHAT_INTERFACE = """
243
  </html>
244
  """
245
 
246
- # الگوریتم مهندسی معکوس برای استخراج و تصفیه متن خالص پاسخ هوش مصنوعی
247
  def clean_text(full_text: str, message: str) -> str:
 
 
 
248
  lines = full_text.split('\n')
249
  cleaned_lines = []
250
 
251
- # واژه‌های کاربری منوهای ناوبری که نباید به هیچ وجه نشت پیدا کنند
252
  global_junk = ["تصاویر", "ویدیوها", "اخبار", "نقشه‌ها", "خرید کردن", "کتاب‌ها", "مالی", "حالت موضوع‌محور", "ورود", "همه", "ویدئوها", "ارسال", "پاسخ «حالت هوشواره‌ای» آماده است", "All items removed", message]
253
 
254
  for line in lines:
255
  line_str = line.strip()
256
  if not line_str or line_str in global_junk:
257
  continue
258
- # به محض رسیدن به لایه فوتر سلب مسئولیت، کلاً خواندن را قطع کن
259
- if "هوشواره ممکن است اشتباه" in line_str or "در پاسخ‌های" in line_str or "بیشتر بدانید" in line_str:
260
  break
261
  cleaned_lines.append(line_str)
262
 
@@ -268,10 +238,6 @@ async def chat_interface():
268
 
269
  @app.post("/api/chat")
270
  async def chat_endpoint(message: str = Form(...), image: UploadFile = File(None)):
271
- global global_browser
272
- if not global_browser:
273
- return StreamingResponse(iter(["خطا: وب سرور کروم فعال نیست."]), media_type="text/plain")
274
-
275
  saved_image_path = None
276
  if image:
277
  try:
@@ -281,103 +247,125 @@ async def chat_endpoint(message: str = Form(...), image: UploadFile = File(None)
281
  except Exception as e:
282
  return StreamingResponse(iter([f"خطا در ذخیره تصویر روی داکر: {str(e)}"]), media_type="text/plain")
283
 
284
- # ساخت سشن ژنراتور استریمینگ زنده کلمات برای پاسخ در لحظه
285
  async def response_generator():
286
- # استفاده از domcontentloaded برای بالا بردن سرعت اولیه رندر فرم‌ها بدون معطلی تصاویر حجیم
287
- context = await global_browser.new_context(
288
- user_agent="Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36",
289
- viewport={"width": 412, "height": 915},
290
- locale="fa-IR", timezone_id="Asia/Tehran"
291
- )
292
- page = await context.new_page()
293
-
294
- try:
295
- url = "https://www.google.com/search?q=سلام&udm=50"
296
- await page.goto(url, wait_until="domcontentloaded", timeout=30000)
 
297
 
298
- # تزریق ۱۰۰٪ پناهنده فایل به اینپوت‌های مخفی لایه وب گوگل
299
- if saved_image_path and os.path.exists(saved_image_path):
300
- try:
301
- file_input = await page.query_selector("input[type='file']")
302
- if file_input:
303
- await file_input.set_input_files(saved_image_path)
304
- await page.wait_for_timeout(1500)
305
- except Exception as e:
306
- print(f"Bypassed image setup: {e}")
307
-
308
- # درج کوئری متنی کاربر درون کادر چت
309
- input_box = None
310
- candidates = await page.query_selector_all("textarea, input, [contenteditable='true']")
311
- for el in candidates:
312
- try:
313
- if await el.is_visible():
314
- placeholder = await el.get_attribute("placeholder") or ""
315
- if any(w in placeholder for w in ["بپرسید", "ask", "پیام", "چطور", "هرچه"]):
316
- input_box = el
317
- break
318
- except:
319
- continue
320
 
321
- if not input_box:
322
- input_box = await page.query_selector("textarea") or await page.query_selector("input[type='text']")
323
-
324
- if input_box:
325
- await input_box.focus()
326
- await input_box.fill(message)
327
- await page.wait_for_timeout(300)
328
- await input_box.press("Enter")
329
 
330
- # فایر کردن دکمه فیزیکی ارسال جهت اطمینان فرم در کلاینت‌های کند
331
- try:
332
- send_buttons = await page.query_selector_all("button, [role='button']")
333
- for btn in send_buttons:
334
- if await btn.is_visible():
335
- aria = (await btn.get_attribute("aria-label") or "").lower()
336
- if any(w in aria for w in ["send", "ارسال", "arrow", "up"]):
337
- await btn.click(timeout=1000)
 
 
 
 
 
 
 
 
 
 
 
338
  break
339
- except:
340
- pass
341
-
342
- # 🚀 حلقه مانیتورینگ استریمینگ: واکشی کلمات در زمان واقعی بدون ثانیه‌ای معطلی ثمر بخش
343
- last_sent_text = ""
344
- no_change_count = 0
345
-
346
- for _ in range(40): # حداکثر ۱۶ ثانیه پایش زنده صفحه
347
- await page.wait_for_timeout(400) # بررسی صفحه هر ۴۰۰ میلی‌ثانیه برای استریم فوق سریع
348
- full_text = await page.evaluate("() => document.body.innerText")
349
- current_clean_text = clean_text(full_text, message)
350
 
351
- # اگر متن جدیدی تایپ شده بود، تفاوت آن را فوراً روانه کلاینت کن
352
- if len(current_clean_text) > len(last_sent_text):
353
- new_chunk = current_clean_text[len(last_sent_text):]
354
- yield new_chunk
355
- last_sent_text = current_clean_text
356
- no_change_count = 0
357
- else:
358
- if len(current_clean_text) > 0:
359
- no_change_count += 1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
360
 
361
- # اگر کادر هشدار گوگل لود شده بود یا تایپ متن به پایان رسیده بود، استریم را ببند
362
- if "هوشواره ممکن است اشتباه" in full_text or "بیشتر بدانید" in full_text or "پاسخ‌های مربوط به افراد" in full_text:
363
- # ارسال نهایی باقیمانده کاراکترها برای احتیاط
364
  full_text = await page.evaluate("() => document.body.innerText")
365
  current_clean_text = clean_text(full_text, message)
 
 
 
 
 
 
 
366
  if len(current_clean_text) > len(last_sent_text):
367
- yield current_clean_text[len(last_sent_text):]
368
- break
369
-
370
- # اگر برای ۳ ثانیه هیچ متنی اضافه نشد یعنی پاسخ متوقف شده است
371
- if no_change_count > 7 and len(last_sent_text) > 0:
372
- break
373
-
374
- except Exception as e:
375
- yield f"خطای پردازش هوش مصنوعی: {str(e)}"
376
- finally:
377
- await context.close()
378
- if saved_image_path and os.path.exists(saved_image_path):
379
- try: os.remove(saved_image_path)
380
- except: pass
 
 
 
 
 
 
 
 
 
 
 
 
 
381
 
382
  return StreamingResponse(response_generator(), media_type="text/event-stream")
383
 
 
5
  from fastapi import FastAPI, Form, File, UploadFile
6
  from fastapi.responses import HTMLResponse, StreamingResponse
7
  from playwright.async_api import async_playwright
 
8
 
9
+ app = FastAPI()
10
  UPLOAD_DIR = "temp_uploads"
11
  os.makedirs(UPLOAD_DIR, exist_ok=True)
12
 
13
+ # رابط کاربری شیشه‌ای (Glassmorphism) با فرانت‌اند پایدار
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  HTML_CHAT_INTERFACE = """
15
  <!DOCTYPE html>
16
  <html lang="fa" dir="rtl">
17
  <head>
18
  <meta charset="UTF-8">
19
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
20
+ <title>Google AI Free Chatbot + Stealth Streaming</title>
21
  <style>
22
  :root {
23
  --bg-gradient: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
 
146
  formData.append('message', text || "این تصویر را تحلیل کن");
147
  if (file) formData.append('image', file);
148
 
 
149
  const aiMessageDiv = document.createElement('div');
150
  aiMessageDiv.classList.add('message', 'ai');
151
  messagesBox.appendChild(aiMessageDiv);
152
 
153
  try {
154
  const response = await fetch('/api/chat', { method: 'POST', body: formData });
 
155
  if (!response.ok) {
156
+ aiMessageDiv.innerText = "خطا در برقراری ارتباط با سرور یا مسدود شدن آی‌پی توسط گوگل.";
157
  setLoading(false);
158
  return;
159
  }
160
 
 
161
  const reader = response.body.getReader();
162
  const decoder = new TextDecoder("utf-8");
 
 
163
  let firstChunk = true;
164
 
165
  while (true) {
 
169
  const chunk = decoder.decode(value, { stream: true });
170
  if (chunk) {
171
  if (firstChunk) {
 
172
  const indicator = document.getElementById('typingIndicator');
173
  if (indicator) indicator.remove();
174
  firstChunk = false;
 
210
  </html>
211
  """
212
 
213
+ # الگوریتم تصفیه رادیکال: فیلتر کامل المان‌های زائد هدر/فوتر و استخراج متن ۱۰۰٪ خالص
214
  def clean_text(full_text: str, message: str) -> str:
215
+ if "ترافیک غیر عادی" in full_text or "Our systems have detected unusual traffic" in full_text:
216
+ return "🚨 سیستم امنیتی گوگل فعال شد و آی‌پی سرور هاگینگ‌فیس را مسدود کرد (کپچا رخ داد).\nبرای حل دائمی این مشکل باید روی داکر از پراکسی مسکونی (Residential Proxy) استفاده کنیم."
217
+
218
  lines = full_text.split('\n')
219
  cleaned_lines = []
220
 
221
+ # واژه‌های منوهای سرچ گوگل که نباید وارد پاسخ چت‌باکس شوند
222
  global_junk = ["تصاویر", "ویدیوها", "اخبار", "نقشه‌ها", "خرید کردن", "کتاب‌ها", "مالی", "حالت موضوع‌محور", "ورود", "همه", "ویدئوها", "ارسال", "پاسخ «حالت هوشواره‌ای» آماده است", "All items removed", message]
223
 
224
  for line in lines:
225
  line_str = line.strip()
226
  if not line_str or line_str in global_junk:
227
  continue
228
+ # کات کردن پاسخ به محض رسیدن به پاپ‌آپ‌ها یا فوتر سلب مسئولیت گوگل
229
+ if "هوشواره ممکن است اشتباه" in line_str or "در پاسخ‌های" in line_str or "بیشتر بدانید" in line_str or "پاسخ «حالت" in line_str:
230
  break
231
  cleaned_lines.append(line_str)
232
 
 
238
 
239
  @app.post("/api/chat")
240
  async def chat_endpoint(message: str = Form(...), image: UploadFile = File(None)):
 
 
 
 
241
  saved_image_path = None
242
  if image:
243
  try:
 
247
  except Exception as e:
248
  return StreamingResponse(iter([f"خطا در ذخیره تصویر روی داکر: {str(e)}"]), media_type="text/plain")
249
 
250
+ # ساخت سشن داینامیک: لود مرورگر واقعی و تازه برای هر درخواست کلاینت (عین گام اول برای فرار از ردگیری سشن)
251
  async def response_generator():
252
+ async with async_playwright() as p:
253
+ # لانچ مرورگر نو با لایه‌های ضد ردگیری (Anti-Automation Footprint)
254
+ browser = await p.chromium.launch(
255
+ headless=True,
256
+ args=[
257
+ "--no-sandbox",
258
+ "--disable-setuid-sandbox",
259
+ "--disable-dev-shm-usage",
260
+ "--disable-blink-features=AutomationControlled",
261
+ "--disable-web-security"
262
+ ]
263
+ )
264
 
265
+ # فیک کردن دقیق یوزر ایجنت و هدرهای اورجینال کروم اندروید ۱۳ سامسونگ
266
+ context = await browser.new_context(
267
+ user_agent="Mozilla/5.0 (Linux; Android 13; SM-S908B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36",
268
+ viewport={"width": 412, "height": 915},
269
+ locale="fa-IR", timezone_id="Asia/Tehran",
270
+ extra_http_headers={
271
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
272
+ "Accept-Language": "fa-IR,fa;q=0.9,en-US;q=0.8,en;q=0.7",
273
+ "Upgrade-Insecure-Requests": "1"
274
+ }
275
+ )
276
+ page = await context.new_page()
 
 
 
 
 
 
 
 
 
 
277
 
278
+ try:
279
+ url = "https://www.google.com/search?q=سلام&udm=50"
280
+ await page.goto(url, wait_until="domcontentloaded", timeout=30000)
 
 
 
 
 
281
 
282
+ # بررسی زنده لود تصویر روی لایه فایل آپلودر گوگل لنز
283
+ if saved_image_path and os.path.exists(saved_image_path):
284
+ try:
285
+ file_input = await page.query_selector("input[type='file']")
286
+ if file_input:
287
+ await file_input.set_input_files(saved_image_path)
288
+ await page.wait_for_timeout(1500)
289
+ except Exception as e:
290
+ print(f"Bypassed image file setup: {e}")
291
+
292
+ # واکشی المنت کادر گفتگو
293
+ input_box = None
294
+ candidates = await page.query_selector_all("textarea, input, [contenteditable='true']")
295
+ for el in candidates:
296
+ try:
297
+ if await el.is_visible():
298
+ placeholder = await el.get_attribute("placeholder") or ""
299
+ if any(w in placeholder for w in ["بپرسید", "ask", "پیام", "چطور", "هرچه"]):
300
+ input_box = el
301
  break
302
+ except:
303
+ continue
 
 
 
 
 
 
 
 
 
304
 
305
+ if not input_box:
306
+ input_box = await page.query_selector("textarea") or await page.query_selector("input[type='text']")
307
+
308
+ if input_box:
309
+ await input_box.focus()
310
+ await input_box.fill(message)
311
+ await page.wait_for_timeout(300)
312
+ await input_box.press("Enter")
313
+
314
+ # سابمیت مکمل فرم برای کلاینت‌های دیتاسنتری
315
+ try:
316
+ send_buttons = await page.query_selector_all("button, [role='button']")
317
+ for btn in send_buttons:
318
+ if await btn.is_visible():
319
+ aria = (await btn.get_attribute("aria-label") or "").lower()
320
+ if any(w in aria for w in ["send", "ارسال", "arrow", "up"]):
321
+ await btn.click(timeout=1000)
322
+ break
323
+ except:
324
+ pass
325
+
326
+ # ⚡ حلقه استریم فوق سریع: واکشی خطوط بدون تاخیرهای استاتیک با چکاپ هر ۳۰۰ میلی‌ثانیه
327
+ last_sent_text = ""
328
+ no_change_count = 0
329
 
330
+ for _ in range(45):
331
+ await page.wait_for_timeout(300)
 
332
  full_text = await page.evaluate("() => document.body.innerText")
333
  current_clean_text = clean_text(full_text, message)
334
+
335
+ # مدیریت خروجی کپچا
336
+ if "سیستم امنیتی گوگل فعال شد" in current_clean_text:
337
+ yield current_clean_text
338
+ break
339
+
340
+ # استریم کلمات جدید رندر شده روی مرورگر داکر
341
  if len(current_clean_text) > len(last_sent_text):
342
+ new_chunk = current_clean_text[len(last_sent_text):]
343
+ yield new_chunk
344
+ last_sent_text = current_clean_text
345
+ no_change_count = 0
346
+ else:
347
+ if len(current_clean_text) > 0:
348
+ no_change_count += 1
349
+
350
+ # خروج سریع به محض پایان یافتن پردازش گوگل
351
+ if "هوشواره ممکن است اشتباه" in full_text or "بیشتر بدانید" in full_text or "پاسخ‌های مربوط به افراد" in full_text:
352
+ full_text = await page.evaluate("() => document.body.innerText")
353
+ current_clean_text = clean_text(full_text, message)
354
+ if len(current_clean_text) > len(last_sent_text):
355
+ yield current_clean_text[len(last_sent_text):]
356
+ break
357
+
358
+ if no_change_count > 8 and len(last_sent_text) > 0:
359
+ break
360
+
361
+ except Exception as e:
362
+ yield f"خطای پردازش هوش مصنوعی: {str(e)}"
363
+ finally:
364
+ await context.close()
365
+ await browser.close()
366
+ if saved_image_path and os.path.exists(saved_image_path):
367
+ try: os.remove(saved_image_path)
368
+ except: pass
369
 
370
  return StreamingResponse(response_generator(), media_type="text/event-stream")
371