prepare("SELECT u.id, u.tenant_id, u.name, u.role FROM users u WHERE u.phone_hash = ? AND u.is_active = 1 LIMIT 1"); $stmt->execute([$phoneHash]); $user = $stmt->fetch(); // 2. Handle commands $textLower = mb_strtolower(trim($text)); // === LINK COMMAND === if (str_starts_with($textLower, 'ربط ') || str_starts_with($textLower, 'link ')) { $code = trim(str_replace(['ربط', 'link'], '', $text)); handleLinkCommand($db, $wa, $from, $phoneHash, $code); exit; } // === HELP COMMAND === if (in_array($textLower, ['مساعدة', 'help', '؟', '?'])) { $wa->sendMessage($from, "🤖 *أوامر مُصادَق:*\n\n" . "📸 أرسل صورة فاتورة → نستخرج البيانات بالـ AI\n" . "🔗 ربط [الكود] → لربط رقمك بحسابك\n" . "📊 حالتي → ملخص حسابك\n" . "❓ مساعدة → هذه الرسالة\n\n" . "للتسجيل: musadaq.intaleqapp.com"); json_success(null, 'Help sent'); exit; } // === ACCOUNT NOT LINKED === if (!$user) { $wa->sendMessage($from, "👋 مرحباً!\n\n" . "رقمك غير مربوط بحساب مُصادَق.\n" . "لربط حسابك، أرسل: *ربط [الكود]*\n\n" . "للحصول على الكود، افتح تطبيق مُصادَق → الإعدادات → ربط واتساب.\n\n" . "أو سجّل حساب جديد: musadaq.intaleqapp.com"); json_success(null, 'Unlinked user guided'); exit; } $userName = Encryption::decrypt($user['name']) ?: 'المستخدم'; // === STATUS COMMAND === if (in_array($textLower, ['حالتي', 'status', 'حالة'])) { handleStatusCommand($db, $wa, $from, $user, $userName); exit; } // === IMAGE/INVOICE PROCESSING === if ($imageData || $imageUrl) { handleInvoiceImage($db, $wa, $from, $user, $userName, $imageData, $imageUrl, $mimeType); exit; } // === DEFAULT: Unknown text === $wa->sendMessage($from, "مرحباً {$userName} 👋\n\n" . "لم أفهم طلبك. يمكنك:\n" . "📸 إرسال صورة فاتورة لاستخراج البيانات\n" . "📊 كتابة *حالتي* لملخص حسابك\n" . "❓ كتابة *مساعدة* لقائمة الأوامر"); json_success(null, 'Default response sent'); } catch (\Throwable $e) { error_log("[whatsapp/webhook] Error: " . $e->getMessage()); try { $wa->sendMessage($from, "⚠️ حدث خطأ أثناء المعالجة. يرجى المحاولة مرة أخرى."); } catch (\Throwable $ignore) {} json_success(null, 'Error handled'); // Return 200 so the bot doesn't retry } // ═══════════════════════════════════════════ // HANDLER FUNCTIONS // ═══════════════════════════════════════════ function handleLinkCommand($db, $wa, string $from, string $phoneHash, string $code): void { if (empty($code)) { $wa->sendMessage($from, "❌ يرجى إرسال الكود. مثال: *ربط ABC123*"); json_success(null, 'Empty code'); return; } // Find user by link code $stmt = $db->prepare("SELECT id, tenant_id FROM users WHERE whatsapp_link_code = ? AND is_active = 1 LIMIT 1"); $stmt->execute([strtoupper(trim($code))]); $targetUser = $stmt->fetch(); if (!$targetUser) { $wa->sendMessage($from, "❌ الكود غير صحيح. تأكد من الكود في تطبيق مُصادَق → الإعدادات → ربط واتساب."); json_success(null, 'Invalid code'); return; } // Update user's phone hash $updateStmt = $db->prepare("UPDATE users SET phone_hash = ?, whatsapp_linked = 1, whatsapp_link_code = NULL WHERE id = ?"); $updateStmt->execute([$phoneHash, $targetUser['id']]); $wa->sendMessage($from, "✅ تم ربط رقمك بحسابك بنجاح! 🎉\n\n" . "الآن يمكنك إرسال صور الفواتير مباشرة هنا وسنستخرج البيانات تلقائياً."); json_success(null, 'Account linked'); } function handleStatusCommand($db, $wa, string $from, array $user, string $userName): void { $tenantId = $user['tenant_id']; // Get stats $invoiceStmt = $db->prepare("SELECT COUNT(*) as total, SUM(CASE WHEN status='extracted' THEN 1 ELSE 0 END) as pending FROM invoices WHERE tenant_id = ?"); $invoiceStmt->execute([$tenantId]); $stats = $invoiceStmt->fetch(); $subStmt = $db->prepare("SELECT plan_slug, invoices_used_this_month, max_invoices_per_month FROM subscriptions WHERE tenant_id = ?"); $subStmt->execute([$tenantId]); $sub = $subStmt->fetch(); $plan = $sub['plan_slug'] ?? 'free'; $used = $sub['invoices_used_this_month'] ?? 0; $max = $sub['max_invoices_per_month'] ?? 15; $msg = "📊 *ملخص حسابك، {$userName}:*\n\n" . "📋 إجمالي الفواتير: {$stats['total']}\n" . "⏳ بانتظار المراجعة: {$stats['pending']}\n" . "📦 الباقة: {$plan}\n" . "🔢 الاستخدام: {$used}/{$max} فاتورة هذا الشهر\n\n" . "🌐 لوحة التحكم: musadaq.intaleqapp.com"; $wa->sendMessage($from, $msg); json_success(null, 'Status sent'); } function handleInvoiceImage($db, $wa, string $from, array $user, string $userName, ?string $imageData, ?string $imageUrl, string $mimeType): void { $wa->sendMessage($from, "📸 استلمت الصورة! جارٍ استخراج البيانات بالذكاء الاصطناعي... ⏳"); // Get image data if (!$imageData && $imageUrl) { $imageContent = @file_get_contents($imageUrl); if (!$imageContent) { $wa->sendMessage($from, "❌ فشل تحميل الصورة. يرجى إرسالها مرة أخرى."); json_success(null, 'Image download failed'); return; } $imageData = base64_encode($imageContent); } if (!$imageData) { $wa->sendMessage($from, "❌ لم أتمكن من قراءة الصورة."); json_success(null, 'No image data'); return; } // Run AI extraction $extracted = AI::extractInvoiceData($imageData, $mimeType); if (!$extracted) { $wa->sendMessage($from, "⚠️ لم أتمكن من استخراج البيانات. تأكد أن الصورة واضحة وتحتوي على فاتورة."); json_success(null, 'AI extraction failed'); return; } // Format response $supplierName = $extracted['supplier']['name'] ?? 'غير محدد'; $invoiceNum = $extracted['invoice_number'] ?? '-'; $invoiceDate = $extracted['invoice_date'] ?? '-'; $subtotal = number_format((float)($extracted['subtotal'] ?? 0), 2); $tax = number_format((float)($extracted['tax_amount'] ?? 0), 2); $total = number_format((float)($extracted['grand_total'] ?? 0), 2); $linesCount = count($extracted['lines'] ?? []); $msg = "✅ *تم استخراج بيانات الفاتورة:*\n\n" . "🏢 المورد: {$supplierName}\n" . "🔢 رقم الفاتورة: {$invoiceNum}\n" . "📅 التاريخ: {$invoiceDate}\n" . "📦 البنود: {$linesCount}\n" . "───────────────\n" . "💰 المبلغ قبل الضريبة: {$subtotal} دينار\n" . "🏛️ الضريبة: {$tax} دينار\n" . "📊 *الإجمالي: {$total} دينار*\n\n"; // Add warnings if any if (!empty($extracted['validation_warnings'])) { $msg .= "⚠️ *تحذيرات:*\n"; foreach ($extracted['validation_warnings'] as $w) { $msg .= "• {$w}\n"; } $msg .= "\n"; } $msg .= "💡 لحفظ هذه الفاتورة رسمياً، ارفعها من تطبيق مُصادَق."; $wa->sendMessage($from, $msg); // Log the interaction try { AuditLogger::log('whatsapp.invoice_extracted', 'whatsapp', null, null, [ 'from' => substr($from, 0, 6) . '****', 'invoice_number' => $invoiceNum, 'total' => $total, ], ['user_id' => $user['id'], 'tenant_id' => $user['tenant_id'], 'role' => $user['role']]); } catch (\Throwable $e) { // Non-critical } json_success(null, 'Invoice extracted via WhatsApp'); }