botToken = config('services.telegram.bot_token'); $this->webhookUrl = rtrim(config('app.url'), '/') . '/telegram/webhook'; } public function handleUpdate(array $update): array { if (!isset($update['message'])) { return ['ok' => false, 'error' => 'No message found']; } $message = $update['message']; $chatId = $message['chat']['id']; $text = $message['text'] ?? ''; Log::info('Telegram update received', ['chat_id' => $chatId, 'text' => $text]); $telegramAccount = TelegramAccount::where('chat_id', $chatId)->first(); if (!$telegramAccount || !$telegramAccount->is_verified) { return $this->handleUnverifiedUser($chatId, $text); } if ($telegramAccount->bot_state) { return $this->handleBotState($telegramAccount, $text, $chatId); } return $this->handleCommand($telegramAccount, $text, $chatId); } private function handleUnverifiedUser(string $chatId, string $text): array { if (strlen($text) === 6 && is_numeric($text)) { $telegramAccount = TelegramAccount::where('verification_code', $text) ->where('is_verified', false) ->first(); if ($telegramAccount) { $telegramAccount->update(['chat_id' => $chatId, 'is_verified' => true, 'verification_code' => null]); $this->sendMenu($chatId, "¡Hola {$telegramAccount->user->name}! 👋\n\nVerificación exitosa. He activado tu menú de control:"); return ['ok' => true, 'verified' => true]; } } $this->sendMessage($chatId, "👋 ¡Hola! Soy el bot de Nómina Pegaso.\n\nPara empezar, envía el código de 6 dígitos de tu panel web."); return ['ok' => true, 'verified' => false]; } private function handleBotState(TelegramAccount $account, string $text, string $chatId): array { try { $state = $account->bot_state; $data = $account->bot_data ?? []; // Permitir cancelar if (strtolower(trim($text)) === '/cancelar' || $text === '❌ Cancelar') { $account->update(['bot_state' => null, 'bot_data' => null]); $this->sendMenu($chatId, "❌ Operación cancelada."); return ['ok' => true]; } switch ($state) { // --- FLUJO VENTA --- case 'AWAITING_SALE_DATE': $data['date'] = $this->parseDate($text); $account->update(['bot_state' => 'AWAITING_SALE_USER_AMOUNT', 'bot_data' => $data]); $this->sendMessage($chatId, "💰 Ingresa el monto vendido por el *usuario*:", $this->cancelKeyboard()); break; case 'AWAITING_SALE_USER_AMOUNT': if (!is_numeric($text)) return $this->sendInvalidNumeric($chatId); $data['user_sales'] = (float) $text; $account->update(['bot_state' => 'AWAITING_SALE_SYSTEM_AMOUNT', 'bot_data' => $data]); $this->sendMessage($chatId, "🖥️ Ingresa el monto vendido según el *sistema*:", $this->cancelKeyboard()); break; case 'AWAITING_SALE_SYSTEM_AMOUNT': if (!is_numeric($text)) return $this->sendInvalidNumeric($chatId); $data['system_sales'] = (float) $text; $user = $account->user; $month = $user->getCurrentMonth(); if (!$month) return $this->sendNoOpenMonth($chatId, $account); $month->dailySales()->create([ 'user_id' => $user->id, 'date' => $data['date'], 'user_sales' => $data['user_sales'], 'system_sales' => $data['system_sales'], ]); $account->update(['bot_state' => null, 'bot_data' => null]); $this->sendMenu($chatId, "✅ *Venta registrada!*\n\n📅 {$data['date']}\n👤 U: $" . number_format($data['user_sales'], 2) . "\n🖥️ S: $" . number_format($data['system_sales'], 2)); break; // --- FLUJO GASTO --- case 'AWAITING_EXPENSE_DATE': $data['date'] = $this->parseDate($text); $account->update(['bot_state' => 'AWAITING_EXPENSE_AMOUNT', 'bot_data' => $data]); $this->sendMessage($chatId, "💸 Ingresa el monto del gasto:", $this->cancelKeyboard()); break; case 'AWAITING_EXPENSE_AMOUNT': if (!is_numeric($text)) return $this->sendInvalidNumeric($chatId); $data['amount'] = (float) $text; $account->update(['bot_state' => 'AWAITING_EXPENSE_DESC', 'bot_data' => $data]); $this->sendMessage($chatId, "📝 Describe el gasto:", $this->cancelKeyboard()); break; case 'AWAITING_EXPENSE_DESC': $data['description'] = $text; $user = $account->user; $month = $user->getCurrentMonth(); if (!$month) return $this->sendNoOpenMonth($chatId, $account); $month->expenses()->create([ 'user_id' => $user->id, 'date' => $data['date'], 'amount' => $data['amount'], 'description' => $data['description'], 'type' => 'other', ]); $account->update(['bot_state' => null, 'bot_data' => null]); $this->sendMenu($chatId, "✅ *Gasto registrado!*\n\n📅 {$data['date']}\n💰 $" . number_format($data['amount'], 2) . "\n📝 {$data['description']}"); break; } } catch (\Exception $e) { Log::error('Bot interaction error: ' . $e->getMessage()); $account->update(['bot_state' => null, 'bot_data' => null]); $this->sendMenu($chatId, "❌ Ocurrió un error al guardar los datos. Por favor intenta de nuevo."); } return ['ok' => true]; } private function handleCommand(TelegramAccount $account, string $text, string $chatId): array { $cmd = strtolower(trim($text)); $user = $account->user; switch ($cmd) { case '/start': case '/menu': case '🏠 menú': $this->sendMenu($chatId, "Control de Nómina - {$user->name}"); break; case '/resumen': case '💵 resumen': $this->showSummary($user, $chatId); break; case '/agregar_venta': case '➕ agregar venta': $account->update(['bot_state' => 'AWAITING_SALE_DATE']); $this->sendMessage($chatId, "🗓️ ¿Fecha de la venta?", $this->dateKeyboard()); break; case '/agregar_gasto': case '➖ agregar gasto': $account->update(['bot_state' => 'AWAITING_EXPENSE_DATE']); $this->sendMessage($chatId, "🗓️ ¿Fecha del gasto?", $this->dateKeyboard()); break; case '/ventas': case '💰 mis ventas': $this->showSales($user, $chatId); break; case '/gastos': case '📝 mis gastos': $this->showExpenses($user, $chatId); break; case '/help': $this->sendMenu($chatId, "Usa los botones del teclado para navegar o enviar comandos."); break; default: $this->sendMenu($chatId, "❓ No entiendo ese comando."); } return ['ok' => true]; } private function sendMenu(string $chatId, string $text) { $keyboard = [ 'keyboard' => [ [['text' => '💵 Resumen'], ['text' => '🏠 Menú']], [['text' => '➕ Agregar Venta'], ['text' => '➖ Agregar Gasto']], [['text' => '💰 Mis Ventas'], ['text' => '📝 Mis Gastos']] ], 'resize_keyboard' => true, 'one_time_keyboard' => false ]; return $this->sendMessage($chatId, $text, $keyboard); } private function dateKeyboard() { return [ 'keyboard' => [[['text' => 'Hoy'], ['text' => 'Ayer']], [['text' => '❌ Cancelar']]], 'resize_keyboard' => true, 'one_time_keyboard' => true ]; } private function cancelKeyboard() { return [ 'keyboard' => [[['text' => '❌ Cancelar']]], 'resize_keyboard' => true, 'one_time_keyboard' => true ]; } public function sendMessage(string $chatId, string $text, $replyMarkup = null): array { if (!$this->botToken) return ['ok' => false]; $params = ['chat_id' => $chatId, 'text' => $text, 'parse_mode' => 'Markdown']; if ($replyMarkup) $params['reply_markup'] = json_encode($replyMarkup); try { return Http::post("https://api.telegram.org/bot{$this->botToken}/sendMessage", $params)->json(); } catch (\Exception $e) { return ['ok' => false]; } } private function parseDate(string $text): string { $text = strtolower(trim($text)); if ($text === 'hoy') return date('Y-m-d'); if ($text === 'ayer') return date('Y-m-d', strtotime('-1 day')); $ts = strtotime($text); return $ts ? date('Y-m-d', $ts) : date('Y-m-d'); } private function sendInvalidNumeric(string $chatId): array { $this->sendMessage($chatId, "⚠️ Ingresa solo números.", $this->cancelKeyboard()); return ['ok' => true]; } private function sendNoOpenMonth(string $chatId, $account): array { $account->update(['bot_state' => null, 'bot_data' => null]); $this->sendMenu($chatId, "❌ No hay mes abierto."); return ['ok' => true]; } private function showSummary(User $user, string $chatId) { $month = $user->getCurrentMonth(); if (!$month) return $this->sendMessage($chatId, "No hay mes abierto."); $data = \App\Services\CommissionCalculator::calculateForMonth($user, $month); $text = "💵 *Resumen Financiero*\n\n" . "• Comisiones: \$" . number_format($data['commission_amount'], 2) . "\n" . "• Salario: \$" . number_format($data['monthly_salary'], 2) . "\n" . "• Gastos: \$" . number_format($data['total_expenses'], 2) . "\n" . "⭐ *Neto: \$" . number_format($data['total_earning'], 2) . "*"; $this->sendMenu($chatId, $text); } private function showSales(User $user, string $chatId) { $month = $user->getCurrentMonth(); if (!$month) return; $u = $month->dailySales()->sum('user_sales'); $s = $month->dailySales()->sum('system_sales'); $this->sendMenu($chatId, "💰 *Ventas*\n👤 U: \$" . number_format($u, 2) . "\n🖥️ S: \$" . number_format($s, 2)); } private function showExpenses(User $user, string $chatId) { $month = $user->getCurrentMonth(); if (!$month) return; $t = $month->expenses()->sum('amount'); $this->sendMenu($chatId, "📝 *Total Gastos:* \$" . number_format($t, 2)); } public static function generateVerificationCode(): string { return str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT); } public function setWebhook(): bool { try { return Http::post("https://api.telegram.org/bot{$this->botToken}/setWebhook", ['url' => $this->webhookUrl])->json('ok', false); } catch (\Exception $e) { return false; } } public function getWebhookInfo(): array { try { return Http::get("https://api.telegram.org/bot{$this->botToken}/getWebhookInfo")->json(); } catch (\Exception $e) { return ['ok' => false]; } } public function deleteWebhook(): bool { try { return Http::post("https://api.telegram.org/bot{$this->botToken}/deleteWebhook")->json('ok', false); } catch (\Exception $e) { return false; } } }