diff --git a/backend/app/Controllers/ChatbotController.php b/backend/app/Controllers/ChatbotController.php index c796df5..f8aabf6 100644 --- a/backend/app/Controllers/ChatbotController.php +++ b/backend/app/Controllers/ChatbotController.php @@ -108,12 +108,12 @@ class ChatbotController extends BaseController // 4. Retrieve the Gemini API key (custom or system default) $rules = ChatbotRule::findAllByCompany($request->company_id); - $apiKey = null; + $configuredKey = null; if (!empty($rules) && !empty($rules[0]['gemini_api_key'])) { - $apiKey = $rules[0]['gemini_api_key']; + $configuredKey = $rules[0]['gemini_api_key']; } - $apiKey = $apiKey ?: getenv('GEMINI_API_KEY'); + $apiKey = \App\Services\GeminiService::getGeminiApiKey($configuredKey); if (empty($apiKey)) { $response->status(500)->json(['status' => 'error', 'message' => 'Gemini API Key is not configured in the system']); diff --git a/backend/app/Controllers/WhatsAppController.php b/backend/app/Controllers/WhatsAppController.php index f290c68..e65c0a6 100644 --- a/backend/app/Controllers/WhatsAppController.php +++ b/backend/app/Controllers/WhatsAppController.php @@ -358,7 +358,8 @@ class WhatsAppController extends BaseController $replyText = $rule['ai_prompt']; // Under keyword rules, ai_prompt stores the predefined static reply } } elseif ($rule['trigger_type'] === 'gemini_ai') { - $apiKey = $rule['gemini_api_key'] ?: getenv('GEMINI_API_KEY'); + $configuredGeminiKey = ($rule && !empty($rule['gemini_api_key'])) ? $rule['gemini_api_key'] : null; + $apiKey = \App\Services\GeminiService::getGeminiApiKey($configuredGeminiKey); if (empty($apiKey)) { error_log("[Chatbot Warning] Gemini API Key is not set globally or for company " . $session['company_id']); return; @@ -399,8 +400,11 @@ class WhatsAppController extends BaseController if (strpos($mimeType, ';') !== false) { $mimeType = trim(explode(';', $mimeType)[0]); } - $elApiKey = !empty($rule['elevenlabs_api_key']) ? $rule['elevenlabs_api_key'] : (getenv('ELEVENLABS_API_KEY') ?: null); - $elVoiceId = !empty($rule['elevenlabs_voice_id']) ? $rule['elevenlabs_voice_id'] : (getenv('ELEVENLABS_VOICE_ID') ?: null); + $configuredElKey = !empty($rule['elevenlabs_api_key']) ? $rule['elevenlabs_api_key'] : null; + $elApiKey = \App\Services\GeminiService::getElevenLabsApiKey($configuredElKey); + + $configuredVoiceId = !empty($rule['elevenlabs_voice_id']) ? $rule['elevenlabs_voice_id'] : null; + $elVoiceId = \App\Services\GeminiService::getElevenLabsVoiceId($configuredVoiceId); // Try generating native audio response first $audioResponse = \App\Services\GeminiService::generateAudioResponseFromAudio( diff --git a/backend/app/Core/Flows/ConversationFlowEngine.php b/backend/app/Core/Flows/ConversationFlowEngine.php index 92427a2..b4a2d81 100644 --- a/backend/app/Core/Flows/ConversationFlowEngine.php +++ b/backend/app/Core/Flows/ConversationFlowEngine.php @@ -47,7 +47,8 @@ class ConversationFlowEngine $isAudio = !empty($msgData['audio']) && !empty($msgData['mimeType']); if ($isAudio) { $rule = \App\Models\ChatbotRule::findActiveForRule($companyId); - $apiKey = ($rule && !empty($rule['gemini_api_key'])) ? $rule['gemini_api_key'] : getenv('GEMINI_API_KEY'); + $configuredGeminiKey = ($rule && !empty($rule['gemini_api_key'])) ? $rule['gemini_api_key'] : null; + $apiKey = \App\Services\GeminiService::getGeminiApiKey($configuredGeminiKey); if (!empty($apiKey)) { $transcription = \App\Services\GeminiService::transcribeAudio($apiKey, $msgData['audio'], $msgData['mimeType']); if ($transcription) { @@ -123,7 +124,40 @@ class ConversationFlowEngine } // 5. Send reply if one is provided - if ($result->getReplyText() !== '' || $result->getMediaUrl() !== null) { + $replySent = false; + if ($companyId === 1 && $flowName === 'driver_registration_flow' && $result->getReplyText() !== '') { + $rule = \App\Models\ChatbotRule::findActiveForRule($companyId); + $configuredGeminiKey = ($rule && !empty($rule['gemini_api_key'])) ? $rule['gemini_api_key'] : null; + $apiKey = \App\Services\GeminiService::getGeminiApiKey($configuredGeminiKey); + + if (!empty($apiKey)) { + $configuredElKey = ($rule && !empty($rule['elevenlabs_api_key'])) ? $rule['elevenlabs_api_key'] : null; + $elApiKey = \App\Services\GeminiService::getElevenLabsApiKey($configuredElKey); + + $configuredVoiceId = ($rule && !empty($rule['elevenlabs_voice_id'])) ? $rule['elevenlabs_voice_id'] : null; + $elVoiceId = \App\Services\GeminiService::getElevenLabsVoiceId($configuredVoiceId); + + // Generate the audio voice note + $audioData = \App\Services\GeminiService::generateAudioResponse( + $apiKey, + "أنت خدمة تسجيل كباتن تطبيق انطلق، تتحدث بلهجة سورية ودودة ومرحبة ومهنية جداً كأنك إنسان حقيقي.", + $result->getReplyText(), + 'Puck', + $elApiKey, + $elVoiceId + ); + + if ($audioData && !empty($audioData['audio'])) { + // Send the text message first + self::sendReply($session, $phone, $result->getReplyText(), $result->getMediaUrl()); + // Then send the voice note + self::sendReply($session, $phone, '', null, $audioData['audio'], $audioData['mimeType']); + $replySent = true; + } + } + } + + if (!$replySent && ($result->getReplyText() !== '' || $result->getMediaUrl() !== null)) { self::sendReply($session, $phone, $result->getReplyText(), $result->getMediaUrl()); } diff --git a/backend/app/Core/Flows/DriverRegistrationFlow.php b/backend/app/Core/Flows/DriverRegistrationFlow.php index 895be7f..b65c8f1 100644 --- a/backend/app/Core/Flows/DriverRegistrationFlow.php +++ b/backend/app/Core/Flows/DriverRegistrationFlow.php @@ -169,7 +169,8 @@ EOT // Check if user requests postponement/delay (only if already started and not finished) if ($step !== 'start' && $step !== 'finished' && !empty($text)) { $rule = ChatbotRule::findActiveForRule($companyId); - $apiKey = ($rule && !empty($rule['gemini_api_key'])) ? $rule['gemini_api_key'] : getenv('GEMINI_API_KEY'); + $configuredGeminiKey = ($rule && !empty($rule['gemini_api_key'])) ? $rule['gemini_api_key'] : null; + $apiKey = GeminiService::getGeminiApiKey($configuredGeminiKey); if (!empty($apiKey)) { $postponeData = $this->detectPostponement($text, $apiKey); if ($postponeData !== null) { @@ -366,7 +367,8 @@ EOT $companyId = $context['company_id'] ?? 1; $rule = ChatbotRule::findActiveForRule($companyId); - $apiKey = ($rule && !empty($rule['gemini_api_key'])) ? $rule['gemini_api_key'] : getenv('GEMINI_API_KEY'); + $configuredGeminiKey = ($rule && !empty($rule['gemini_api_key'])) ? $rule['gemini_api_key'] : null; + $apiKey = GeminiService::getGeminiApiKey($configuredGeminiKey); if (empty($apiKey)) { error_log("[DriverRegistrationFlow] Gemini API key not configured."); diff --git a/backend/app/Services/GeminiService.php b/backend/app/Services/GeminiService.php index 2f0517c..381a93d 100644 --- a/backend/app/Services/GeminiService.php +++ b/backend/app/Services/GeminiService.php @@ -6,6 +6,51 @@ class GeminiService { public const DEFAULT_MODEL = 'gemini-3.1-flash-lite'; + /** + * Get a random Gemini API key from a comma-separated list of keys + */ + public static function getGeminiApiKey(?string $configuredKey = null): string + { + $keySource = !empty($configuredKey) ? $configuredKey : getenv('GEMINI_API_KEY'); + if (empty($keySource)) { + return ''; + } + $keys = array_filter(array_map('trim', explode(',', $keySource))); + if (empty($keys)) { + return ''; + } + return $keys[array_rand($keys)]; + } + + /** + * Get a random ElevenLabs API key from a comma-separated list of keys + */ + public static function getElevenLabsApiKey(?string $configuredKey = null): ?string + { + $keySource = !empty($configuredKey) ? $configuredKey : getenv('ELEVENLABS_API_KEY'); + if (empty($keySource)) { + return null; + } + $keys = array_filter(array_map('trim', explode(',', $keySource))); + if (empty($keys)) { + return null; + } + return $keys[array_rand($keys)]; + } + + /** + * Get a random ElevenLabs Voice ID from a comma-separated list of Voice IDs + */ + public static function getElevenLabsVoiceId(?string $configuredVoiceId = null): string + { + $voiceIdSource = !empty($configuredVoiceId) ? $configuredVoiceId : (getenv('ELEVENLABS_VOICE_ID') ?: 'pNInz6obpgDQGcFmaJgB'); + $voiceIds = array_filter(array_map('trim', explode(',', $voiceIdSource))); + if (empty($voiceIds)) { + return 'pNInz6obpgDQGcFmaJgB'; + } + return $voiceIds[array_rand($voiceIds)]; + } + /** * Call Gemini API to generate a response */ diff --git a/backend/public/index.php b/backend/public/index.php index 7eca031..dff205a 100644 --- a/backend/public/index.php +++ b/backend/public/index.php @@ -311,9 +311,14 @@ $router->get('/api/external/cron/send-reminders', function ($request, $response) // Get company chatbot rule details $rule = \App\Models\ChatbotRule::findActiveForRule($companyId); - $geminiKey = ($rule && !empty($rule['gemini_api_key'])) ? $rule['gemini_api_key'] : getenv('GEMINI_API_KEY'); - $elApiKey = ($rule && !empty($rule['elevenlabs_api_key'])) ? $rule['elevenlabs_api_key'] : getenv('ELEVENLABS_API_KEY'); - $elVoiceId = ($rule && !empty($rule['elevenlabs_voice_id'])) ? $rule['elevenlabs_voice_id'] : getenv('ELEVENLABS_VOICE_ID'); + $configuredGeminiKey = ($rule && !empty($rule['gemini_api_key'])) ? $rule['gemini_api_key'] : null; + $geminiKey = \App\Services\GeminiService::getGeminiApiKey($configuredGeminiKey); + + $configuredElKey = ($rule && !empty($rule['elevenlabs_api_key'])) ? $rule['elevenlabs_api_key'] : null; + $elApiKey = \App\Services\GeminiService::getElevenLabsApiKey($configuredElKey); + + $configuredVoiceId = ($rule && !empty($rule['elevenlabs_voice_id'])) ? $rule['elevenlabs_voice_id'] : null; + $elVoiceId = \App\Services\GeminiService::getElevenLabsVoiceId($configuredVoiceId); // Fetch company WhatsApp session $session = \App\Models\WhatsAppSession::findByCompany($companyId);