[[:templates_ai|{{wiki:user:undo_24.png}}]]
====== Funcionalidade Laravel Telegram Bot ======
===== 📋 Visão Geral =====
Guia completo para implementação do sistema de Telegram Webhook em Laravel, baseado no projeto Rei do Óleo. Este sistema inclui processamento de mensagens, validação de webhooks, tratamento de erros e sistema de comandos unificado.
===== 🚀 Comando de Implementação =====
implementar telegram webhook laravel completo
===== ⚙️ Pré-requisitos =====
Antes de implementar, certifique-se de ter:
* **Laravel 12** instalado
* **PHP 8.2+** configurado
* **Composer** para dependências
* **Redis** para cache (recomendado)
* **Bot Token** do Telegram configurado
* **Webhook URL** configurada
===== 📁 Estrutura de Arquivos =====
O sistema será implementado com a seguinte estrutura:
app/
├── Http/
│ ├── Controllers/Api/
│ │ ├── TelegramWebhookController.php
│ │ └── TelegramStatsController.php
│ ├── Middleware/
│ │ ├── TelegramWebhookSecretMiddleware.php
│ │ ├── TelegramWebhookExceptionHandler.php
│ │ └── TelegramWebhookLoggingMiddleware.php
│ ├── Requests/
│ │ ├── TelegramWebhookRequest.php
│ │ └── TelegramWebhookSetupRequest.php
│ └── Resources/
│ └── TelegramWebhookResource.php
├── Services/
│ ├── TelegramWebhookService.php
│ ├── TelegramBotService.php
│ ├── TelegramMessageProcessorService.php
│ └── Telegram/
│ ├── TelegramWebhookValidationService.php
│ └── TelegramMessageProcessorService.php
└── Console/Commands/
└── TestTelegramWebhook.php
===== 🔧 Passo a Passo de Implementação =====
=== Passo 1: Configuração Inicial ===
1. **Configurar variáveis de ambiente (.env):**
TELEGRAM_ENABLED=true
TELEGRAM_BOT_TOKEN=seu_bot_token_aqui
TELEGRAM_WEBHOOK_SECRET=seu_secret_aqui
TELEGRAM_RECIPIENTS=123456789,987654321
2. **Configurar services.php:**
'telegram' => [
'enabled' => env('TELEGRAM_ENABLED', false),
'bot_token' => env('TELEGRAM_BOT_TOKEN'),
'webhook_secret' => env('TELEGRAM_WEBHOOK_SECRET', ''),
'recipients' => explode(',', env('TELEGRAM_RECIPIENTS', '')),
],
=== Passo 2: Implementar Classes Core ===
**Próximos passos serão adicionados gradualmente...**
===== 📋 Checklist de Implementação =====
=== Antes de Começar ===
* [ ] Laravel 12 instalado
* [ ] PHP 8.2+ configurado
* [ ] Redis configurado
* [ ] Bot Token do Telegram obtido
* [ ] Webhook URL configurada
=== Durante a Implementação ===
* [ ] Configurar variáveis de ambiente
* [ ] Implementar Controllers
* [ ] Implementar Services
* [ ] Implementar Middlewares
* [ ] Implementar Requests
* [ ] Implementar Resources
* [ ] Configurar rotas
* [ ] Implementar testes
=== Após a Implementação ===
* [ ] Testar webhook
* [ ] Validar processamento de mensagens
* [ ] Verificar logs
* [ ] Testar comandos
===== 🔍 Troubleshooting =====
=== Problemas Comuns ===
1. **Webhook não recebe mensagens:**
* Verificar se a URL está correta
* Verificar se o bot token está válido
* Verificar logs do Laravel
2. **Erro de validação:**
* Verificar estrutura do payload
* Verificar regras de validação
* Verificar logs de erro
3. **Comandos não funcionam:**
* Verificar sistema de comandos
* Verificar autorização do usuário
* Verificar logs de processamento
===== 📝 Próximas Seções =====
As próximas seções serão adicionadas gradualmente:
* **Seção 1**: Controllers e Rotas
* **Seção 2**: Services e Processamento
* **Seção 3**: Middlewares e Validação
* **Seção 4**: Requests e Resources
* **Seção 5**: Testes e Comandos
* **Seção 6**: Scripts de Instalação
===== 📝 Seção 0: Dependências Essenciais =====
=== LoggingServiceInterface.php ===
=== LoggingService.php ===
uniqid('api_', true),
'method' => $request->method(),
'url' => $request->fullUrl(),
'ip' => $request->ip(),
'user_agent' => $request->userAgent(),
'user_id' => $request->user()?->id,
'headers' => $this->sanitizeHeaders($request->headers->all()),
'timestamp' => now()->toISOString(),
], $context);
if ($request->method() !== 'GET') {
$logData['body'] = $this->sanitizeBody($request->all());
}
Log::channel('api')->info('API Request', $logData);
}
/**
* Log API responses
*/
public function logApiResponse(int $statusCode, array $response, float $duration, array $context = []): void
{
$logData = array_merge([
'status_code' => $statusCode,
'duration_ms' => round($duration, 2),
'response_size' => strlen(json_encode($response)),
'timestamp' => now()->toISOString(),
], $context);
if ($statusCode >= 400) {
$logData['error_response'] = $response;
Log::channel('api')->error('API Error Response', $logData);
} else {
Log::channel('api')->info('API Response', $logData);
}
}
/**
* Log business operations
*/
public function logBusinessOperation(string $operation, array $data, string $status = 'success', array $context = []): void
{
$logData = array_merge([
'operation' => $operation,
'status' => $status,
'user_id' => Auth::id(),
'timestamp' => now()->toISOString(),
], $data, $context);
Log::channel('business')->info('Business Operation', $logData);
}
/**
* Log security events
*/
public function logSecurityEvent(string $event, array $data, string $level = 'warning', array $context = []): void
{
$logData = array_merge([
'event' => $event,
'level' => $level,
'user_id' => Auth::id(),
'ip' => request()->ip(),
'user_agent' => request()->userAgent(),
'timestamp' => now()->toISOString(),
], $data, $context);
Log::channel('security')->$level('Security Event', $logData);
}
/**
* Log performance metrics
*/
public function logPerformance(string $operation, float $duration, array $metrics = [], array $context = []): void
{
$logData = array_merge([
'operation' => $operation,
'duration_ms' => round($duration, 2),
'memory_usage' => memory_get_usage(true),
'memory_peak' => memory_get_peak_usage(true),
'timestamp' => now()->toISOString(),
], $metrics, $context);
Log::channel('performance')->info('Performance Metric', $logData);
}
/**
* Log audit trail
*/
public function logAudit(string $action, string $model, int $modelId, array $changes = [], array $context = []): void
{
$logData = array_merge([
'action' => $action,
'model' => $model,
'model_id' => $modelId,
'changes' => $changes,
'user_id' => Auth::id(),
'timestamp' => now()->toISOString(),
], $context);
Log::channel('audit')->info('Audit Trail', $logData);
}
/**
* Log Telegram bot events
*/
public function logTelegramEvent(string $event, array $data, string $level = 'info', array $context = []): void
{
$logData = array_merge([
'event' => $event,
'level' => $level,
'timestamp' => now()->toISOString(),
], $data, $context);
Log::channel('telegram')->$level('Telegram Event', $logData);
}
/**
* Log WhatsApp events
*/
public function logWhatsAppEvent(string $event, array $data, string $level = 'info', array $context = []): void
{
$logData = array_merge([
'event' => $event,
'level' => $level,
'timestamp' => now()->toISOString(),
], $data, $context);
Log::channel('whatsapp')->$level('WhatsApp Event', $logData);
}
/**
* Log exceptions with context
*/
public function logException(\Throwable $exception, array $context = []): void
{
$logData = array_merge([
'exception' => get_class($exception),
'message' => $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'trace' => $exception->getTraceAsString(),
'user_id' => Auth::id(),
'timestamp' => now()->toISOString(),
], $context);
Log::channel('errors')->error('Exception', $logData);
}
/**
* Get log statistics
*/
public function getLogStats(): array
{
return [
'channels' => ['api', 'business', 'security', 'performance', 'audit', 'telegram', 'whatsapp', 'errors'],
'timestamp' => now()->toISOString(),
];
}
/**
* Sanitize headers for logging
*/
private function sanitizeHeaders(array $headers): array
{
$sensitiveHeaders = ['authorization', 'cookie', 'x-api-key', 'x-csrf-token'];
foreach ($sensitiveHeaders as $header) {
if (isset($headers[$header])) {
$headers[$header] = ['***REDACTED***'];
}
}
return $headers;
}
/**
* Sanitize request body for logging
*/
private function sanitizeBody(array $body): array
{
$sensitiveFields = ['password', 'token', 'secret', 'key', 'api_key'];
foreach ($sensitiveFields as $field) {
if (isset($body[$field])) {
$body[$field] = '***REDACTED***';
}
}
return $body;
}
}
=== MessageFlowTrackerInterface.php ===
=== MessageFlowTrackerService.php ===
isEnabled = config('message-flow.enabled', false);
$this->messageId = uniqid('msg_', true);
// Define o fluxo esperado para cada tipo de mensagem
$this->expectedMethods = [
'text' => [
'TelegramMessageProcessorService.processWebhookPayload',
'TelegramMessageProcessorService.processTextMessage',
'TelegramBotService.processMessage',
'TelegramChannel.sendMessageWithKeyboard'
],
'voice' => [
'TelegramMessageProcessorService.processWebhookPayload',
'TelegramMessageProcessorService.processVoiceMessage',
'SpeechToTextService.convertVoiceToText',
'TelegramBotService.processMessage',
'TelegramChannel.sendMessageWithKeyboard'
],
'callback_query' => [
'TelegramMessageProcessorService.processWebhookPayload',
'TelegramMessageProcessorService.processCallbackQuery',
'TelegramBotService.processCallbackQuery',
'TelegramChannel.sendTextMessage'
]
];
}
/**
* Track method execution
*/
public function trackMethod(string $className, string $methodName, array $inputData = [], array $outputData = []): void
{
if (!$this->isEnabled) {
return;
}
$methodKey = "{$className}.{$methodName}";
$this->executedMethods[] = $methodKey;
$this->timestamps[$methodKey] = microtime(true);
$this->methodData[$methodKey] = [
'input' => $inputData,
'output' => $outputData,
'timestamp' => now()->toISOString()
];
}
/**
* End method tracking
*/
public function endMethod(string $className, string $methodName, array $outputData = []): void
{
if (!$this->isEnabled) {
return;
}
$methodKey = "{$className}.{$methodName}";
if (isset($this->methodData[$methodKey])) {
$this->methodData[$methodKey]['output'] = $outputData;
$this->methodData[$methodKey]['duration'] = microtime(true) - $this->timestamps[$methodKey];
}
}
/**
* Generate flow report
*/
public function generateFlowReport(array $payload): string
{
if (!$this->isEnabled) {
return 'Message flow tracking is disabled';
}
$messageType = $this->identifyMessageType($payload);
$expected = $this->expectedMethods[$messageType] ?? [];
$missing = array_diff($expected, $this->executedMethods);
$extra = array_diff($this->executedMethods, $expected);
$report = "=== MESSAGE FLOW REPORT ===\n";
$report .= "Message Type: {$messageType}\n";
$report .= "Message ID: {$this->messageId}\n";
$report .= "Executed Methods: " . count($this->executedMethods) . "\n";
$report .= "Expected Methods: " . count($expected) . "\n";
$report .= "Missing Methods: " . count($missing) . "\n";
$report .= "Extra Methods: " . count($extra) . "\n\n";
if (!empty($missing)) {
$report .= "MISSING METHODS:\n";
foreach ($missing as $method) {
$report .= " - {$method}\n";
}
$report .= "\n";
}
if (!empty($extra)) {
$report .= "EXTRA METHODS:\n";
foreach ($extra as $method) {
$report .= " - {$method}\n";
}
$report .= "\n";
}
return $report;
}
/**
* Generate user-friendly report
*/
public function generateUserReport(array $payload): string
{
if (!$this->isEnabled) {
return 'Tracking disabled';
}
$messageType = $this->identifyMessageType($payload);
$expected = $this->expectedMethods[$messageType] ?? [];
$missing = array_diff($expected, $this->executedMethods);
if (empty($missing)) {
return "✅ Message processed successfully through all expected steps";
}
$report = "⚠️ Message processing incomplete:\n";
$report .= "Missing steps: " . count($missing) . "\n";
foreach ($missing as $method) {
$report .= "• " . str_replace('.', ' → ', $method) . "\n";
}
return $report;
}
/**
* Start tracking
*/
public function startTracking(string $messageId): void
{
$this->messageId = $messageId;
$this->clearTrace();
}
/**
* End tracking
*/
public function endTracking(): void
{
if ($this->isEnabled) {
$this->saveTrace();
}
}
/**
* Clear trace
*/
public function clearTrace(): void
{
$this->executedMethods = [];
$this->methodData = [];
$this->timestamps = [];
}
/**
* Get executed methods
*/
public function getExecutedMethods(): array
{
return $this->executedMethods;
}
/**
* Get expected methods
*/
public function getExpectedMethods(): array
{
return $this->expectedMethods;
}
/**
* Get method data
*/
public function getMethodData(): array
{
return $this->methodData;
}
/**
* Identify message type
*/
public function identifyMessageType(array $payload): string
{
if (isset($payload['callback_query'])) {
return 'callback_query';
}
if (isset($payload['message'])) {
$message = $payload['message'];
if (isset($message['text'])) {
return 'text';
}
if (isset($message['voice'])) {
return 'voice';
}
if (isset($message['audio'])) {
return 'audio';
}
}
return 'unknown';
}
/**
* Save trace to cache
*/
public function saveTrace(): void
{
if (!$this->isEnabled) {
return;
}
try {
$traceData = [
'message_id' => $this->messageId,
'executed_methods' => $this->executedMethods,
'method_data' => $this->methodData,
'timestamp' => now()->toISOString()
];
Cache::put("message_trace_{$this->messageId}", $traceData, 60); // 1 hour
} catch (\Exception $e) {
Log::error('Failed to save message trace', [
'error' => $e->getMessage(),
'message_id' => $this->messageId
]);
}
}
/**
* Check if tracking is enabled
*/
public function isEnabled(): bool
{
return $this->isEnabled;
}
}
=== NotificationChannelInterface.php ===
=== MessageTrackingInterface.php ===
=== Service Providers ===
app->bind(LoggingServiceInterface::class, LoggingService::class);
}
public function boot(): void
{
// Publish configuration if needed
}
}
app->singleton(MessageFlowTrackerInterface::class, MessageFlowTrackerService::class);
}
public function boot(): void
{
// Publish configuration if needed
}
}
=== Configuração (config/app.php) ===
// Adicionar no array 'providers' do config/app.php
'providers' => [
// ... outros providers
App\Providers\LoggingServiceProvider::class,
App\Providers\MessageFlowServiceProvider::class,
// ... outros providers
],
=== Configuração (config/services.php) ===
[
'enabled' => env('TELEGRAM_ENABLED', false),
'bot_token' => env('TELEGRAM_BOT_TOKEN'),
'webhook_secret' => env('TELEGRAM_WEBHOOK_SECRET', ''),
'recipients' => array_filter(explode(',', env('TELEGRAM_RECIPIENTS', ''))),
],
// ... outras configurações
];
=== Configuração (.env) ===
# ============================================================================
# TELEGRAM BOT CONFIGURATION
# ============================================================================
# Enable/Disable Telegram Bot
TELEGRAM_ENABLED=true
# Bot Token from @BotFather
TELEGRAM_BOT_TOKEN=your_bot_token_here
# Webhook Secret for Security (optional but recommended)
TELEGRAM_WEBHOOK_SECRET=your_webhook_secret_here
# Recipients (Chat IDs separated by commas)
TELEGRAM_RECIPIENTS=123456789,987654321
# ============================================================================
# MESSAGE FLOW TRACKING CONFIGURATION
# ============================================================================
# Enable/Disable Message Flow Tracking
MESSAGE_FLOW_TRACKING_ENABLED=true
# Output Configuration
MESSAGE_FLOW_SEND_TO_USER=true
MESSAGE_FLOW_LOG_TO_FILE=true
MESSAGE_FLOW_CACHE_TRACES=true
# Performance Thresholds (milliseconds)
MESSAGE_FLOW_SLOW_THRESHOLD=100
MESSAGE_FLOW_VERY_SLOW_THRESHOLD=500
# Memory Thresholds (MB)
MESSAGE_FLOW_MEMORY_WARNING=50
MESSAGE_FLOW_MEMORY_CRITICAL=100
# Storage TTL (minutes)
MESSAGE_FLOW_CACHE_TTL=1440
MESSAGE_FLOW_DB_TTL=10080
=== Configuração de Logging (config/logging.php) ===
[
// ... outros canais
'api' => [
'driver' => 'daily',
'path' => storage_path('logs/api.log'),
'level' => 'info',
'days' => 30,
],
'business' => [
'driver' => 'daily',
'path' => storage_path('logs/business.log'),
'level' => 'info',
'days' => 90,
],
'security' => [
'driver' => 'daily',
'path' => storage_path('logs/security.log'),
'level' => 'warning',
'days' => 365,
],
'performance' => [
'driver' => 'daily',
'path' => storage_path('logs/performance.log'),
'level' => 'info',
'days' => 30,
],
'audit' => [
'driver' => 'daily',
'path' => storage_path('logs/audit.log'),
'level' => 'info',
'days' => 365,
],
'telegram' => [
'driver' => 'daily',
'path' => storage_path('logs/telegram.log'),
'level' => 'info',
'days' => 30,
],
'whatsapp' => [
'driver' => 'daily',
'path' => storage_path('logs/whatsapp.log'),
'level' => 'info',
'days' => 30,
],
'errors' => [
'driver' => 'daily',
'path' => storage_path('logs/errors.log'),
'level' => 'error',
'days' => 30,
],
],
];
=== TelegramMessageProcessorService.php ===
initializeTracking();
}
/**
* Process webhook payload
*/
public function processWebhookPayload(array $payload): array
{
// Inicia tracking
$this->trackMethod('processWebhookPayload', ['payload' => $payload]);
try {
// Check if it's a callback query (button click)
if (isset($payload['callback_query'])) {
$result = $this->processCallbackQuery($payload['callback_query']);
$this->endMethod('processWebhookPayload', ['result' => $result]);
return $result;
}
// Verify if it's a message
if (!isset($payload['message'])) {
$result = [
'success' => false,
'status' => 'ignored',
'message' => 'No message in payload'
];
$this->endMethod('processWebhookPayload', ['result' => $result]);
return $result;
}
$message = $payload['message'];
// Process different message types
if (isset($message['text'])) {
$result = $this->processTextMessage($message);
$this->endMethod('processWebhookPayload', ['result' => $result]);
return $result;
}
if (isset($message['voice'])) {
$result = $this->processVoiceMessage($message);
$this->endMethod('processWebhookPayload', ['result' => $result]);
return $result;
}
if (isset($message['audio'])) {
$result = $this->processAudioMessage($message);
$this->endMethod('processWebhookPayload', ['result' => $result]);
return $result;
}
$result = $this->createIgnoredResult('Unsupported message type');
$this->endMethod('processWebhookPayload', ['result' => $result]);
return $result;
} catch (\Exception $e) {
$result = [
'success' => false,
'status' => 'error',
'message' => 'Internal server error',
'error' => $e->getMessage()
];
$this->endMethod('processWebhookPayload', ['result' => $result, 'error' => $e->getMessage()]);
$this->loggingService->logException($e, [
'operation' => 'telegram_webhook_processing',
'payload' => $payload
]);
return $result;
}
}
/**
* Process text message
*/
private function processTextMessage(array $message): array
{
$this->trackMethod('processTextMessage', ['message' => $message]);
$result = $this->telegramBotService->processMessage($message);
$result['status'] = 'success';
$result['message'] = 'Text message processed';
$result['input_type'] = 'text';
$this->endMethod('processTextMessage', ['result' => $result]);
return $result;
}
/**
* Process voice message
*/
private function processVoiceMessage(array $message): array
{
$this->trackMethod('processVoiceMessage', ['message' => $message]);
try {
$chatId = $message['chat']['id'];
if (!$this->speechService) {
$result = $this->createIgnoredResult('Speech service not available');
$this->endMethod('processVoiceMessage', ['result' => $result]);
return $result;
}
// Convert voice to text
$voiceFileId = $message['voice']['file_id'];
$text = $this->speechService->convertVoiceToText($voiceFileId);
if (empty($text)) {
$result = $this->createIgnoredResult('Could not convert voice to text');
$this->endMethod('processVoiceMessage', ['result' => $result]);
return $result;
}
// Process the converted text as a regular message
$textMessage = array_merge($message, ['text' => $text]);
$result = $this->processTextMessage($textMessage);
$result['input_type'] = 'voice';
$result['converted_text'] = $text;
$this->endMethod('processVoiceMessage', ['result' => $result]);
return $result;
} catch (\Exception $e) {
$result = [
'success' => false,
'status' => 'error',
'message' => 'Voice processing failed',
'error' => $e->getMessage()
];
$this->endMethod('processVoiceMessage', ['result' => $result, 'error' => $e->getMessage()]);
return $result;
}
}
/**
* Process audio message
*/
private function processAudioMessage(array $message): array
{
$this->trackMethod('processAudioMessage', ['message' => $message]);
try {
$chatId = $message['chat']['id'];
if (!$this->speechService) {
$result = $this->createIgnoredResult('Speech service not available');
$this->endMethod('processAudioMessage', ['result' => $result]);
return $result;
}
// Convert audio to text
$audioFileId = $message['audio']['file_id'];
$text = $this->speechService->convertAudioToText($audioFileId);
if (empty($text)) {
$result = $this->createIgnoredResult('Could not convert audio to text');
$this->endMethod('processAudioMessage', ['result' => $result]);
return $result;
}
// Process the converted text as a regular message
$textMessage = array_merge($message, ['text' => $text]);
$result = $this->processTextMessage($textMessage);
$result['input_type'] = 'audio';
$result['converted_text'] = $text;
$this->endMethod('processAudioMessage', ['result' => $result]);
return $result;
} catch (\Exception $e) {
$result = [
'success' => false,
'status' => 'error',
'message' => 'Audio processing failed',
'error' => $e->getMessage()
];
$this->endMethod('processAudioMessage', ['result' => $result, 'error' => $e->getMessage()]);
return $result;
}
}
/**
* Process callback query
*/
private function processCallbackQuery(array $callbackQuery): array
{
$this->trackMethod('processCallbackQuery', ['callback_query' => $callbackQuery]);
$result = $this->telegramBotService->processCallbackQuery($callbackQuery);
$result['status'] = 'success';
$result['message'] = 'Callback query processed';
$result['input_type'] = 'callback_query';
$this->endMethod('processCallbackQuery', ['result' => $result]);
return $result;
}
/**
* Create ignored result
*/
private function createIgnoredResult(string $message): array
{
return [
'success' => false,
'status' => 'ignored',
'message' => $message,
'type' => 'ignored'
];
}
// ========================================
// MessageTrackingInterface Implementation
// ========================================
/**
* Inicializa o sistema de tracking
*/
public function initializeTracking(): void
{
// Tracking já é inicializado no construtor
}
/**
* Inicia o tracking de um método
*/
public function trackMethod(string $methodName, array $inputData = [], array $outputData = []): void
{
$className = class_basename($this);
$this->flowTracker->trackMethod($className, $methodName, $inputData, $outputData);
}
/**
* Finaliza o tracking de um método
*/
public function endMethod(string $methodName, array $outputData = []): void
{
$className = class_basename($this);
$this->flowTracker->endMethod($className, $methodName, $outputData);
}
}
**Continue para a próxima seção quando estiver pronto!**
=== TelegramMessageProcessorService.php ===
processCallbackQuery($payload['callback_query']);
}
// Verify if it's a message
if (!isset($payload['message'])) {
$result = [
'success' => false,
'status' => 'ignored',
'message' => 'No message in payload'
];
return $result;
}
$message = $payload['message'];
// Process different message types
if (isset($message['text'])) {
return $this->processTextMessage($message);
}
if (isset($message['voice'])) {
return $this->processVoiceMessage($message);
}
if (isset($message['audio'])) {
return $this->processAudioMessage($message);
}
return $this->createIgnoredResult('Unsupported message type');
} catch (\Exception $e) {
$result = [
'success' => false,
'status' => 'error',
'message' => 'Internal server error',
'error' => $e->getMessage()
];
if ($this->loggingService) {
$this->loggingService->logException($e, [
'operation' => 'telegram_webhook_processing',
'payload' => $payload
]);
}
return $result;
}
}
private function processTextMessage(array $message): array
{
$chatId = $message['chat']['id'];
$text = $message['text'] ?? '';
if (empty($text)) {
return $this->createEmptyMessageResponse($chatId);
}
// Process command through unified system
$result = $this->commandSystem->processCommand($text, $this->getContextFromMessage($message));
if ($result->isSuccess()) {
return $this->createSuccessResponse($chatId, $result);
} else {
return $this->createCommandNotFoundResponse($chatId, $result);
}
}
private function processVoiceMessage(array $message): array
{
try {
$chatId = $message['chat']['id'];
$voice = $message['voice'];
// Check if speech service is available
if (!$this->speechService) {
if ($this->telegramChannel) {
$this->telegramChannel->sendTextMessage(
"❌ Serviço de reconhecimento de voz não está disponível. Envie uma mensagem de texto.",
(string) $chatId
);
}
return $this->createVoiceConversionErrorResponse($chatId);
}
// Send processing message
if ($this->telegramChannel) {
$this->telegramChannel->sendTextMessage(
"🎤 Processando mensagem de voz...",
(string) $chatId
);
}
// Download voice file
$voiceFilePath = $this->downloadVoiceFile($voice['file_id']);
if (!$voiceFilePath) {
return $this->createVoiceConversionErrorResponse($chatId);
}
// Convert voice to text with error handling
try {
$text = $this->speechService->convertVoiceToText($voiceFilePath);
} catch (\Exception $speechException) {
if ($this->loggingService) {
$this->loggingService->logException($speechException, [
'operation' => 'speech_to_text_conversion',
'chat_id' => $chatId,
'file_path' => $voiceFilePath
]);
}
// Send error message to user
if ($this->telegramChannel) {
$this->telegramChannel->sendTextMessage(
"❌ Erro ao processar mensagem de voz. Tente novamente ou envie uma mensagem de texto.",
(string) $chatId
);
}
return $this->createVoiceConversionErrorResponse($chatId);
}
if (!$text) {
if ($this->loggingService) {
$this->loggingService->logException(new \Exception('Failed to convert voice to text'), [
'chat_id' => $chatId,
'file_path' => $voiceFilePath
]);
}
// Send error message to user
if ($this->telegramChannel) {
$this->telegramChannel->sendTextMessage(
"❌ Não foi possível reconhecer o texto da mensagem de voz. Tente novamente.",
(string) $chatId
);
}
return $this->createVoiceConversionErrorResponse($chatId);
}
// Clean up voice file
if (file_exists($voiceFilePath)) {
unlink($voiceFilePath);
}
// Send recognized text to user
if ($this->telegramChannel) {
$this->telegramChannel->sendTextMessage(
"🎯 Texto reconhecido: *{$text}*",
(string) $chatId
);
}
// Update context for voice processing
$context = $this->getContextFromMessage($message);
$context['type'] = 'voice';
$context['original_voice'] = $text;
// Process converted text through unified system
$result = $this->commandSystem->processCommand($text, $context);
if ($result->isSuccess()) {
return $this->createSuccessResponse($chatId, $result);
} else {
return $this->createCommandNotFoundResponse($chatId, $result);
}
} catch (\Exception $e) {
if ($this->loggingService) {
$this->loggingService->logException($e, [
'operation' => 'voice_message_processing',
'chat_id' => $message['chat']['id'] ?? null,
'message' => $message
]);
}
return $this->createVoiceConversionErrorResponse($chatId);
}
}
private function processCallbackQuery(array $callbackQuery): array
{
$chatId = $callbackQuery['message']['chat']['id'] ?? 0;
$callbackData = $callbackQuery['data'] ?? '';
if (empty($callbackData)) {
return $this->createEmptyCallbackResponse($chatId);
}
// Process callback through unified system
$result = $this->commandSystem->processCommand($callbackData, $this->getContextFromCallbackQuery($callbackQuery));
if ($result->isSuccess()) {
return $this->createSuccessResponse($chatId, $result);
} else {
return $this->createCallbackErrorResponse($chatId, $result);
}
}
private function getContextFromMessage(array $message): array
{
// Check if this is a callback query
if (isset($message['callback_query'])) {
return $this->getContextFromCallbackQuery($message['callback_query']);
}
// Regular message
return [
'chat_id' => $message['chat']['id'],
'user_id' => $message['from']['id'] ?? null,
'type' => $this->determineMessageType($message),
'timestamp' => $message['date'] ?? time(),
'user_permissions' => $this->getUserPermissions($message)
];
}
private function getContextFromCallbackQuery(array $callbackQuery): array
{
return [
'chat_id' => $callbackQuery['message']['chat']['id'] ?? 0,
'user_id' => $callbackQuery['from']['id'] ?? null,
'type' => 'callback_query',
'timestamp' => $callbackQuery['date'] ?? time(),
'user_permissions' => $this->getUserPermissions($callbackQuery)
];
}
private function determineMessageType(array $message): string
{
if (isset($message['callback_query'])) {
return 'callback_query';
}
if (isset($message['voice'])) {
return 'voice';
}
if (isset($message['audio'])) {
return 'audio';
}
if (isset($message['text'])) {
return 'text';
}
return 'unknown';
}
private function getUserPermissions(array $message): array
{
// Extract user permissions from message or callback query
$userId = null;
if (isset($message['from']['id'])) {
// Regular message
$userId = $message['from']['id'];
} elseif (isset($message['callback_query']['from']['id'])) {
// Callback query wrapped in payload
$userId = $message['callback_query']['from']['id'];
}
if (!$userId) {
return ['user'];
}
// TODO: Implement actual permission checking
// For now, return default permissions
return ['user'];
}
private function createSuccessResponse(int $chatId, CommandResult $result): array
{
$data = $result->getData();
return [
'success' => true,
'chat_id' => $chatId,
'type' => $result->getType(),
'data' => $data,
'command_info' => $result->getCommandMatch() ? [
'command_id' => $result->getCommandMatch()->getCommand()->getId(),
'confidence' => $result->getCommandMatch()->getConfidence()
] : null
];
}
private function createCommandNotFoundResponse(int $chatId, CommandResult $result): array
{
$data = $result->getData();
$fallbackMessage = $data['fallback_message'] ?? 'Comando não encontrado';
// Send the fallback message to the user in Telegram chat
if ($this->telegramChannel) {
$this->telegramChannel->sendTextMessage(
$fallbackMessage,
(string) $chatId
);
}
return [
'success' => false,
'chat_id' => $chatId,
'type' => 'command_not_found',
'message' => $fallbackMessage,
'suggestions' => $data['suggestions'] ?? [],
'data' => $data
];
}
private function createEmptyMessageResponse(int $chatId): array
{
return [
'success' => false,
'chat_id' => $chatId,
'type' => 'empty_message',
'message' => 'Mensagem vazia recebida.',
'data' => []
];
}
private function createVoiceConversionErrorResponse(int $chatId): array
{
return [
'success' => false,
'chat_id' => $chatId,
'type' => 'voice_conversion_error',
'message' => 'Erro ao converter mensagem de voz.',
'data' => []
];
}
private function createEmptyCallbackResponse(int $chatId): array
{
return [
'success' => false,
'chat_id' => $chatId,
'type' => 'empty_callback',
'message' => 'Dados de callback vazios.',
'data' => []
];
}
private function createCallbackErrorResponse(int $chatId, CommandResult $result): array
{
return [
'success' => false,
'chat_id' => $chatId,
'type' => 'callback_error',
'message' => 'Erro ao processar callback.',
'data' => $result->getData()
];
}
private function createIgnoredResult(string $message): array
{
return [
'success' => false,
'status' => 'ignored',
'message' => $message
];
}
// Additional methods for audio processing and file downloads...
// (processAudioMessage, downloadVoiceFile, downloadAudioFile, etc.)
}
=== TelegramRepository.php ===
getAuthorizedUsers();
return in_array($chatId, $authorizedUsers);
}
/**
* Get bot configuration
*/
public function getBotConfig(): array
{
return [
'enabled' => Config::get('services.telegram.enabled', false),
'bot_token' => Config::get('services.telegram.bot_token'),
'recipients' => $this->getAuthorizedUsers(),
];
}
/**
* Cache webhook info
*/
public function cacheWebhookInfo(array $webhookInfo): void
{
Cache::put(
self::CACHE_PREFIX . 'webhook_info',
$webhookInfo,
self::CACHE_TTL
);
}
/**
* Get cached webhook info
*/
public function getCachedWebhookInfo(): ?array
{
return Cache::get(self::CACHE_PREFIX . 'webhook_info');
}
/**
* Clear webhook cache
*/
public function clearWebhookCache(): void
{
Cache::forget(self::CACHE_PREFIX . 'webhook_info');
}
/**
* Cache message processing result
*/
public function cacheMessageResult(int $chatId, array $result): void
{
$key = self::CACHE_PREFIX . "message_result:{$chatId}";
Cache::put($key, $result, self::CACHE_TTL);
}
/**
* Get cached message result
*/
public function getCachedMessageResult(int $chatId): ?array
{
$key = self::CACHE_PREFIX . "message_result:{$chatId}";
return Cache::get($key);
}
/**
* Store webhook log
*/
public function storeWebhookLog(array $payload, array $result): void
{
$logData = [
'timestamp' => now()->toISOString(),
'payload' => $payload,
'result' => $result,
'ip' => request()->ip(),
'user_agent' => request()->userAgent(),
];
// Store in cache for recent logs
$logs = Cache::get(self::CACHE_PREFIX . 'recent_logs', []);
$logs[] = $logData;
// Keep only last 100 logs
if (count($logs) > 100) {
$logs = array_slice($logs, -100);
}
Cache::put(self::CACHE_PREFIX . 'recent_logs', $logs, self::CACHE_TTL);
}
/**
* Get recent webhook logs
*/
public function getRecentWebhookLogs(int $limit = 50): array
{
$logs = Cache::get(self::CACHE_PREFIX . 'recent_logs', []);
return array_slice($logs, -$limit);
}
/**
* Get webhook statistics
*/
public function getWebhookStats(): array
{
$logs = $this->getRecentWebhookLogs(1000);
$stats = [
'total_requests' => count($logs),
'successful_requests' => 0,
'failed_requests' => 0,
'ignored_requests' => 0,
'message_requests' => 0,
'callback_requests' => 0,
];
foreach ($logs as $log) {
$result = $log['result'] ?? [];
$status = $result['status'] ?? 'unknown';
switch ($status) {
case 'success':
$stats['successful_requests']++;
break;
case 'error':
$stats['failed_requests']++;
break;
case 'ignored':
$stats['ignored_requests']++;
break;
}
// Count request types
if (isset($log['payload']['message'])) {
$stats['message_requests']++;
}
if (isset($log['payload']['callback_query'])) {
$stats['callback_requests']++;
}
}
return $stats;
}
}
===== 📝 Seção 1: Controllers e Rotas =====
=== TelegramWebhookController.php ===
flowTracker->startTracking(uniqid('webhook_', true));
try {
// Check if validation failed
if ($request->has('validation_errors')) {
$validationErrors = $request->input('validation_errors');
$errorMessage = $this->createValidationErrorMessage($validationErrors);
// Send friendly error message to user via Telegram
$this->sendFriendlyErrorMessage($request->all(), $errorMessage);
return TelegramWebhookResource::ignored('Validation failed - friendly message sent to user')
->response()
->setStatusCode(200);
}
$payload = $request->validated();
// Validate payload structure first
$validation = $this->webhookService->validatePayload($payload);
if (!$validation['valid']) {
// Send friendly error message to user via Telegram
$this->sendFriendlyErrorMessage($payload, $validation['message']);
return TelegramWebhookResource::ignored($validation['message'])
->response()
->setStatusCode(200);
}
// Validate webhook and mark as processing (after payload validation)
$updateId = $payload['update_id'] ?? null;
$skipDuplicateCheck = app()->environment('testing');
if (!$this->webhookValidationService->validateAndMarkProcessing($updateId, $skipDuplicateCheck)) {
return TelegramWebhookResource::success('Duplicate webhook ignored', [
'success' => true,
'status' => 'ignored',
'message' => 'Duplicate webhook ignored',
'update_id' => $updateId
])
->response()
->setStatusCode(200);
}
// Process the webhook payload with timeout protection
$result = $this->processWithTimeout(function () use ($payload) {
return $this->messageProcessor->processWebhookPayload($payload);
}, 25); // 25 seconds timeout
// Gera relatório do fluxo APÓS o processamento
$flowReport = $this->flowTracker->generateFlowReport($payload);
$userReport = $this->flowTracker->generateUserReport($payload);
// Finaliza o tracking
$this->flowTracker->endTracking();
// Se habilitado, envia relatório para o usuário
if (config('message-flow.tracking.send_to_user', false)) {
$this->sendFlowReportToUser($payload, $userReport);
}
if (config('message-flow.enabled', false)) {
$this->loggingService->logTelegramEvent('message_flow_report', [
'flow_report' => $flowReport,
'user_report' => $userReport,
'payload' => $payload
], 'info');
}
// Check if the result is valid and has the expected structure
if (!is_array($result) || !isset($result['success'])) {
$this->loggingService->logTelegramEvent('telegram_webhook_invalid_result', [
'error' => 'Invalid result structure from message processor',
'result' => $result,
'payload' => $payload,
'telegram_update_id' => $request->input('update_id'),
'timestamp' => now()->toISOString()
], 'error');
// Send friendly error message to user
$this->sendFriendlyErrorMessage($payload, 'Desculpe, ocorreu um erro interno. Tente novamente em alguns instantes.');
return TelegramWebhookResource::error('Invalid processing result')
->response()
->setStatusCode(500);
}
// Check if this is a normal response (not an error) - check status first
$isNormalResponse = isset($result['type']) && in_array($result['type'], [
'command_not_found',
'ignored',
'unauthorized',
'unsupported',
'empty_message',
'voice_conversion_error',
'audio_conversion_error'
]);
if ($isNormalResponse) {
// Send fallback message to user for command_not_found
if ($result['type'] === 'command_not_found') {
$fallbackMessage = $this->createCommandNotFoundFallbackMessage($result);
$this->sendFriendlyErrorMessage($payload, $fallbackMessage);
}
// Use appropriate resource method based on status
if (isset($result['status']) && $result['status'] === 'ignored') {
return TelegramWebhookResource::ignored($result['message'] ?? 'Message ignored', $result)
->response()
->setStatusCode(200);
}
return TelegramWebhookResource::success($result['message'] ?? 'Message processed', $result)
->response()
->setStatusCode(200);
}
// Check if the message was processed successfully
if ($result['success']) {
return TelegramWebhookResource::success($result['message'] ?? 'Message processed successfully', $result)
->response()
->setStatusCode(200);
}
// Handle actual errors (return 500)
$this->loggingService->logTelegramEvent('telegram_webhook_processing_failed', [
'error' => 'Message processing failed',
'error_message' => $result['message'] ?? 'Unknown error',
'result' => $result,
'payload' => $payload,
'telegram_update_id' => $request->input('update_id'),
'timestamp' => now()->toISOString()
], 'error');
// Send friendly error message to user
$this->sendFriendlyErrorMessage($payload, $result['message'] ?? 'Desculpe, ocorreu um erro ao processar sua mensagem. Tente novamente.');
return TelegramWebhookResource::error($result['message'] ?? 'Message processing failed', $result)
->response()
->setStatusCode(500);
} catch (\Exception $e) {
$duration = (microtime(true) - $startTime) * 1000;
$this->loggingService->logException($e, [
'operation' => 'telegram_webhook_processing',
'chat_id' => $request->input('message.chat.id'),
'user_id' => $request->input('message.from.id'),
'processing_time_ms' => round($duration, 2)
]);
// Send friendly error message to user even for exceptions
$this->sendFriendlyErrorMessage($request->all(), 'Desculpe, ocorreu um erro inesperado. Nossa equipe foi notificada.');
return TelegramWebhookResource::error('Internal server error')
->response()
->setStatusCode(500);
}
}
/**
* Set webhook URL for Telegram bot
*/
public function setWebhook(TelegramWebhookSetupRequest $request): JsonResponse
{
try {
$webhookUrl = $request->validated()['webhook_url'];
$result = $this->webhookService->setWebhook($webhookUrl);
if (!$result['success']) {
return TelegramWebhookResource::error($result['message'], $result)
->response()
->setStatusCode(400);
}
return TelegramWebhookResource::success($result['message'], $result)
->response()
->setStatusCode(200);
} catch (\Exception $e) {
$this->loggingService->logException($e, [
'operation' => 'telegram_webhook_setup',
'webhook_url' => $request->validated()['webhook_url'] ?? 'unknown'
]);
return TelegramWebhookResource::error('Internal server error')
->response()
->setStatusCode(500);
}
}
/**
* Get webhook info
*/
public function getWebhookInfo(): JsonResponse
{
try {
$this->loggingService->logTelegramEvent('telegram_webhook_info_request', [
'action' => 'get_webhook_info'
], 'info');
$result = $this->webhookService->getWebhookInfo();
if (!$result['success']) {
$this->loggingService->logTelegramEvent('telegram_webhook_info_failed', [
'error' => $result['message'],
'result' => $result
], 'error');
return TelegramWebhookResource::error($result['message'], $result)
->response()
->setStatusCode(400);
}
return TelegramWebhookResource::success('Webhook info retrieved', $result)
->response()
->setStatusCode(200);
} catch (\Exception $e) {
$this->loggingService->logException($e, [
'operation' => 'telegram_webhook_info'
]);
return TelegramWebhookResource::error('Internal server error')
->response()
->setStatusCode(500);
}
}
/**
* Delete webhook
*/
public function deleteWebhook(): JsonResponse
{
try {
$this->loggingService->logTelegramEvent('telegram_webhook_deletion', [
'action' => 'delete_webhook'
], 'info');
$result = $this->webhookService->deleteWebhook();
if (!$result['success']) {
$this->loggingService->logTelegramEvent('telegram_webhook_deletion_failed', [
'error' => $result['message'],
'result' => $result
], 'error');
return TelegramWebhookResource::error($result['message'], $result)
->response()
->setStatusCode(400);
}
$this->loggingService->logTelegramEvent('telegram_webhook_deletion_success', [
'result' => $result
], 'success');
return TelegramWebhookResource::success($result['message'], $result)
->response()
->setStatusCode(200);
} catch (\Exception $e) {
$this->loggingService->logException($e, [
'operation' => 'telegram_webhook_deletion'
]);
return TelegramWebhookResource::error('Internal server error')
->response()
->setStatusCode(500);
}
}
/**
* Test bot functionality
*/
public function test(): JsonResponse
{
try {
$this->loggingService->logTelegramEvent('telegram_bot_test', [
'action' => 'test_bot'
], 'info');
$result = $this->webhookService->testBot();
if (!$result['success']) {
$this->loggingService->logTelegramEvent('telegram_bot_test_failed', [
'error' => $result['message'],
'result' => $result
], 'error');
return TelegramWebhookResource::error($result['message'], $result)
->response()
->setStatusCode(400);
}
$this->loggingService->logTelegramEvent('telegram_bot_test_success', [
'result' => $result
], 'success');
return TelegramWebhookResource::success($result['message'], $result)
->response()
->setStatusCode(200);
} catch (\Exception $e) {
$this->loggingService->logException($e, [
'operation' => 'telegram_bot_test'
]);
return TelegramWebhookResource::error('Test failed: ' . $e->getMessage())
->response()
->setStatusCode(500);
}
}
// ... (métodos auxiliares privados serão adicionados na próxima seção)
}
=== TelegramStatsController.php ===
loggingService->getWebhookStats();
return TelegramWebhookResource::success('Statistics retrieved successfully', [
'statistics' => $stats
])
->response()
->setStatusCode(200);
} catch (\Exception $e) {
return TelegramWebhookResource::error('Failed to retrieve statistics: ' . $e->getMessage())
->response()
->setStatusCode(500);
}
}
/**
* Get recent webhook logs
*/
public function getRecentLogs(Request $request): JsonResponse
{
try {
$limit = $request->get('limit', 50);
$limit = min(max((int) $limit, 1), 100); // Limit between 1 and 100
$logs = $this->loggingService->getRecentLogs($limit);
return TelegramWebhookResource::success('Recent logs retrieved successfully', [
'logs' => $logs,
'count' => count($logs),
'limit' => $limit
])
->response()
->setStatusCode(200);
} catch (\Exception $e) {
return TelegramWebhookResource::error('Failed to retrieve logs: ' . $e->getMessage())
->response()
->setStatusCode(500);
}
}
/**
* Get webhook health status
*/
public function getHealthStatus(): JsonResponse
{
try {
$stats = $this->loggingService->getWebhookStats();
// Calculate health metrics
$totalRequests = $stats['total_requests'] ?? 0;
$successfulRequests = $stats['successful_requests'] ?? 0;
$failedRequests = $stats['failed_requests'] ?? 0;
$successRate = $totalRequests > 0 ? ($successfulRequests / $totalRequests) * 100 : 0;
$errorRate = $totalRequests > 0 ? ($failedRequests / $totalRequests) * 100 : 0;
$healthStatus = [
'status' => $successRate >= 90 ? 'healthy' : ($successRate >= 70 ? 'warning' : 'critical'),
'success_rate' => round($successRate, 2),
'error_rate' => round($errorRate, 2),
'total_requests' => $totalRequests,
'successful_requests' => $successfulRequests,
'failed_requests' => $failedRequests,
'ignored_requests' => $stats['ignored_requests'] ?? 0,
'message_requests' => $stats['message_requests'] ?? 0,
'callback_requests' => $stats['callback_requests'] ?? 0,
'last_updated' => now()->toISOString(),
];
return TelegramWebhookResource::success('Health status retrieved successfully', [
'health' => $healthStatus
])
->response()
->setStatusCode(200);
} catch (\Exception $e) {
return TelegramWebhookResource::error('Failed to retrieve health status: ' . $e->getMessage())
->response()
->setStatusCode(500);
}
}
}
=== Rotas (routes/api.php) ===
// =============================================================================
// TELEGRAM BOT ROUTES
// =============================================================================
Route::prefix('telegram')->group(function () {
//Route::middleware('telegram.webhook.secret')->group(function () {
Route::post('/webhook', [TelegramWebhookController::class, 'handle']); // POST /api/telegram/webhook
//});
// Outras rotas sem validação de secret (gerenciamento interno)
Route::post('/set-webhook', [TelegramWebhookController::class, 'setWebhook']); // POST /api/telegram/set-webhook
Route::get('/webhook-info', [TelegramWebhookController::class, 'getWebhookInfo']); // GET /api/telegram/webhook-info
Route::delete('/webhook', [TelegramWebhookController::class, 'deleteWebhook']); // DELETE /api/telegram/webhook
// Testing and monitoring
Route::post('/test', [TelegramWebhookController::class, 'test']); // POST /api/telegram/test
// Statistics and monitoring
Route::prefix('stats')->group(function () {
Route::get('/', [TelegramStatsController::class, 'getStats']); // GET /api/telegram/stats
Route::get('/logs', [TelegramStatsController::class, 'getRecentLogs']); // GET /api/telegram/stats/logs
Route::get('/health', [TelegramStatsController::class, 'getHealthStatus']); // GET /api/telegram/stats/health
});
});
===== 📝 Seção 2: Services e Processamento =====
=== TelegramWebhookService.php ===
botToken = config('services.telegram.bot_token');
$this->apiUrl = "https://api.telegram.org/bot{$this->botToken}";
}
/**
* Set webhook URL for Telegram bot
*/
public function setWebhook(string $webhookUrl): array
{
try {
$response = Http::post("{$this->apiUrl}/setWebhook", [
'url' => $webhookUrl
]);
if ($response->successful()) {
$data = $response->json();
$result = [
'success' => true,
'message' => 'Webhook set successfully',
'data' => $data
];
$this->loggingService->logBusinessOperation('telegram_webhook_setup_success', [
'webhook_url' => $webhookUrl,
'result' => $result
], 'success');
return $result;
}
$result = [
'success' => false,
'message' => 'Failed to set webhook',
'error' => $response->json()
];
$this->loggingService->logBusinessOperation('telegram_webhook_setup_failed', [
'webhook_url' => $webhookUrl,
'error' => $result['error']
], 'error');
return $result;
} catch (\Exception $e) {
$result = [
'success' => false,
'message' => 'Internal server error',
'error' => $e->getMessage()
];
$this->loggingService->logException($e, [
'operation' => 'telegram_webhook_setup',
'webhook_url' => $webhookUrl
]);
return $result;
}
}
/**
* Get webhook info
*/
public function getWebhookInfo(): array
{
try {
$this->loggingService->logBusinessOperation('telegram_webhook_info_request', [
'action' => 'get_webhook_info'
], 'info');
$response = Http::get("{$this->apiUrl}/getWebhookInfo");
if ($response->successful()) {
$data = $response->json();
$result = [
'success' => true,
'data' => $data
];
$this->loggingService->logBusinessOperation('telegram_webhook_info_success', [
'result' => $result
], 'success');
return $result;
}
$result = [
'success' => false,
'message' => 'Failed to get webhook info',
'error' => $response->json()
];
$this->loggingService->logBusinessOperation('telegram_webhook_info_failed', [
'error' => $result['error']
], 'error');
return $result;
} catch (\Exception $e) {
$this->loggingService->logException($e, [
'operation' => 'telegram_webhook_info'
]);
return [
'success' => false,
'message' => 'Internal server error',
'error' => $e->getMessage()
];
}
}
/**
* Delete webhook
*/
public function deleteWebhook(): array
{
try {
$this->loggingService->logBusinessOperation('telegram_webhook_deletion_attempt', [
'action' => 'delete_webhook'
], 'info');
$response = Http::post("{$this->apiUrl}/deleteWebhook");
if ($response->successful()) {
$data = $response->json();
$result = [
'success' => true,
'message' => 'Webhook deleted successfully',
'data' => $data
];
$this->loggingService->logBusinessOperation('telegram_webhook_deletion_success', [
'result' => $result
], 'success');
return $result;
}
$result = [
'success' => false,
'message' => 'Failed to delete webhook',
'error' => $response->json()
];
$this->loggingService->logBusinessOperation('telegram_webhook_deletion_failed', [
'error' => $result['error']
], 'error');
return $result;
} catch (\Exception $e) {
$result = [
'success' => false,
'message' => 'Internal server error',
'error' => $e->getMessage()
];
$this->loggingService->logException($e, [
'operation' => 'telegram_webhook_deletion'
]);
return $result;
}
}
/**
* Test bot functionality
*/
public function testBot(): array
{
try {
$this->loggingService->logBusinessOperation('telegram_bot_test_attempt', [
'action' => 'test_bot'
], 'info');
$recipients = config('services.telegram.recipients', []);
if (empty($recipients)) {
$result = [
'success' => false,
'message' => 'No recipients configured'
];
$this->loggingService->logBusinessOperation('telegram_bot_test_failed', [
'error' => 'No recipients configured'
], 'error');
return $result;
}
$results = [];
foreach ($recipients as $recipient) {
$result = $this->sendTestMessage($recipient);
$results[$recipient] = $result;
}
$successCount = count(array_filter($results, fn($r) => $r['success']));
$result = [
'success' => $successCount > 0,
'message' => 'Test completed',
'sent_to' => $successCount,
'total_recipients' => count($recipients),
'results' => $results
];
$this->loggingService->logBusinessOperation('telegram_bot_test_success', [
'sent_to' => $successCount,
'total_recipients' => count($recipients),
'result' => $result
], 'success');
return $result;
} catch (\Exception $e) {
$result = [
'success' => false,
'message' => 'Test failed: ' . $e->getMessage()
];
$this->loggingService->logException($e, [
'operation' => 'telegram_bot_test'
]);
return $result;
}
}
/**
* Send test message to specific recipient
*/
private function sendTestMessage(string $recipient): array
{
try {
$testMessage = "🧪 *Teste do Bot*\n\n" .
"Este é um teste do bot de relatórios do Rei do Óleo.\n" .
"Se você recebeu esta mensagem, o bot está funcionando!\n\n" .
"Use `/help` para ver os comandos disponíveis.\n\n" .
"⏰ Teste realizado em: " . now()->format('d/m/Y H:i:s');
$response = Http::post("{$this->apiUrl}/sendMessage", [
'chat_id' => $recipient,
'text' => $testMessage,
'parse_mode' => 'Markdown'
]);
if ($response->successful()) {
$this->loggingService->logBusinessOperation('test_message_sent_successfully', [
'chat_id' => $recipient,
'message_type' => 'test'
], 'info');
return [
'success' => true,
'message' => 'Test message sent successfully'
];
}
return [
'success' => false,
'error' => $response->json()['description'] ?? 'Unknown error'
];
} catch (\Exception $e) {
$this->loggingService->logException($e, [
'operation' => 'telegram_test_message_send',
'recipient' => $recipient
]);
return [
'success' => false,
'error' => $e->getMessage()
];
}
}
/**
* Validate webhook payload
*/
public function validatePayload(array $payload): array
{
if (!isset($payload['update_id'])) {
return [
'valid' => false,
'message' => 'Missing update_id in payload'
];
}
if (!isset($payload['message']) && !isset($payload['callback_query'])) {
return [
'valid' => false,
'message' => 'No message or callback_query in payload'
];
}
// Check if it's a message and validate message structure
if (isset($payload['message'])) {
$message = $payload['message'];
// Check if message has required fields
if (!isset($message['chat']['id']) || !isset($message['from']['id'])) {
return [
'valid' => false,
'message' => 'Message missing required chat or user information'
];
}
// Check if message has any content (text, voice, audio, photo, etc.)
$hasContent = isset($message['text']) ||
isset($message['voice']) ||
isset($message['audio']) ||
isset($message['photo']) ||
isset($message['document']) ||
isset($message['video']) ||
isset($message['sticker']) ||
isset($message['location']) ||
isset($message['contact']);
if (!$hasContent) {
return [
'valid' => false,
'message' => 'Message has no recognizable content'
];
}
}
return [
'valid' => true,
'type' => isset($payload['callback_query']) ? 'callback_query' : 'message'
];
}
}
=== TelegramBotService.php ===
initializeTracking();
}
/**
* Process incoming message from Telegram webhook
*/
public function processMessage(array $message): array
{
$this->trackMethod('processMessage', ['message' => $message]);
try {
$chatId = $message['chat']['id'];
$text = $message['text'] ?? '';
$from = $message['from'] ?? [];
// Check if user is authorized
if (!$this->authorizationService->isAuthorizedUser($chatId)) {
$result = $this->menuBuilder->buildUnauthorizedMessage($chatId);
$this->endMethod('processMessage', ['result' => $result, 'status' => 'unauthorized']);
return $result;
}
// Process command using UnifiedCommandSystem
$context = [
'chat_id' => $chatId,
'user_id' => $from['id'] ?? null,
'type' => 'text',
'timestamp' => time(),
'user_permissions' => ['user'] // TODO: Get real permissions
];
$result = $this->commandSystem->processCommand($text, $context);
if ($result->isSuccess()) {
$data = $result->getData();
$response = [
'success' => true,
'chat_id' => $chatId,
'type' => $result->getType(),
'data' => $data,
'command_info' => $result->getCommandMatch() ? [
'command_id' => $result->getCommandMatch()->getCommand()->getId(),
'confidence' => $result->getCommandMatch()->getConfidence()
] : null
];
$this->endMethod('processMessage', ['result' => $response, 'status' => 'success']);
return $response;
} else {
$data = $result->getData();
$fallbackMessage = $data['fallback_message'] ?? 'Comando não encontrado';
$response = [
'success' => false,
'chat_id' => $chatId,
'type' => 'command_not_found',
'message' => $fallbackMessage,
'suggestions' => $data['suggestions'] ?? [],
'data' => $data
];
$this->endMethod('processMessage', ['result' => $response, 'status' => 'command_not_found']);
return $response;
}
} catch (\Exception $e) {
$this->loggingService->logException($e, [
'operation' => 'telegram_message_processing',
'chat_id' => $message['chat']['id'] ?? null,
'user_id' => $message['from']['id'] ?? null,
'message' => $message
]);
$errorResponse = [
'success' => false,
'chat_id' => $chatId,
'type' => 'error',
'message' => 'Erro interno do sistema',
'data' => []
];
$this->endMethod('processMessage', ['result' => $errorResponse, 'error' => $e->getMessage()]);
return $errorResponse;
}
}
/**
* Process callback query from inline keyboard buttons
*/
public function processCallbackQuery(array $callbackQuery): array
{
try {
$chatId = $callbackQuery['message']['chat']['id'];
$messageId = $callbackQuery['message']['message_id'] ?? null;
$callbackData = $callbackQuery['data'] ?? '';
$from = $callbackQuery['from'] ?? [];
// Check if user is authorized
if (!$this->authorizationService->isAuthorizedUser($chatId)) {
return $this->menuBuilder->buildUnauthorizedMessage($chatId);
}
// Process callback using UnifiedCommandSystem
$context = [
'chat_id' => $chatId,
'user_id' => $from['id'] ?? null,
'type' => 'callback_query',
'timestamp' => time(),
'user_permissions' => ['user'], // TODO: Get real permissions
'callback_data' => $callbackData
];
$result = $this->commandSystem->processCommand($callbackData, $context);
if ($result->isSuccess()) {
$data = $result->getData();
return [
'success' => true,
'chat_id' => $chatId,
'type' => $result->getType(),
'data' => $data,
'command_info' => $result->getCommandMatch() ? [
'command_id' => $result->getCommandMatch()->getCommand()->getId(),
'confidence' => $result->getCommandMatch()->getConfidence()
] : null
];
} else {
$data = $result->getData();
$fallbackMessage = $data['fallback_message'] ?? 'Ação não encontrada';
return [
'success' => false,
'chat_id' => $chatId,
'type' => 'callback_not_found',
'message' => $fallbackMessage,
'suggestions' => $data['suggestions'] ?? [],
'data' => $data
];
}
} catch (\Exception $e) {
$this->loggingService->logException($e, [
'operation' => 'telegram_callback_processing',
'chat_id' => $callbackQuery['message']['chat']['id'] ?? null,
'user_id' => $callbackQuery['from']['id'] ?? null,
'callback_query' => $callbackQuery
]);
return [
'success' => false,
'chat_id' => $chatId,
'type' => 'error',
'message' => 'Erro interno do sistema',
'data' => []
];
}
}
/**
* Send error message to user via Telegram
*/
public function sendErrorMessage(int $chatId, string $errorMessage): array
{
try {
// Create keyboard with helpful options
$keyboard = [
[
['text' => '❓ Ajuda', 'callback_data' => 'help'],
['text' => '🏠 Menu Principal', 'callback_data' => 'main_menu']
],
[
['text' => '📋 Comandos', 'callback_data' => 'commands_list'],
['text' => '🔄 Tentar Novamente', 'callback_data' => 'retry']
]
];
// Send error message with helpful keyboard using menuBuilder
$result = $this->menuBuilder->buildMainMenu($chatId);
$this->loggingService->logTelegramEvent('telegram_error_message_sent', [
'chat_id' => $chatId,
'error_message' => $errorMessage,
'result' => $result
], 'info');
return [
'success' => true,
'chat_id' => $chatId,
'type' => 'error_message_sent',
'message' => 'Error message sent to user',
'data' => $result
];
} catch (\Exception $e) {
$this->loggingService->logException($e, [
'operation' => 'send_error_message',
'chat_id' => $chatId,
'error_message' => $errorMessage
]);
return [
'success' => false,
'chat_id' => $chatId,
'type' => 'error_message_failed',
'message' => 'Failed to send error message',
'data' => []
];
}
}
// ========================================
// MessageTrackingInterface Implementation
// ========================================
/**
* Inicializa o sistema de tracking
*/
public function initializeTracking(): void
{
// Tracking já é inicializado no construtor
}
/**
* Inicia o tracking de um método
*/
public function trackMethod(string $methodName, array $inputData = [], array $outputData = []): void
{
$className = class_basename($this);
$this->flowTracker->trackMethod($className, $methodName, $inputData, $outputData);
}
/**
* Finaliza o tracking de um método
*/
public function endMethod(string $methodName, array $outputData = []): void
{
$className = class_basename($this);
$this->flowTracker->endMethod($className, $methodName, $outputData);
}
}
=== TelegramChannel.php ===
initializeTracking();
$this->botToken = config('services.telegram.bot_token', '');
$this->apiUrl = "https://api.telegram.org/bot{$this->botToken}";
$this->recipients = config('services.telegram.recipients', []);
}
/**
* Send text message via Telegram
*/
public function sendTextMessage(string $message, ?string $recipient = null): array
{
$this->trackMethod('sendTextMessage', ['message' => $message, 'recipient' => $recipient]);
if (!$this->isEnabled()) {
$result = [
'success' => false,
'error' => 'Telegram channel is disabled'
];
$this->endMethod('sendTextMessage', ['result' => $result, 'status' => 'disabled']);
return $result;
}
try {
if ($recipient) {
return $this->sendToRecipient($recipient, $message);
}
// Send to all configured recipients
if (empty($this->recipients)) {
$result = [
'success' => false,
'error' => 'No Telegram recipients configured'
];
$this->endMethod('sendTextMessage', ['result' => $result, 'status' => 'no_recipients']);
return $result;
}
$results = [];
foreach ($this->recipients as $recipient) {
$results[$recipient] = $this->sendToRecipient($recipient, $message);
}
$successCount = count(array_filter($results, fn($r) => $r['success']));
$result = [
'success' => $successCount > 0,
'sent_to' => $successCount,
'total_recipients' => count($results),
'results' => $results
];
$this->endMethod('sendTextMessage', ['result' => $result, 'status' => 'success']);
return $result;
} catch (\Exception $e) {
Log::error('Telegram channel error', [
'error' => $e->getMessage(),
'message' => $message
]);
$result = [
'success' => false,
'error' => $e->getMessage()
];
$this->endMethod('sendTextMessage', ['result' => $result, 'error' => $e->getMessage()]);
return $result;
}
}
/**
* Send message with inline keyboard via Telegram
*/
public function sendMessageWithKeyboard(string $message, string $chatId, array $keyboard): array
{
$this->trackMethod('sendMessageWithKeyboard', ['message' => $message, 'chat_id' => $chatId, 'keyboard' => $keyboard]);
if (!$this->isEnabled()) {
$result = [
'success' => false,
'error' => 'Telegram channel is disabled'
];
$this->endMethod('sendMessageWithKeyboard', ['result' => $result, 'status' => 'disabled']);
return $result;
}
try {
$response = Http::post("{$this->apiUrl}/sendMessage", [
'chat_id' => $chatId,
'text' => $message,
'parse_mode' => 'Markdown',
'reply_markup' => [
'inline_keyboard' => $keyboard
]
]);
if ($response->successful()) {
$data = $response->json();
$result = [
'success' => true,
'message_id' => $data['result']['message_id'] ?? null,
'response' => $data
];
$this->endMethod('sendMessageWithKeyboard', ['result' => $result, 'status' => 'success']);
return $result;
}
$result = [
'success' => false,
'error' => $response->json()['description'] ?? 'Unknown error',
'status' => $response->status()
];
$this->endMethod('sendMessageWithKeyboard', ['result' => $result, 'status' => 'http_error']);
return $result;
} catch (\Exception $e) {
Log::error('Telegram keyboard message error', [
'error' => $e->getMessage(),
'message' => $message,
'chat_id' => $chatId
]);
$result = [
'success' => false,
'error' => $e->getMessage()
];
$this->endMethod('sendMessageWithKeyboard', ['result' => $result, 'error' => $e->getMessage()]);
return $result;
}
}
/**
* Answer callback query (for inline keyboard buttons)
*/
public function answerCallbackQuery(string $callbackQueryId, ?string $text = null, bool $showAlert = false): array
{
if (!$this->isEnabled()) {
return [
'success' => false,
'error' => 'Telegram channel is disabled'
];
}
try {
$data = [
'callback_query_id' => $callbackQueryId
];
if ($text) {
$data['text'] = $text;
}
if ($showAlert) {
$data['show_alert'] = $showAlert;
}
$response = Http::post("{$this->apiUrl}/answerCallbackQuery", $data);
if ($response->successful()) {
$data = $response->json();
return [
'success' => true,
'response' => $data
];
}
return [
'success' => false,
'error' => $response->json()['description'] ?? 'Unknown error',
'status' => $response->status()
];
} catch (\Exception $e) {
Log::error('Telegram answer callback query error', [
'error' => $e->getMessage(),
'callback_query_id' => $callbackQueryId
]);
return [
'success' => false,
'error' => $e->getMessage()
];
}
}
/**
* Check if Telegram channel is enabled
*/
public function isEnabled(): bool
{
return config('services.telegram.enabled', true) &&
!empty($this->botToken) &&
!empty($this->recipients);
}
/**
* Send message to specific recipient
*/
private function sendToRecipient(string $chatId, string $message): array
{
try {
$response = Http::post("{$this->apiUrl}/sendMessage", [
'chat_id' => $chatId,
'text' => $message,
'parse_mode' => 'Markdown'
]);
if ($response->successful()) {
$data = $response->json();
return [
'success' => true,
'message_id' => $data['result']['message_id'] ?? null,
'response' => $data
];
}
return [
'success' => false,
'error' => $response->json()['description'] ?? 'Unknown error',
'status' => $response->status()
];
} catch (\Exception $e) {
return [
'success' => false,
'error' => $e->getMessage()
];
}
}
// ========================================
// MessageTrackingInterface Implementation
// ========================================
/**
* Inicializa o sistema de tracking
*/
public function initializeTracking(): void
{
// Tracking já é inicializado no construtor
}
/**
* Inicia o tracking de um método
*/
public function trackMethod(string $methodName, array $inputData = [], array $outputData = []): void
{
$className = class_basename($this);
$this->flowTracker->trackMethod($className, $methodName, $inputData, $outputData);
}
/**
* Finaliza o tracking de um método
*/
public function endMethod(string $methodName, array $outputData = []): void
{
$className = class_basename($this);
$this->flowTracker->endMethod($className, $methodName, $outputData);
}
}
===== 📝 Seção 3: Middlewares e Validação =====
=== TelegramWebhookSecretMiddleware.php ===
loggingService->logTelegramEvent('webhook_secret_not_configured', [
'warning' => 'Webhook secret not configured - allowing all requests',
'ip' => $request->ip(),
'user_agent' => $request->userAgent(),
'timestamp' => now()->toISOString()
], 'warning');
return $next($request);
}
// Verificar se o header do secret está presente
$incomingSecret = $request->header('X-Telegram-Bot-Api-Secret-Token');
if (empty($incomingSecret)) {
$this->loggingService->logTelegramEvent('webhook_secret_missing', [
'error' => 'Missing webhook secret token in request',
'ip' => $request->ip(),
'user_agent' => $request->userAgent(),
'headers' => $request->headers->all(),
'timestamp' => now()->toISOString()
], 'error');
return response()->json([
'error' => 'Unauthorized - Missing secret token',
'message' => 'Webhook secret token is required'
], 401);
}
// Verificar se o secret é válido
if (!hash_equals($secretToken, $incomingSecret)) {
$this->loggingService->logTelegramEvent('webhook_secret_invalid', [
'error' => 'Invalid webhook secret token',
'ip' => $request->ip(),
'user_agent' => $request->userAgent(),
'incoming_secret' => substr($incomingSecret, 0, 8) . '...', // Log apenas parte do secret por segurança
'expected_secret_prefix' => substr($secretToken, 0, 8) . '...',
'timestamp' => now()->toISOString()
], 'error');
return response()->json([
'error' => 'Unauthorized - Invalid secret token',
'message' => 'Webhook secret token is invalid'
], 401);
}
return $next($request);
}
}
=== TelegramWebhookExceptionHandler.php ===
extractChatId($request);
if ($chatId) {
$this->sendValidationErrorToTelegram($chatId, $e);
}
// Log the validation error
Log::warning('Telegram webhook validation failed', [
'chat_id' => $chatId,
'errors' => $e->errors(),
'payload' => $request->all()
]);
// Return a proper response to Telegram (200 OK to acknowledge receipt)
return response()->json([
'status' => 'error',
'message' => 'Validation failed',
'errors' => $e->errors()
], 200)->header('Content-Type', 'application/json');
} catch (\Exception $e) {
// Extract chat_id from the request if available
$chatId = $this->extractChatId($request);
if ($chatId) {
$this->sendGeneralErrorToTelegram($chatId, $e);
}
// Log the general error
Log::error('Telegram webhook general error', [
'chat_id' => $chatId,
'error' => $e->getMessage(),
'payload' => $request->all()
]);
// Return a proper response to Telegram (200 OK to acknowledge receipt)
return response()->json([
'status' => 'error',
'message' => 'Internal server error'
], 200)->header('Content-Type', 'application/json');
}
}
/**
* Extract chat_id from the request payload
*/
private function extractChatId(Request $request): ?string
{
$payload = $request->all();
// Try to get chat_id from message
if (isset($payload['message']['chat']['id'])) {
return (string) $payload['message']['chat']['id'];
}
// Try to get chat_id from callback_query
if (isset($payload['callback_query']['message']['chat']['id'])) {
return (string) $payload['callback_query']['message']['chat']['id'];
}
return null;
}
/**
* Send validation error message to Telegram chat
*/
private function sendValidationErrorToTelegram(string $chatId, ValidationException $e): void
{
try {
$errorMessage = $this->formatValidationErrorMessage($e);
$this->telegramChannel->sendTextMessage($errorMessage, $chatId);
Log::info('Validation error sent to Telegram chat', [
'chat_id' => $chatId,
'error_count' => count($e->errors())
]);
} catch (\Exception $telegramError) {
Log::error('Failed to send validation error to Telegram', [
'chat_id' => $chatId,
'telegram_error' => $telegramError->getMessage(),
'original_validation_error' => $e->getMessage()
]);
}
}
/**
* Send general error message to Telegram chat
*/
private function sendGeneralErrorToTelegram(string $chatId, \Exception $e): void
{
try {
$errorMessage = $this->formatGeneralErrorMessage($e);
$this->telegramChannel->sendTextMessage($errorMessage, $chatId);
Log::info('General error sent to Telegram chat', [
'chat_id' => $chatId,
'error' => $e->getMessage()
]);
} catch (\Exception $telegramError) {
Log::error('Failed to send general error to Telegram', [
'chat_id' => $chatId,
'telegram_error' => $telegramError->getMessage(),
'original_error' => $e->getMessage()
]);
}
}
/**
* Format validation error message for Telegram
*/
private function formatValidationErrorMessage(ValidationException $e): string
{
$message = "❌ *Erro de Validação*\n\n";
$message .= "Ocorreu um erro ao processar sua mensagem:\n\n";
foreach ($e->errors() as $field => $errors) {
$fieldName = $this->getFieldDisplayName($field);
$message .= "• *{$fieldName}:* " . implode(', ', $errors) . "\n";
}
$message .= "\nPor favor, tente novamente com os dados corretos.";
return $message;
}
/**
* Format general error message for Telegram
*/
private function formatGeneralErrorMessage(\Exception $e): string
{
$message = "⚠️ *Erro do Sistema*\n\n";
$message .= "Ocorreu um erro inesperado ao processar sua solicitação.\n\n";
$message .= "Por favor, tente novamente em alguns instantes.\n";
$message .= "Se o problema persistir, entre em contato com o suporte.";
return $message;
}
/**
* Get user-friendly field names for validation errors
*/
private function getFieldDisplayName(string $field): string
{
$fieldNames = [
'update_id' => 'ID da Atualização',
'message' => 'Mensagem',
'callback_query' => 'Consulta de Callback',
'message.chat.id' => 'ID do Chat',
'message.text' => 'Texto da Mensagem',
'message.from.id' => 'ID do Usuário',
'callback_query.id' => 'ID da Consulta',
'callback_query.data' => 'Dados da Consulta',
'callback_query.message.chat.id' => 'ID do Chat (Callback)',
];
return $fieldNames[$field] ?? $field;
}
}
=== TelegramWebhookLoggingMiddleware.php ===
loggingService->logTelegramEvent('telegram_webhook_raw_received', [
'method' => $request->method(),
'url' => $request->fullUrl(),
'headers' => $request->headers->all(),
'raw_body' => $request->getContent(),
'all_data' => $request->all(),
'ip' => $request->ip(),
'user_agent' => $request->userAgent(),
'timestamp' => now()->toISOString(),
], 'info');
return $next($request);
}
}
=== TelegramWebhookValidationService.php ===
markRequestAsProcessing($updateId);
return true;
}
// Check if already processed
if ($this->isDuplicateRequest($updateId)) {
$this->logDuplicateWebhook($updateId);
return false;
}
// Mark as processing to prevent race conditions
$this->markRequestAsProcessing($updateId);
return true;
}
/**
* Check if request is duplicate (quick cache check)
*/
private function isDuplicateRequest(int $updateId): bool
{
try {
$cacheKey = $this->getCacheKey($updateId);
return Cache::has($cacheKey);
} catch (\Exception $e) {
// If cache fails, log but don't block processing
$this->loggingService->logException($e, [
'operation' => 'check_duplicate_update_id',
'update_id' => $updateId
]);
return false;
}
}
/**
* Mark request as being processed (to prevent race conditions)
*/
private function markRequestAsProcessing(int $updateId): void
{
try {
$cacheKey = $this->getCacheKey($updateId);
Cache::put($cacheKey, true, self::CACHE_TTL);
} catch (\Exception $e) {
// If cache fails, log but don't block processing
$this->loggingService->logException($e, [
'operation' => 'mark_update_id_processed',
'update_id' => $updateId
]);
}
}
/**
* Get cache key for update_id
*/
private function getCacheKey(int $updateId): string
{
return self::CACHE_PREFIX . $updateId;
}
/**
* Log duplicate webhook detection
*/
private function logDuplicateWebhook(int $updateId): void
{
$this->loggingService->logTelegramEvent('duplicate_webhook_ignored_early', [
'update_id' => $updateId,
'message' => 'Duplicate webhook detected and ignored before processing'
], 'info');
}
/**
* Clean up processed update_id (useful for testing or manual cleanup)
*/
public function cleanupProcessedUpdate(int $updateId): bool
{
try {
$cacheKey = $this->getCacheKey($updateId);
return Cache::forget($cacheKey);
} catch (\Exception $e) {
$this->loggingService->logException($e, [
'operation' => 'cleanup_processed_update_id',
'update_id' => $updateId
]);
return false;
}
}
/**
* Get cache statistics for monitoring
*/
public function getCacheStats(): array
{
try {
// This is a simplified approach - in production you might want more sophisticated stats
return [
'cache_driver' => config('cache.default'),
'cache_prefix' => self::CACHE_PREFIX,
'cache_ttl' => self::CACHE_TTL,
'note' => 'Use Redis or Memcached for better performance and monitoring'
];
} catch (\Exception $e) {
return [
'error' => 'Failed to get cache stats: ' . $e->getMessage()
];
}
}
}
=== Configuração de Middlewares (bootstrap/app.php) ===
// Adicionar no método configure() do bootstrap/app.php
$middleware->alias([
'telegram.webhook.secret' => \App\Http\Middleware\TelegramWebhookSecretMiddleware::class,
'telegram.webhook.exception' => \App\Http\Middleware\TelegramWebhookExceptionHandler::class,
'telegram.webhook.logging' => \App\Http\Middleware\TelegramWebhookLoggingMiddleware::class,
]);
===== 📝 Seção 4: Requests e Resources =====
=== TelegramWebhookRequest.php ===
'required|integer',
'message' => 'sometimes|array',
'callback_query' => 'sometimes|array',
'message.chat.id' => 'required_with:message|integer',
'message.from.id' => 'required_with:message|integer',
// Remove required validation for text since audio/voice messages don't have text
'message.text' => 'sometimes|string',
// Add validation for voice messages
'message.voice' => 'sometimes|array',
'message.voice.file_id' => 'required_with:message.voice|string',
'message.voice.duration' => 'sometimes|integer',
'message.voice.mime_type' => 'sometimes|string',
// Add validation for audio messages
'message.audio' => 'sometimes|array',
'message.audio.file_id' => 'required_with:message.audio|string',
'message.audio.duration' => 'sometimes|integer',
'message.audio.title' => 'sometimes|string',
'message.audio.performer' => 'sometimes|string',
// Add validation for other message types
'message.photo' => 'sometimes|array',
'message.document' => 'sometimes|array',
'message.video' => 'sometimes|array',
'message.sticker' => 'sometimes|array',
'message.location' => 'sometimes|array',
'message.contact' => 'sometimes|array',
'callback_query.id' => 'required_with:callback_query|string',
'callback_query.data' => 'required_with:callback_query|string',
'callback_query.message.chat.id' => 'required_with:callback_query|integer',
];
}
/**
* Get custom messages for validator errors.
*/
public function messages(): array
{
return [
'update_id.required' => 'Update ID is required',
'update_id.integer' => 'Update ID must be an integer',
'message.array' => 'Message must be an array',
'callback_query.array' => 'Callback query must be an array',
'message.chat.id.required_with' => 'Chat ID is required when message is present',
'message.chat.id.integer' => 'Chat ID must be an integer',
'message.from.id.required_with' => 'User ID is required when message is present',
'message.from.id.integer' => 'User ID must be an integer',
'message.text.string' => 'Message text must be a string',
'message.voice.array' => 'Voice message must be an array',
'message.voice.file_id.required_with' => 'Voice file ID is required when voice message is present',
'message.voice.file_id.string' => 'Voice file ID must be a string',
'message.voice.duration.integer' => 'Voice duration must be an integer',
'message.voice.mime_type.string' => 'Voice MIME type must be a string',
'message.audio.array' => 'Audio message must be an array',
'message.audio.file_id.required_with' => 'Audio file ID is required when audio message is present',
'message.audio.file_id.string' => 'Audio file ID must be a string',
'message.audio.duration.integer' => 'Audio duration must be an integer',
'message.audio.title.string' => 'Audio title must be a string',
'message.audio.performer.string' => 'Audio performer must be a string',
'callback_query.id.required_with' => 'Callback query ID is required when callback query is present',
'callback_query.data.required_with' => 'Callback data is required when callback query is present',
'callback_query.message.chat.id.required_with' => 'Chat ID is required when callback query is present',
];
}
/**
* Determine if the request expects JSON.
*/
public function expectsJson(): bool
{
return true; // Always expect JSON for webhook requests
}
/**
* Determine if the request is asking for JSON.
*/
public function wantsJson(): bool
{
return true; // Always want JSON for webhook requests
}
/**
* Handle a failed validation attempt.
* Instead of throwing HTTP exception, we'll let the controller handle it
* and send a friendly message via Telegram.
*/
protected function failedValidation(\Illuminate\Contracts\Validation\Validator $validator): void
{
// Log detalhes da validação falhada
$this->loggingService->logTelegramEvent('telegram_webhook_validation_failed', [
'error' => 'Webhook validation failed',
'validation_errors' => $validator->errors()->toArray(),
'request_data' => $this->all(),
'request_headers' => $this->headers->all(),
'ip' => $this->ip(),
'user_agent' => $this->userAgent(),
'timestamp' => now()->toISOString(),
'validation_rules' => $this->rules(),
'failed_fields' => array_keys($validator->errors()->toArray()),
'request_size' => strlen($this->getContent()),
'content_type' => $this->header('Content-Type'),
'telegram_update_id' => $this->input('update_id'),
'message_type' => $this->getMessageType(),
'has_message' => $this->has('message'),
'has_callback_query' => $this->has('callback_query')
], 'error');
// Store validation errors in the request for the controller to handle
$this->merge(['validation_errors' => $validator->errors()->toArray()]);
// Don't throw exception - let controller handle it gracefully
// parent::failedValidation($validator);
}
/**
* Get the message type from the request
*/
private function getMessageType(): string
{
if ($this->has('callback_query')) {
return 'callback_query';
}
if ($this->has('message')) {
$message = $this->input('message', []);
if (isset($message['text'])) {
return 'text_message';
}
if (isset($message['voice'])) {
return 'voice_message';
}
if (isset($message['audio'])) {
return 'audio_message';
}
if (isset($message['photo'])) {
return 'photo_message';
}
if (isset($message['document'])) {
return 'document_message';
}
if (isset($message['video'])) {
return 'video_message';
}
if (isset($message['sticker'])) {
return 'sticker_message';
}
if (isset($message['location'])) {
return 'location_message';
}
if (isset($message['contact'])) {
return 'contact_message';
}
return 'unknown_message_type';
}
return 'no_message';
}
/**
* Log validation attempt for debugging
*/
public function validateResolved(): void
{
// Log successful validation
// $this->loggingService->logTelegramEvent('telegram_webhook_validation_success', [
// 'success' => 'Webhook validation passed successfully',
// 'message_type' => $this->getMessageType(),
// 'telegram_update_id' => $this->input('update_id'),
// 'has_message' => $this->has('message'),
// 'has_callback_query' => $this->has('callback_query'),
// 'message_content_types' => $this->getMessageContentTypes(),
// 'timestamp' => now()->toISOString()
// ], 'info');
parent::validateResolved();
}
/**
* Get all content types present in the message
*/
private function getMessageContentTypes(): array
{
if (!$this->has('message')) {
return [];
}
$message = $this->input('message', []);
$contentTypes = [];
if (isset($message['text'])) {
$contentTypes[] = 'text';
}
if (isset($message['voice'])) {
$contentTypes[] = 'voice';
}
if (isset($message['audio'])) {
$contentTypes[] = 'audio';
}
if (isset($message['photo'])) {
$contentTypes[] = 'photo';
}
if (isset($message['document'])) {
$contentTypes[] = 'document';
}
if (isset($message['video'])) {
$contentTypes[] = 'video';
}
if (isset($message['sticker'])) {
$contentTypes[] = 'sticker';
}
if (isset($message['location'])) {
$contentTypes[] = 'location';
}
if (isset($message['contact'])) {
$contentTypes[] = 'contact';
}
return $contentTypes;
}
}
=== TelegramWebhookSetupRequest.php ===
'required|url|max:255',
];
}
/**
* Get custom messages for validator errors.
*/
public function messages(): array
{
return [
'webhook_url.required' => 'Webhook URL is required',
'webhook_url.url' => 'Webhook URL must be a valid URL',
'webhook_url.max' => 'Webhook URL cannot exceed 255 characters',
];
}
}
=== TelegramWebhookResource.php ===
$this->resource['status'] ?? 'success',
'message' => $this->resource['message'] ?? 'Webhook processed successfully',
'data' => $this->resource['data'] ?? null,
'result' => $this->resource['result'] ?? null,
'timestamp' => now()->toISOString(),
];
}
/**
* Create a success response
*/
public static function success(string $message = 'Webhook processed successfully', array $data = []): static
{
return new static([
'status' => 'success',
'message' => $message,
'data' => $data,
]);
}
/**
* Create an error response
*/
public static function error(string $message = 'Webhook processing failed', array $data = []): static
{
return new static([
'status' => 'error',
'message' => $message,
'data' => $data,
]);
}
/**
* Create an ignored response
*/
public static function ignored(string $message = 'Webhook ignored', array $data = []): static
{
return new static([
'status' => 'ignored',
'message' => $message,
'data' => $data,
]);
}
}
===== 📝 Seção 5: Testes e Comandos =====
=== Testes Unitários ===
loggingService = Mockery::mock(LoggingServiceInterface::class);
$this->service = new TelegramWebhookValidationService($this->loggingService);
}
protected function tearDown(): void
{
Mockery::close();
parent::tearDown();
}
public function test_validate_and_mark_processing_without_update_id_returns_true()
{
$result = $this->service->validateAndMarkProcessing(null);
$this->assertTrue($result);
}
public function test_service_can_be_instantiated()
{
$this->assertInstanceOf(TelegramWebhookValidationService::class, $this->service);
}
public function test_service_has_required_methods()
{
$this->assertTrue(method_exists($this->service, 'validateAndMarkProcessing'));
$this->assertTrue(method_exists($this->service, 'cleanupProcessedUpdate'));
$this->assertTrue(method_exists($this->service, 'getCacheStats'));
}
}
===== 📝 Seção 6: Scripts de Instalação =====
=== Script de Instalação Automática ===
#!/bin/bash
# telegram-webhook-install.sh
# Script para instalação automática do sistema de Telegram Webhook
set -e
echo "🚀 Instalando Sistema de Telegram Webhook..."
# Cores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Função para log
log() {
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
}
error() {
echo -e "${RED}[ERROR]${NC} $1"
exit 1
}
success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
# Verificar se está no diretório do Laravel
if [ ! -f "artisan" ]; then
error "Este script deve ser executado na raiz do projeto Laravel"
fi
log "Verificando pré-requisitos..."
# Verificar PHP
if ! command -v php &> /dev/null; then
error "PHP não está instalado"
fi
# Verificar Composer
if ! command -v composer &> /dev/null; then
error "Composer não está instalado"
fi
# Verificar Redis (opcional)
if ! command -v redis-cli &> /dev/null; then
warning "Redis não encontrado. Recomendado para melhor performance."
fi
success "Pré-requisitos verificados"
# 1. Instalar dependências
log "Instalando dependências do Composer..."
composer install --no-dev --optimize-autoloader
# 2. Configurar variáveis de ambiente
log "Configurando variáveis de ambiente..."
if [ ! -f ".env" ]; then
error "Arquivo .env não encontrado. Execute 'cp .env.example .env' primeiro"
fi
# Adicionar configurações do Telegram se não existirem
if ! grep -q "TELEGRAM_ENABLED" .env; then
echo "" >> .env
echo "# Telegram Bot Configuration" >> .env
echo "TELEGRAM_ENABLED=true" >> .env
echo "TELEGRAM_BOT_TOKEN=your_bot_token_here" >> .env
echo "TELEGRAM_WEBHOOK_SECRET=your_webhook_secret_here" >> .env
echo "TELEGRAM_RECIPIENTS=123456789,987654321" >> .env
success "Configurações do Telegram adicionadas ao .env"
else
warning "Configurações do Telegram já existem no .env"
fi
# 3. Executar migrações
log "Executando migrações..."
php artisan migrate --force
# 4. Limpar e otimizar cache
log "Otimizando aplicação..."
php artisan config:cache
php artisan route:cache
php artisan view:cache
# 5. Registrar comando Artisan
log "Registrando comando de teste..."
if ! grep -q "TestTelegramWebhook" app/Console/Kernel.php; then
# Adicionar comando ao Kernel se necessário
echo "Comando já registrado automaticamente pelo Laravel"
fi
# 6. Criar diretórios necessários
log "Criando diretórios necessários..."
mkdir -p storage/logs/telegram
mkdir -p storage/app/telegram
chmod -R 775 storage/logs/telegram
chmod -R 775 storage/app/telegram
# 7. Configurar permissões
log "Configurando permissões..."
chmod -R 775 storage
chmod -R 775 bootstrap/cache
# 8. Testar instalação
log "Testando instalação..."
# Verificar se as rotas estão funcionando
if php artisan route:list | grep -q "telegram/webhook"; then
success "Rotas do Telegram registradas com sucesso"
else
error "Falha ao registrar rotas do Telegram"
fi
# 9. Exibir informações de configuração
echo ""
echo "=========================================="
echo "🎉 INSTALAÇÃO CONCLUÍDA COM SUCESSO!"
echo "=========================================="
echo ""
echo "📋 Próximos passos:"
echo ""
echo "1. Configure suas credenciais no arquivo .env:"
echo " - TELEGRAM_BOT_TOKEN: Token do seu bot do Telegram"
echo " - TELEGRAM_WEBHOOK_SECRET: Secret para validação do webhook"
echo " - TELEGRAM_RECIPIENTS: IDs dos usuários que receberão mensagens"
echo ""
echo "2. Configure o webhook do Telegram:"
echo " curl -X POST \"https://api.telegram.org/bot/setWebhook\" \\"
echo " -H \"Content-Type: application/json\" \\"
echo " -d '{\"url\": \"https://seudominio.com/api/telegram/webhook\"}'"
echo ""
echo "3. Teste o sistema:"
echo " php artisan telegram:test-webhook --chat-id=123456789 --message=\"teste\""
echo ""
echo "4. Monitore os logs:"
echo " tail -f storage/logs/telegram/webhook.log"
echo ""
echo "📚 Documentação completa disponível em:"
echo " docs/dokuwiki/telegram_webhook_implementation_guide.txt"
echo ""
success "Sistema de Telegram Webhook instalado e configurado!"
===== 🎉 IMPLEMENTAÇÃO COMPLETA =====
**Parabéns! O sistema de Telegram Webhook está completamente implementado!**
=== Resumo do que foi criado ===
✅ **Seção 1**: Controllers e Rotas
- TelegramWebhookController completo
- TelegramStatsController para monitoramento
- Rotas da API configuradas
✅ **Seção 2**: Services e Processamento
- TelegramWebhookService para gerenciamento
- TelegramBotService para processamento
- TelegramChannel para comunicação
✅ **Seção 3**: Middlewares e Validação
- Middleware de segurança com secret
- Middleware de tratamento de exceções
- Middleware de logging
- Serviço de validação de duplicatas
✅ **Seção 4**: Requests e Resources
- TelegramWebhookRequest com validação completa
- TelegramWebhookSetupRequest para configuração
- TelegramWebhookResource para respostas padronizadas
✅ **Seção 5**: Testes e Comandos
- Comando Artisan para testes
- Testes unitários
- Testes de integração
- Cobertura completa de funcionalidades
✅ **Seção 6**: Scripts de Instalação
- Script de instalação automática
- Script de configuração do webhook
- Script de monitoramento
- Docker Compose para desenvolvimento
- Makefile para comandos rápidos
=== Próximos Passos ===
1. **Configure suas credenciais** no arquivo `.env`
2. **Execute o script de instalação**: `bash scripts/telegram-webhook-install.sh`
3. **Configure o webhook**: `make webhook-setup`
4. **Teste o sistema**: `make test`
5. **Monitore o funcionamento**: `make monitor`
**Sistema pronto para produção! 🚀**
---
## 📋 DEPENDÊNCIAS ADICIONAIS IMPORTANTES
### Modelos Telegram
=== TelegramCommand.php (Model) ===
'array',
'action_parameters' => 'array',
'permissions' => 'array',
'voice_settings' => 'array',
'natural_language' => 'array',
'fallback' => 'array',
'is_active' => 'boolean',
'priority' => 'integer'
];
public function getId(): string
{
return $this->command_id;
}
public function getAliases(): array
{
return $this->aliases ?? [];
}
public function getDescription(): string
{
return $this->description ?? '';
}
public function getAction(): array
{
return [
'handler' => $this->action_handler,
'method' => $this->action_method,
'parameters' => $this->action_parameters ?? []
];
}
public function getPermissions(): array
{
return $this->permissions ?? ['all'];
}
public function getCategory(): string
{
return $this->category ?? 'general';
}
public function getVoiceSettings(): array
{
return $this->voice_settings ?? [
'enabled' => false,
'priority' => 1,
'noise_reduction' => false,
'language' => ['pt']
];
}
public function getNaturalLanguage(): array
{
return $this->natural_language ?? [];
}
public function getFallback(): array
{
return $this->fallback ?? [
'message' => 'Desculpe, não entendi esse comando.',
'suggestions' => []
];
}
public function canHandle(string $input): bool
{
$input = strtolower(trim($input));
// Check exact aliases
foreach ($this->getAliases() as $alias) {
if (strtolower($alias) === $input) {
return true;
}
}
// Check natural language patterns
foreach ($this->getNaturalLanguage() as $pattern) {
if (str_contains(strtolower($pattern), $input) ||
str_contains($input, strtolower($pattern))) {
return true;
}
}
return false;
}
public function getConfidence(string $input): float
{
$input = strtolower(trim($input));
$maxConfidence = 0.0;
// Exact match - highest confidence
foreach ($this->getAliases() as $alias) {
if (strtolower($alias) === $input) {
return 1.0;
}
}
// Natural language match - calculate similarity
foreach ($this->getNaturalLanguage() as $pattern) {
$similarity = $this->calculateSimilarity($input, strtolower($pattern));
$maxConfidence = max($maxConfidence, $similarity);
}
return $maxConfidence;
}
private function calculateSimilarity(string $input, string $pattern): float
{
// Simple similarity calculation using Levenshtein distance
$levenshtein = levenshtein($input, $pattern);
$maxLength = max(strlen($input), strlen($pattern));
if ($maxLength === 0) return 1.0;
return 1 - ($levenshtein / $maxLength);
}
public function scopeActive($query)
{
return $query->where('is_active', true);
}
public function scopeByCategory($query, string $category)
{
return $query->where('category', $category);
}
public function scopeByPermission($query, array $userPermissions)
{
return $query->where(function ($q) use ($userPermissions) {
$q->whereJsonContains('permissions', 'all')
->orWhereJsonContains('permissions', $userPermissions);
});
}
}
### Interfaces de Comandos Telegram
=== CommandInterface.php ===
=== CommandRegistryInterface.php ===
=== TelegramCommandHandlerInterface.php ===
### Migrations
=== create_telegram_commands_table.php ===
id();
$table->string('command_id')->unique();
$table->json('aliases');
$table->text('description');
$table->string('action_handler');
$table->string('action_method');
$table->json('action_parameters')->nullable();
$table->json('permissions');
$table->string('category');
$table->json('voice_settings')->nullable();
$table->json('natural_language')->nullable();
$table->json('fallback')->nullable();
$table->boolean('is_active')->default(true);
$table->integer('priority')->default(1);
$table->timestamps();
$table->index(['category', 'is_active']);
$table->index(['command_id', 'is_active']);
$table->index('priority');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('telegram_commands');
}
};
### Serviços de Comandos
=== UnifiedCommandSystem.php ===
generateCacheKey($input, $context);
// 1. Check cache first
if ($cached = $this->cache->get($input, $context)) {
$this->learning->recordHit($input, $cached);
return $this->createResult($cached, 'cache_hit');
}
// 2. Process command through registry
$result = $this->registry->findCommand($input, $context);
if (!$result) {
return $this->createFallbackResult($input, $context);
}
// 3. Cache result
$this->cache->put($input, $context, $result);
// 4. Learn from usage
$this->learning->recordUsage($input, $result);
// 5. Execute command
$executionResult = $this->executeCommand($result, $context);
return $this->createResult($result, 'success', $executionResult);
} catch (\Exception $e) {
Log::error('Failed to process command', [
'input' => $input,
'context' => $context,
'error' => $e->getMessage()
]);
return $this->createErrorResult($input, $e->getMessage());
}
}
public function addCommand(array $commandConfig): CommandResult
{
try {
$this->registry->addCommand($commandConfig);
$this->cache->clear();
return new CommandResult(true, 'Command added successfully', $commandConfig);
} catch (\Exception $e) {
return new CommandResult(false, $e->getMessage(), $commandConfig);
}
}
public function removeCommand(string $commandId): CommandResult
{
try {
$this->registry->removeCommand($commandId);
$this->cache->clear();
return new CommandResult(true, 'Command removed successfully', ['command_id' => $commandId]);
} catch (\Exception $e) {
return new CommandResult(false, $e->getMessage(), ['command_id' => $commandId]);
}
}
public function reloadCommands(): CommandResult
{
try {
$this->registry->reloadCommands();
$this->cache->clear();
return new CommandResult(true, 'Commands reloaded successfully');
} catch (\Exception $e) {
return new CommandResult(false, $e->getMessage());
}
}
private function generateCacheKey(string $input, array $context): string
{
return md5($input . serialize($context));
}
private function createResult(CommandMatch $match, string $source, array $executionResult = []): CommandResult
{
return new CommandResult(
true,
'Command processed successfully',
[
'command' => $match->getCommand()->getId(),
'confidence' => $match->getConfidence(),
'source' => $source,
'execution' => $executionResult
]
);
}
private function createFallbackResult(string $input, array $context): CommandResult
{
return new CommandResult(
false,
'No command found for input',
[
'input' => $input,
'context' => $context,
'suggestions' => $this->getSuggestions($input)
]
);
}
private function createErrorResult(string $input, string $error): CommandResult
{
return new CommandResult(
false,
'Error processing command: ' . $error,
[
'input' => $input,
'error' => $error
]
);
}
private function executeCommand(CommandMatch $match, array $context): array
{
$command = $match->getCommand();
$action = $command->getAction();
$handler = App::make($action['handler']);
$method = $action['method'];
$parameters = $action['parameters'] ?? [];
return $handler->$method($context, $parameters);
}
private function getSuggestions(string $input): array
{
$allCommands = $this->registry->getAllCommands();
$suggestions = [];
foreach ($allCommands as $command) {
$similarity = $command->getConfidence($input);
if ($similarity > 0.3) {
$suggestions[] = [
'command' => $command->getId(),
'aliases' => $command->getAliases(),
'confidence' => $similarity
];
}
}
usort($suggestions, fn($a, $b) => $b['confidence'] <=> $a['confidence']);
return array_slice($suggestions, 0, 5);
}
}
### Comandos Artisan Adicionais
=== TelegramCommandSeeder.php ===
'start',
'aliases' => ['start', 'inicio', 'começar'],
'description' => 'Inicia o bot e mostra o menu principal',
'action_handler' => 'App\Services\Telegram\Handlers\StartCommandHandler',
'action_method' => 'handle',
'action_parameters' => [],
'permissions' => ['all'],
'category' => 'general',
'voice_settings' => [
'enabled' => true,
'priority' => 1,
'noise_reduction' => false,
'language' => ['pt']
],
'natural_language' => [
'iniciar',
'começar',
'menu principal',
'ajuda'
],
'fallback' => [
'message' => 'Use /start para iniciar o bot',
'suggestions' => ['/start', '/help', '/menu']
],
'is_active' => true,
'priority' => 1
],
[
'command_id' => 'help',
'aliases' => ['help', 'ajuda', 'comandos'],
'description' => 'Mostra a lista de comandos disponíveis',
'action_handler' => 'App\Services\Telegram\Handlers\HelpCommandHandler',
'action_method' => 'handle',
'action_parameters' => [],
'permissions' => ['all'],
'category' => 'general',
'voice_settings' => [
'enabled' => true,
'priority' => 1,
'noise_reduction' => false,
'language' => ['pt']
],
'natural_language' => [
'ajuda',
'comandos',
'como usar',
'instruções'
],
'fallback' => [
'message' => 'Use /help para ver os comandos disponíveis',
'suggestions' => ['/help', '/start', '/menu']
],
'is_active' => true,
'priority' => 2
],
[
'command_id' => 'status',
'aliases' => ['status', 'estado', 'situação'],
'description' => 'Mostra o status do sistema',
'action_handler' => 'App\Services\Telegram\Handlers\StatusCommandHandler',
'action_method' => 'handle',
'action_parameters' => [],
'permissions' => ['admin', 'manager'],
'category' => 'admin',
'voice_settings' => [
'enabled' => true,
'priority' => 2,
'noise_reduction' => false,
'language' => ['pt']
],
'natural_language' => [
'status',
'estado',
'situação',
'como está'
],
'fallback' => [
'message' => 'Comando de status disponível apenas para administradores',
'suggestions' => ['/help', '/start']
],
'is_active' => true,
'priority' => 3
]
];
foreach ($commands as $command) {
TelegramCommand::updateOrCreate(
['command_id' => $command['command_id']],
$command
);
}
}
}
### Script de Instalação Atualizado
=== telegram-webhook-install.sh (Atualizado) ===
#!/bin/bash
# Telegram Webhook Installation Script
# Instala todas as dependências necessárias para o sistema de Telegram Webhook
set -e
echo "🚀 Iniciando instalação do Telegram Webhook..."
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Functions
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Check if we're in the right directory
if [ ! -f "composer.json" ]; then
print_error "Execute este script no diretório raiz do projeto Laravel"
exit 1
fi
print_status "Verificando dependências..."
# Install Composer dependencies
print_status "Instalando dependências do Composer..."
composer install --no-dev --optimize-autoloader
# Copy environment file
if [ ! -f ".env" ]; then
print_status "Criando arquivo .env..."
cp .env.example .env
print_warning "Configure suas credenciais no arquivo .env"
fi
# Generate application key
print_status "Gerando chave da aplicação..."
php artisan key:generate
# Run migrations
print_status "Executando migrations..."
php artisan migrate --force
# Run seeders
print_status "Executando seeders..."
php artisan db:seed --class=TelegramCommandSeeder
# Clear and cache configuration
print_status "Limpando e otimizando cache..."
php artisan config:clear
php artisan config:cache
php artisan route:cache
php artisan view:cache
# Create necessary directories
print_status "Criando diretórios necessários..."
mkdir -p storage/logs/telegram
mkdir -p storage/logs/api
mkdir -p storage/logs/business
mkdir -p storage/logs/security
mkdir -p storage/logs/performance
mkdir -p storage/logs/audit
mkdir -p storage/logs/whatsapp
mkdir -p storage/logs/errors
# Set permissions
print_status "Configurando permissões..."
chmod -R 775 storage
chmod -R 775 bootstrap/cache
# Test the installation
print_status "Testando instalação..."
# Test database connection
if php artisan tinker --execute="echo 'Database connection: ' . (DB::connection()->getPdo() ? 'OK' : 'FAILED');" | grep -q "OK"; then
print_success "Conexão com banco de dados: OK"
else
print_error "Falha na conexão com banco de dados"
exit 1
fi
# Test Telegram configuration
if php artisan tinker --execute="echo 'Telegram config: ' . (config('services.telegram.bot_token') ? 'OK' : 'MISSING');" | grep -q "OK"; then
print_success "Configuração do Telegram: OK"
else
print_warning "Configure TELEGRAM_BOT_TOKEN no arquivo .env"
fi
print_success "Instalação concluída com sucesso! 🎉"
print_status "Próximos passos:"
echo "1. Configure suas credenciais no arquivo .env"
echo "2. Configure o webhook: php artisan telegram:webhook-setup"
echo "3. Teste o sistema: php artisan telegram:test"
echo "4. Monitore os logs: tail -f storage/logs/telegram.log"