netgescon-day0/app/Http/Controllers/Api/CommunicationWebhookController.php

213 lines
9.2 KiB
PHP

<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\ChiamataPostIt;
use App\Models\CommunicationMessage;
use App\Models\InsuranceClaim;
use App\Models\SystemSetting;
use App\Models\Ticket;
use App\Models\TicketMessage;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
class CommunicationWebhookController extends Controller
{
public function sendTelegramTest(Request $request): JsonResponse
{
$validated = $request->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);
}
}