THEZYZSTUDIO/Pcmodcomputer
0
1"""2THE Z AI — Computer Mode Server v5 — Smart Terminal3====================================================4المشكلة: curl يفشل أحياناً، الذكاء يستسلم5الحل في السيرفر:6 - عندما يأتي أمر terminal → ننفذه7 - إذا فشل أو جاء فارغ → نجرب بدائل تلقائياً8 - نرجع النتائج الحقيقية دائماً9 - دعم 8+ مصادر بحث تلقائية10"""11 12import asyncio13import base6414import io15import json16import os17import re18import subprocess19import time20import urllib.parse21 22from fastapi import FastAPI, WebSocket, WebSocketDisconnect23from fastapi.middleware.cors import CORSMiddleware24from fastapi.responses import JSONResponse25import uvicorn26 27DISPLAY = os.environ.get("DISPLAY", ":1")28os.environ["DISPLAY"] = DISPLAY29 30app = FastAPI(title="Z-Computer-Mode API v5")31app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True,32 allow_methods=["*"], allow_headers=["*"])33 34active_connections: list[WebSocket] = []35stream_active = False36stream_fps = 337stream_quality = 6038stream_scale = 0.539 40# ─── CURL HEADERS — يعمل مع جميع المواقع ───────────41CURL_HEADERS = (42 '-H "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 '43 '(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" '44 '-H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" '45 '-H "Accept-Language: en-US,en;q=0.9,ar;q=0.8" '46 '-H "Accept-Encoding: gzip, deflate" '47 '--compressed '48 '--max-time 15 '49 '-L ' # follow redirects50 '-s '51)52 53# ─── مصادر البحث — 8 مصادر ──────────────────────────54def _build_search_sources(query: str) -> list[dict]:55 """يبني قائمة مصادر بحث بديلة للكيورى"""56 q = urllib.parse.quote_plus(query)57 q_raw = query.replace(' ', '+')58 return [59 {60 "name": "DuckDuckGo Instant",61 "cmd": f"curl -s 'https://api.duckduckgo.com/?q={q}&format=json&no_html=1&skip_disambig=1' | python3 -c \"import sys,json; d=json.load(sys.stdin); ans=d.get('AbstractText',''); rels=d.get('RelatedTopics',[]); print('ANSWER:',ans if ans else 'no direct answer'); [print('-',r.get('Text','')[:250]) for r in rels if isinstance(r,dict) and r.get('Text')]\""62 },63 {64 "name": "Google News RSS",65 "cmd": f"curl -sL {CURL_HEADERS} 'https://news.google.com/rss/search?q={q_raw}&hl=en&gl=US&ceid=US:en' | python3 -c \"import sys,re; xml=sys.stdin.read(); titles=re.findall(r'<title><!\\[CDATA\\[(.*?)\\]\\]></title>|<title>(.*?)</title>',xml); descs=re.findall(r'<description>(.*?)</description>',xml); clean=lambda s:re.sub('<[^>]+>','',s); results=[(a or b).strip() for a,b in titles if (a or b).strip()][1:9]; [print(str(i+1)+'. '+t[:180]) for i,t in enumerate(results)]\""66 },67 {68 "name": "Wikipedia English",69 "cmd": f"curl -s 'https://en.wikipedia.org/api/rest_v1/page/summary/{q}' | python3 -c \"import sys,json; d=json.load(sys.stdin); print(d.get('title','')+'\\n'+d.get('extract','')[:1500])\" 2>/dev/null || curl -s 'https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch={q}&format=json&srlimit=5' | python3 -c \"import sys,json; d=json.load(sys.stdin); [print(str(i+1)+'. '+r['title']+': '+re.sub('<[^>]+>','',r.get('snippet',''))[:200]) for i,r in enumerate(d.get('query',{{}}).get('search',[]))]\" 2>/dev/null"70 },71 {72 "name": "DuckDuckGo HTML",73 "cmd": f"curl -sL {CURL_HEADERS} 'https://html.duckduckgo.com/html/?q={q}' | python3 -c \"import sys,re; h=sys.stdin.read(); snippets=re.findall(r'class=.result__snippet[^>]*>(.*?)</a>',h,re.DOTALL); titles=re.findall(r'class=.result__title[^>]*>.*?<a[^>]*>(.*?)</a>',h,re.DOTALL); clean=lambda s:re.sub('<[^>]+>','',s).strip(); [print(str(i+1)+'. '+clean(titles[i] if i<len(titles) else '')+'\\n '+clean(s)[:200]) for i,s in enumerate(snippets[:7])]\""74 },75 {76 "name": "Bing News RSS",77 "cmd": f"curl -sL {CURL_HEADERS} 'https://www.bing.com/news/search?q={q}&format=RSS' | python3 -c \"import sys,re; xml=sys.stdin.read(); items=re.findall(r'<item>(.*?)</item>',xml,re.DOTALL); [print(str(i+1)+'. '+re.sub('<[^>]+>','',re.search(r'<title>(.*?)</title>',it).group(1) if re.search(r'<title>',it) else '')+'\\n '+re.sub('<[^>]+>','',re.search(r'<description>(.*?)</description>',it).group(1) if re.search(r'<description>',it) else '')[:150]) for i,it in enumerate(items[:6])]\""78 },79 {80 "name": "Reddit Search",81 "cmd": f"curl -sL {CURL_HEADERS} -H 'Accept: application/json' 'https://www.reddit.com/search.json?q={q}&sort=new&limit=8&type=link' | python3 -c \"import sys,json; d=json.load(sys.stdin); posts=d.get('data',{{}}).get('children',[]); [print(str(i+1)+'. '+p['data'].get('title','')+'\\n r/'+p['data'].get('subreddit','')+' Score:'+str(p['data'].get('score',''))+'\\n '+p['data'].get('selftext','')[:200]) for i,p in enumerate(posts[:6])]\""82 },83 {84 "name": "HackerNews",85 "cmd": f"curl -s 'https://hn.algolia.com/api/v1/search?query={q}&hitsPerPage=8&tags=story' | python3 -c \"import sys,json; d=json.load(sys.stdin); hits=d.get('hits',[]); [print(str(i+1)+'. '+h.get('title','')+'\\n Points:'+str(h.get('points','0'))+' | '+h.get('url','')[:80]) for i,h in enumerate(hits[:6])]\""86 },87 {88 "name": "ArXiv (academic)",89 "cmd": f"curl -s 'https://export.arxiv.org/api/query?search_query=all:{q}&start=0&max_results=5' | python3 -c \"import sys,re; xml=sys.stdin.read(); titles=re.findall(r'<title>(.*?)</title>',xml)[1:]; summaries=re.findall(r'<summary>(.*?)</summary>',xml,re.DOTALL); [print(str(i+1)+'. '+t.strip()+'\\n '+summaries[i].strip()[:200] if i<len(summaries) else '') for i,t in enumerate(titles[:5])]\""90 },91 ]92 93 94def _extract_query_from_cmd(cmd: str) -> str:95 """يستخرج كلمات البحث من أمر curl"""96 patterns = [97 r'[?&]q=([^&\'"]+)',98 r'[?&]query=([^&\'"]+)',99 r'[?&]search_query=all:([^&\'"]+)',100 r"search\?q=([^&'\"\s]+)",101 ]102 for p in patterns:103 m = re.search(p, cmd)104 if m:105 q = m.group(1).replace('+', ' ').replace('%20', ' ')106 return urllib.parse.unquote(q)107 return ""108 109 110def _is_empty_result(stdout: str) -> bool:111 """يتحقق إذا كانت النتيجة فارغة أو بلا معلومات"""112 if not stdout or len(stdout.strip()) < 10:113 return True114 empty_signals = [115 "no direct answer",116 "no results",117 "0 results",118 "no items",119 "[]",120 "{}",121 "error",122 "not found",123 "answer: no",124 ]125 s = stdout.strip().lower()126 meaningful_lines = [l for l in s.split('\n') if l.strip() and not any(sig in l for sig in empty_signals)]127 if len(meaningful_lines) < 1:128 return True129 return False130 131 132# ─── Screen Capture ─────────────────────────────────133 134def capture_screen(scale=0.6, quality=65) -> str:135 try:136 tmp = f"/tmp/zs_{int(time.time()*1000)}.png"137 r = subprocess.run(["scrot", "-q", "90", tmp],138 env={**os.environ, "DISPLAY": DISPLAY},139 timeout=8, capture_output=True)140 if r.returncode != 0 or not os.path.exists(tmp):141 return _capture_xwd(scale, quality)142 from PIL import Image143 img = Image.open(tmp)144 os.unlink(tmp)145 if scale < 1.0:146 img = img.resize((int(img.width*scale), int(img.height*scale)), Image.LANCZOS)147 buf = io.BytesIO()148 img.convert("RGB").save(buf, format="JPEG", quality=quality, optimize=True)149 return base64.b64encode(buf.getvalue()).decode()150 except Exception as e:151 print(f"[capture] {e}")152 return ""153 154 155def _capture_xwd(scale=0.6, quality=65) -> str:156 try:157 xwd = f"/tmp/zs_{int(time.time()*1000)}.xwd"158 png = xwd.replace(".xwd", ".png")159 subprocess.run(["xwd", "-root", "-silent", "-out", xwd],160 env={**os.environ, "DISPLAY": DISPLAY}, timeout=8, capture_output=True)161 subprocess.run(["convert", xwd, png], timeout=8, capture_output=True)162 from PIL import Image163 if not os.path.exists(png):164 return ""165 img = Image.open(png)166 for f in [xwd, png]:167 try: os.unlink(f)168 except: pass169 if scale < 1.0:170 img = img.resize((int(img.width*scale), int(img.height*scale)), Image.LANCZOS)171 buf = io.BytesIO()172 img.convert("RGB").save(buf, format="JPEG", quality=quality, optimize=True)173 return base64.b64encode(buf.getvalue()).decode()174 except:175 return ""176 177 178# ─── Command Runner ──────────────────────────────────179 180def run_raw_command(cmd: str, timeout: int = 45) -> dict:181 """ينفذ أمر bash خام"""182 env = {**os.environ, "DISPLAY": DISPLAY,183 "PYTHONIOENCODING": "utf-8", "LANG": "en_US.UTF-8"}184 try:185 result = subprocess.run(cmd, shell=True, capture_output=True,186 text=True, timeout=timeout, env=env,187 executable="/bin/bash")188 return {189 "stdout": result.stdout[-15000:],190 "stderr": result.stderr[-3000:],191 "returncode": result.returncode,192 }193 except subprocess.TimeoutExpired:194 return {"stdout": "", "stderr": f"⏱️ Timeout {timeout}s", "returncode": -1}195 except Exception as e:196 return {"stdout": "", "stderr": str(e), "returncode": -1}197 198 199def run_command_smart(cmd: str, timeout: int = 45) -> dict:200 """201 ينفذ الأمر — إذا كان curl بحث وفشل/فرغ → يجرب مصادر بديلة تلقائياً202 يرجع النتيجة الأولى التي تحتوي معلومات حقيقية203 """204 res = run_raw_command(cmd, timeout=timeout)205 stdout = res["stdout"].strip()206 207 if res["returncode"] == 0 and not _is_empty_result(stdout):208 return res209 210 is_curl_search = "curl" in cmd and any(x in cmd for x in [211 "duckduckgo", "google", "bing", "wikipedia", "reddit",212 "hackernews", "hn.algolia", "arxiv", "news", "search"213 ])214 215 if not is_curl_search:216 return res217 218 query = _extract_query_from_cmd(cmd)219 if not query or len(query) < 3:220 words = [w for w in cmd.split() if len(w) > 3 and not w.startswith('-')221 and 'http' not in w and 'python3' not in w and 'curl' not in w]222 query = ' '.join(words[:5]) if words else ""223 224 if not query:225 return res226 227 sources = _build_search_sources(query)228 tried_names = []229 all_results = []230 231 for source in sources:232 src_name = source["name"]233 if any(x in cmd.lower() for x in [src_name.lower().split()[0]]):234 tried_names.append(f"⏭️ {src_name} (مجرب)")235 continue236 237 tried_names.append(f"🔍 {src_name}")238 src_res = run_raw_command(source["cmd"], timeout=20)239 src_out = src_res["stdout"].strip()240 241 if not _is_empty_result(src_out):242 combined_header = (243 f"[تعذر الحصول على نتائج من المصدر الأصلي]\n"244 f"[البديل المستخدم: {src_name}]\n"245 f"{'='*50}\n"246 )247 all_results.append(src_out)248 for source2 in sources:249 if source2["name"] != src_name:250 s2 = run_raw_command(source2["cmd"], timeout=15)251 s2_out = s2["stdout"].strip()252 if not _is_empty_result(s2_out):253 all_results.append(f"\n[مصدر إضافي: {source2['name']}]\n{s2_out}")254 break255 256 final_out = combined_header + "\n\n".join(all_results)257 return {258 "stdout": final_out[:15000],259 "stderr": "",260 "returncode": 0,261 "_sources_tried": tried_names,262 "_fallback_used": src_name,263 }264 265 return {266 "stdout": f"[لم تُرجع أي مصادر نتائج لـ: {query}]\nالمصادر التي جربتها: {', '.join(tried_names[:5])}",267 "stderr": res["stderr"],268 "returncode": -1,269 "_sources_tried": tried_names,270 }271 272 273def xdo(args: list, timeout=10) -> dict:274 r = subprocess.run(["xdotool"] + args,275 env={**os.environ, "DISPLAY": DISPLAY},276 timeout=timeout, capture_output=True, text=True)277 return {"rc": r.returncode, "out": r.stdout, "err": r.stderr}278 279 280async def broadcast(msg: dict):281 txt = json.dumps(msg, ensure_ascii=False)282 dead = []283 for ws in active_connections:284 try: await ws.send_text(txt)285 except: dead.append(ws)286 for ws in dead:287 if ws in active_connections:288 active_connections.remove(ws)289 290 291async def screen_stream_loop():292 global stream_active293 interval = 1.0 / max(1, stream_fps)294 while stream_active and active_connections:295 try:296 frame = capture_screen(scale=stream_scale, quality=stream_quality)297 if frame:298 await broadcast({"type": "frame", "data": frame})299 except: pass300 await asyncio.sleep(interval)301 stream_active = False302 303 304# ─── Action Handler ──────────────────────────────────305 306async def handle_action(ws: WebSocket, msg: dict):307 action = msg.get("action", "")308 data = msg.get("data", {})309 310 async def send(obj):311 await ws.send_text(json.dumps(obj, ensure_ascii=False))312 313 async def auto_shot(label="", delay=0.4):314 await asyncio.sleep(delay)315 frame = capture_screen(scale=0.7, quality=68)316 if frame:317 await send({"type": "screenshot", "data": frame,318 "ts": int(time.time()*1000), "auto": True, "label": label})319 return frame320 321 if action == "screenshot":322 frame = capture_screen(scale=0.8, quality=75)323 await send({"type": "screenshot", "data": frame, "ts": int(time.time()*1000)})324 325 elif action == "terminal":326 cmd = data.get("cmd", "")327 timeout = int(data.get("timeout", 45))328 if not cmd:329 await send({"type": "terminal_result", "cmd": "", "stdout": "",330 "stderr": "no command", "returncode": -1})331 return332 333 res = run_command_smart(cmd, timeout=timeout)334 335 await send({336 "type": "terminal_result",337 "cmd": cmd,338 "stdout": res["stdout"],339 "stderr": res.get("stderr", ""),340 "returncode": res["returncode"],341 "fallback_used": res.get("_fallback_used", None),342 "sources_tried": res.get("_sources_tried", []),343 })344 345 await auto_shot(f"بعد: {cmd[:45]}", delay=0.3)346 347 elif action == "mouse_move":348 x, y = int(data.get("x", 0)), int(data.get("y", 0))349 xdo(["mousemove", str(x), str(y)])350 await send({"type": "ack", "action": "mouse_move"})351 352 elif action == "mouse_click":353 x, y = int(data.get("x", 0)), int(data.get("y", 0))354 btn_num = {"left": "1", "middle": "2", "right": "3"}.get(data.get("button","left"), "1")355 xdo(["mousemove", str(x), str(y)])356 await asyncio.sleep(0.05)357 if data.get("double"):358 xdo(["click", "--repeat", "2", "--delay", "100", btn_num])359 else:360 xdo(["click", btn_num])361 await send({"type": "ack", "action": "mouse_click"})362 await auto_shot("بعد النقر", 0.4)363 364 elif action == "keyboard_type":365 text = data.get("text", "")366 if text:367 xdo(["type", "--clearmodifiers", "--delay", "30", text])368 await send({"type": "ack", "action": "keyboard_type"})369 await auto_shot("بعد الكتابة", 0.3)370 371 elif action == "keyboard_hotkey":372 keys = data.get("keys", [])373 if keys:374 xdo(["key", "--clearmodifiers", "+".join(keys)])375 await send({"type": "ack", "action": "keyboard_hotkey"})376 await auto_shot("بعد الاختصار", 0.4)377 378 elif action == "keyboard_press":379 key = data.get("key", "")380 if key:381 xdo(["key", "--clearmodifiers", key])382 await send({"type": "ack", "action": "keyboard_press"})383 384 elif action == "clipboard_write":385 proc = subprocess.Popen(["xclip", "-selection", "clipboard"],386 stdin=subprocess.PIPE,387 env={**os.environ, "DISPLAY": DISPLAY})388 proc.communicate(data.get("text","").encode())389 await send({"type": "ack", "action": "clipboard_write"})390 391 elif action == "clipboard_read":392 res = run_raw_command("xclip -selection clipboard -o", timeout=5)393 await send({"type": "clipboard_content", "text": res["stdout"]})394 395 elif action == "scroll":396 x, y = int(data.get("x", 0)), int(data.get("y", 0))397 clicks = int(data.get("clicks", 3))398 btn = "4" if clicks > 0 else "5"399 xdo(["mousemove", str(x), str(y)])400 for _ in range(abs(clicks)):401 xdo(["click", btn])402 await send({"type": "ack", "action": "scroll"})403 404 elif action == "open_app":405 app_cmd = data.get("cmd", "")406 if app_cmd:407 subprocess.Popen(app_cmd, shell=True,408 env={**os.environ, "DISPLAY": DISPLAY},409 stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)410 await send({"type": "ack", "action": "open_app", "cmd": app_cmd})411 await auto_shot(f"بعد فتح: {app_cmd}", 3.0)412 else:413 await send({"type": "ack", "action": "open_app"})414 415 elif action == "mouse_drag":416 x1,y1 = int(data.get("x1",0)), int(data.get("y1",0))417 x2,y2 = int(data.get("x2",0)), int(data.get("y2",0))418 xdo(["mousemove", str(x1), str(y1)])419 xdo(["mousedown", "1"])420 await asyncio.sleep(0.1)421 xdo(["mousemove", str(x2), str(y2)])422 await asyncio.sleep(0.1)423 xdo(["mouseup", "1"])424 await send({"type": "ack", "action": "mouse_drag"})425 426 elif action == "start_stream":427 global stream_active, stream_fps, stream_quality, stream_scale428 stream_fps = int(data.get("fps", 3))429 stream_quality = int(data.get("quality", 60))430 stream_scale = float(data.get("scale", 0.5))431 if not stream_active:432 stream_active = True433 asyncio.create_task(screen_stream_loop())434 await send({"type": "ack", "action": "start_stream"})435 436 elif action == "stop_stream":437 stream_active = False438 await send({"type": "ack", "action": "stop_stream"})439 440 elif action == "screen_info":441 res = run_raw_command("xdotool getdisplaygeometry", timeout=5)442 try: parts = res["stdout"].strip().split(); w,h = int(parts[0]),int(parts[1])443 except: w,h = 1920,1080444 res2 = run_raw_command("xdotool getmouselocation", timeout=5)445 try:446 mx = int(re.search(r"x:(\d+)", res2["stdout"]).group(1))447 my = int(re.search(r"y:(\d+)", res2["stdout"]).group(1))448 except: mx,my = 0,0449 await send({"type":"screen_info","width":w,"height":h,"mouse_x":mx,"mouse_y":my})450 451 else:452 await send({"type": "error", "msg": f"Unknown action: {action}"})453 454 455# ─── WebSocket ───────────────────────────────────────456 457@app.websocket("/ws")458async def websocket_endpoint(ws: WebSocket):459 await ws.accept()460 active_connections.append(ws)461 res = run_raw_command("xdotool getdisplaygeometry", timeout=5)462 try: parts = res["stdout"].strip().split(); w,h = int(parts[0]),int(parts[1])463 except: w,h = 1920,1080464 await ws.send_text(json.dumps({465 "type": "connected", "screen_width": w, "screen_height": h,466 "msg": "Z Computer Mode v5 — Smart Terminal + Multi-Source Search"467 }, ensure_ascii=False))468 frame = capture_screen(scale=0.75, quality=72)469 if frame:470 await ws.send_text(json.dumps({"type":"screenshot","data":frame,471 "ts":int(time.time()*1000),"label":"الشاشة الأولية"}))472 try:473 while True:474 raw = await ws.receive_text()475 await handle_action(ws, json.loads(raw))476 except WebSocketDisconnect: pass477 except Exception as e: print(f"[ws] {e}")478 finally:479 if ws in active_connections:480 active_connections.remove(ws)481 482 483# ─── REST ────────────────────────────────────────────484 485@app.get("/screenshot")486async def rest_screenshot():487 return JSONResponse({"image": capture_screen(0.75,70), "ts": int(time.time()*1000)})488 489@app.post("/terminal")490async def rest_terminal(body: dict):491 return JSONResponse(run_command_smart(body.get("cmd",""), body.get("timeout",45)))492 493@app.get("/health")494async def health():495 display = run_raw_command("xdotool getdisplaygeometry", 4)496 test = run_command_smart("curl -s 'https://api.duckduckgo.com/?q=test&format=json&no_html=1'", 15)497 return {498 "status": "ok",499 "version": "v5-smart-terminal",500 "display_working": display["returncode"] == 0,501 "search_working": test["returncode"] == 0,502 "fallback_used": test.get("_fallback_used"),503 }504 505@app.get("/search/{query}")506async def quick_search(query: str):507 res = run_command_smart(508 f"curl -s 'https://api.duckduckgo.com/?q={urllib.parse.quote_plus(query)}&format=json&no_html=1'",509 timeout=30510 )511 return JSONResponse(res)512 513 514if __name__ == "__main__":515 port = int(os.environ.get("PORT", 7860))516 uvicorn.run("app:app", host="0.0.0.0", port=port, log_level="info")517 