diff --git a/app/Http/Controllers/TelegramWebhookController.php b/app/Http/Controllers/TelegramWebhookController.php index ac751f1..e0b848d 100644 --- a/app/Http/Controllers/TelegramWebhookController.php +++ b/app/Http/Controllers/TelegramWebhookController.php @@ -29,6 +29,18 @@ class TelegramWebhookController extends Controller return response()->json(['ok' => true]); } + // Voice / audio messages + if (isset($update['message']['voice']) || isset($update['message']['audio'])) { + $chatId = (string) $update['message']['chat']['id']; + $fileId = $update['message']['voice']['file_id'] ?? $update['message']['audio']['file_id']; + $nombre = trim( + ($update['message']['from']['first_name'] ?? '') . ' ' . + ($update['message']['from']['last_name'] ?? '') + ); + $bot->handleVoice($chatId, $fileId, $nombre); + return response()->json(['ok' => true]); + } + // Text messages if (isset($update['message']['text'])) { $chatId = (string) $update['message']['chat']['id']; diff --git a/app/Http/Livewire/Chat/ShowConfiguracionChat.php b/app/Http/Livewire/Chat/ShowConfiguracionChat.php index 3dc4e93..0ceda8a 100644 --- a/app/Http/Livewire/Chat/ShowConfiguracionChat.php +++ b/app/Http/Livewire/Chat/ShowConfiguracionChat.php @@ -27,6 +27,11 @@ class ShowConfiguracionChat extends Component public string $validacion_accion_auto = 'solo_notificar'; public string $validacion_monto_tolerancia = '0'; + // ── Whisper STT ─────────────────────────────────────────── + public string $whisper_url = ''; + public string $whisper_token = ''; + public string $whisper_habilitado = '0'; + // ── Bre-B ───────────────────────────────────────────────── public string $recarga_banco_nombre = ''; public string $recarga_banco_llave = ''; @@ -38,6 +43,7 @@ class ShowConfiguracionChat extends Component 'telegram_token', 'mensaje_bienvenida', 'mensaje_transferencia', 'gemini_api_key', 'gemini_habilitado', 'gemini_prompt_extra', 'validacion_foto_habilitada', 'validacion_accion_auto', 'validacion_monto_tolerancia', + 'whisper_url', 'whisper_token', 'whisper_habilitado', 'recarga_banco_nombre', 'recarga_banco_llave', 'recarga_banco_titular', ]; @@ -68,6 +74,7 @@ class ShowConfiguracionChat extends Component 'telegram_token', 'mensaje_bienvenida', 'mensaje_transferencia', 'gemini_api_key', 'gemini_habilitado', 'gemini_prompt_extra', 'validacion_foto_habilitada', 'validacion_accion_auto', 'validacion_monto_tolerancia', + 'whisper_url', 'whisper_token', 'whisper_habilitado', 'recarga_banco_nombre', 'recarga_banco_llave', 'recarga_banco_titular', ]; diff --git a/app/Services/TelegramBotService.php b/app/Services/TelegramBotService.php index d62af41..20b356b 100644 --- a/app/Services/TelegramBotService.php +++ b/app/Services/TelegramBotService.php @@ -196,6 +196,71 @@ class TelegramBotService } } + public function handleVoice(string $chatId, string $fileId, string $nombre = ''): void + { + $state = $this->getState($chatId); + $this->convId = $state['conv_id'] ?? null; + + if (ChatConfig::get('whisper_habilitado', '0') !== '1') { + $this->send($chatId, "🎤 Recibí tu audio, pero la transcripción de voz no está habilitada. Por favor escribe tu mensaje."); + return; + } + + $this->send($chatId, "🎤 Transcribiendo tu audio..."); + + try { + $audioData = $this->downloadTelegramFile($fileId); + if (! $audioData) { + $this->send($chatId, "No pude descargar el audio. Intenta de nuevo o escribe tu mensaje."); + return; + } + + $texto = $this->transcribeAudio($audioData['content']); + if (! $texto) { + $this->send($chatId, "No pude entender el audio. Intenta de nuevo o escribe tu mensaje."); + return; + } + + // Confirmar lo que se escuchó y procesar como texto normal + $this->send($chatId, "🎙️ Escuché: _\"{$texto}\"_"); + $this->handleText($chatId, $texto, $nombre); + + } catch (\Throwable $e) { + Log::warning('[TelegramBot] handleVoice error: ' . $e->getMessage()); + $this->send($chatId, "Error procesando el audio. Escribe tu mensaje."); + } + } + + private function transcribeAudio(string $audioContent): ?string + { + $whisperUrl = ChatConfig::get('whisper_url'); + $whisperToken = ChatConfig::get('whisper_token'); + + if (! $whisperUrl) { + return null; + } + + try { + $response = Http::timeout(30) + ->withBasicAuth('whisper', $whisperToken) + ->attach('audio_file', $audioContent, 'audio.ogg') + ->post($whisperUrl, ['response_format' => 'json']); + + if (! $response->successful()) { + Log::warning('[TelegramBot] Whisper error: ' . $response->body()); + return null; + } + + // Whisper devuelve {"text": "..."} o {"transcription": "..."} + $json = $response->json(); + return trim($json['text'] ?? $json['transcription'] ?? ''); + + } catch (\Throwable $e) { + Log::warning('[TelegramBot] Whisper exception: ' . $e->getMessage()); + return null; + } + } + // ─── Auth flow ──────────────────────────────────────────── private function askEmail(string $chatId): void diff --git a/resources/views/livewire/chat/show-configuracion-chat.blade.php b/resources/views/livewire/chat/show-configuracion-chat.blade.php index d4de7da..8f964f1 100644 --- a/resources/views/livewire/chat/show-configuracion-chat.blade.php +++ b/resources/views/livewire/chat/show-configuracion-chat.blade.php @@ -87,6 +87,33 @@
+ {{-- Whisper STT --}} +
+

Transcripción de voz (Whisper)

+

Cuando un usuario envíe un audio en Telegram, se transcribirá con Whisper y el texto pasará al sistema de intención como si lo hubiera escrito.

+
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ {{-- Validacion de pagos --}}

Validacion de pagos por foto + correo IMAP