Files
weval-l99/wevia-webchat-api.py
2026-04-16 03:34:12 +02:00

169 lines
6.6 KiB
Python
Executable File

#!/usr/bin/env python3
"""
WEVIA Web Chat API — Playwright headless multi-chat ILLIMITÉ
Port: 8902 — Same architecture as deepseek-web-api.py (port 8901)
"""
import json, os, time, threading, traceback
from http.server import HTTPServer, BaseHTTPRequestHandler
PORT = 8902
SESSION_DIR = '/opt/weval-l99/webchat-session'
LOCK = threading.Lock()
browser = None
context = None
pages = {}
ready = False
last_error = ''
SERVICES = {
"copilot": {"url": "https://copilot.microsoft.com", "input": "textarea#userInput,textarea", "wait": 10},
"huggingchat": {"url": "https://huggingface.co/chat", "input": "textarea[placeholder]", "wait": 12},
"duckduckgo": {"url": "https://duck.ai", "input": "textarea", "wait": 15},
"qwen": {"url": "https://chat.qwen.ai", "input": "textarea,div[contenteditable]", "wait": 12},
"lechat": {"url": "https://chat.mistral.ai", "input": "textarea", "wait": 12},
"perplexity": {"url": "https://perplexity.ai", "input": "textarea[placeholder],textarea", "wait": 12},
"meta": {"url": "https://www.meta.ai", "input": "div[contenteditable],textarea", "wait": 10},
"deepseek": {"url": "https://chat.deepseek.com", "input": "textarea", "wait": 15},
"claude": {"url": "https://claude.ai/new", "input": "div[contenteditable]", "wait": 15},
}
def init_browser():
global browser, context, ready, last_error
try:
from playwright.sync_api import sync_playwright
pw = sync_playwright().start()
browser = pw.chromium.launch(
headless=True,
args=['--no-sandbox', '--disable-gpu', '--disable-blink-features=AutomationControlled']
)
os.makedirs(SESSION_DIR, exist_ok=True)
context = browser.new_context(
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
)
ready = True
print(f"[WEVIA WebChat] Browser ready")
except Exception as e:
last_error = str(e)
print(f"[ERR] {e}")
def chat(service, message):
with LOCK:
if not ready:
return {"error": "not ready", "detail": last_error}
cfg = SERVICES.get(service)
if not cfg:
return {"error": f"unknown: {service}", "available": list(SERVICES.keys())}
try:
# Open page if needed
if service not in pages or pages[service].is_closed():
p = context.new_page()
p.goto(cfg["url"], timeout=30000, wait_until="domcontentloaded")
time.sleep(3)
pages[service] = p
page = pages[service]
# Find textarea
ta = page.query_selector(cfg["input"])
if not ta:
ta = page.query_selector("textarea")
if not ta:
return {"error": "no input", "url": page.url, "title": page.title()}
# Count existing messages before
before = len(page.query_selector_all("[class*='message'],[class*='response'],[class*='prose'],.group"))
# Type + submit
ta.fill("")
ta.type(message, delay=30)
time.sleep(0.3)
ta.press("Enter")
# Wait for new content
time.sleep(cfg["wait"])
# Try to get the last response
# Generic: get all text blocks and take the last substantial one
selectors = [
"[class*='message']", "[class*='response']", "[class*='prose']",
"[class*='answer']", "[class*='reply']", "[class*='content']",
".group", "article", "main p"
]
best = ""
for sel in selectors:
els = page.query_selector_all(sel)
if els:
for el in reversed(els):
txt = el.inner_text().strip()
if len(txt) > len(best) and txt != message:
best = txt
if len(best) > 50:
break
if len(best) > 10:
return {
"content": best[:3000],
"provider": f"WEVIA WebChat ({service})",
"model": service,
"cost": "0EUR UNLIMITED",
}
# Last resort: full page text
body = page.inner_text("body")
lines = [l.strip() for l in body.split("\n") if len(l.strip()) > 20 and l.strip() != message]
if lines:
return {
"content": "\n".join(lines[-5:]),
"provider": f"WEVIA WebChat ({service})",
"model": service,
"cost": "0EUR",
}
return {"error": "no response detected", "url": page.url}
except Exception as e:
# Reset page on error
if service in pages:
try: pages[service].close()
except: pass
del pages[service]
return {"error": str(e)}
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps({
"service": "WEVIA WebChat Multi UNLIMITED",
"port": PORT, "ready": ready,
"services": list(SERVICES.keys()),
"active": list(pages.keys()),
"unlimited": True
}).encode())
def do_POST(self):
body = json.loads(self.rfile.read(int(self.headers.get('Content-Length', 0)))) if int(self.headers.get('Content-Length', 0)) > 0 else {}
svc = body.get('service', body.get('model', '')).replace('web-', '')
msg = body.get('message', body.get('prompt', ''))
result = chat(svc, msg) if msg and svc else {"error": "need service+message"}
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
self.wfile.write(json.dumps(result).encode())
def do_OPTIONS(self):
self.send_response(200)
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Headers', 'Content-Type')
self.end_headers()
def log_message(self, *a): pass
if __name__ == '__main__':
print(f"[WEVIA WebChat] Init browser...")
init_browser() # MAIN THREAD — same as DS Web
print(f"[WEVIA WebChat] Serving on :{PORT}")
HTTPServer(('0.0.0.0', PORT), Handler).serve_forever()