CyberArena / app /services /chatbot_service.py
Hussien Haider
H
dfd40ba
Raw
History Blame Contribute Delete
11.8 kB
"""Global AI Chatbot — helps users understand the CyberArena platform.
Uses the same five-tier AI fallback as the generators:
Mistral → Cloudflare → Groq → NVIDIA → fallback.
"""
from typing import Optional
import httpx
from app.core.config import (
MISTRAL_API_KEY, MISTRAL_MODEL, MISTRAL_API_URL,
CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID,
GROQ_API_KEY, GROQ_API_URL, GROQ_MODEL,
NVIDIA_API_KEY, NVIDIA_MODEL, NVIDIA_URL,
)
# --------------------------------------------------------------------------- #
# System prompt — comprehensive description of the entire platform #
# --------------------------------------------------------------------------- #
_CHATBOT_MISTRAL_MODEL = "mistral-small-latest"
PLATFORM_SYSTEM_PROMPT = """
أنت Cyber، المساعد الذكي الرسمي لمنصة CyberArena.
## تعريف المنصة
CyberArena هي منصة تفاعلية لتعلم الأمن السيبراني (Cybersecurity) مدعومة بالذكاء الاصطناعي.
المنصة موجهة للمبتدئين والمحترفين العرب، والواجهة باللغة العربية مع إمكانية التبديل إلى الإنجليزية.
## الصفحات المتاحة
1. **الرئيسية (Home)**: الصفحة التعريفية، فيها شرح عن المنصة وميزاتها.
2. **تسجيل الدخول (Auth)**: صفحة إنشاء حساب أو تسجيل الدخول.
3. **لوحة التحكم (Dashboard)**: الصفحة الرئيسية بعد تسجيل الدخول، فيها:
- عرض الإحصائيات (المستوى، XP، التحديات المنجزة)
- اختيار نوع التحدي (Red Team أو Blue Team)
- الوصول إلى الصفحات الأخرى (الملف الشخصي، لوحة المتصدرين، 1v1)
4. **الملف الشخصي (Profile)**: عرض معلومات المستخدم، الصورة الرمزية، الإنجازات، الشهادات.
5. **مسار التحديات (Training Path)**: اختيار نوع التحدي من التخصصات المتاحة.
6. **التحدي (Training Session)**: واجهة حل التحدي (تظهر فقط عند الدخول في تحدي).
7. **لوحة المتصدرين (Leaderboard)**: ترتيب المستخدمين حسب XP.
8. **وضع 1v1 (One-vs-One)**: مواجهة مباشرة بين لاعبين في نفس التحدي، أول من يحل يفوز.
9. **وضع أزرق ضد أحمر (Blue vs Red)**: تحديات مختلطة للفريقين.
10. **التحقق من الشهادة (Verify)**: إدخال رمز التحقق للشهادة والتأكد من صحتها.
## أنواع التحديات
1. **التشفير (Crypto) - الفريق الأحمر**: تحديات في فك التشفير، تحليل الهاش، اختراق أنظمة التشفير.
2. **تصحيح الأكواد (Code Fixing) - الفريق الأزرق**: إيجاد وإصلاح الثغرات الأمنية في كود برمجي.
3. **تحليل السجلات (Log Analysis) - الفريق الأزرق**: تحليل سجلات النظام لاكتشاف الهجمات.
4. **صيد الثغرات (Vulnerability Hunter) - الفريق الأزرق**: تحديد نوع الثغرة ووصفها في كود معين.
5. **استغلال الويب (Web Exploitation) - الفريق الأحمر**: هجمات ويب مثل XSS, SQLi, CSRF وغيرها.
## الفرق
- **الفريق الأحمر (Red Team)**: الهجوم — اختراق الأنظمة، فك التشفير، استغلال الثغرات.
- **الفريق الأزرق (Blue Team)**: الدفاع — تحليل السجلات، إصلاح الأكواد، صيد الثغرات.
## آلية عمل التحديات
- التحديات تُنشأ تلقائياً بالذكاء الاصطناعي (5 مستويات احتياطية).
- كل تحدي له قصة وسيناريو واقعي.
- يوجد مرشد ذكي (AI Mentor) داخل كل تحدي يساعد بدون إعطاء الحل النهائي.
- XP تُمنح عند حل التحدي بنجاح.
## الشهادات
- بعد إكمال عدد معين من التحديات في تخصص معين، يحصل المستخدم على شهادة إتمام.
- الشهادة تحتوي على رمز QR للتحقق.
- يمكن التحقق من صحة الشهادة عبر صفحة التحقق (Verify).
## نظام XP والمستويات
- كل تحدي يمنح XP حسب صعوبته: مبتدئ (100)، سهل (150)، متوسط (200).
- مع تزايد XP يرتفع مستوى المستخدم.
## التكنولوجيا المستخدمة
- الواجهة: React + TypeScript
- الخلفية: Python + FastAPI
- قاعدة البيانات: Supabase (PostgreSQL)
- الذكاء الاصطناعي: Mistral API (الأساسي)، Cloudflare Workers AI (احتياطي)، Groq API، NVIDIA/DeepSeek
## تعليمات للمساعد Cyber
1. أجب باللغة العربية الفصحى المبسطة دائماً.
2. كن ودوداً ومشجعاً.
3. إذا سأل المستخدم عن شيء خارج المنصة، قل له أنك متخصص فقط في CyberArena.
4. إذا احتاج مساعدة في ميزة معينة، اشرحها خطوة بخطوة.
5. شجع المستخدم على تجربة الميزات المختلفة.
6. لا تعطي حلول التحديات — هذا دور المرشد المختص (AI Mentor) داخل التحدي نفسه.
7. إذا سأل عن تحدي معين، وجهه لفتح التحدي من لوحة التحكم.
8. للأسئلة عن الـ API أو التقنيات التقنية، اشرح بشكل مبسط مناسب للمبتدئين.
9. اختصر الردود — لا تطيل أكثر من 3-4 فقرات قصيرة.
10. إذا ما عرفت الإجابة، اعترف بذلك بصراحة.
"""
# --------------------------------------------------------------------------- #
# Public entry point #
# --------------------------------------------------------------------------- #
async def get_chatbot_response(
message: str,
conversation_history: Optional[list[dict[str, str]]] = None,
page: str = "",
lang: str = "ar",
) -> str:
"""Ask the platform AI chatbot for help."""
lang_instr = (
"أجب دائمًا باللغة العربية الفصحى."
if lang == "ar"
else "Always respond in English."
)
prompt = f"{lang_instr}\n\n{message}"
if page:
prompt += f"\n\n(PAGE: {page})"
result = await _call_chatbot_ai(prompt)
if result is None:
if lang == "ar":
return "عذراً، واجهت مشكلة في الاتصال. حاول مرة أخرى بعد قليل."
return "Sorry, I had trouble connecting. Please try again later."
return result.strip()[:1000]
# --------------------------------------------------------------------------- #
# Five-tier AI fallback #
# --------------------------------------------------------------------------- #
async def _call_chatbot_ai(prompt: str) -> Optional[str]:
for attempt in (_try_mistral, _try_cloudflare, _try_groq, _try_nvidia):
result = await attempt(prompt)
if result is not None:
return result
return None
async def _try_mistral(prompt: str) -> Optional[str]:
if not MISTRAL_API_KEY:
return None
try:
async with httpx.AsyncClient(timeout=30) as client:
resp = await client.post(
MISTRAL_API_URL,
json={
"model": _CHATBOT_MISTRAL_MODEL,
"messages": [
{"role": "system", "content": PLATFORM_SYSTEM_PROMPT},
{"role": "user", "content": prompt},
],
"temperature": 0.7,
"max_tokens": 600,
},
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {MISTRAL_API_KEY}",
},
)
if resp.status_code == 200:
return resp.json()["choices"][0]["message"]["content"]
except Exception:
pass
return None
CLOUDFLARE_FALLBACKS = [
"@cf/qwen/qwen2.5-coder-32b-instruct",
"@cf/meta/llama-3.3-70b-instruct-fp8-fast",
"@cf/meta/llama-3.1-70b-instruct",
"@cf/mistralai/mistral-small-3.1-24b-instruct",
"@cf/openai/gpt-oss-120b",
"@cf/meta/llama-3.1-8b-instruct",
]
async def _try_cloudflare(prompt: str) -> Optional[str]:
if not CLOUDFLARE_API_TOKEN or not CLOUDFLARE_ACCOUNT_ID:
return None
for model in CLOUDFLARE_FALLBACKS:
url = (
f"https://api.cloudflare.com/client/v4/accounts/"
f"{CLOUDFLARE_ACCOUNT_ID}/ai/run/{model}"
)
try:
async with httpx.AsyncClient(timeout=45) as client:
resp = await client.post(
url,
json={
"messages": [
{"role": "system", "content": PLATFORM_SYSTEM_PROMPT},
{"role": "user", "content": prompt},
],
"max_tokens": 600,
},
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {CLOUDFLARE_API_TOKEN}",
},
)
if resp.status_code == 200:
data = resp.json()
result = data.get("result", {})
content = result.get("response", "")
if content:
return content
except Exception:
continue
return None
async def _try_groq(prompt: str) -> Optional[str]:
if not GROQ_API_KEY:
return None
try:
async with httpx.AsyncClient(timeout=60) as client:
resp = await client.post(
GROQ_API_URL,
json={
"model": GROQ_MODEL,
"messages": [
{"role": "system", "content": PLATFORM_SYSTEM_PROMPT},
{"role": "user", "content": prompt},
],
"temperature": 0.7,
"max_tokens": 600,
},
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {GROQ_API_KEY}",
},
)
if resp.status_code == 200:
return resp.json()["choices"][0]["message"]["content"]
except Exception:
pass
return None
async def _try_nvidia(prompt: str) -> Optional[str]:
if not NVIDIA_API_KEY:
return None
try:
async with httpx.AsyncClient(timeout=60) as client:
resp = await client.post(
NVIDIA_URL,
json={
"model": NVIDIA_MODEL,
"messages": [
{"role": "system", "content": PLATFORM_SYSTEM_PROMPT},
{"role": "user", "content": prompt},
],
"temperature": 0.7,
"max_tokens": 600,
},
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {NVIDIA_API_KEY}",
},
)
if resp.status_code == 200:
return resp.json()["choices"][0]["message"]["content"]
except Exception:
pass
return None