validate([ 'chat_id' => ['required', 'string'], 'text' => ['required', 'string', 'max:4000'], ]); $botToken = (string) SystemSetting::getValue('telegram_bot_token', ''); if ($botToken === '') { return response()->json(['ok' => false, 'error' => 'telegram_bot_token_not_configured'], 422); } $response = Http::post("https://api.telegram.org/bot{$botToken}/sendMessage", [ 'chat_id' => $validated['chat_id'], 'text' => $validated['text'], ]); return response()->json([ 'ok' => $response->ok(), 'status' => $response->status(), 'response' => $response->json(), ], $response->ok() ? 200 : 422); } public function sendWhatsappTest(Request $request): JsonResponse { $validated = $request->validate([ 'to' => ['required', 'string'], 'text' => ['required', 'string', 'max:4000'], ]); $phoneNumberId = (string) SystemSetting::getValue('whatsapp_phone_number_id', ''); $accessToken = (string) SystemSetting::getValue('whatsapp_access_token', ''); if ($phoneNumberId === '' || $accessToken === '') { return response()->json(['ok' => false, 'error' => 'whatsapp_credentials_not_configured'], 422); } $response = Http::withToken($accessToken) ->post("https://graph.facebook.com/v22.0/{$phoneNumberId}/messages", [ 'messaging_product' => 'whatsapp', 'to' => $validated['to'], 'type' => 'text', 'text' => [ 'preview_url' => false, 'body' => $validated['text'], ], ]); return response()->json([ 'ok' => $response->ok(), 'status' => $response->status(), 'response' => $response->json(), ], $response->ok() ? 200 : 422); } public function telegram(Request $request): JsonResponse { if (! $this->authorizeWebhook($request, 'telegram_webhook_token')) { return response()->json(['ok' => false, 'error' => 'unauthorized'], 401); } $payload = $request->all(); $message = data_get($payload, 'message', []); return $this->storeIncoming([ 'channel' => 'telegram', 'external_chat_id' => (string) data_get($message, 'chat.id', ''), 'external_message_id' => (string) data_get($message, 'message_id', ''), 'sender_name' => trim((string) data_get($message, 'from.first_name', '') . ' ' . (string) data_get($message, 'from.last_name', '')), 'phone_number' => null, 'message_text' => (string) data_get($message, 'text', ''), 'attachments' => null, 'received_at' => now(), 'metadata' => ['raw' => $payload], ], $request); } public function whatsapp(Request $request): JsonResponse { if (! $this->authorizeWebhook($request, 'whatsapp_webhook_token')) { return response()->json(['ok' => false, 'error' => 'unauthorized'], 401); } $payload = $request->all(); $entry = data_get($payload, 'entry.0.changes.0.value', []); $message = data_get($entry, 'messages.0', []); $contact = data_get($entry, 'contacts.0', []); $attachments = []; foreach (['image', 'document', 'video', 'audio'] as $type) { if (data_get($message, $type)) { $attachments[] = [ 'type' => $type, 'id' => (string) data_get($message, $type . '.id', ''), 'mime_type' => (string) data_get($message, $type . '.mime_type', ''), 'caption' => (string) data_get($message, $type . '.caption', ''), ]; } } return $this->storeIncoming([ 'channel' => 'whatsapp', 'external_chat_id' => (string) data_get($message, 'from', ''), 'external_message_id' => (string) data_get($message, 'id', ''), 'sender_name' => (string) data_get($contact, 'profile.name', ''), 'phone_number' => (string) data_get($message, 'from', ''), 'message_text' => (string) data_get($message, 'text.body', ''), 'attachments' => $attachments, 'received_at' => now(), 'metadata' => ['raw' => $payload], ], $request); } private function storeIncoming(array $data, Request $request): JsonResponse { $handlingMode = (string) $request->query('mode', 'log_only'); $ticketId = (int) $request->query('ticket_id', 0); $postItId = (int) $request->query('post_it_id', 0); $insuranceClaimId = (int) $request->query('insurance_claim_id', 0); $ticket = $ticketId > 0 ? Ticket::query()->find($ticketId) : null; $postIt = $postItId > 0 ? ChiamataPostIt::query()->find($postItId) : null; $insuranceClaim = $insuranceClaimId > 0 ? InsuranceClaim::query()->find($insuranceClaimId) : null; if ($handlingMode === 'auto_post_it' && ! $ticket && ! $postIt) { $postIt = ChiamataPostIt::query()->create([ 'telefono' => $data['phone_number'] ?? null, 'nome_chiamante' => $data['sender_name'] ?: 'Contatto esterno', 'oggetto' => strtoupper((string) $data['channel']) . ' messaggio in ingresso', 'nota' => (string) ($data['message_text'] ?? ''), 'stato' => 'post_it', 'priorita' => 'Media', 'chiamata_il' => now(), ]); } $record = CommunicationMessage::query()->create([ 'channel' => $data['channel'], 'direction' => 'inbound', 'external_chat_id' => $data['external_chat_id'] ?: null, 'external_message_id' => $data['external_message_id'] ?: null, 'phone_number' => $data['phone_number'] ?: null, 'sender_name' => $data['sender_name'] ?: null, 'message_text' => $data['message_text'] ?: null, 'attachments' => $data['attachments'], 'ticket_id' => $ticket?->id, 'post_it_id' => $postIt?->id, 'insurance_claim_id' => $insuranceClaim?->id, 'status' => 'received', 'received_at' => $data['received_at'], 'metadata' => array_merge((array) $data['metadata'], ['handling_mode' => $handlingMode]), ]); if ($ticket) { TicketMessage::query()->create([ 'ticket_id' => (int) $ticket->id, 'user_id' => null, 'messaggio' => (string) ($data['message_text'] ?: 'Messaggio in ingresso da canale esterno.'), 'canale' => (string) $data['channel'], 'direzione' => 'inbound', 'external_message_id' => (string) ($data['external_message_id'] ?: null), 'inviato_il' => $data['received_at'], 'metadata' => ['communication_message_id' => $record->id], ]); } if ($postIt && ! $ticket) { $existing = trim((string) ($postIt->nota ?? '')); $line = '[' . now()->format('d/m/Y H:i') . '] ' . strtoupper((string) $data['channel']) . ': ' . (string) ($data['message_text'] ?: '(allegato)'); $postIt->nota = $existing !== '' ? ($existing . "\n" . $line) : $line; $postIt->save(); } return response()->json([ 'ok' => true, 'protocol' => $record->protocol_number, 'communication_message_id' => $record->id, 'ticket_id' => $ticket?->id, 'post_it_id' => $postIt?->id, 'insurance_claim_id' => $insuranceClaim?->id, ]); } private function authorizeWebhook(Request $request, string $settingKey): bool { $expected = (string) SystemSetting::getValue($settingKey, ''); if ($expected === '') { return true; } $provided = (string) ($request->header('X-Netgescon-Webhook-Token') ?: $request->query('token', '')); return $provided !== '' && hash_equals($expected, $provided); } }