<?php
 namespace App\Http\Controllers\Webhook; use App\Http\Controllers\Controller; use App\Models\Admin; use App\Models\Setting; use App\Models\Ticket; use App\Models\TicketReply; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; class SlackWebhookController extends Controller { public function handle(Request $request): JsonResponse { if (!$this->verifySignature($request)) { Log::warning('Slack webhook: Invalid signature'); return response()->json(['error' => 'Invalid signature'], 403); } $payload = $request->all(); if (($payload['type'] ?? '') === 'url_verification') { return response()->json(['challenge' => $payload['challenge']]); } if (($payload['type'] ?? '') === 'event_callback') { $event = $payload['event'] ?? []; $eventType = $event['type'] ?? ''; if ($eventType === 'message' && !isset($event['bot_id']) && !isset($event['subtype'])) { $this->handleMessage($event); } } return response()->json(['ok' => true]); } private function handleMessage(array $event): void { $channelId = $event['channel'] ?? ''; $slackUserId = $event['user'] ?? ''; $text = $event['text'] ?? ''; $threadTs = $event['thread_ts'] ?? null; if (empty($channelId) || empty($text)) { return; } $ticket = Ticket::where('slack_channel_id', $channelId)->first(); if (!$ticket) { return; } $admin = Admin::where('slack_user_id', $slackUserId)->first(); if (!$admin) { $admin = $this->findAdminBySlackUser($slackUserId); } if (!$admin) { Log::info('Slack webhook: Unknown user, ignoring', ['slack_user_id' => $slackUserId, 'channel' => $channelId]); return; } if (!$ticket->isAccessibleBy($admin)) { Log::warning('Slack webhook: Admin lacks department access', ['admin' => $admin->id, 'ticket' => $ticket->id]); return; } $reply = TicketReply::create([ 'ticket_id' => $ticket->id, 'admin_id' => $admin->id, 'mail' => $admin->email, 'message' => $text . "\n\n_[Slack üzerinden yanıtlandı]_", ]); $ticket->update(['last_admin_reply_at' => now()]); \App\Events\TicketRepliedByAdmin::dispatch($ticket, $reply, $admin); Log::info('Slack webhook: Reply created', [ 'ticket_id' => $ticket->id, 'reply_id' => $reply->id, 'admin_id' => $admin->id, 'source' => 'slack', ]); } private function findAdminBySlackUser(string $slackUserId): ?Admin { $botToken = Setting::get('slack_bot_token', ''); if (empty($botToken)) { return null; } try { $response = \Illuminate\Support\Facades\Http::withToken($botToken) ->get('https://slack.com/api/users.info', ['user' => $slackUserId]); $data = $response->json(); if (!($data['ok'] ?? false)) { return null; } $email = $data['user']['profile']['email'] ?? null; if (!$email) { return null; } $admin = Admin::where('email', $email)->first(); if ($admin && !$admin->slack_user_id) { $admin->update(['slack_user_id' => $slackUserId]); } return $admin; } catch (\Exception $e) { Log::error('Slack user lookup failed', ['error' => $e->getMessage()]); return null; } } private function verifySignature(Request $request): bool { $signingSecret = Setting::get('slack_signing_secret', config('ticket-integrations.slack.signing_secret', '')); if (empty($signingSecret)) { return true; } $timestamp = $request->header('X-Slack-Request-Timestamp'); $signature = $request->header('X-Slack-Signature'); if (!$timestamp || !$signature) { return false; } if (abs(time() - (int) $timestamp) > 300) { return false; } $sigBasestring = "v0:{$timestamp}:{$request->getContent()}"; $expectedSignature = 'v0=' . hash_hmac('sha256', $sigBasestring, $signingSecret); return hash_equals($expectedSignature, $signature); } } 