Files
intaleq_v3_pure_php/functions.php
2026-04-28 13:04:27 +03:00

463 lines
16 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(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://location.intaleq.xyz:2021";
$INTERNAL_KEY = trim(@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(@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);
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 = getenv('LOCATION_SOCKET_URL');
if (!$url) throw new RuntimeException('LOCATION_SOCKET_URL not configured');
$INTERNAL_KEY = trim(@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 = getenv('LOCATION_SOCKET_URL');
if (!$url) throw new RuntimeException('LOCATION_SOCKET_URL not configured');
$INTERNAL_KEY = trim(@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) {
// الرابط المحلي لسيرفر سوكيت الركاب
$url = getenv('PASSENGER_SOCKET_URL');
if (!$url) {
error_log("[FATAL] PASSENGER_SOCKET_URL not configured");
throw new RuntimeException('PASSENGER_SOCKET_URL not configured');
}
$INTERNAL_KEY = trim(@file_get_contents('/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, 1000);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"x-internal-key: $INTERNAL_KEY"
]);
$response = curl_exec($ch);
if (curl_errno($ch)) {
error_log("Curl Error (Passenger Socket): " . curl_error($ch));
}
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 = getenv('LOCATION_SOCKET_URL');
if (!$socketUrl) throw new RuntimeException('LOCATION_SOCKET_URL not configured');
$internalKeyPath = '/home/intaleq-api/.internal_socket_key';
$internalKey = file_exists($internalKeyPath) ? trim(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);
}