The-observer / app.py
ali3133's picture
Upload app.py
7716dff verified
import gradio as gr
import folium
import json
import random
from datetime import datetime, timedelta
# ===== بيانات الأحداث =====
EVENTS = [
{
"id": 1, "title": "رصد غارة جوية على مواقع في جنوب لبنان",
"type": "strike", "severity": "critical", "category": "غارة جوية",
"lat": 33.27, "lng": 35.20, "region": "middleeast",
"location": "جنوب لبنان", "details": "غارات متعددة على مواقع عسكرية",
"sources": ["Bamqam", "Conflictly"], "minutes_ago": 30
},
{
"id": 2, "title": "إطلاق صواريخ باليستية من اليمن باتجاه البحر الأحمر",
"type": "missile", "severity": "critical", "category": "صاروخ باليستي",
"lat": 15.35, "lng": 44.20, "region": "redSea",
"location": "اليمن — البحر الأحمر", "details": "رصد إطلاق صاروخين باليستيين",
"sources": ["WorldMonitor", "PIZZINT"], "minutes_ago": 60
},
{
"id": 3, "title": "تحليق طائرة استطلاع أمريكية RQ-4 فوق شرق المتوسط",
"type": "aircraft", "severity": "high", "category": "طائرة استطلاع",
"lat": 34.50, "lng": 33.80, "region": "middleeast",
"location": "شرق المتوسط", "details": "طائرة بدون طيار للاستطلاع على ارتفاع 55,000 قدم",
"sources": ["ADS-B Exchange", "Flightradar24"], "minutes_ago": 120
},
{
"id": 4, "title": "تحركات مدرعات روسية قرب الحدود الأوكرانية",
"type": "troops", "severity": "high", "category": "تحرك قوات",
"lat": 50.45, "lng": 36.60, "region": "europe",
"location": "بيلغورود — روسيا", "details": "رصد رتل مدرعات متحرك",
"sources": ["Conflictly", "WorldMonitor"], "minutes_ago": 90
},
{
"id": 5, "title": "اعتراض طائرة مسيّرة فوق مضيق هرمز",
"type": "missile", "severity": "high", "category": "اعتراض",
"lat": 26.56, "lng": 56.25, "region": "middleeast",
"location": "مضيق هرمز", "details": "اعتراض ناجح بصاروخ أرض-جو",
"sources": ["Bamqam", "PIZZINT"], "minutes_ago": 150
},
{
"id": 6, "title": "قصف مدفعي متبادل على خط التماس في دونباس",
"type": "strike", "severity": "critical", "category": "قصف مدفعي",
"lat": 48.00, "lng": 37.80, "region": "europe",
"location": "دونباس — أوكرانيا", "details": "أكثر من 200 قذيفة خلال 3 ساعات",
"sources": ["Conflictly", "WorldMonitor"], "minutes_ago": 45
},
{
"id": 7, "title": "تحليق مقاتلات F-35 فوق العراق",
"type": "aircraft", "severity": "medium", "category": "دورية جوية",
"lat": 33.30, "lng": 44.36, "region": "middleeast",
"location": "أجواء العراق", "details": "دورية جوية روتينية — 4 مقاتلات",
"sources": ["ADS-B Exchange", "Flightradar24"], "minutes_ago": 180
},
{
"id": 8, "title": "تعزيزات بحرية أمريكية في البحر الأحمر",
"type": "naval", "severity": "medium", "category": "نشاط بحري",
"lat": 14.80, "lng": 42.50, "region": "redSea",
"location": "البحر الأحمر", "details": "حاملة طائرات ومدمرتان",
"sources": ["WorldMonitor", "Bamqam"], "minutes_ago": 240
},
{
"id": 9, "title": "رصد طائرة شحن عسكري C-17 متجهة لقاعدة العديد",
"type": "aircraft", "severity": "low", "category": "شحن عسكري",
"lat": 25.12, "lng": 51.31, "region": "middleeast",
"location": "قطر — العديد", "details": "شحن عسكري لوجستي",
"sources": ["Flightradar24", "ADS-B Exchange"], "minutes_ago": 300
},
{
"id": 10, "title": "غارات على مواقع في غزة",
"type": "strike", "severity": "critical", "category": "غارة جوية",
"lat": 31.42, "lng": 34.35, "region": "middleeast",
"location": "قطاع غزة", "details": "غارات مكثفة على عدة أحياء",
"sources": ["Bamqam", "Conflictly", "WorldMonitor"], "minutes_ago": 10
},
{
"id": 11, "title": "تحليق طائرة P-8 فوق البحر الأسود",
"type": "aircraft", "severity": "medium", "category": "طائرة استطلاع",
"lat": 43.50, "lng": 34.00, "region": "europe",
"location": "البحر الأسود", "details": "طائرة مراقبة بحرية — استطلاع",
"sources": ["ADS-B Exchange"], "minutes_ago": 360
},
{
"id": 12, "title": "إطلاق صواريخ كروز من البحر المتوسط",
"type": "missile", "severity": "critical", "category": "صاروخ كروز",
"lat": 35.00, "lng": 32.00, "region": "middleeast",
"location": "شرق المتوسط", "details": "رصد إطلاق 4 صواريخ كروز",
"sources": ["PIZZINT", "WorldMonitor"], "minutes_ago": 70
},
{
"id": 13, "title": "تحرك فرقة مشاة سودانية قرب الفاشر",
"type": "troops", "severity": "high", "category": "تحرك قوات",
"lat": 13.63, "lng": 25.35, "region": "africa",
"location": "الفاشر — السودان", "details": "تعزيزات عسكرية كبيرة",
"sources": ["Conflictly"], "minutes_ago": 200
},
{
"id": 14, "title": "طائرة MQ-9 Reaper فوق سوريا",
"type": "aircraft", "severity": "medium", "category": "طائرة مسيّرة",
"lat": 35.20, "lng": 38.90, "region": "middleeast",
"location": "شمال شرق سوريا", "details": "طائرة مسيّرة للمراقبة والاستطلاع",
"sources": ["ADS-B Exchange", "Flightradar24"], "minutes_ago": 270
},
{
"id": 15, "title": "انفجارات قرب قاعدة عين الأسد",
"type": "strike", "severity": "high", "category": "قصف صاروخي",
"lat": 33.80, "lng": 43.50, "region": "middleeast",
"location": "غرب العراق", "details": "سقوط صواريخ كاتيوشا",
"sources": ["Bamqam", "PIZZINT"], "minutes_ago": 133
},
]
FLIGHTS = [
{"callsign": "FORTE12", "type": "استطلاع", "aircraft": "RQ-4 Global Hawk", "alt": "55,000 ft", "speed": "340 kts", "lat": 34.5, "lng": 33.8, "heading": "090°", "origin": "سيغونيلا"},
{"callsign": "JAKE21", "type": "مقاتلة", "aircraft": "F-35A Lightning", "alt": "35,000 ft", "speed": "480 kts", "lat": 33.3, "lng": 44.4, "heading": "270°", "origin": "العديد"},
{"callsign": "RCH405", "type": "شحن", "aircraft": "C-17 Globemaster", "alt": "32,000 ft", "speed": "450 kts", "lat": 25.1, "lng": 51.3, "heading": "180°", "origin": "رامشتاين"},
{"callsign": "REAP01", "type": "مسيّرة", "aircraft": "MQ-9 Reaper", "alt": "25,000 ft", "speed": "200 kts", "lat": 35.2, "lng": 38.9, "heading": "045°", "origin": "الأردن"},
{"callsign": "NAVY04", "type": "استطلاع", "aircraft": "P-8A Poseidon", "alt": "28,000 ft", "speed": "380 kts", "lat": 43.5, "lng": 34.0, "heading": "315°", "origin": "كريت"},
{"callsign": "HAWK77", "type": "مقاتلة", "aircraft": "F-15E Strike Eagle", "alt": "38,000 ft", "speed": "520 kts", "lat": 32.0, "lng": 47.0, "heading": "135°", "origin": "الظفرة"},
]
# ===== ألوان وأيقونات =====
TYPE_CONFIG = {
"strike": {"color": "#ef4444", "icon": "💥", "name": "غارة / ضربة"},
"missile": {"color": "#f97316", "icon": "🚀", "name": "صاروخ"},
"aircraft": {"color": "#06b6d4", "icon": "✈️", "name": "طائرة"},
"troops": {"color": "#eab308", "icon": "🎖️", "name": "تحرك قوات"},
"naval": {"color": "#22c55e", "icon": "🚢", "name": "نشاط بحري"},
}
SEVERITY_AR = {
"critical": "🔴 حرج",
"high": "🟠 مرتفع",
"medium": "🟡 متوسط",
"low": "🔵 منخفض",
}
REGION_MAP = {
"all": "جميع المناطق",
"middleeast": "الشرق الأوسط",
"europe": "أوروبا الشرقية",
"africa": "شمال أفريقيا",
"redSea": "البحر الأحمر",
"asia": "جنوب آسيا",
}
# ===== دوال المساعدة =====
def time_ago(minutes):
if minutes < 60:
return f"منذ {minutes} دقيقة"
hours = minutes // 60
return f"منذ {hours} ساعة"
def remaining_time(minutes):
remaining_mins = max(0, 1440 - minutes)
h = remaining_mins // 60
m = remaining_mins % 60
return f"{h} ساعة و {m} دقيقة"
def remaining_pct(minutes):
return max(0, ((1440 - minutes) / 1440) * 100)
# ===== بناء الخريطة =====
def build_map(region="all", severity=None, event_type=None, search=""):
if severity is None:
severity = ["critical", "high", "medium", "low"]
if event_type is None:
event_type = ["strike", "missile", "aircraft", "troops", "naval"]
m = folium.Map(
location=[30, 40],
zoom_start=4,
tiles=None,
control_scale=True,
)
folium.TileLayer(
tiles="https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png",
attr="CartoDB Dark",
name="خريطة مظلمة",
control=False,
).add_to(m)
filtered = EVENTS
if region != "all":
filtered = [e for e in filtered if e["region"] == region]
sev_map = {"🔴 حرج": "critical", "🟠 مرتفع": "high", "🟡 متوسط": "medium", "🔵 منخفض": "low"}
active_sevs = [sev_map.get(s, s) for s in severity]
filtered = [e for e in filtered if e["severity"] in active_sevs]
type_map = {"💥 غارات": "strike", "🚀 صواريخ": "missile", "✈️ طيران": "aircraft", "🎖️ قوات": "troops", "🚢 بحري": "naval"}
active_types = [type_map.get(t, t) for t in event_type]
filtered = [e for e in filtered if e["type"] in active_types]
if search:
filtered = [e for e in filtered if search in e["title"] or search in e["location"]]
for ev in filtered:
cfg = TYPE_CONFIG.get(ev["type"], {"color": "#0ea5e9", "icon": "📍"})
color = cfg["color"]
icon_char = cfg["icon"]
popup_html = f"""
<div style="direction:rtl;font-family:Arial,sans-serif;min-width:220px;padding:4px;">
<h4 style="margin:0 0 6px;color:{color};font-size:14px;">{icon_char} {ev['title']}</h4>
<p style="margin:2px 0;font-size:12px;">📍 <b>{ev['location']}</b></p>
<p style="margin:2px 0;font-size:12px;">📋 {ev['details']}</p>
<p style="margin:2px 0;font-size:12px;">🕐 {time_ago(ev['minutes_ago'])}</p>
<p style="margin:2px 0;font-size:12px;">⏱️ متبقي: {remaining_time(ev['minutes_ago'])}</p>
<p style="margin:2px 0;font-size:12px;">📡 {' • '.join(ev['sources'])}</p>
<div style="margin-top:6px;height:4px;background:#333;border-radius:2px;">
<div style="height:100%;width:{remaining_pct(ev['minutes_ago'])}%;background:{color};border-radius:2px;"></div>
</div>
</div>
"""
icon_html = f"""
<div style="
width:28px;height:28px;border-radius:50%;
background:radial-gradient(circle,{color}55,{color}20);
border:2px solid {color};
display:flex;align-items:center;justify-content:center;
font-size:14px;
box-shadow:0 0 12px {color}80;
">{icon_char}</div>
"""
folium.Marker(
location=[ev["lat"], ev["lng"]],
popup=folium.Popup(popup_html, max_width=300),
icon=folium.DivIcon(
html=icon_html,
icon_size=(28, 28),
icon_anchor=(14, 14),
),
).add_to(m)
folium.CircleMarker(
location=[ev["lat"], ev["lng"]],
radius=18,
color=color,
fill=True,
fill_color=color,
fill_opacity=0.08,
weight=1,
opacity=0.3,
).add_to(m)
for fl in FLIGHTS:
fl_popup = f"""
<div style="direction:rtl;font-family:Arial,sans-serif;min-width:180px;">
<h4 style="margin:0 0 4px;color:#06b6d4;">{fl['callsign']}</h4>
<p style="margin:2px 0;font-size:12px;">🛩️ {fl['aircraft']}</p>
<p style="margin:2px 0;font-size:12px;">📏 الارتفاع: {fl['alt']}</p>
<p style="margin:2px 0;font-size:12px;">⚡ السرعة: {fl['speed']}</p>
<p style="margin:2px 0;font-size:12px;">🧭 الاتجاه: {fl['heading']}</p>
<p style="margin:2px 0;font-size:12px;">🏠 القاعدة: {fl['origin']}</p>
<p style="margin:2px 0;font-size:12px;">📋 النوع: {fl['type']}</p>
</div>
"""
folium.Marker(
location=[fl["lat"], fl["lng"]],
popup=folium.Popup(fl_popup, max_width=250),
icon=folium.DivIcon(
html=f'<div style="font-size:22px;filter:drop-shadow(0 0 6px #06b6d4);">✈️</div>',
icon_size=(24, 24),
icon_anchor=(12, 12),
),
).add_to(m)
legend_html = """
<div style="
position:fixed;bottom:30px;right:30px;z-index:9999;
background:rgba(15,20,30,0.92);color:#e2e8f0;
padding:14px 18px;border-radius:10px;font-size:12px;
border:1px solid #2a3a4e;font-family:Arial,sans-serif;
direction:rtl;backdrop-filter:blur(8px);
box-shadow:0 4px 20px rgba(0,0,0,0.4);
">
<div style="font-weight:bold;margin-bottom:8px;font-size:13px;">🗺️ دليل الرموز</div>
<div style="display:flex;align-items:center;gap:8px;margin:4px 0;"><span style="width:10px;height:10px;border-radius:50%;background:#ef4444;display:inline-block;"></span> غارة / ضربة</div>
<div style="display:flex;align-items:center;gap:8px;margin:4px 0;"><span style="width:10px;height:10px;border-radius:50%;background:#f97316;display:inline-block;"></span> صاروخ</div>
<div style="display:flex;align-items:center;gap:8px;margin:4px 0;"><span style="width:10px;height:10px;border-radius:50%;background:#06b6d4;display:inline-block;"></span> طائرة</div>
<div style="display:flex;align-items:center;gap:8px;margin:4px 0;"><span style="width:10px;height:10px;border-radius:50%;background:#eab308;display:inline-block;"></span> تحرك قوات</div>
<div style="display:flex;align-items:center;gap:8px;margin:4px 0;"><span style="width:10px;height:10px;border-radius:50%;background:#22c55e;display:inline-block;"></span> نشاط بحري</div>
<div style="display:flex;align-items:center;gap:8px;margin:4px 0;"><span style="font-size:14px;">✈️</span> طائرات مراقَبة</div>
</div>
"""
m.get_root().html.add_child(folium.Element(legend_html))
return m._repr_html_()
# ===== بناء جدول الأحداث =====
def build_events_table(region="all", severity=None, event_type=None, search=""):
if severity is None:
severity = ["🔴 حرج", "🟠 مرتفع", "🟡 متوسط", "🔵 منخفض"]
if event_type is None:
event_type = ["💥 غارات", "🚀 صواريخ", "✈️ طيران", "🎖️ قوات", "🚢 بحري"]
sev_map = {"🔴 حرج": "critical", "🟠 مرتفع": "high", "🟡 متوسط": "medium", "🔵 منخفض": "low"}
type_map = {"💥 غارات": "strike", "🚀 صواريخ": "missile", "✈️ طيران": "aircraft", "🎖️ قوات": "troops", "🚢 بحري": "naval"}
filtered = EVENTS
if region != "all" and region != "جميع المناطق":
region_key = {v: k for k, v in REGION_MAP.items()}.get(region, region)
filtered = [e for e in filtered if e["region"] == region_key]
active_sevs = [sev_map.get(s, s) for s in severity]
filtered = [e for e in filtered if e["severity"] in active_sevs]
active_types = [type_map.get(t, t) for t in event_type]
filtered = [e for e in filtered if e["type"] in active_types]
if search:
filtered = [e for e in filtered if search in e["title"] or search in e["location"]]
filtered.sort(key=lambda x: x["minutes_ago"])
sev_colors = {"critical": "#ef4444", "high": "#f97316", "medium": "#eab308", "low": "#0ea5e9"}
sev_names = {"critical": "حرج", "high": "مرتفع", "medium": "متوسط", "low": "منخفض"}
cards_html = ""
for ev in filtered:
cfg = TYPE_CONFIG.get(ev["type"], {"color": "#0ea5e9", "icon": "📍", "name": "—"})
sc = sev_colors[ev["severity"]]
pct = remaining_pct(ev["minutes_ago"])
cards_html += f"""
<div style="
background:linear-gradient(135deg,#1a2332,#1f2937);
border:1px solid #2a3a4e;border-right:3px solid {sc};
border-radius:10px;padding:14px 16px;margin-bottom:8px;
transition:all 0.2s;
">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;">
<span style="background:{sc}20;color:{sc};padding:2px 10px;border-radius:4px;font-size:11px;font-weight:bold;">
{sev_names[ev['severity']]}
</span>
<span style="font-size:11px;color:#64748b;">{cfg['icon']} {ev['category']}</span>
</div>
<div style="font-size:14px;font-weight:bold;margin-bottom:8px;color:#e2e8f0;line-height:1.6;">
{ev['title']}
</div>
<div style="display:flex;gap:16px;font-size:11px;color:#94a3b8;margin-bottom:8px;">
<span>📍 {ev['location']}</span>
<span>🕐 {time_ago(ev['minutes_ago'])}</span>
</div>
<div style="display:flex;gap:4px;margin-bottom:8px;">
{''.join(f'<span style="font-size:10px;padding:2px 8px;border-radius:4px;background:rgba(6,182,212,0.1);color:#06b6d4;border:1px solid rgba(6,182,212,0.2);">{s}</span>' for s in ev['sources'])}
</div>
<div style="display:flex;align-items:center;gap:8px;font-size:10px;color:#f97316;padding-top:8px;border-top:1px solid #2a3a4e;">
<span>⏱️ متبقي: {remaining_time(ev['minutes_ago'])}</span>
<div style="flex:1;height:3px;background:#2a3a4e;border-radius:2px;">
<div style="height:100%;width:{pct}%;background:#f97316;border-radius:2px;"></div>
</div>
</div>
</div>
"""
return cards_html if cards_html else '<div style="text-align:center;padding:40px;color:#64748b;">لا توجد نتائج مطابقة للفلاتر المحددة</div>'
# ===== بناء قائمة الطيران =====
def build_flights_html():
type_colors = {"استطلاع": "#a855f7", "مقاتلة": "#ef4444", "شحن": "#22c55e", "مسيّرة": "#f97316"}
html = ""
for fl in FLIGHTS:
tc = type_colors.get(fl["type"], "#06b6d4")
html += f"""
<div style="
background:linear-gradient(135deg,#1a2332,#1f2937);
border:1px solid #2a3a4e;border-radius:10px;
padding:14px 16px;margin-bottom:8px;
">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;">
<span style="font-size:16px;font-weight:bold;color:#06b6d4;direction:ltr;">{fl['callsign']}</span>
<span style="background:{tc}20;color:{tc};padding:2px 10px;border-radius:4px;font-size:11px;font-weight:bold;">{fl['type']}</span>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;font-size:12px;">
<div style="display:flex;justify-content:space-between;"><span style="color:#64748b;">الطائرة</span><span style="color:#e2e8f0;font-weight:600;">{fl['aircraft']}</span></div>
<div style="display:flex;justify-content:space-between;"><span style="color:#64748b;">الارتفاع</span><span style="color:#e2e8f0;font-weight:600;direction:ltr;">{fl['alt']}</span></div>
<div style="display:flex;justify-content:space-between;"><span style="color:#64748b;">السرعة</span><span style="color:#e2e8f0;font-weight:600;direction:ltr;">{fl['speed']}</span></div>
<div style="display:flex;justify-content:space-between;"><span style="color:#64748b;">الاتجاه</span><span style="color:#e2e8f0;font-weight:600;direction:ltr;">{fl['heading']}</span></div>
<div style="display:flex;justify-content:space-between;grid-column:span 2;"><span style="color:#64748b;">القاعدة</span><span style="color:#e2e8f0;font-weight:600;">{fl['origin']}</span></div>
</div>
</div>
"""
return html
# ===== بناء المصادر =====
def build_sources_html():
sources = [
{"name": "Bamqam", "url": "https://bamqam.com", "status": "متصل", "color": "#22c55e", "desc": "منصة رصد الأحداث العسكرية والنزاعات", "events": 8},
{"name": "WorldMonitor", "url": "https://worldmonitor.app", "status": "متصل", "color": "#22c55e", "desc": "مراقب النزاعات العالمية المباشر", "events": 6},
{"name": "Conflictly", "url": "https://conflictly.app", "status": "متصل", "color": "#22c55e", "desc": "تتبع وتحليل النزاعات المسلحة", "events": 7},
{"name": "PIZZINT", "url": "https://pizzint.watch", "status": "جزئي", "color": "#eab308", "desc": "استخبارات مفتوحة المصدر OSINT", "events": 4},
{"name": "ADS-B Exchange", "url": "https://globe.adsbexchange.com", "status": "متصل", "color": "#22c55e", "desc": "تتبع حركة الطيران بدون فلاتر", "events": 12},
{"name": "Flightradar24", "url": "https://flightradar24.com", "status": "متصل", "color": "#22c55e", "desc": "رادار الطيران العالمي", "events": 9},
]
html = ""
for s in sources:
html += f"""
<div style="
background:linear-gradient(135deg,#1a2332,#1f2937);
border:1px solid #2a3a4e;border-radius:10px;
padding:14px 16px;margin-bottom:8px;
">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;">
<span style="font-size:14px;font-weight:bold;color:#e2e8f0;">{s['name']}</span>
<span style="display:flex;align-items:center;gap:4px;font-size:11px;color:{s['color']};">
<span style="width:7px;height:7px;border-radius:50%;background:{s['color']};display:inline-block;"></span>
{s['status']}
</span>
</div>
<div style="font-size:11px;color:#94a3b8;margin-bottom:6px;">{s['desc']}</div>
<div style="display:flex;justify-content:space-between;font-size:10px;color:#64748b;">
<span>🔗 <a href="{s['url']}" target="_blank" style="color:#06b6d4;text-decoration:none;">{s['url'].replace('https://','')}</a></span>
<span>📊 {s['events']} حدث</span>
</div>
</div>
"""
return html
# ===== بناء الإحصائيات =====
def build_stats_html():
total = len(EVENTS)
strikes = len([e for e in EVENTS if e["type"] == "strike"])
missiles = len([e for e in EVENTS if e["type"] == "missile"])
aircraft = len([e for e in EVENTS if e["type"] == "aircraft"])
troops = len([e for e in EVENTS if e["type"] == "troops"])
naval = len([e for e in EVENTS if e["type"] == "naval"])
critical = len([e for e in EVENTS if e["severity"] == "critical"])
now = datetime.now().strftime("%Y/%m/%d — %H:%M:%S")
return f"""
<div style="display:flex;flex-wrap:wrap;gap:8px;direction:rtl;">
<div style="flex:1;min-width:130px;background:linear-gradient(135deg,#1a2332,#1f2937);border:1px solid #2a3a4e;border-radius:10px;padding:14px;text-align:center;">
<div style="font-size:28px;font-weight:900;color:#ef4444;">{total}</div>
<div style="font-size:11px;color:#94a3b8;">إجمالي الأحداث</div>
</div>
<div style="flex:1;min-width:130px;background:linear-gradient(135deg,#1a2332,#1f2937);border:1px solid #2a3a4e;border-radius:10px;padding:14px;text-align:center;">
<div style="font-size:28px;font-weight:900;color:#f97316;">{strikes}</div>
<div style="font-size:11px;color:#94a3b8;">💥 غارات / ضربات</div>
</div>
<div style="flex:1;min-width:130px;background:linear-gradient(135deg,#1a2332,#1f2937);border:1px solid #2a3a4e;border-radius:10px;padding:14px;text-align:center;">
<div style="font-size:28px;font-weight:900;color:#a855f7;">{missiles}</div>
<div style="font-size:11px;color:#94a3b8;">🚀 صواريخ</div>
</div>
<div style="flex:1;min-width:130px;background:linear-gradient(135deg,#1a2332,#1f2937);border:1px solid #2a3a4e;border-radius:10px;padding:14px;text-align:center;">
<div style="font-size:28px;font-weight:900;color:#06b6d4;">{aircraft}</div>
<div style="font-size:11px;color:#94a3b8;">✈️ طائرات</div>
</div>
<div style="flex:1;min-width:130px;background:linear-gradient(135deg,#1a2332,#1f2937);border:1px solid #2a3a4e;border-radius:10px;padding:14px;text-align:center;">
<div style="font-size:28px;font-weight:900;color:#eab308;">{troops}</div>
<div style="font-size:11px;color:#94a3b8;">🎖️ قوات</div>
</div>
<div style="flex:1;min-width:130px;background:linear-gradient(135deg,#1a2332,#1f2937);border:1px solid #2a3a4e;border-radius:10px;padding:14px;text-align:center;">
<div style="font-size:28px;font-weight:900;color:#22c55e;">{naval}</div>
<div style="font-size:11px;color:#94a3b8;">🚢 بحري</div>
</div>
</div>
<div style="margin-top:10px;text-align:center;font-size:11px;color:#64748b;">
🕐 آخر تحديث: {now} &nbsp;|&nbsp; 🔴 أحداث حرجة: {critical} &nbsp;|&nbsp; ⏱️ الأحداث تنتهي بعد 24 ساعة
</div>
"""
# ===== الدالة الرئيسية للتحديث =====
def update_dashboard(region, severity, event_type, search):
map_html = build_map(region, severity, event_type, search)
events_html = build_events_table(region, severity, event_type, search)
stats_html = build_stats_html()
return map_html, events_html, stats_html
def update_all(region, severity, event_type, search):
map_h, events_h, stats_h = update_dashboard(region, severity, event_type, search)
return stats_h, map_h, events_h
# ===== CSS =====
CUSTOM_CSS = """
@import url('https://fonts.googleapis.com/css2?family=Cairo:wght@300;400;600;700;800;900&display=swap');
* { font-family: 'Cairo', sans-serif !important; }
.gradio-container {
max-width: 100% !important;
background: #0a0e17 !important;
color: #e2e8f0 !important;
}
.dark { background: #0a0e17 !important; }
#header-bar {
background: linear-gradient(135deg, #0f172a, #1e293b) !important;
border: 1px solid #2a3a4e !important;
border-radius: 12px !important;
padding: 16px 24px !important;
margin-bottom: 8px !important;
}
.tab-nav button {
background: #1a2332 !important;
color: #94a3b8 !important;
border: 1px solid #2a3a4e !important;
border-radius: 8px !important;
font-weight: 700 !important;
font-size: 13px !important;
}
.tab-nav button.selected {
background: rgba(14,165,233,0.15) !important;
color: #0ea5e9 !important;
border-color: #0ea5e9 !important;
}
.block { background: transparent !important; border: none !important; }
label { color: #94a3b8 !important; font-weight: 600 !important; }
input, select, textarea, .wrap {
background: #1a2332 !important;
border: 1px solid #2a3a4e !important;
color: #e2e8f0 !important;
border-radius: 8px !important;
}
.checkbox-group label { color: #e2e8f0 !important; }
button.primary {
background: linear-gradient(135deg, #0ea5e9, #06b6d4) !important;
border: none !important;
font-weight: 700 !important;
border-radius: 8px !important;
}
.ticker-bar {
background: rgba(239,68,68,0.08);
border: 1px solid rgba(239,68,68,0.2);
border-radius: 8px;
padding: 8px 16px;
text-align: center;
font-size: 13px;
color: #ef4444;
font-weight: 700;
overflow: hidden;
white-space: nowrap;
margin-bottom: 8px;
}
footer { display: none !important; }
"""
# ===== بناء التطبيق =====
with gr.Blocks(
css=CUSTOM_CSS,
title="مركز رصد النزاعات والتطورات العسكرية",
theme=gr.themes.Base(
primary_hue="sky",
neutral_hue="slate",
),
) as app:
# ===== الرأس =====
gr.HTML("""
<div id="header-bar" style="
background:linear-gradient(135deg,#0f172a,#1e293b);
border:1px solid #2a3a4e;border-radius:12px;
padding:16px 24px;margin-bottom:8px;
display:flex;justify-content:space-between;align-items:center;
direction:rtl;
">
<div style="display:flex;align-items:center;gap:14px;">
<div style="
width:48px;height:48px;border-radius:12px;
background:linear-gradient(135deg,#0ea5e9,#06b6d4);
display:flex;align-items:center;justify-content:center;
font-size:24px;
">🛡️</div>
<div>
<h1 style="margin:0;font-size:22px;font-weight:900;
background:linear-gradient(135deg,#e2e8f0,#0ea5e9);
-webkit-background-clip:text;-webkit-text-fill-color:transparent;">
مركز رصد النزاعات والتطورات العسكرية
</h1>
<p style="margin:0;font-size:12px;color:#64748b;">نظام المراقبة والإنذار المبكر — تحديث تلقائي — بيانات من 6 مصادر موثوقة</p>
</div>
</div>
<div style="display:flex;align-items:center;gap:16px;">
<div style="
display:flex;align-items:center;gap:8px;
background:rgba(239,68,68,0.1);border:1px solid rgba(239,68,68,0.3);
padding:6px 16px;border-radius:20px;font-size:13px;
color:#ef4444;font-weight:700;
">
<span style="width:8px;height:8px;background:#ef4444;border-radius:50%;display:inline-block;animation:pulse 1.5s infinite;"></span>
بث مباشر
</div>
</div>
</div>
<style>@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.4}}</style>
""")
# شريط الأخبار العاجلة
gr.HTML("""
<div class="ticker-bar">
🔴 عاجل: رصد تحركات عسكرية غير اعتيادية &nbsp;|&nbsp;
⚠️ تنبيه: طائرة استطلاع فوق مناطق النزاع &nbsp;|&nbsp;
🔴 تقارير عن إطلاق صواريخ باليستية &nbsp;|&nbsp;
📡 رصد: تغيير في أنماط الطيران العسكري فوق البحر الأحمر
</div>
""")
# الإحصائيات
stats_display = gr.HTML(value=build_stats_html())
# ===== الفلاتر =====
with gr.Row():
with gr.Column(scale=1):
region_dd = gr.Dropdown(
choices=list(REGION_MAP.values()),
value="جميع المناطق",
label="🌍 المنطقة",
)
with gr.Column(scale=2):
severity_cb = gr.CheckboxGroup(
choices=["🔴 حرج", "🟠 مرتفع", "🟡 متوسط", "🔵 منخفض"],
value=["🔴 حرج", "🟠 مرتفع", "🟡 متوسط", "🔵 منخفض"],
label="⚡ مستوى الخطورة",
)
with gr.Column(scale=2):
type_cb = gr.CheckboxGroup(
choices=["💥 غارات", "🚀 صواريخ", "✈️ طيران", "🎖️ قوات", "🚢 بحري"],
value=["💥 غارات", "🚀 صواريخ", "✈️ طيران", "🎖️ قوات", "🚢 بحري"],
label="📋 نوع الحدث",
)
with gr.Column(scale=1):
search_box = gr.Textbox(label="🔍 بحث", placeholder="ابحث في الأحداث...")
apply_btn = gr.Button("🔄 تطبيق الفلاتر وتحديث", variant="primary", size="lg")
# ===== المحتوى الرئيسي =====
with gr.Tabs():
# === تبويب الخريطة والأحداث ===
with gr.Tab("🗺️ الخريطة والأحداث"):
with gr.Row():
with gr.Column(scale=3):
map_display = gr.HTML(
value=build_map(),
label="الخريطة",
)
with gr.Column(scale=1):
events_display = gr.HTML(
value=build_events_table(),
label="آخر الأحداث",
)
# === تبويب الطيران ===
with gr.Tab("✈️ حركة الطيران"):
gr.HTML("""
<div style="
background:linear-gradient(135deg,#0f172a,#1e293b);
border:1px solid #2a3a4e;border-radius:10px;padding:14px;margin-bottom:10px;
text-align:center;direction:rtl;
">
<span style="font-size:14px;font-weight:700;color:#06b6d4;">📡 الطائرات المراقَبة حالياً</span>
<span style="font-size:12px;color:#94a3b8;margin-right:12px;">
المصادر: ADS-B Exchange • Flightradar24
</span>
</div>
""")
flights_display = gr.HTML(value=build_flights_html())
# === تبويب المصادر ===
with gr.Tab("📡 المصادر"):
gr.HTML("""
<div style="
background:linear-gradient(135deg,#0f172a,#1e293b);
border:1px solid #2a3a4e;border-radius:10px;padding:14px;margin-bottom:10px;
text-align:center;direction:rtl;
">
<span style="font-size:14px;font-weight:700;color:#0ea5e9;">🔗 مصادر البيانات المتصلة</span>
<span style="font-size:12px;color:#94a3b8;margin-right:12px;">
6 مصادر — 5 متصل — 1 جزئي
</span>
</div>
""")
sources_display = gr.HTML(value=build_sources_html())
# === تبويب حول ===
with gr.Tab("ℹ️ حول النظام"):
gr.HTML("""
<div style="direction:rtl;padding:20px;max-width:700px;margin:0 auto;">
<div style="background:linear-gradient(135deg,#0f172a,#1e293b);border:1px solid #2a3a4e;border-radius:12px;padding:24px;">
<h2 style="color:#0ea5e9;margin-top:0;">🛡️ مركز رصد النزاعات والتطورات العسكرية</h2>
<p style="color:#94a3b8;line-height:1.8;font-size:14px;">
نظام متكامل لمراقبة وتتبع التطورات العسكرية والنزاعات حول العالم.
يعتمد النظام على جمع البيانات من مصادر متعددة وموثوقة وعرضها
بشكل تفاعلي على خريطة مع تنبيهات فورية.
</p>
<h3 style="color:#e2e8f0;margin-top:16px;">📋 المزايا الرئيسية</h3>
<ul style="color:#94a3b8;line-height:2;font-size:13px;">
<li>خريطة تفاعلية مظلمة بنمط تكتيكي عسكري</li>
<li>تصنيف الأحداث: غارات، صواريخ، طيران، قوات، نشاط بحري</li>
<li>مستويات خطورة: حرج، مرتفع، متوسط، منخفض</li>
<li>مؤقت 24 ساعة لكل حدث — ينتهي تلقائياً</li>
<li>ربط كل خبر بموقعه الجغرافي على الخريطة</li>
<li>تتبع حركة الطيران العسكري والاستطلاعي</li>
<li>فلاتر متقدمة: منطقة، خطورة، نوع، بحث نصي</li>
</ul>
<h3 style="color:#e2e8f0;margin-top:16px;">📡 المصادر</h3>
<ul style="color:#94a3b8;line-height:2;font-size:13px;">
<li><a href="https://bamqam.com" target="_blank" style="color:#06b6d4;">Bamqam</a> — رصد الأحداث العسكرية</li>
<li><a href="https://worldmonitor.app" target="_blank" style="color:#06b6d4;">WorldMonitor</a> — مراقب النزاعات العالمية</li>
<li><a href="https://conflictly.app" target="_blank" style="color:#06b6d4;">Conflictly</a> — تتبع النزاعات المسلحة</li>
<li><a href="https://pizzint.watch" target="_blank" style="color:#06b6d4;">PIZZINT</a> — استخبارات مفتوحة المصدر</li>
<li><a href="https://globe.adsbexchange.com" target="_blank" style="color:#06b6d4;">ADS-B Exchange</a> — تتبع الطيران</li>
<li><a href="https://flightradar24.com" target="_blank" style="color:#06b6d4;">Flightradar24</a> — رادار الطيران</li>
</ul>
</div>
</div>
""")
# ===== الشريط السفلي =====
gr.HTML("""
<div style="
background:rgba(15,23,42,0.9);border:1px solid #2a3a4e;
border-radius:10px;padding:10px 20px;margin-top:8px;
display:flex;justify-content:space-between;align-items:center;
font-size:11px;color:#64748b;direction:rtl;
">
<div style="display:flex;gap:12px;align-items:center;">
<span>المصادر:</span>
<span style="display:flex;align-items:center;gap:3px;"><span style="width:6px;height:6px;border-radius:50%;background:#22c55e;display:inline-block;"></span> Bamqam</span>
<span style="display:flex;align-items:center;gap:3px;"><span style="width:6px;height:6px;border-radius:50%;background:#22c55e;display:inline-block;"></span> WorldMonitor</span>
<span style="display:flex;align-items:center;gap:3px;"><span style="width:6px;height:6px;border-radius:50%;background:#22c55e;display:inline-block;"></span> Conflictly</span>
<span style="display:flex;align-items:center;gap:3px;"><span style="width:6px;height:6px;border-radius:50%;background:#eab308;display:inline-block;"></span> PIZZINT</span>
<span style="display:flex;align-items:center;gap:3px;"><span style="width:6px;height:6px;border-radius:50%;background:#22c55e;display:inline-block;"></span> ADS-B</span>
<span style="display:flex;align-items:center;gap:3px;"><span style="width:6px;height:6px;border-radius:50%;background:#22c55e;display:inline-block;"></span> FR24</span>
</div>
<div>
⏱️ الأحداث تنتهي بعد 24 ساعة &nbsp;|&nbsp; 🛡️ مركز رصد النزاعات v1.0
</div>
</div>
""")
# ===== الأحداث =====
apply_btn.click(
fn=update_all,
inputs=[region_dd, severity_cb, type_cb, search_box],
outputs=[stats_display, map_display, events_display],
)
for inp in [region_dd, severity_cb, type_cb]:
inp.change(
fn=update_all,
inputs=[region_dd, severity_cb, type_cb, search_box],
outputs=[stats_display, map_display, events_display],
)
search_box.submit(
fn=update_all,
inputs=[region_dd, severity_cb, type_cb, search_box],
outputs=[stats_display, map_display, events_display],
)
# ===== التشغيل =====
if __name__ == "__main__":
app.launch(
share=False,
server_name="0.0.0.0",
server_port=7860,
show_error=True,
)