Files
Siro/backend/core/Services/SiroGeminiService.php
2026-06-21 18:58:13 +03:00

130 lines
5.7 KiB
PHP

<?php
// ============================================================
// core/Services/SiroGeminiService.php
// Siro AI Market Analysis & Marketing Content Generation Service
// ============================================================
declare(strict_types=1);
class SiroGeminiService {
private ?string $apiKey;
private string $baseUrl;
public function __construct() {
$this->apiKey = getenv('GEMINI_API_KEY') ?: null;
$this->baseUrl = "https://generativelanguage.googleapis.com/v1beta/models/";
}
/**
* Analyze market prices and generate target promotion response
*
* @param array $competitorPrices Array of competitor pricing information
* @param float $siroBasePrice Current Siro base pricing for this region/country
* @param string $regionName Name of the region/city
* @param string $countryCode Country code (e.g. SY, JO, EG, IQ)
* @param string $model Override AI model to use (default: gemini-1.5-flash)
* @return array|null Decoded JSON response from Gemini or null on failure
*/
public function analyzeMarketAndDraftCampaign(
array $competitorPrices,
float $siroBasePrice,
string $regionName,
string $countryCode,
string $model = 'gemini-1.5-flash'
): ?array {
if (!$this->apiKey) {
error_log("[SiroGeminiService] API Key is missing.");
return null;
}
$dialect = match (strtoupper($countryCode)) {
'SY' => 'السورية (الشامية)',
'JO' => 'الأردنية',
'EG' => 'المصرية',
'IQ' => 'العراقية',
default => 'العربية الفصحى البسيطة'
};
$prompt = "
أنت خبير تسويق ذكي ومحلل أسعار لتطبيق Siro لخدمات نقل الركاب.
قم بتحليل أسعار المنافسين في منطقة '$regionName' وصياغة حملة تسويقية وعرض ترويجي منافس.
بيانات الإدخال:
1. أسعار المنافسين الحالية: " . json_encode($competitorPrices, JSON_UNESCAPED_UNICODE) . "
2. سعر رحلة Siro الأساسي الحالي: $siroBasePrice
المطلوب:
1. دراسة الأسعار وتحديد هل توجد فرصة تسويقية واضحة لجذب الركاب (opportunity_detected: true/false).
2. تحديد معامل التخفيض المقترح أو قيمة خصم مناسبة (discount_value) والنسبة المئوية (discount_percentage).
3. كتابة رسالة تسويقية إعلانية جذابة وقصيرة جداً ومقنعة لإرسالها كإشعار (Push Notification) للركاب النشطين بالهجة $dialect.
4. كتابة رسالة استعادة جذابة ومغرية وقصيرة لإرسالها عبر SMS أو WhatsApp للركاب المنقطعين بالهجة $dialect مع ذكر كود الخصم المقترح.
5. اقتراح كود خصم مناسب للحملة (promo_code) ليكون سهل الحفظ ومناسباً للحدث.
الخرج المطلوب (يجب أن يكون JSON صالحاً تماماً وخالياً من أي شرح خارجي، باللغة العربية):
{
\"opportunity_detected\": true/false,
\"recommended_price\": 12000,
\"discount_percentage\": 15,
\"promo_code\": \"SIROGO15\",
\"push_title\": \"عنوان الإشعار\",
\"push_body\": \"محتوى الإشعار القصير والمثير للاهتمام\",
\"sms_body\": \"محتوى رسالة الاستعادة القصير والمقنع للـ SMS أو الواتساب\"
}
";
$apiUrl = $this->baseUrl . $model . ":generateContent?key=" . $this->apiKey;
$payload = [
'contents' => [
[
'parts' => [
['text' => $prompt]
]
]
]
];
$ch = curl_init($apiUrl);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_TIMEOUT => 30,
CURLOPT_CONNECTTIMEOUT => 5
]);
$response = curl_exec($ch);
$curlErr = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($curlErr) {
error_log("[SiroGeminiService] Curl Error: $curlErr");
return null;
}
if ($httpCode !== 200) {
error_log("[SiroGeminiService] HTTP Error $httpCode. Response: $response");
return null;
}
try {
$responseData = json_decode($response, true);
$rawText = $responseData['candidates'][0]['content']['parts'][0]['text'] ?? '';
// تنظيف أي علامات كود ماركداون محتملة من الموديل
$cleanJson = trim(preg_replace('/```json|```/', '', $rawText));
$decoded = json_decode($cleanJson, true);
if (json_last_error() !== JSON_ERROR_NONE) {
error_log("[SiroGeminiService] JSON Decode Error: " . json_last_error_msg() . " | Raw text: $rawText");
return null;
}
return $decoded;
} catch (Exception $e) {
error_log("[SiroGeminiService] Exception: " . $e->getMessage());
return null;
}
}
}