المستشفى * من 31.95,35.91 الى المستشفى (إحداثيات مباشرة) */ require_once __DIR__ . '/helpers.php'; /** * ‏هل هذه الرسالة طلب رحلة أصلاً؟ * * ‏الويب‌هوك نفسه يستقبل إشعارات الدفع من البنوك، فلا بد من فرز قبل * ‏التحليل. طلب الرحلة يحوي فاصلاً بين نقطتين؛ إشعار الدفع يحوي مبلغاً * ‏وكلمات مالية. */ function smsLooksLikeRideRequest(string $body): bool { $b = smsNormalizeArabic($body); // ‏كلمات الإشعارات المالية — وجودها يرجّح أنها ليست طلب رحلة. if (preg_match('/(حواله|حوالة|رصيد|محفظتك|دينار|جنيه|ليره|مزود الخدمه|Orange Money)/ui', $b)) { return false; } // ‏الأنماط على النص الموحَّد: «الى/إلى» صارتا «الي» بعد التوحيد. return (bool) preg_match('/(^|\s)من\s+.+\s+(الي|علي|ل)\s*\S/u', $b) || (bool) preg_match('/\S\s+[-]\s+\S/u', $b) || (bool) preg_match('/\S\s*[<>→]\s*\S/u', $b); } /** * ‏يستخرج نصّي النقطتين. لا يلمس قاعدة البيانات — الفصل يجعل اختباره * ‏ممكناً بلا بيئة كاملة، وهذا المكوّن هو الأكثر عرضة للتعديل. * * @return array{start:string,end:string}|null */ function smsExtractEndpoints(string $body): ?array { $b = smsNormalizeArabic($body); // ‏١) «من … إلى …» — الصيغة الأوضح، ونجرّبها أولاً. // // ‏تنبيه: النص هنا **بعد** التوحيد، والتوحيد يحوّل ى إلى ي — // ‏فـ«الى» و«إلى» كلتاهما صارتا «الي». مطابقة «الى» الأصلية هنا // ‏تفشل دائماً، وهو ما كان يُسقط أشيع صيغة يكتبها الناس. // // ‏و«ل» الملتصقة تُعالَج على حدة: «للمستشفى» = ل + المستشفى، // ‏فابتلاع اللامين معاً يترك «مستشفى» بلا تعريف ويُفشل المطابقة. if (preg_match('/من\s+(.+?)\s+(الي|علي|ل)\s*(.+)$/u', $b, $m)) { $start = trim($m[1]); $end = trim($m[3]); // ‏«لل…» وصلت مقسومة: الفاصل «ل» والباقي يبدأ بـ«ل». نعيد // ‏بناء أداة التعريف بدل تركها مبتورة. if ($m[2] === 'ل' && str_starts_with($end, 'ل')) { $end = 'ا' . $end; } // ‏«… لل الصويفية» بمسافة: يبقى «ال» معلّقاً وحده فنسقطه. $end = preg_replace('/^ال\s+/u', '', $end) ?? $end; if ($start !== '' && $end !== '') { return ['start' => $start, 'end' => $end]; } } // ‏٢) فاصل رمزي: «الجامعة - المستشفى». // ‏ننتبه ألا نقطع إحداثيات سالبة أو تاريخاً — لذلك نشترط مسافة // ‏حول الشرطة، بخلاف < و > اللذين لا يلتبسان بشيء. if (preg_match('/^(.+?)\s+[-]\s+(.+)$/u', $b, $m) || preg_match('/^(.+?)\s*[<>→]\s*(.+)$/u', $b, $m)) { $start = trim($m[1]); $end = trim($m[2]); if ($start !== '' && $end !== '') { return ['start' => $start, 'end' => $end]; } } return null; } /** * ‏يحوّل نصاً واحداً إلى نقطة: إمّا إحداثيات مكتوبة صراحةً، أو معلم * ‏نبحث عنه في جدول المعالم. * * @return array{lat:float,lng:float,name:string}|null */ function smsResolvePoint(PDO $con, string $text): ?array { $t = trim($text); // ‏إحداثيات صريحة — يرسلها من نسخ موقعه من تطبيق آخر. if (preg_match('/^(-?\d{1,3}\.\d+)\s*,\s*(-?\d{1,3}\.\d+)$/', $t, $m)) { $lat = (float) $m[1]; $lng = (float) $m[2]; if ($lat >= -90 && $lat <= 90 && $lng >= -180 && $lng <= 180) { return ['lat' => $lat, 'lng' => $lng, 'name' => $t]; } } // ‏كلمات حشو شائعة في بداية اسم المكان — إزالتها ترفع نسبة المطابقة. $t = preg_replace('/^(هنا|موقعي|عند|قرب|جنب|حد|في)\s+/ui', '', $t) ?? $t; return smsFindPlace($con, $t); } /** * ‏التحليل الكامل: نص خام ← نقطتان محلولتان. * * @return array{ * ok: bool, reason?: string, * raw_start?: string, raw_end?: string, * start?: array, end?: array * } */ function smsParseRideRequest(PDO $con, string $body): array { $ends = smsExtractEndpoints($body); if (!$ends) { return ['ok' => false, 'reason' => 'format']; } $start = smsResolvePoint($con, $ends['start']); $end = smsResolvePoint($con, $ends['end']); // ‏نفرّق بين «لم أفهم الصيغة» و«لم أجد المكان»: الرسالتان الراجعتان // ‏للراكب مختلفتان تماماً، والخلط بينهما يجعله يعيد نفس الخطأ. if (!$start || !$end) { return [ 'ok' => false, 'reason' => 'place', 'raw_start' => $ends['start'], 'raw_end' => $ends['end'], 'missing' => !$start ? $ends['start'] : $ends['end'], ]; } return [ 'ok' => true, 'raw_start' => $ends['start'], 'raw_end' => $ends['end'], 'start' => $start, 'end' => $end, ]; }