$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 { $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 false; } }