Update codebase

This commit is contained in:
Hamza-Ayed
2026-08-09 16:56:13 +03:00
parent 95e2e4f35d
commit b64debaa88
1058 changed files with 164327 additions and 113928 deletions
+155
View File
@@ -0,0 +1,155 @@
<?php
/**
* sms/parser.php — تحليل نص الرسالة إلى نقطتين
* ─────────────────────────────────────────────────────────────
* ‏الراكب يكتب بلغته لا بصيغة نموذج. لا يمكن فرض قالب صارم على من
* ‏لجأ إلى الرسائل أصلاً لأنه لا يستطيع استعمال التطبيق — فالمحلّل
* ‏يقبل ما يقبله المنطق، ويرد بسؤال واضح حين يعجز.
*
* ‏الصيغ المدعومة:
* من الجامعة الاردنية الى المستشفى التخصصي
* من الجامعة للمستشفى
* الجامعة - المستشفى
* الجامعة > المستشفى
* من 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,
];
}