= 300) { error_log("Matrix webhook failed for ticket #{$id}. HTTP {$httpCode}: {$response}"); } } private static function notifyUsers(): array { $raw = $GLOBALS['config']['MATRIX_NOTIFY_USERS'] ?? ''; return array_values(array_filter(array_map('trim', explode(',', $raw)))); } // ─── Public event methods ───────────────────────────────────────────────── /** * New ticket created (manual or automated/API). */ public static function sendTicketNotification($ticketId, array $ticketData, string $trigger = 'manual'): void { preg_match('/^\[([^\]]+)\]/', $ticketData['title'] ?? '', $m); $source = $m[1] ?? ($trigger === 'automated' ? 'Automated' : 'Manual'); self::fire([ 'event' => 'ticket_created', 'ticket_id' => $ticketId, 'title' => $ticketData['title'] ?? 'Untitled', 'priority' => (int)($ticketData['priority'] ?? 4), 'category' => $ticketData['category'] ?? 'General', 'type' => $ticketData['type'] ?? 'Issue', 'status' => $ticketData['status'] ?? 'Open', 'source' => $source, 'url' => UrlHelper::ticketUrl($ticketId), 'trigger' => $trigger, 'notify_users' => self::notifyUsers(), ]); } /** * Ticket status changed. * * @param string|int $ticketId * @param string $oldStatus * @param string $newStatus * @param string $ticketTitle * @param string|null $changedByDisplay Display name of the user who changed status */ public static function sendStatusChangeNotification($ticketId, string $oldStatus, string $newStatus, string $ticketTitle, ?string $changedByDisplay = null): void { self::fire([ 'event' => 'status_changed', 'ticket_id' => $ticketId, 'title' => $ticketTitle, 'old_status' => $oldStatus, 'new_status' => $newStatus, 'changed_by' => $changedByDisplay, 'url' => UrlHelper::ticketUrl($ticketId), 'notify_users' => self::notifyUsers(), ]); } /** * New comment posted (non-mention; use sendMentionNotification for @mentions). * * @param string|int $ticketId * @param string $ticketTitle * @param string $commentText Plain text (first 200 chars will be sent) * @param string|null $authorDisplay Display name of commenter * @param bool $isInternal True if the comment is internal-only */ public static function sendCommentNotification($ticketId, string $ticketTitle, string $commentText, ?string $authorDisplay = null, bool $isInternal = false): void { // Skip if this is an internal-only comment — only the assignee/admin need to know $notifyUsers = self::notifyUsers(); if (empty($notifyUsers)) { return; } self::fire([ 'event' => 'comment_added', 'ticket_id' => $ticketId, 'title' => $ticketTitle, 'author' => $authorDisplay, 'preview' => mb_strimwidth($commentText, 0, 200, '…'), 'is_internal' => $isInternal, 'url' => UrlHelper::ticketUrl($ticketId), 'notify_users' => $notifyUsers, ]); } /** * @mention detected in a comment. * * @param string|int $ticketId * @param string $ticketTitle * @param string $commentText * @param string|null $authorDisplay * @param array $mentionedMatrixIds Matrix user IDs derived from @usernames */ public static function sendMentionNotification($ticketId, string $ticketTitle, string $commentText, ?string $authorDisplay, array $mentionedMatrixIds): void { if (empty($mentionedMatrixIds)) { return; } self::fire([ 'event' => 'mention', 'ticket_id' => $ticketId, 'title' => $ticketTitle, 'author' => $authorDisplay, 'preview' => mb_strimwidth($commentText, 0, 200, '…'), 'url' => UrlHelper::ticketUrl($ticketId), 'notify_users' => $mentionedMatrixIds, ]); } /** * Ticket assigned (or reassigned) to a user. * * @param string|int $ticketId * @param string $ticketTitle * @param string|null $assigneeName Display name of new assignee * @param string|null $assigneeMatrix Matrix user ID of new assignee (to DM) * @param string|null $changedByDisplay */ public static function sendAssignmentNotification($ticketId, string $ticketTitle, ?string $assigneeName, ?string $assigneeMatrix, ?string $changedByDisplay = null): void { $notifyUsers = self::notifyUsers(); // Also notify the assignee directly if we know their Matrix ID if ($assigneeMatrix && !in_array($assigneeMatrix, $notifyUsers, true)) { $notifyUsers[] = $assigneeMatrix; } if (empty($notifyUsers)) { return; } self::fire([ 'event' => 'assigned', 'ticket_id' => $ticketId, 'title' => $ticketTitle, 'assignee' => $assigneeName, 'changed_by' => $changedByDisplay, 'url' => UrlHelper::ticketUrl($ticketId), 'notify_users' => $notifyUsers, ]); } } ?>