213 lines
9.2 KiB
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);
|
|
}
|
|
}
|