Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
# ============================================================
|
| 2 |
-
# analyzer_agent_gradio/app.py — Telegram Analyzer Agent (
|
| 3 |
# Mamba + GGUF LLM + python-telegram-bot + Gradio
|
| 4 |
# ============================================================
|
| 5 |
|
|
@@ -22,9 +22,10 @@ from transformers import AutoTokenizer, AutoModelForCausalLM
|
|
| 22 |
import telegram
|
| 23 |
from telegram.error import TelegramError, BadRequest
|
| 24 |
# استيرادات Webhook/Handlers
|
| 25 |
-
from telegram.ext import Application, CommandHandler, MessageHandler, filters, CallbackContext
|
| 26 |
from llama_cpp import Llama
|
| 27 |
from huggingface_hub import hf_hub_download
|
|
|
|
| 28 |
|
| 29 |
|
| 30 |
# ---------------- Logging ----------------
|
|
@@ -35,26 +36,27 @@ log = logging.getLogger("analyzer")
|
|
| 35 |
# ---------------- Env & config ----------------
|
| 36 |
TG_BOT_TOKEN = os.getenv("TG_BOT_TOKEN")
|
| 37 |
TG_CHANNEL = os.getenv("TG_CHANNEL")
|
|
|
|
|
|
|
| 38 |
LOG_PATH = os.getenv("ANALYZER_LOG", "analyzer_log.json")
|
| 39 |
POSTS_LIMIT = int(os.getenv("ANALYZER_LIMIT", "80"))
|
| 40 |
|
| 41 |
MAMBA_MODEL_PATH = os.getenv("MAMBA_MODEL_PATH", "state-spaces/mamba-1.4b-hf")
|
| 42 |
|
| 43 |
# ---------------- Webhook Config ----------------
|
| 44 |
-
# يجب تحديد هذا المتغير في بيئة التشغيل للعمل بوضع Webhook
|
| 45 |
-
# مثال: https://your-domain.com/telegram_webhook_path
|
| 46 |
WEBHOOK_URL = os.getenv("WEBHOOK_URL")
|
| 47 |
-
WEBHOOK_PORT = int(os.getenv("WEBHOOK_PORT", "8443"))
|
| 48 |
LISTEN_ADDRESS = os.getenv("LISTEN_ADDRESS", "0.0.0.0")
|
| 49 |
|
| 50 |
# ---------------- Initialization Check ----------------
|
| 51 |
-
if not all([TG_BOT_TOKEN, TG_CHANNEL]):
|
| 52 |
-
log.error("Telegram Bot Token
|
| 53 |
IS_SERVICE_READY = False
|
| 54 |
-
STATUS_MESSAGE = "❌ النظام غير جاهز: بيانات اعتماد
|
| 55 |
else:
|
| 56 |
IS_SERVICE_READY = True
|
| 57 |
-
STATUS_MESSAGE = "✅ النظام جاهز للتحليل (Bot API)."
|
|
|
|
| 58 |
|
| 59 |
# ---------------- Helpers for Non-Async Blocking Operations ----------------
|
| 60 |
def async_wrap_blocking(func):
|
|
@@ -100,7 +102,7 @@ try:
|
|
| 100 |
model_path=LLM_LOCAL_PATH,
|
| 101 |
n_ctx=4096,
|
| 102 |
n_threads=4,
|
| 103 |
-
n_gpu_layers=0
|
| 104 |
)
|
| 105 |
log.info("GGUF model loaded successfully.")
|
| 106 |
except Exception as e:
|
|
@@ -109,18 +111,17 @@ except Exception as e:
|
|
| 109 |
STATUS_MESSAGE = "❌ فشل تحميل نموذج GGUF."
|
| 110 |
|
| 111 |
|
| 112 |
-
# ---------------- Telegram Bot Client ----------------
|
| 113 |
-
#
|
| 114 |
if IS_SERVICE_READY:
|
| 115 |
try:
|
| 116 |
-
#
|
| 117 |
-
|
| 118 |
except Exception as e:
|
| 119 |
log.error(f"Failed to initialize Telegram Bot: {e}")
|
| 120 |
IS_SERVICE_READY = False
|
| 121 |
STATUS_MESSAGE = "❌ فشل تهيئة كائن البوت."
|
| 122 |
-
|
| 123 |
-
bot_client = None
|
| 124 |
|
| 125 |
# ---------------- Core Helpers ----------------
|
| 126 |
def save_log(entry: Dict[str, Any]):
|
|
@@ -190,50 +191,83 @@ def interpret_with_llm(mamba_output: str) -> str:
|
|
| 190 |
return res["choices"][0]["text"].strip()
|
| 191 |
|
| 192 |
|
| 193 |
-
# ----------------
|
| 194 |
async def fetch_telegram_stats(limit: int = POSTS_LIMIT) -> List[Dict[str, Any]]:
|
| 195 |
-
"""
|
| 196 |
-
|
| 197 |
-
|
|
|
|
|
|
|
| 198 |
raise RuntimeError(STATUS_MESSAGE)
|
| 199 |
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
# 1. جلب معلومات القناة الأساسية
|
| 204 |
-
chat_info = await bot_client.get_chat(chat_id=TG_CHANNEL)
|
| 205 |
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 219 |
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 237 |
|
| 238 |
|
| 239 |
# ---------------- Main Analysis Pipeline ----------------
|
|
@@ -362,7 +396,6 @@ def daily_job_wrapper():
|
|
| 362 |
"""Synchronous wrapper to run the async job in the scheduler."""
|
| 363 |
log.info("Running scheduled analysis job via Gradio wrapper...")
|
| 364 |
try:
|
| 365 |
-
# تشغيل الدالة async في حلقة الحدث الخاصة بها
|
| 366 |
result = asyncio.run(run_analysis_pipeline())
|
| 367 |
log.info(f"Scheduled job completed. Status: {result.get('status')}")
|
| 368 |
except Exception as e:
|
|
@@ -376,6 +409,7 @@ with gr.Blocks(title="Telegram Channel Analyzer Agent") as demo:
|
|
| 376 |
gr.Markdown("# 🤖 وكيل تحليل قناة Telegram (Webhook/Scheduled)")
|
| 377 |
gr.Markdown(f"**حالة الخدمة:** {STATUS_MESSAGE}")
|
| 378 |
gr.Markdown(f"**القناة المستهدفة:** `{TG_CHANNEL}`")
|
|
|
|
| 379 |
gr.Markdown("---")
|
| 380 |
|
| 381 |
with gr.Tab("تشغيل التحليل يدوياً"):
|
|
@@ -448,19 +482,15 @@ if __name__ == "__main__":
|
|
| 448 |
# وضع Webhook
|
| 449 |
log.info(f"Setting Webhook URL to: {WEBHOOK_URL}")
|
| 450 |
try:
|
| 451 |
-
# يجب تعيين الـ URL بالكامل لـ Telegram
|
| 452 |
application.bot.set_webhook(url=WEBHOOK_URL)
|
| 453 |
|
| 454 |
log.info(f"Starting Webhook server on {LISTEN_ADDRESS}:{WEBHOOK_PORT}...")
|
| 455 |
-
# المسار الذي سيستمع عليه الخادم (يجب أن يتطابق مع الجزء الأخير من WEBHOOK_URL)
|
| 456 |
url_path = WEBHOOK_URL.split('/')[-1] if WEBHOOK_URL.count('/') > 2 else ""
|
| 457 |
|
| 458 |
-
# تشغيل خادم Webhook
|
| 459 |
application.run_webhook(
|
| 460 |
listen=LISTEN_ADDRESS,
|
| 461 |
port=WEBHOOK_PORT,
|
| 462 |
url_path=url_path,
|
| 463 |
-
# Note: يجب استخدام HTTPS وشهادة SSL/TLS في بيئة الإنتاج
|
| 464 |
)
|
| 465 |
except Exception as e:
|
| 466 |
log.error(f"Failed to start Webhook: {e}")
|
|
|
|
| 1 |
# ============================================================
|
| 2 |
+
# analyzer_agent_gradio/app.py — Telegram Analyzer Agent (Via Replit Proxy)
|
| 3 |
# Mamba + GGUF LLM + python-telegram-bot + Gradio
|
| 4 |
# ============================================================
|
| 5 |
|
|
|
|
| 22 |
import telegram
|
| 23 |
from telegram.error import TelegramError, BadRequest
|
| 24 |
# استيرادات Webhook/Handlers
|
| 25 |
+
from telegram.ext import Application, CommandHandler, MessageHandler, filters, CallbackContext
|
| 26 |
from llama_cpp import Llama
|
| 27 |
from huggingface_hub import hf_hub_download
|
| 28 |
+
import requests # <<< إضافة هامة للاتصال بالجسر
|
| 29 |
|
| 30 |
|
| 31 |
# ---------------- Logging ----------------
|
|
|
|
| 36 |
# ---------------- Env & config ----------------
|
| 37 |
TG_BOT_TOKEN = os.getenv("TG_BOT_TOKEN")
|
| 38 |
TG_CHANNEL = os.getenv("TG_CHANNEL")
|
| 39 |
+
# المتغير الجديد: رابط تطبيق الجسر على Replit
|
| 40 |
+
REPLIT_PROXY_URL = os.getenv("REPLIT_PROXY_URL")
|
| 41 |
LOG_PATH = os.getenv("ANALYZER_LOG", "analyzer_log.json")
|
| 42 |
POSTS_LIMIT = int(os.getenv("ANALYZER_LIMIT", "80"))
|
| 43 |
|
| 44 |
MAMBA_MODEL_PATH = os.getenv("MAMBA_MODEL_PATH", "state-spaces/mamba-1.4b-hf")
|
| 45 |
|
| 46 |
# ---------------- Webhook Config ----------------
|
|
|
|
|
|
|
| 47 |
WEBHOOK_URL = os.getenv("WEBHOOK_URL")
|
| 48 |
+
WEBHOOK_PORT = int(os.getenv("WEBHOOK_PORT", "8443"))
|
| 49 |
LISTEN_ADDRESS = os.getenv("LISTEN_ADDRESS", "0.0.0.0")
|
| 50 |
|
| 51 |
# ---------------- Initialization Check ----------------
|
| 52 |
+
if not all([TG_BOT_TOKEN, TG_CHANNEL, REPLIT_PROXY_URL]): # <<< تعديل التحقق
|
| 53 |
+
log.error("Telegram Bot Token, Channel ID, or Replit Proxy URL are missing in environment variables.")
|
| 54 |
IS_SERVICE_READY = False
|
| 55 |
+
STATUS_MESSAGE = "❌ النظام غير جاهز: بيانات اعتماد البوت، معرف القناة، أو رابط الجسر مفقودة."
|
| 56 |
else:
|
| 57 |
IS_SERVICE_READY = True
|
| 58 |
+
STATUS_MESSAGE = "✅ النظام جاهز للتحليل (Bot API via Replit Proxy)."
|
| 59 |
+
|
| 60 |
|
| 61 |
# ---------------- Helpers for Non-Async Blocking Operations ----------------
|
| 62 |
def async_wrap_blocking(func):
|
|
|
|
| 102 |
model_path=LLM_LOCAL_PATH,
|
| 103 |
n_ctx=4096,
|
| 104 |
n_threads=4,
|
| 105 |
+
n_gpu_layers=0
|
| 106 |
)
|
| 107 |
log.info("GGUF model loaded successfully.")
|
| 108 |
except Exception as e:
|
|
|
|
| 111 |
STATUS_MESSAGE = "❌ فشل تحميل نموذج GGUF."
|
| 112 |
|
| 113 |
|
| 114 |
+
# ---------------- Telegram Bot Client (for Webhook handlers) ----------------
|
| 115 |
+
# لا يزال هذا الكائن ضرورياً لـ python-telegram-bot لاستقبال الأوامر والرد
|
| 116 |
if IS_SERVICE_READY:
|
| 117 |
try:
|
| 118 |
+
# لا نحتاج لـ bot_client لكن نحتاج للتحقق من التوكن لـ Application.builder
|
| 119 |
+
pass
|
| 120 |
except Exception as e:
|
| 121 |
log.error(f"Failed to initialize Telegram Bot: {e}")
|
| 122 |
IS_SERVICE_READY = False
|
| 123 |
STATUS_MESSAGE = "❌ فشل تهيئة كائن البوت."
|
| 124 |
+
|
|
|
|
| 125 |
|
| 126 |
# ---------------- Core Helpers ----------------
|
| 127 |
def save_log(entry: Dict[str, Any]):
|
|
|
|
| 191 |
return res["choices"][0]["text"].strip()
|
| 192 |
|
| 193 |
|
| 194 |
+
# ---------------- FETCH TELEGRAM STATS (VIA REPLIT PROXY) ----------------
|
| 195 |
async def fetch_telegram_stats(limit: int = POSTS_LIMIT) -> List[Dict[str, Any]]:
|
| 196 |
+
"""
|
| 197 |
+
Fetches general channel statistics by routing requests through the Replit Proxy.
|
| 198 |
+
"""
|
| 199 |
+
|
| 200 |
+
if not IS_SERVICE_READY or not REPLIT_PROXY_URL:
|
| 201 |
raise RuntimeError(STATUS_MESSAGE)
|
| 202 |
|
| 203 |
+
async def fetch_via_proxy(method: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
| 204 |
+
"""Sends a request to the Replit proxy and returns the Telegram response result."""
|
| 205 |
+
url = f"{REPLIT_PROXY_URL}/route_telegram/{method}"
|
|
|
|
|
|
|
| 206 |
|
| 207 |
+
try:
|
| 208 |
+
# نستخدم to_thread لجعل طلب requests متزامن (Blocking) يعمل في خيط منفصل
|
| 209 |
+
response = await asyncio.to_thread(
|
| 210 |
+
requests.post,
|
| 211 |
+
url,
|
| 212 |
+
json=data if data is not None else {},
|
| 213 |
+
timeout=30
|
| 214 |
+
)
|
| 215 |
+
response.raise_for_status()
|
| 216 |
+
|
| 217 |
+
json_response = response.json()
|
| 218 |
+
if not json_response.get('ok'):
|
| 219 |
+
# يتم إلقاء خطأ Telegram API إذا كان الرد سلبياً
|
| 220 |
+
error_description = json_response.get('description', 'Unknown API Error')
|
| 221 |
+
raise requests.exceptions.HTTPError(
|
| 222 |
+
f"Telegram API Error via Proxy: {error_description}",
|
| 223 |
+
response=response
|
| 224 |
+
)
|
| 225 |
+
return json_response['result']
|
| 226 |
|
| 227 |
+
except requests.exceptions.HTTPError as e:
|
| 228 |
+
error_details = str(e)
|
| 229 |
+
log.error(f"Proxy/Telegram HTTP Error during {method}: {error_details}")
|
| 230 |
+
try:
|
| 231 |
+
error_data = e.response.json()
|
| 232 |
+
error_msg = error_data.get("description", error_details)
|
| 233 |
+
except:
|
| 234 |
+
error_msg = error_details
|
| 235 |
+
|
| 236 |
+
if 'unauthorized' in error_msg.lower() or 'forbidden' in error_msg.lower():
|
| 237 |
+
raise RuntimeError(f"فشل مصادقة Telegram (عبر الجسر): {error_msg}. تأكد من صحة TG_BOT_TOKEN في الجسر.")
|
| 238 |
+
else:
|
| 239 |
+
raise RuntimeError(f"خطأ في جلب البيانات عبر الجسر ({method}): {error_msg}.")
|
| 240 |
+
except Exception as e:
|
| 241 |
+
log.error(f"An unexpected error occurred during proxy fetch: {e}")
|
| 242 |
+
raise RuntimeError(f"خطأ غير متوقع في الاتصال بالجسر: {e}.")
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
# --- 1. جلب معلومات القناة الأساسية (getChat) ---
|
| 246 |
+
chat_info_data = await fetch_via_proxy(
|
| 247 |
+
"getChat",
|
| 248 |
+
data={"chat_id": TG_CHANNEL}
|
| 249 |
+
)
|
| 250 |
+
|
| 251 |
+
# --- 2. جلب المشرفين (getChatAdministrators) ---
|
| 252 |
+
admins_list = await fetch_via_proxy(
|
| 253 |
+
"getChatAdministrators",
|
| 254 |
+
data={"chat_id": TG_CHANNEL}
|
| 255 |
+
)
|
| 256 |
+
|
| 257 |
+
admins_count = len(admins_list)
|
| 258 |
+
|
| 259 |
+
# تحويل البيانات إلى تنسيق موحد للتحليل
|
| 260 |
+
stats = {
|
| 261 |
+
"chat_id": chat_info_data.get('id'),
|
| 262 |
+
"title": chat_info_data.get('title'),
|
| 263 |
+
"members": chat_info_data.get('members_count', 'N/A'),
|
| 264 |
+
"admins_count": admins_count,
|
| 265 |
+
"description": chat_info_data.get('description'),
|
| 266 |
+
"date": datetime.utcnow().isoformat(),
|
| 267 |
+
}
|
| 268 |
+
|
| 269 |
+
log.info(f"Successfully fetched stats (via proxy) for channel {stats['title']} with {stats['members']} members.")
|
| 270 |
+
return [stats]
|
| 271 |
|
| 272 |
|
| 273 |
# ---------------- Main Analysis Pipeline ----------------
|
|
|
|
| 396 |
"""Synchronous wrapper to run the async job in the scheduler."""
|
| 397 |
log.info("Running scheduled analysis job via Gradio wrapper...")
|
| 398 |
try:
|
|
|
|
| 399 |
result = asyncio.run(run_analysis_pipeline())
|
| 400 |
log.info(f"Scheduled job completed. Status: {result.get('status')}")
|
| 401 |
except Exception as e:
|
|
|
|
| 409 |
gr.Markdown("# 🤖 وكيل تحليل قناة Telegram (Webhook/Scheduled)")
|
| 410 |
gr.Markdown(f"**حالة الخدمة:** {STATUS_MESSAGE}")
|
| 411 |
gr.Markdown(f"**القناة المستهدفة:** `{TG_CHANNEL}`")
|
| 412 |
+
gr.Markdown(f"**جسر Replit:** `{REPLIT_PROXY_URL}`")
|
| 413 |
gr.Markdown("---")
|
| 414 |
|
| 415 |
with gr.Tab("تشغيل التحليل يدوياً"):
|
|
|
|
| 482 |
# وضع Webhook
|
| 483 |
log.info(f"Setting Webhook URL to: {WEBHOOK_URL}")
|
| 484 |
try:
|
|
|
|
| 485 |
application.bot.set_webhook(url=WEBHOOK_URL)
|
| 486 |
|
| 487 |
log.info(f"Starting Webhook server on {LISTEN_ADDRESS}:{WEBHOOK_PORT}...")
|
|
|
|
| 488 |
url_path = WEBHOOK_URL.split('/')[-1] if WEBHOOK_URL.count('/') > 2 else ""
|
| 489 |
|
|
|
|
| 490 |
application.run_webhook(
|
| 491 |
listen=LISTEN_ADDRESS,
|
| 492 |
port=WEBHOOK_PORT,
|
| 493 |
url_path=url_path,
|
|
|
|
| 494 |
)
|
| 495 |
except Exception as e:
|
| 496 |
log.error(f"Failed to start Webhook: {e}")
|