CoolFace
Apppublic

THEZYZSTUDIO/Pcmodcomputer

sourceHugging Faceupdated 4mo agoView on Hugging Face
0likes
app.py517 linesDownload Raw Back to root
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