Files
Siro/backend/functions.php
2026-06-09 08:40:31 +03:00

485 lines
17 KiB
PHP
Executable File

<?php
//functions.php
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\SignatureInvalidException;
use Firebase\JWT\BeforeValidException;
$INTERNAL_KEY = trim((string)@file_get_contents('/home/intaleq-api/.internal_socket_key'));
/**
* دالة البحث الهجين (Redis + MySQL)
* @param $redis Client : اتصال الريدز
* @param $con PDO : اتصال قاعدة البيانات الرئيسية (Main DB)
* @param $lat float : إحداثيات الراكب
* @param $lng float : إحداثيات الراكب
* @param $carType string: نوع الطلب (comfort, speed, Lady...)
*/
function sendToLocationServer($action, $data) {
// رابط سيرفر اللوكيشن الداخلي أو العام
$url = "http://188.68.36.205:2021";
$INTERNAL_KEY = trim((string)@file_get_contents('/home/intaleq-api/.internal_socket_key'));
$postData = [
'action' => $action,
...$data
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 500); // سريع جداً
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-internal-key: $INTERNAL_KEY"]);
curl_exec($ch);
curl_close($ch);
}
function findBestDrivers($con, $lat, $lng, $carType) {
// 1. الاتصال بـ Redis لجلب الأقرب
$locationServerUrl = "https://location.intaleq.xyz/api_get_nearby.php";
$INTERNAL_KEY = trim((string)@file_get_contents('/home/intaleq-api/.internal_socket_key'));
$postData = ['lat' => $lat, 'lng' => $lng, 'radius' => 5, 'limit' => 100];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $locationServerUrl);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 3);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-internal-key: $INTERNAL_KEY"]);
$response = curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);
error_log("[findBestDrivers] HTTP Code: " . $info['http_code'] . " Response: " . $response);
if ($info['http_code'] !== 200) return [];
$json = json_decode($response, true);
$nearbyDrivers = ($json['status'] ?? false) ? $json['data'] : [];
if (empty($nearbyDrivers)) return [];
// 2. تجهيز البيانات للفلترة
$driverIds = [];
$redisMap = [];
foreach ($nearbyDrivers as $d) {
$driverIds[] = $d['id'];
$redisMap[$d['id']] = $d;
}
$placeholders = implode(',', array_fill(0, count($driverIds), '?'));
// تعريف الثوابت
$CAT_CAR = 1; $CAT_BIKE = 2; $CAT_VAN = 3; $FUEL_ELECTRIC = 3;
// 3. الاستعلام (بدون platform)
$sql = "SELECT
d.id AS driver_id,
dt.token,
cr.year,
cr.vehicle_category_id,
d.gender
FROM driver d
JOIN CarRegistration cr ON cr.driverID = d.id
JOIN driverToken dt ON dt.captain_id = d.id
WHERE d.id IN ($placeholders) ";
$carType = trim($carType);
switch ($carType) {
case 'Comfort':
$sql .= " AND cr.vehicle_category_id = $CAT_CAR AND CAST(TRIM(cr.year) AS UNSIGNED) > 2017 ";
break;
case 'Mishwar Vip':
$sql .= " AND cr.vehicle_category_id = $CAT_CAR AND CAST(TRIM(cr.year) AS UNSIGNED) > 2020 ";
break;
case 'Scooter':
case 'Pink Bike':
$sql .= " AND cr.vehicle_category_id = $CAT_BIKE ";
break;
case 'Electric':
$sql .= " AND cr.vehicle_category_id = $CAT_CAR AND cr.fuel_type_id = $FUEL_ELECTRIC ";
break;
case 'Lady':
$femaleHash = 'bQ6yWJ2EVXKZooHdGclvmFiDlZCM8UYeO+ILFjDUvpQ=';
$sql .= " AND cr.vehicle_category_id = $CAT_CAR AND d.gender = '$femaleHash' ";
break;
case 'Van':
$sql .= " AND cr.vehicle_category_id = $CAT_VAN ";
break;
case 'Awfar Car':
$sql .= " AND cr.vehicle_category_id = $CAT_CAR AND CAST(TRIM(cr.year) AS UNSIGNED) > 1995 ";
break;
case 'Fixed Price':
case 'Speed':
case 'Rayeh Gai':
default:
$sql .= " AND cr.vehicle_category_id = $CAT_CAR AND CAST(TRIM(cr.year) AS UNSIGNED) > 2000 ";
break;
}
try {
$stmt = $con->prepare($sql);
$stmt->execute($driverIds);
$finalDrivers = $stmt->fetchAll(PDO::FETCH_ASSOC);
// دمج البيانات
foreach ($finalDrivers as &$driver) {
$did = $driver['driver_id'];
if (isset($redisMap[$did])) {
$driver['distance_km'] = $redisMap[$did]['distance'];
$driver['lat'] = $redisMap[$did]['lat'];
$driver['lng'] = $redisMap[$did]['lng'];
} else {
$driver['distance_km'] = 999;
}
}
// الترتيب
usort($finalDrivers, function($a, $b) {
return $a['distance_km'] <=> $b['distance_km'];
});
return array_slice($finalDrivers, 0, 30);
} catch (Exception $e) {
error_log("FindBestDrivers Error: " . $e->getMessage());
return [];
}
}
// --- دالة مساعدة لمخاطبة سيرفر السائقين (Location Socket) ---
function notifyDriversRideTaken($rideId, $winnerDriverId) {
// رابط سيرفر السائقين الداخلي (نفس البورت المستخدم في driver_socket.php)
$url = "http://188.68.36.205:2021";
$INTERNAL_KEY = trim((string)@file_get_contents('/home/intaleq-api/.internal_socket_key'));
$postData = [
'action' => 'ride_taken_event', // هذا الأكشن الجديد في السوكيت
'ride_id' => $rideId,
'taken_by_driver_id' => $winnerDriverId
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 500); // نصف ثانية فقط، لا نريد تعطيل الـ API
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-internal-key: $INTERNAL_KEY"]);
$response = curl_exec($ch);
curl_close($ch);
}
function notifyDriversOnLocationServer($drivers_ids_array, $payload, $rideId = null) {
// رابط سيرفر اللوكيشن الخارجي
$url = "http://188.68.36.205:2021";
$INTERNAL_KEY = trim((string)@file_get_contents('/home/intaleq-api/.internal_socket_key'));
$postData = [
'action' => 'dispatch_order', // اسم الحدث المتفق عليه في socket_server.php هناك
'drivers_ids' => json_encode($drivers_ids_array), // نحول المصفوفة لنص JSON
'ride_id' => $rideId ?? '', // ✅ تصحيح اسم المتغير
'payload' => $payload
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 1000); // لا تنتظر أكثر من ثانية
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"x-internal-key: $INTERNAL_KEY"
]);
$response = curl_exec($ch);
if (curl_errno($ch)) {
error_log("Curl Error (Location Socket): " . curl_error($ch));
}
curl_close($ch);
return $response;
}
/**
* 🚀 دالة إشعار الراكب (تعمل على سيرفر الرحلات)
* تخاطب السوكيت الموجود محلياً على نفس السيرفر
*/
function notifyPassengerOnRideServer($passenger_id, $payload) {
// الرابط لسيرفر سوكيت الركاب — IP مباشر لتجاوز مشاكل الجدار الناري والدومين
$url = "http://188.68.36.205:3031";
$INTERNAL_KEY = trim((string)@file_get_contents('/home/intaleq-api/.internal_socket_key'));
if (empty($INTERNAL_KEY)) {
error_log("[SOCKET_CRITICAL] Internal key missing at /home/intaleq-api/.internal_socket_key");
}
$postData = [
'action' => 'update_ride_status',
'passenger_id' => $passenger_id,
'payload' => $payload
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 3000);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"x-internal-key: $INTERNAL_KEY"
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
error_log("[SOCKET_DEBUG] Curl Error (Passenger Socket) to $url: " . curl_error($ch));
} else {
error_log("[SOCKET_DEBUG] Sent to Passenger Socket $url | HTTP: $httpCode | Response: $response | Passenger: $passenger_id");
}
curl_close($ch);
return $response;
}
// ============================================================================
// دالة توزيع الطلب (محدثة لاستخدام sendFCM_Internal)
// ============================================================================
// ============================================================================
// دالة توزيع الطلب (Dispatch Function) - النسخة المصححة
// ============================================================================
function dispatchRideToDrivers($driversData, $rideId, $payloadTemplate, $startNameLoc, $encryptionHelper) {
$countDrivers = count($driversData);
error_log("🚀 [DISPATCH_START] RideID: $rideId | Drivers Count: $countDrivers");
$socketUrl = 'http://188.68.36.205:2021';
$internalKeyPath = '/home/intaleq-api/.internal_socket_key';
$internalKey = file_exists($internalKeyPath) ? trim((string)@file_get_contents($internalKeyPath)) : '';
foreach ($driversData as $driver) {
$driverId = $driver['driver_id'];
$rawToken = $driver['token'] ?? '';
error_log("--------------------------------------------------");
error_log("👤 [DRIVER_PROCESS] Processing Driver ID: $driverId");
// 1. معالجة التوكن
$driverToken = processDriverToken($rawToken, $encryptionHelper);
// تجهيز البيانات الخاصة بالسائق
$payloadForDriver = $payloadTemplate;
$payloadForDriver[6] = (string)$driverId;
$payloadForDriver[18] = (string)$driverId;
// 2. إرسال السوكيت
sendSocketNotification($driverId, $rideId, $payloadForDriver, $socketUrl, $internalKey);
// 3. إرسال FCM
if (!empty($driverToken)) {
$fcmData = [
'DriverList' => $payloadForDriver,
'order_id' => (string)$rideId
];
$fcmResult = sendFcmNotification(
$driverToken,
"طلب جديد 🔔",
"هناك رحلة جديدة من " . $startNameLoc,
$fcmData,
"Order",
"ding"
);
error_log("📲 [FCM_RESULT] " . json_encode($fcmResult));
} else {
error_log("⚠️ [FCM_SKIP] No valid token for Driver $driverId");
}
}
error_log("🏁 [DISPATCH_END] RideID: $rideId");
}
/**
* معالجة توكن السائق وفك تشفيره
*/
function processDriverToken($rawToken, $encryptionHelper) {
if (empty($rawToken)) {
error_log("🚫 [TOKEN_MISSING] No token found.");
return '';
}
try {
$decrypted = $encryptionHelper->decryptData($rawToken);
if ($decrypted !== false && !empty($decrypted)) {
error_log("✅ [TOKEN_DECRYPT] Success.");
return trim($decrypted);
}
error_log("⚠️ [TOKEN_DECRYPT] Failed. Using Raw.");
return $rawToken;
} catch (Exception $e) {
error_log("❌ [TOKEN_EXCEPTION] Error. Using Raw.");
return $rawToken;
}
}
/**
* إرسال إشعار السوكيت لسيرفر اللوكيشن
*/
function sendSocketNotification($driverId, $rideId, $payload, $url, $internalKey) {
$postData = [
'action' => 'dispatch_order',
'drivers_ids' => json_encode([$driverId]),
'ride_id' => $rideId,
'payload' => $payload
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
if (!empty($internalKey)) curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-internal-key: $internalKey"]);
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1);
$res = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
error_log("📡 [SOCKET_SEND] Driver $driverId | HTTP: $httpCode");
}
/**
* إرسال إشعار FCM الموحد
*/
function sendFcmNotification($token, $title, $body, $data, $category, $tone) {
if (class_exists('FcmService')) {
global $redis;
$fcmService = new FcmService($redis ?? null);
return $fcmService->send($token, $title, $body, $data, $category, $tone);
} elseif (function_exists('sendFCM_Internal')) {
return sendFCM_Internal($token, $title, $body, $data, $category, false, $tone);
}
return ['status' => 'error', 'message' => 'FCM service not loaded'];
}
function authenticateJWT(): object
{
global $redis;
if (!class_exists('JwtService')) {
require_once __DIR__ . '/core/Auth/JwtService.php';
}
$jwtService = new JwtService($redis ?? null);
return $jwtService->authenticate();
}
define("MB", 1048576);
/**
* Send WhatsApp message using your server's API
*
* @param string $to The recipient phone number (e.g., 96279xxxxxxx)
* @param string $message The message to send
* @return mixed API response object or false on failure
*/
function sendWhatsAppFromServer($to, $message)
{
// 1) قائمة السيرفرات المتاحة
$servers = [
//"https://botmasa.intaleq.xyz/send",//mayar
// "https://botmasa2.intaleq.xyz/send",//shad
"https://bot5.intaleq.xyz/send",//ramat bus
"https://bot3.intaleq.xyz/send",//shahd
//"https://whatsapp.tripz-egypt.com/send"//tripz
];
// 2) محاولة الإرسال (Primary -> Fallback)
$response = null;
$success = false;
foreach ($servers as $url) {
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 3, // مهلة قصيرة للمحاولة
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
"to" => $to,
"message" => $message
], JSON_UNESCAPED_UNICODE),
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if (!$err) {
$success = true;
break;
} else {
error_log("[sendWhatsAppFromServer] Server $url failed, trying next... Error: $err");
}
}
if (!$success) return false;
return json_decode($response, true);
}
function sendFCM_Internal(
$target,
$title,
$body,
$customData = [],
$category = 'Order',
$isTopic = false,
$tone = 'order'
) {
global $redis;
if (!class_exists('FcmService')) {
require_once __DIR__ . '/core/Services/FcmService.php';
}
$fcm = new FcmService($redis ?? null);
return $fcm->send($target, $title, $body, is_array($customData) ? $customData : [], $category, $tone);
}
function logAudit($con, $adminId, $action, $tableName = null, $recordId = null, $details = null) {
try {
if (empty($adminId)) {
$adminId = 'unknown_admin';
}
$stmt = $con->prepare("
INSERT INTO `admin_audit_log` (`admin_id`, `action`, `table_name`, `record_id`, `details`)
VALUES (:admin_id, :action, :table_name, :record_id, :details)
");
$stmt->execute([
':admin_id' => $adminId,
':action' => $action,
':table_name' => $tableName,
':record_id' => $recordId,
':details' => is_array($details) ? json_encode($details, JSON_UNESCAPED_UNICODE) : $details
]);
return true;
} catch (Exception $e) {
error_log("Audit Log Error: " . $e->getMessage());
return $e->getMessage();
}
}