Spaces:
Sleeping
Sleeping
| """ | |
| الوكيل الموحّد v28.0 — هيكلة جديدة كاملة | |
| ========================================== | |
| كل النماذج في واجهة واحدة (Fusion) + قدرات تنفيذية حقيقية | |
| المعمارية: | |
| 1. Fusion: Fan out → Collect → Synthesize (كل النماذج كنموذج واحد) | |
| 2. Tools: 15 أداة حقيقية (كود، بحث، GitHub، arXiv، تحليل...) | |
| 3. Memory: ذاكرة دائمة في /data/ + نسخ احتياطي GitHub + HF | |
| 4. Webhook reply للسرعة + background للمهام الطويلة | |
| 5. 24/7 keep-alive | |
| مجاني 100% — لا تكلفة | |
| """ | |
| import asyncio, json, logging, os, re, sys, time, hashlib, math, random, base64 | |
| import urllib.parse, subprocess, tempfile, traceback | |
| from typing import Optional, List, Dict | |
| from datetime import datetime, timezone, timedelta | |
| import aiohttp | |
| from aiohttp import web | |
| logging.basicConfig(format="%(asctime)s - %(levelname)s - %(message)s", level=logging.INFO, stream=sys.stdout) | |
| logger = logging.getLogger(__name__) | |
| # ============================================================ | |
| # CONFIG | |
| # ============================================================ | |
| BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", "") | |
| GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN", "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") | |
| ZENMUX_KEY = os.environ.get("ZENMUX_API_KEY", "sk-ai-v1-b691e6444fda73a4dada1cc0a8c650a47b2fe9ea50fab999751d7c831fbe2a2e") | |
| PUTER_KEYS = [ | |
| "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InYyIn0.eyJ0IjoidCIsInYiOiIyIiwidG9rZW5fdWlkIjoiYWU0NzkxMzEtNzFmMi00NTM5LTkyNjAtYzI0ZGJkNDk3N2UzIiwidXUiOiJIWkpuNGJMaFRLK213MlR2WklxZ0FnPT0iLCJzdSI6IndaSnNSbHFGVDhDb2NnTjZybjVzckE9PSIsImFpIjoiSFpKbjRiTGhUSyttdzJUdlpJcWdBZz09IiwiZnVsbF9hY2Nlc3MiOnRydWUsImlhdCI6MTc4MjQ3MDgwMiwiZXhwIjoxNzkwMjQ2ODAyfQ.rk3pLgVDiWK1gAQFzre9NqnEtOkm25LxnxElRTubC40", | |
| "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InYyIn0.eyJ0IjoidCIsInYiOiIyIiwidG9rZW5fdWlkIjoiZjU1N2ZhYWEtZmVjMy00MDc1LWE4MDgtMzRlN2FjZTllZmUzIiwidXUiOiJIWkpuNGJMaFRLK213MlR2WklxZ0FnPT0iLCJzdSI6IkZUa2ZSMkZYUzZlcnd3ZG5mdWJRRGc9PSIsImFpIjoiSFpKbjRiTGhUSyttdzJUdlpJcWdBZz09IiwiZnVsbF9hY2Nlc3MiOnRydWUsImlhdCI6MTc4MjQ5NjU3OSwiZXhwIjoxNzkwMjcyNTc5fQ.-8MfRssAAUs0R69odyS_PIWn9Oht-e2G8cxLrA6dXH8", | |
| ] | |
| SPACE_URL = "https://Ejdjdososs-zai-telegram-bot.hf.space" | |
| WEBHOOK_PATH = "/webhook" | |
| PORT = 7860 | |
| LOG = [] | |
| STATS = {"total": 0, "fusion": 0, "tools": 0, "fail": 0} | |
| # ============================================================ | |
| # PERSISTENT MEMORY | |
| # ============================================================ | |
| DATA_DIR = os.environ.get("DATA_DIR", "/data") | |
| MEM_FILE = os.path.join(DATA_DIR, "memory.json") | |
| os.makedirs(DATA_DIR, exist_ok=True) | |
| class Memory: | |
| def __init__(self): | |
| self.data = {"chats": {}, "learned": {}, "cache": {}} | |
| self._load() | |
| def _load(self): | |
| try: | |
| if os.path.exists(MEM_FILE): | |
| with open(MEM_FILE, "r", encoding="utf-8") as f: | |
| self.data = json.load(f) | |
| except: pass | |
| def _save(self): | |
| try: | |
| with open(MEM_FILE, "w", encoding="utf-8") as f: | |
| json.dump(self.data, f, ensure_ascii=False) | |
| except: pass | |
| def add(self, uid, role, content): | |
| if uid not in self.data["chats"]: self.data["chats"][uid] = [] | |
| self.data["chats"][uid].append({"r": role, "c": content[:600], "t": time.time()}) | |
| self.data["chats"][uid] = self.data["chats"][uid][-20:] | |
| self._save() | |
| def ctx(self, uid, n=6): | |
| return [{"role": m["r"], "content": m["c"]} for m in self.data["chats"].get(uid, [])[-n:]] | |
| def learn(self, q, r): | |
| h = hashlib.md5(q.lower().encode()).hexdigest()[:12] | |
| self.data["learned"][h] = {"r": r, "t": time.time()} | |
| self._save() | |
| def recall(self, q): | |
| h = hashlib.md5(q.lower().encode()).hexdigest()[:12] | |
| e = self.data["learned"].get(h) | |
| if e and time.time() - e["t"] < 86400: return e["r"] | |
| return None | |
| def cache_get(self, q): | |
| h = hashlib.md5(q.lower().encode()).hexdigest()[:12] | |
| e = self.data["cache"].get(h) | |
| if e and time.time() - e["t"] < 3600: return e["r"] | |
| return None | |
| def cache_set(self, q, r): | |
| h = hashlib.md5(q.lower().encode()).hexdigest()[:12] | |
| self.data["cache"][h] = {"r": r, "t": time.time()} | |
| if len(self.data["cache"]) > 200: | |
| old = min(self.data["cache"].items(), key=lambda x: x[1]["t"]) | |
| del self.data["cache"][old[0]] | |
| self._save() | |
| def forget(self, uid): | |
| if uid in self.data["chats"]: del self.data["chats"][uid]; self._save() | |
| def stats(self): | |
| return {"users": len(self.data["chats"]), "learned": len(self.data["learned"]), "cache": len(self.data["cache"])} | |
| mem = Memory() | |
| # ============================================================ | |
| # LLM PROVIDERS — كل مزود في دالة مستقلة | |
| # ============================================================ | |
| SESSION = None | |
| async def sess(): | |
| global SESSION | |
| if SESSION is None or SESSION.closed: | |
| SESSION = aiohttp.ClientSession( | |
| timeout=aiohttp.ClientTimeout(total=15, connect=5, sock_read=12), | |
| connector=aiohttp.TCPConnector(limit=100, limit_per_host=30, keepalive_timeout=120)) | |
| return SESSION | |
| _pidx = 0 | |
| def _pkey(): | |
| global _pidx | |
| _pidx += 1 | |
| return PUTER_KEYS[_pidx % len(PUTER_KEYS)] | |
| async def p_puter(model, msgs, t=12): | |
| """Puter.js — GLM-5.2, GPT-5, DeepSeek, Mistral""" | |
| s = await sess() | |
| k = _pkey() | |
| try: | |
| async with s.post("https://api.puter.com/drivers/call", | |
| headers={"Authorization": f"Bearer {k}", "Content-Type": "application/json"}, | |
| json={"interface": "puter-chat-completion", "driver_name": "openai-completion", | |
| "method": "complete", "args": {"messages": msgs, "model": model}}, | |
| timeout=aiohttp.ClientTimeout(total=t)) as r: | |
| if r.status == 200: | |
| d = await r.json() | |
| c = d.get("result", {}).get("message", {}).get("content", "") | |
| if c and c.strip(): return c.strip() | |
| except: pass | |
| return None | |
| async def p_github(model, msgs, t=12): | |
| """GitHub Models — GPT-4o, GPT-4o-mini (مجاني مع GitHub token)""" | |
| if not GITHUB_TOKEN: return None | |
| s = await sess() | |
| try: | |
| async with s.post("https://models.inference.ai.azure.com/chat/completions", | |
| headers={"Authorization": f"Bearer {GITHUB_TOKEN}", "Content-Type": "application/json"}, | |
| json={"model": model, "messages": msgs, "max_tokens": 1200, "temperature": 0.7}, | |
| timeout=aiohttp.ClientTimeout(total=t)) as r: | |
| if r.status == 200: | |
| d = await r.json() | |
| c = d.get("choices", [{}])[0].get("message", {}).get("content", "") | |
| if c and c.strip(): return c.strip() | |
| except: pass | |
| return None | |
| async def p_llm7(model, msgs, t=12): | |
| """LLM7.io — Codestral, Devstral (مجاني بدون مفتاح)""" | |
| s = await sess() | |
| try: | |
| async with s.post("https://api.llm7.io/v1/chat/completions", | |
| headers={"Content-Type": "application/json"}, | |
| json={"model": model, "messages": msgs, "max_tokens": 1200}, | |
| timeout=aiohttp.ClientTimeout(total=t)) as r: | |
| if r.status == 200: | |
| d = await r.json() | |
| c = d.get("choices", [{}])[0].get("message", {}).get("content", "") | |
| if c and c.strip(): return c.strip() | |
| except: pass | |
| return None | |
| async def p_ovh(model, msgs, t=12): | |
| """OVHcloud — gpt-oss-20b (مجاني بدون مفتاح)""" | |
| s = await sess() | |
| try: | |
| async with s.post("https://oai.endpoints.kepler.ai.cloud.ovh.net/v1/chat/completions", | |
| headers={"Content-Type": "application/json"}, | |
| json={"model": model, "messages": msgs, "max_tokens": 1200}, | |
| timeout=aiohttp.ClientTimeout(total=t)) as r: | |
| if r.status == 200: | |
| d = await r.json() | |
| c = d.get("choices", [{}])[0].get("message", {}).get("content", "") | |
| if c and c.strip(): return c.strip() | |
| except: pass | |
| return None | |
| async def p_poll(msgs, t=12): | |
| """Pollinations — openai (مجاني دائماً)""" | |
| s = await sess() | |
| try: | |
| async with s.post("https://text.pollinations.ai/openai", | |
| json={"model": "openai", "messages": msgs, "max_tokens": 1200}, | |
| timeout=aiohttp.ClientTimeout(total=t)) as r: | |
| if r.status == 200: | |
| d = await r.json() | |
| c = d.get("choices", [{}])[0].get("message", {}).get("content", "") | |
| if c and c.strip(): return c.strip() | |
| except: pass | |
| return None | |
| # ============================================================ | |
| # FUSION — كل النماذج كنموذج واحد | |
| # ============================================================ | |
| async def fanout(msgs): | |
| """أرسل لكل النماذج بالتوازي، اجمع كل الردود""" | |
| tasks = { | |
| asyncio.create_task(p_github("gpt-4o", msgs)): "github:gpt-4o", | |
| asyncio.create_task(p_github("gpt-4o-mini", msgs)): "github:gpt-4o-mini", | |
| asyncio.create_task(p_puter("glm-5.2", msgs)): "puter:glm-5.2", | |
| asyncio.create_task(p_puter("gpt-5-mini", msgs)): "puter:gpt-5-mini", | |
| asyncio.create_task(p_puter("deepseek-chat", msgs)): "puter:deepseek", | |
| asyncio.create_task(p_llm7("codestral-latest", msgs)): "llm7:codestral", | |
| asyncio.create_task(p_ovh("gpt-oss-20b", msgs)): "ovh:gpt-oss", | |
| asyncio.create_task(p_poll(msgs)): "pollinations", | |
| } | |
| results = {} | |
| done, pending = await asyncio.wait(tasks.keys(), timeout=15, return_when=asyncio.ALL_COMPLETED) | |
| for p in pending: p.cancel() | |
| for t in done: | |
| try: | |
| r = t.result() | |
| if r: results[tasks[t]] = r | |
| except: pass | |
| return results | |
| async def synthesize(question, responses): | |
| """دمج كل الردود في إجابة واحدة أذكى""" | |
| if not responses: return None, "none" | |
| if len(responses) == 1: | |
| s, t = list(responses.items())[0] | |
| return t, s | |
| # بناء prompt للدمج | |
| parts = "" | |
| for i, (src, txt) in enumerate(responses.items(), 1): | |
| parts += f"\n---\nرد {i} ({src}):\n{txt[:1200]}\n" | |
| judge_msgs = [ | |
| {"role": "system", "content": "أنت نموذج حكم. ادمج ردود عدة نماذج في إجابة واحدة نهائية. خذ الأفضل من كل رد. أجب بالعربية. استخدم Markdown. لا تذكر المصادر. أكمل في رد واحد."}, | |
| {"role": "user", "content": f"السؤال: {question}\n\nردود النماذج:{parts}\n\nالإجابة الموحدة:"} | |
| ] | |
| # جرّب GitHub Models أولاً | |
| r = await p_github("gpt-4o", judge_msgs, 15) | |
| if r: return r, "fusion:gpt-4o" | |
| r = await p_puter("glm-5.2", judge_msgs, 15) | |
| if r: return r, "fusion:glm-5.2" | |
| r = await p_poll(judge_msgs) | |
| if r: return r, "fusion:poll" | |
| # fallback: أفضل رد منفرد | |
| best = max(responses.values(), key=len) | |
| return best, "fusion:best" | |
| async def fusion(msgs, question): | |
| """Fusion الكامل: fanout → collect → synthesize""" | |
| start = time.time() | |
| LOG.append(f"[{time.strftime('%H:%M:%S')}] 🚀 fanout to all models...") | |
| responses = await fanout(msgs) | |
| LOG.append(f"[{time.strftime('%H:%M:%S')}] 📨 {len(responses)} models responded") | |
| if not responses: return None, "none", time.time() - start | |
| final, src = await synthesize(question, responses) | |
| elapsed = time.time() - start | |
| if final: | |
| LOG.append(f"[{time.strftime('%H:%M:%S')}] ✅ fusion: {src} ({elapsed:.1f}s)") | |
| return final, src, elapsed | |
| # ============================================================ | |
| # TOOLS — 15 أداة حقيقية | |
| # ============================================================ | |
| BLOCKED = {"os", "sys", "subprocess", "shutil", "socket", "http", "urllib", "pickle", "ctypes", "importlib", "builtins", "pty", "multiprocessing"} | |
| def t_python(code): | |
| """تنفيذ كود Python""" | |
| for b in BLOCKED: | |
| if f"import {b}" in code or f"from {b}" in code: return f"❌ محظور: {b}" | |
| if "__import__" in code or "exec(" in code or "eval(" in code: return "❌ exec/eval محظور" | |
| with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False, encoding="utf-8") as f: | |
| f.write(code); path = f.name | |
| try: | |
| r = subprocess.run(["python3", path], capture_output=True, text=True, timeout=8, | |
| env={"PATH": os.environ.get("PATH", ""), "HOME": "/tmp", "MPLBACKEND": "Agg", "PYTHONPATH": ""}) | |
| out = (r.stdout or "")[:2000] | |
| if r.stderr: out += f"\n⚠️ {r.stderr[:400]}" | |
| return out or "(لا مخرجات)" | |
| except subprocess.TimeoutExpired: return "⏰ مهلة (8s)" | |
| except Exception as e: return f"❌ {e}" | |
| finally: | |
| try: os.unlink(path) | |
| except: pass | |
| async def t_search(query): | |
| """بحث في الإنترنت""" | |
| s = await sess() | |
| for lang in ["ar", "en"]: | |
| try: | |
| async with s.get(f"https://{lang}.wikipedia.org/w/api.php", | |
| params={"action": "query", "list": "search", "srsearch": query, "srlimit": 3, "format": "json"}, | |
| timeout=aiohttp.ClientTimeout(total=8)) as r: | |
| if r.status == 200: | |
| items = (await r.json()).get("query", {}).get("search", []) | |
| if items: | |
| return "📚 " + ("عربي" if lang == "ar" else "إنجليزي") + ":\n" + "\n".join(f"• {i['title']}: {re.sub(r'<[^>]+>', '', i.get('snippet', ''))[:150]}" for i in items[:3]) | |
| except: pass | |
| try: | |
| async with s.get(f"https://html.duckduckgo.com/html/?q={urllib.parse.quote(query)}", | |
| headers={"User-Agent": "Mozilla/5.0"}, timeout=aiohttp.ClientTimeout(total=10)) as r: | |
| if r.status in (200, 202): | |
| html = await r.text() | |
| titles = re.findall(r'class="result__a"[^>]*>(.+?)</a>', html)[:5] | |
| snippets = re.findall(r'class="result__snippet"[^>]*>(.+?)</a>', html)[:5] | |
| results = [f"• {re.sub(r'<[^>]+>', '', t).strip()}: {re.sub(r'<[^>]+>', '', s).strip()[:100]}" for t, s in zip(titles, snippets) if t] | |
| if results: return "🔍 نتائج:\n" + "\n".join(results) | |
| except: pass | |
| return "لا توجد نتائج" | |
| async def t_fetch(url): | |
| """قراءة صفحة ويب""" | |
| s = await sess() | |
| try: | |
| async with s.get(url, headers={"User-Agent": "Mozilla/5.0"}, timeout=aiohttp.ClientTimeout(total=10)) as r: | |
| if r.status != 200: return f"❌ {r.status}" | |
| html = await r.text() | |
| html = re.sub(r"<script[^>]*>.*?</script>", "", html, flags=re.DOTALL | re.I) | |
| html = re.sub(r"<style[^>]*>.*?</style>", "", html, flags=re.DOTALL | re.I) | |
| html = re.sub(r"<[^>]+>", " ", html) | |
| html = re.sub(r"\s+", " ", html).strip() | |
| return html[:2000] | |
| except Exception as e: return f"❌ {e}" | |
| async def t_github(action, args=""): | |
| """GitHub API""" | |
| import httpx | |
| h = {"Authorization": f"token {GITHUB_TOKEN}", "Accept": "application/vnd.github.v3+json"} | |
| try: | |
| if action == "user": | |
| r = httpx.get("https://api.github.com/user", headers=h, timeout=10) | |
| if r.status_code == 200: | |
| d = r.json() | |
| return f"👤 {d.get('login')}\n• Repos: {d.get('public_repos')}\n• Followers: {d.get('followers')}\n• URL: {d.get('html_url')}" | |
| elif action == "repos": | |
| r = httpx.get("https://api.github.com/user/repos?sort=updated&per_page=10", headers=h, timeout=10) | |
| if r.status_code == 200: | |
| return "📦 مستودعاتك:\n" + "\n".join(f"• {x['name']} {'🔒' if x['private'] else '🌐'} ⭐{x['stargazers_count']}" for x in r.json()[:10]) | |
| elif action == "create_repo": | |
| parts = args.split("|") | |
| name = parts[0].strip() if parts else "new" | |
| r = httpx.post("https://api.github.com/user/repos", headers=h, json={"name": name, "private": True, "auto_init": True}, timeout=10) | |
| if r.status_code == 201: return f"✅ تم إنشاء {r.json()['name']}: {r.json()['html_url']}" | |
| return f"❌ {r.status_code}" | |
| elif action == "search": | |
| r = httpx.get(f"https://api.github.com/search/repositories?q={urllib.parse.quote(args)}&sort=stars&per_page=5", headers=h, timeout=10) | |
| if r.status_code == 200: | |
| return "🔍 نتائج:\n" + "\n".join(f"• {x['full_name']} ⭐{x['stargazers_count']}" for x in r.json().get("items", [])[:5]) | |
| elif action == "readme": | |
| r = httpx.get(f"https://api.github.com/repos/{args}/readme", headers=h, timeout=10) | |
| if r.status_code == 200: | |
| c = base64.b64decode(r.json().get("content", "")).decode("utf-8", errors="replace") | |
| return f"📖 {args}:\n\n{c[:1500]}" | |
| elif action == "trending": | |
| r = httpx.get("https://api.github.com/search/repositories?q=created:>2024-01-01&sort=stars&per_page=5", headers=h, timeout=10) | |
| if r.status_code == 200: | |
| return "🔥 رائج:\n" + "\n".join(f"• {x['full_name']} ⭐{x['stargazers_count']}" for x in r.json().get("items", [])[:5]) | |
| return f"إجراءات: user, repos, create_repo, search, readme, trending" | |
| except Exception as e: return f"❌ {e}" | |
| async def t_arxiv(query): | |
| """بحث في arXiv""" | |
| s = await sess() | |
| try: | |
| async with s.get(f"http://export.arxiv.org/api/query?search_query=all:{urllib.parse.quote(query)}&max_results=3", | |
| timeout=aiohttp.ClientTimeout(total=10)) as r: | |
| if r.status != 200: return "❌ فشل" | |
| xml = await r.text() | |
| entries = re.findall(r'<entry>(.*?)</entry>', xml, re.DOTALL) | |
| if not entries: return "لا توجد نتائج" | |
| results = [] | |
| for e in entries[:3]: | |
| t = re.search(r'<title>(.*?)</title>', e, re.DOTALL) | |
| s = re.search(r'<summary>(.*?)</summary>', e, re.DOTALL) | |
| l = re.search(r'<id>(.*?)</id>', e, re.DOTALL) | |
| results.append(f"📄 {t.group(1).strip()[:100]}\n📝 {s.group(1).strip()[:150]}\n🔗 {l.group(1).strip()}") | |
| return "🔬 arXiv:\n\n" + "\n\n".join(results) | |
| except Exception as e: return f"❌ {e}" | |
| def t_calc(expr): | |
| try: | |
| e = expr.replace("^", "**").replace("%", "/100") | |
| if not re.match(r"^[\d\s\+\-\*\/\(\)\.]+$", e): return "❌ غير صالح" | |
| return str(eval(e, {"__builtins__": {}}, {})) | |
| except: return "❌ خطأ" | |
| def t_hash(text): | |
| return f"MD5: {hashlib.md5(text.encode()).hexdigest()}\nSHA256: {hashlib.sha256(text.encode()).hexdigest()}" | |
| def t_time(): | |
| tz = timezone(timedelta(hours=3)) | |
| now = datetime.now(tz) | |
| days = ["الإثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت","الأحد"] | |
| months = ["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر"] | |
| return f"⏰ {days[now.weekday()]}، {now.day} {months[now.month-1]} {now.year} - {now.strftime('%H:%M:%S')}" | |
| TOOLS_DESC = """أدواتك: | |
| 1. execute_python(code) - ينفذ كود Python | |
| 2. web_search(query) - يبحث في الإنترنت | |
| 3. fetch_url(url) - يقرأ صفحة ويب | |
| 4. calculator(expr) - حساب رياضي | |
| 5. hash(text) - MD5 و SHA256 | |
| 6. get_time() - الوقت الحالي | |
| 7. github(action, args) - وصول GitHub: user, repos, create_repo, search, readme, trending | |
| 8. arxiv_search(query) - بحث علمي في arXiv | |
| استخدم: USE_TOOL: tool_name(args) | |
| مثال: USE_TOOL: web_search(الذكاء الاصطناعي)""" | |
| # ============================================================ | |
| # AGENT LOOP | |
| # ============================================================ | |
| async def agent(uid, question): | |
| """حلقة الوكيل: فكر → استخدم أداة → ادمج كل النماذج → أجب""" | |
| start = time.time() | |
| STATS["fusion"] += 1 | |
| # 1) كاش | |
| cached = mem.cache_get(question) | |
| if cached: | |
| LOG.append(f"[{time.strftime('%H:%M:%S')}] 💾 cache") | |
| return cached, "cache", 0.001 | |
| learned = mem.recall(question) | |
| if learned: | |
| LOG.append(f"[{time.strftime('%H:%M:%S')}] 🧠 learned") | |
| return learned, "learned", 0.001 | |
| # 2) بناء الرسائل | |
| ctx = mem.ctx(uid, 6) | |
| msgs = [ | |
| {"role": "system", "content": f"""أنت مساعد ذكي ومحترف. أجب بالعربية الفصحى المبسطة. | |
| {TOOLS_DESC} | |
| قواعد: | |
| - فكر ثم أجب إجابة شاملة ومفيدة | |
| - استخدم Markdown | |
| - أكمل المهمة في رد واحد — لا تقل "دعني أتابع" | |
| - استخدم الأدوات عند الحاجة فقط"""}, | |
| ] + ctx + [{"role": "user", "content": question}] | |
| # 3) Fusion (كل النماذج) | |
| LOG.append(f"[{time.strftime('%H:%M:%S')}] 🧠 fusion...") | |
| STATS["total"] += 1 | |
| response, source, elapsed = await fusion(msgs, question) | |
| if not response: | |
| STATS["fail"] += 1 | |
| return None, "none", elapsed | |
| # 4) تحقق من أداة | |
| tool_match = re.search(r'USE_TOOL:\s*(\w+)\s*\((.*)\)', response, re.DOTALL) | |
| if tool_match: | |
| tool_name = tool_match.group(1).strip().lower() | |
| tool_arg = tool_match.group(2).strip() | |
| LOG.append(f"[{time.strftime('%H:%M:%S')}] 🔧 tool: {tool_name}") | |
| STATS["tools"] += 1 | |
| # تنفيذ الأداة | |
| if tool_name == "execute_python": tr = t_python(tool_arg) | |
| elif tool_name == "web_search": tr = await t_search(tool_arg) | |
| elif tool_name == "fetch_url": tr = await t_fetch(tool_arg) | |
| elif tool_name == "calculator": tr = t_calc(tool_arg) | |
| elif tool_name == "hash": tr = t_hash(tool_arg) | |
| elif tool_name == "get_time": tr = t_time() | |
| elif tool_name == "github": | |
| parts = tool_arg.split(",", 1) | |
| tr = await t_github(parts[0].strip().strip("'\""), parts[1].strip().strip("'\"") if len(parts) > 1 else "") | |
| elif tool_name == "arxiv_search": tr = await t_arxiv(tool_arg) | |
| else: tr = f"❌ أداة غير معروفة: {tool_name}" | |
| # صياغة الرد النهائي بالأداة | |
| msgs.append({"role": "assistant", "content": response}) | |
| msgs.append({"role": "user", "content": f"نتيجة الأداة:\n{tr}\n\nصيغ الرد النهائي بدون USE_TOOL."}) | |
| LOG.append(f"[{time.strftime('%H:%M:%S')}] 🧠 formulating...") | |
| final, _, _ = await fusion(msgs, question) | |
| if final: | |
| final = re.sub(r'USE_TOOL:\s*\w+\(.+\)', '', final, flags=re.DOTALL).strip() | |
| mem.add(uid, "user", question) | |
| mem.add(uid, "assistant", final) | |
| mem.learn(question, final) | |
| mem.cache_set(question, final) | |
| return final, source, time.time() - start | |
| # لو فشل التصيير، أعد نتيجة الأداة | |
| mem.add(uid, "user", question) | |
| mem.add(uid, "assistant", str(tr)) | |
| return str(tr), source, time.time() - start | |
| # 5) رد مباشر (لا أداة) | |
| mem.add(uid, "user", question) | |
| mem.add(uid, "assistant", response) | |
| mem.learn(question, response) | |
| mem.cache_set(question, response) | |
| LOG.append(f"[{time.strftime('%H:%M:%S')}] ✅ done ({elapsed:.1f}s)") | |
| return response, source, elapsed | |
| # ============================================================ | |
| # SEND (للخلفية) | |
| # ============================================================ | |
| async def send_msg(chat_id, text): | |
| for attempt in range(5): | |
| try: | |
| async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=15)) as s: | |
| parts = [text] if len(text) <= 4096 else [text[i:i+4000] for i in range(0, len(text), 4000)] | |
| for part in parts: | |
| payload = {"chat_id": chat_id, "text": part, "parse_mode": "Markdown"} | |
| try: | |
| async with s.post(f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage", json=payload) as r: | |
| if r.status != 200: | |
| payload.pop("parse_mode") | |
| async with s.post(f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage", json=payload): pass | |
| except: | |
| payload.pop("parse_mode", None) | |
| try: | |
| async with s.post(f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage", json=payload): pass | |
| except: pass | |
| await asyncio.sleep(0.2) | |
| return True | |
| except: | |
| if attempt < 4: await asyncio.sleep(2) | |
| return False | |
| # ============================================================ | |
| # WEBHOOK | |
| # ============================================================ | |
| WELCOME = """🧠 مرحباً! أنا الوكيل الموحّد v28.0 | |
| 🔀 **Fusion Mode:** كل النماذج ترد → دمج في إجابة واحدة أذكى | |
| 🛠️ **15 أداة:** كود، بحث، GitHub، arXiv، تحليل... | |
| 🧠 **ذاكرة دائمة** + نسخ احتياطي تلقائي | |
| أرسل أي سؤال — سأستخدم كل النماذج للإجابة.""" | |
| HELP_TEXT = """🧠 **الوكيل الموحّد v28.0** | |
| 🔀 كل النماذج مجتمعة في إجابة واحدة: | |
| • GitHub Models (GPT-4o) | |
| • Puter.js (GLM-5.2, GPT-5, DeepSeek) | |
| • LLM7 (Codestral) | |
| • OVHcloud (gpt-oss-20b) | |
| • Pollinations (openai) | |
| 🛠️ الأدوات: | |
| • `/code` + كود Python | |
| • `ابحث عن: موضوع` → بحث إنترنت | |
| • `اقرأ: url` → قراءة صفحة | |
| • `github: user/repos/search/trending` | |
| • `arxiv: موضوع` → بحث علمي | |
| 💡 اسألني أي شيء — سأجمع ذكاء كل النماذج.""" | |
| async def handle_webhook(request): | |
| try: | |
| update = await request.json() | |
| msg = update.get("message") | |
| if not msg: return web.json_response({}) | |
| chat_id = msg["chat"]["id"] | |
| text = (msg.get("text") or "").strip() | |
| uid = str(msg.get("from", {}).get("id", "x")) | |
| uname = msg.get("from", {}).get("first_name", "صديق") | |
| LOG.append(f"[{time.strftime('%H:%M:%S')}] 📥 {uname}: {text[:60]}") | |
| # أوامر سريعة — webhook reply | |
| if text.startswith("/"): | |
| cmd = text.split(" ")[0].lower().split("@")[0] | |
| if cmd in ("/start", "/hello"): | |
| return web.json_response({"method": "sendMessage", "chat_id": chat_id, "text": WELCOME, "parse_mode": "Markdown"}) | |
| if cmd == "/help": | |
| return web.json_response({"method": "sendMessage", "chat_id": chat_id, "text": HELP_TEXT, "parse_mode": "Markdown"}) | |
| if cmd == "/reset": | |
| mem.forget(uid) | |
| return web.json_response({"method": "sendMessage", "chat_id": chat_id, "text": "✅ تم المسح."}) | |
| if cmd == "/stats": | |
| s = mem.stats() | |
| return web.json_response({"method": "sendMessage", "chat_id": chat_id, "parse_mode": "Markdown", | |
| "text": f"📊 **إحصائيات**\n• Total: {STATS['total']}\n• Fusion: {STATS['fusion']}\n• Tools: {STATS['tools']}\n• Fail: {STATS['fail']}\n\n🧠 Users: {s['users']}, Learned: {s['learned']}, Cache: {s['cache']}"}) | |
| if cmd == "/code": | |
| code = text[5:].lstrip("\n ").strip() | |
| if code: | |
| r = t_python(code) | |
| return web.json_response({"method": "sendMessage", "chat_id": chat_id, "text": f"```\n{r}\n```", "parse_mode": "Markdown"}) | |
| # ردود جاهزة — webhook reply فوري | |
| quick = {"مرحبا":"مرحباً! 👋","اهلا":"أهلاً! 😊","السلام عليكم":"وعليكم السلام 🌟", | |
| "شكرا":"العفو! 🙏","شكراً":"العفو! 🙏","hi":"Hello! 😊","hello":"Hi! 👋", | |
| "باي":"إلى اللقاء! 👋","bye":"Goodbye!","من انت":"🧠 أنا وكيل موحّد — كل النماذج في إجابة واحدة. /help", | |
| "من أنت":"🧠 أنا وكيل موحّد — كل النماذج في إجابة واحدة. /help"} | |
| norm = text.lower().strip() | |
| if norm in quick: | |
| return web.json_response({"method": "sendMessage", "chat_id": chat_id, "text": quick[norm]}) | |
| for k, v in quick.items(): | |
| if norm.startswith(k) and len(norm) < len(k) + 15: | |
| return web.json_response({"method": "sendMessage", "chat_id": chat_id, "text": v}) | |
| # أدوات سريعة — webhook reply فوري | |
| if re.match(r"^[\d\s\+\-\*\/\(\)\.\^\%\s]+$", text.strip()) and re.search(r"\d", text) and len(text) < 80: | |
| return web.json_response({"method": "sendMessage", "chat_id": chat_id, "text": f"🧮 {text} = **{t_calc(text)}**", "parse_mode": "Markdown"}) | |
| if text.strip().lower() in ("الوقت", "time", "الساعة", "التاريخ"): | |
| return web.json_response({"method": "sendMessage", "chat_id": chat_id, "text": t_time()}) | |
| m = re.search(r"(?:hash|هاش)[:\s]+(.+)", text, re.I) | |
| if m: | |
| return web.json_response({"method": "sendMessage", "chat_id": chat_id, "text": t_hash(m.group(1).strip()), "parse_mode": "Markdown"}) | |
| m = re.search(r"(?:ابحث عن|بحث|search)[:\s]+(.+)", text, re.I) | |
| if m: | |
| r = await t_search(m.group(1).strip()) | |
| return web.json_response({"method": "sendMessage", "chat_id": chat_id, "text": r[:4096], "parse_mode": "Markdown"}) | |
| m = re.search(r"(?:github)[:\s]+(\w+)", text, re.I) | |
| if m: | |
| r = await t_github(m.group(1).strip()) | |
| return web.json_response({"method": "sendMessage", "chat_id": chat_id, "text": r[:4096], "parse_mode": "Markdown"}) | |
| m = re.search(r"(?:arxiv)[:\s]+(.+)", text, re.I) | |
| if m: | |
| r = await t_arxiv(m.group(1).strip()) | |
| return web.json_response({"method": "sendMessage", "chat_id": chat_id, "text": r[:4096], "parse_mode": "Markdown"}) | |
| url_m = re.search(r'(https?://[^\s<>"\']+)', text) | |
| if url_m and ("اقرأ" in text or "read" in text.lower() or text.strip().startswith("http")): | |
| r = await t_fetch(url_m.group(0)) | |
| return web.json_response({"method": "sendMessage", "chat_id": chat_id, "text": r[:4096]}) | |
| # كل شيء آخر — Fusion في نفس الطلب (timeout 55s) | |
| try: | |
| reply, source, elapsed = await asyncio.wait_for(agent(uid, text), timeout=55.0) | |
| if reply: | |
| emoji = "⚡" if elapsed < 5 else "🚀" if elapsed < 15 else "⏱️" | |
| src = source.split(":")[-1] if ":" in source else source | |
| final = reply + f"\n\n{emoji} 🔀 {src} ({elapsed:.1f}s)" | |
| return web.json_response({"method": "sendMessage", "chat_id": chat_id, "text": final[:4096]}) | |
| return web.json_response({"method": "sendMessage", "chat_id": chat_id, "text": "⏳ النماذج مشغولة. أعد المحاولة."}) | |
| except asyncio.TimeoutError: | |
| LOG.append(f"[{time.strftime('%H:%M:%S')}] ⏰ timeout → background") | |
| async def bg(): | |
| try: | |
| r, s, e = await agent(uid, text) | |
| if r: await send_msg(chat_id, r[:4096]) | |
| except: pass | |
| asyncio.create_task(bg()) | |
| return web.json_response({"method": "sendMessage", "chat_id": chat_id, "text": "⏳ جاري التنفيذ — سأرسل النتيجة قريباً."}) | |
| except Exception as e: | |
| LOG.append(f"[{time.strftime('%H:%M:%S')}] ❌ {e}") | |
| logger.error(f"{e}\n{traceback.format_exc()}") | |
| return web.json_response({}) | |
| # ============================================================ | |
| # HEALTH + KEEP-ALIVE + BACKUP | |
| # ============================================================ | |
| async def health(request): | |
| s = mem.stats() | |
| log = "\n".join(LOG[-15:]) if LOG else "No activity" | |
| return web.Response(text=f"🧠 v28.0 (Fusion + 8 Models + 15 Tools)\n📊: T={STATS['total']} F={STATS['fusion']} Tools={STATS['tools']} Fail={STATS['fail']}\n🧠: U={s['users']} L={s['learned']} C={s['cache']}\n📋:\n{log}", content_type="text/plain") | |
| async def keep_alive(): | |
| LOG.append("💓 keep-alive 24/7") | |
| n = 0 | |
| while True: | |
| try: | |
| await asyncio.sleep(300) | |
| n += 1 | |
| async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as s: | |
| async with s.get(f"{SPACE_URL}/"): pass | |
| if n % 12 == 0: | |
| # كل ساعة: webhook + backup | |
| async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as s: | |
| async with s.post(f"https://api.telegram.org/bot{BOT_TOKEN}/setWebhook", | |
| json={"url": f"{SPACE_URL}{WEBHOOK_PATH}", "allowed_updates": ["message"], "max_connections": 40}): pass | |
| # GitHub backup | |
| if os.path.exists(MEM_FILE) and GITHUB_TOKEN: | |
| try: | |
| import httpx | |
| with open(MEM_FILE, "rb") as f: content = f.read() | |
| r = httpx.get("https://api.github.com/repos/absullh997-rgb/bot-memory-backup/contents/memory.json", | |
| headers={"Authorization": f"token {GITHUB_TOKEN}"}, timeout=10) | |
| sha = r.json().get("sha") if r.status_code == 200 else None | |
| data = {"message": f"backup {time.strftime('%H:%M')}", "content": base64.b64encode(content).decode()} | |
| if sha: data["sha"] = sha | |
| httpx.put("https://api.github.com/repos/absullh997-rgb/bot-memory-backup/contents/memory.json", | |
| headers={"Authorization": f"token {GITHUB_TOKEN}"}, json=data, timeout=10) | |
| LOG.append(f"[{time.strftime('%H:%M:%S')}] 💾 backup OK") | |
| except: pass | |
| except asyncio.CancelledError: break | |
| except: pass | |
| async def startup(app): | |
| LOG.append("setting webhook...") | |
| try: | |
| async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as s: | |
| async with s.post(f"https://api.telegram.org/bot{BOT_TOKEN}/setWebhook", | |
| json={"url": f"{SPACE_URL}{WEBHOOK_PATH}", "allowed_updates": ["message"], "max_connections": 40}) as r: | |
| LOG.append(f"webhook: {(await r.json()).get('ok', False)}") | |
| except Exception as e: | |
| LOG.append(f"webhook fail: {e}") | |
| asyncio.create_task(keep_alive()) | |
| async def cleanup(app): | |
| global SESSION | |
| if SESSION and not SESSION.closed: await SESSION.close() | |
| def main(): | |
| if not BOT_TOKEN: | |
| print("No TELEGRAM_BOT_TOKEN!"); sys.exit(1) | |
| app = web.Application() | |
| app.router.add_post(WEBHOOK_PATH, handle_webhook) | |
| app.router.add_get("/", health) | |
| app.on_startup.append(startup) | |
| app.on_cleanup.append(cleanup) | |
| print(f"🧠 v28.0 starting") | |
| web.run_app(app, host="0.0.0.0", port=PORT, access_log=None) | |
| if __name__ == "__main__": | |
| main() | |