337 lines
11 KiB
PHP
Executable File
337 lines
11 KiB
PHP
Executable File
<?php
|
||
|
||
namespace App\Services;
|
||
|
||
use App\Models\TelegramAccount;
|
||
use App\Models\User;
|
||
use Illuminate\Support\Facades\Http;
|
||
use Illuminate\Support\Facades\Log;
|
||
|
||
class TelegramBotService
|
||
{
|
||
private ?string $botToken;
|
||
private ?string $webhookUrl;
|
||
|
||
public function __construct()
|
||
{
|
||
$this->botToken = config('services.telegram.bot_token');
|
||
$this->webhookUrl = rtrim(config('app.url'), '/') . '/telegram/webhook';
|
||
}
|
||
|
||
/**
|
||
* Procesar actualización recibida del 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'] ?? '';
|
||
$from = $message['from'] ?? [];
|
||
|
||
Log::info('Telegram update received', [
|
||
'chat_id' => $chatId,
|
||
'text' => $text,
|
||
'from' => $from
|
||
]);
|
||
|
||
// Verificar si el usuario está verificado
|
||
$telegramAccount = TelegramAccount::where('chat_id', $chatId)->first();
|
||
|
||
if (!$telegramAccount || !$telegramAccount->is_verified) {
|
||
return $this->handleUnverifiedUser($chatId, $text);
|
||
}
|
||
|
||
// Procesar comandos del usuario verificado
|
||
return $this->handleCommand($telegramAccount->user, $text, $chatId);
|
||
}
|
||
|
||
/**
|
||
* Manejar usuario no verificado
|
||
*/
|
||
private function handleUnverifiedUser(string $chatId, string $text): array
|
||
{
|
||
// Si es un código de verificación (6 dígitos numéricos)
|
||
if (strlen($text) === 6 && is_numeric($text)) {
|
||
// Buscamos la cuenta que tiene este código de verificación y no está verificada
|
||
$telegramAccount = TelegramAccount::where('verification_code', $text)
|
||
->where('is_verified', false)
|
||
->first();
|
||
|
||
if ($telegramAccount) {
|
||
// Actualizar la cuenta con el chat_id del usuario que mandó el código
|
||
$telegramAccount->update([
|
||
'chat_id' => $chatId,
|
||
'is_verified' => true,
|
||
'verification_code' => null
|
||
]);
|
||
|
||
$user = $telegramAccount->user;
|
||
$this->sendMessage($chatId, "¡Hola {$user->name}! 👋\n\nVerificación exitosa. Tu cuenta de Telegram ha sido vinculada correctamente. Ahora puedes usar comandos como /resumen para ver tu estado.");
|
||
|
||
return ['ok' => true, 'verified' => true];
|
||
} else {
|
||
$this->sendMessage($chatId, "❌ El código $text es inválido o ya ha sido usado. Por favor, genera un código nuevo en tu panel de usuario.");
|
||
return ['ok' => true, 'verified' => false];
|
||
}
|
||
}
|
||
|
||
// Mensaje de bienvenida para usuarios no verificados
|
||
$this->sendMessage($chatId, "👋 ¡Hola! Soy el bot de Nómina Pegaso.\n\nPara usar este bot, primero debes vincular tu cuenta:\n\n1️⃣ Ve a tu panel web\n2️⃣ Sección Telegram -> Vincular\n3️⃣ Envía aquí el código de 6 dígitos que veas allá.");
|
||
|
||
return ['ok' => true, 'verified' => false];
|
||
}
|
||
|
||
/**
|
||
* Manejar comandos de usuario verificado
|
||
*/
|
||
private function handleCommand(User $user, string $text, string $chatId): array
|
||
{
|
||
$command = strtolower(trim($text));
|
||
$commandParts = explode(' ', $command);
|
||
$mainCommand = $commandParts[0] ?? '';
|
||
|
||
switch ($mainCommand) {
|
||
case '/start':
|
||
$this->sendMessage($chatId, "¡Hola {$user->name}! Usa /help para ver los comandos disponibles.");
|
||
break;
|
||
|
||
case '/help':
|
||
$this->sendHelp($chatId);
|
||
break;
|
||
|
||
case '/mes':
|
||
$this->showCurrentMonth($user, $chatId);
|
||
break;
|
||
|
||
case '/ventas':
|
||
$this->showSales($user, $chatId);
|
||
break;
|
||
|
||
case '/gastos':
|
||
$this->showExpenses($user, $chatId);
|
||
break;
|
||
|
||
case '/resumen':
|
||
$this->showSummary($user, $chatId);
|
||
break;
|
||
|
||
default:
|
||
$this->sendMessage($chatId, "Comando no reconocido. Usa /help para ver los comandos disponibles.");
|
||
}
|
||
|
||
return ['ok' => true];
|
||
}
|
||
|
||
/**
|
||
* Enviar mensaje
|
||
*/
|
||
public function sendMessage(string $chatId, string $text): array
|
||
{
|
||
if (!$this->botToken) {
|
||
Log::warning('Telegram bot token not configured');
|
||
return ['ok' => false, 'error' => 'Bot token not configured'];
|
||
}
|
||
|
||
try {
|
||
$response = Http::post("https://api.telegram.org/bot{$this->botToken}/sendMessage", [
|
||
'chat_id' => $chatId,
|
||
'text' => $text,
|
||
'parse_mode' => 'Markdown'
|
||
]);
|
||
|
||
return $response->json();
|
||
} catch (\Exception $e) {
|
||
Log::error('Telegram send message error', ['error' => $e->getMessage()]);
|
||
return ['ok' => false, 'error' => $e->getMessage()];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Enviar mensaje de ayuda
|
||
*/
|
||
private function sendHelp(string $chatId): void
|
||
{
|
||
$text = "📋 *Comandos disponibles:*\n\n" .
|
||
"• /start - Iniciar bot\n" .
|
||
"• /help - Mostrar ayuda\n" .
|
||
"• /mes - Ver mes actual\n" .
|
||
"• /ventas - Ver ventas del mes\n" .
|
||
"• /gastos - Ver gastos del mes\n" .
|
||
"• /resumen - Resumen de comisiones\n";
|
||
|
||
$this->sendMessage($chatId, $text);
|
||
}
|
||
|
||
/**
|
||
* Mostrar mes actual
|
||
*/
|
||
private function showCurrentMonth(User $user, string $chatId): void
|
||
{
|
||
$month = $user->getCurrentMonth();
|
||
|
||
if (!$month) {
|
||
$this->sendMessage($chatId, "No tienes ningún mes abierto actualmente.");
|
||
return;
|
||
}
|
||
|
||
$statusText = match($month->status) {
|
||
'open' => '🟢 Abierto',
|
||
'closed' => '🟡 Cerrado',
|
||
'paid' => '✅ Pagado',
|
||
default => 'Desconocido'
|
||
};
|
||
|
||
$text = "📅 *Mes Actual*\n\n" .
|
||
"• *Nombre:* {$month->name} {$month->year}\n" .
|
||
"• *Estado:* {$statusText}";
|
||
|
||
$this->sendMessage($chatId, $text);
|
||
}
|
||
|
||
/**
|
||
* Mostrar ventas del mes
|
||
*/
|
||
private function showSales(User $user, string $chatId): void
|
||
{
|
||
$month = $user->getCurrentMonth();
|
||
|
||
if (!$month) {
|
||
$this->sendMessage($chatId, "No tienes ningún mes abierto actualmente.");
|
||
return;
|
||
}
|
||
|
||
$totalUserSales = $month->dailySales()->sum('user_sales');
|
||
$totalSystemSales = $month->dailySales()->sum('system_sales');
|
||
$diff = $totalUserSales - $totalSystemSales;
|
||
|
||
$text = "💰 *Ventas del Mes*\n\n" .
|
||
"• *Usuario:* $" . number_format($totalUserSales, 2) . "\n" .
|
||
"• *Sistema:* $" . number_format($totalSystemSales, 2) . "\n" .
|
||
"• *Diferencia:* $" . number_format($diff, 2);
|
||
|
||
$this->sendMessage($chatId, $text);
|
||
}
|
||
|
||
/**
|
||
* Mostrar gastos del mes
|
||
*/
|
||
private function showExpenses(User $user, string $chatId): void
|
||
{
|
||
$month = $user->getCurrentMonth();
|
||
|
||
if (!$month) {
|
||
$this->sendMessage($chatId, "No tienes ningún mes abierto actualmente.");
|
||
return;
|
||
}
|
||
|
||
$totalExpenses = $month->expenses()->sum('amount');
|
||
$expensesList = $month->expenses()->latest()->limit(5)->get();
|
||
|
||
$text = "📝 *Gastos del Mes*\n\n" .
|
||
"• *Total:* $" . number_format($totalExpenses, 2) . "\n\n";
|
||
|
||
if ($expensesList->count() > 0) {
|
||
$text .= "Últimos gastos:\n";
|
||
foreach ($expensesList as $expense) {
|
||
$text .= "• {$expense->description}: \$" . number_format($expense->amount, 2) . "\n";
|
||
}
|
||
}
|
||
|
||
$this->sendMessage($chatId, $text);
|
||
}
|
||
|
||
/**
|
||
* Mostrar resumen de comisiones
|
||
*/
|
||
private function showSummary(User $user, string $chatId): void
|
||
{
|
||
$month = $user->getCurrentMonth();
|
||
|
||
if (!$month) {
|
||
$this->sendMessage($chatId, "No tienes ningún mes abierto actualmente.");
|
||
return;
|
||
}
|
||
|
||
$data = CommissionCalculator::calculateForMonth($user, $month);
|
||
|
||
$text = "💵 *Resumen de Comisiones*\n\n" .
|
||
"• *Mes:* {$data['month_name']}\n" .
|
||
"• *Ventas Sistema:* \$" . number_format($data['total_system_sales'], 2) . "\n" .
|
||
"• *Comisión ({$data['commission_percentage']}%):* \$" . number_format($data['commission_amount'], 2) . "\n" .
|
||
"• *Salario:* \$" . number_format($data['monthly_salary'], 2) . "\n" .
|
||
"• *Gastos:* \$" . number_format($data['total_expenses'], 2) . "\n" .
|
||
"• *Total a Recibir:* \$" . number_format($data['total_earning'], 2);
|
||
|
||
$this->sendMessage($chatId, $text);
|
||
}
|
||
|
||
/**
|
||
* Generar código de verificación
|
||
*/
|
||
public static function generateVerificationCode(): string
|
||
{
|
||
return str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
|
||
}
|
||
|
||
/**
|
||
* Configurar webhook
|
||
*/
|
||
public function setWebhook(): bool
|
||
{
|
||
if (!$this->botToken || !$this->webhookUrl) {
|
||
Log::warning('Cannot set webhook: missing configuration');
|
||
return false;
|
||
}
|
||
|
||
try {
|
||
$response = Http::post("https://api.telegram.org/bot{$this->botToken}/setWebhook", [
|
||
'url' => $this->webhookUrl
|
||
]);
|
||
|
||
return $response->json('ok', false);
|
||
} catch (\Exception $e) {
|
||
Log::error('Telegram set webhook error', ['error' => $e->getMessage()]);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Obtener información del webhook
|
||
*/
|
||
public function getWebhookInfo(): array
|
||
{
|
||
if (!$this->botToken) {
|
||
return ['ok' => false, 'error' => 'Bot token not configured'];
|
||
}
|
||
|
||
try {
|
||
$response = Http::get("https://api.telegram.org/bot{$this->botToken}/getWebhookInfo");
|
||
return $response->json();
|
||
} catch (\Exception $e) {
|
||
Log::error('Telegram get webhook info error', ['error' => $e->getMessage()]);
|
||
return ['ok' => false, 'error' => $e->getMessage()];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Borrar webhook
|
||
*/
|
||
public function deleteWebhook(): bool
|
||
{
|
||
if (!$this->botToken) {
|
||
return false;
|
||
}
|
||
|
||
try {
|
||
$response = Http::post("https://api.telegram.org/bot{$this->botToken}/deleteWebhook");
|
||
return $response->json('ok', false);
|
||
} catch (\Exception $e) {
|
||
Log::error('Telegram delete webhook error', ['error' => $e->getMessage()]);
|
||
return false;
|
||
}
|
||
}
|
||
} |