From ae3e3f84883711d72b23987bc054b6a0118fb3dc Mon Sep 17 00:00:00 2001 From: Andy Dixon Date: Thu, 22 Jan 2026 17:18:35 +0000 Subject: [PATCH] Break parsers into subfolder --- docs.htm | 96 ++++ index.php | 1131 +--------------------------------------- parsers/github.php | 396 ++++++++++++++ parsers/helpers.php | 91 ++++ parsers/wxinteract.php | 604 +++++++++++++++++++++ 5 files changed, 1214 insertions(+), 1104 deletions(-) create mode 100644 parsers/github.php create mode 100644 parsers/helpers.php create mode 100644 parsers/wxinteract.php diff --git a/docs.htm b/docs.htm index a1dc5cd..a5741d8 100644 --- a/docs.htm +++ b/docs.htm @@ -354,6 +354,102 @@ Invalid email address provided in URL path. + +
+

GitHub Webhook Parser

+

Get beautifully formatted emails specifically designed for GitHub webhooks with enhanced event details.

+ +

How to Use

+

Simply add /github to the end of your webhook URL:

+
+https:///{email-address}/github +
+ +

URL Format Examples

+
+
+
+
+ Standard Webhook +
+
+ /john%40example.com +
+
+
+
+
+
+ GitHub Webhook +
+
+ /john%40example.com/github +
+
+
+
+ +

Supported GitHub Events

+

The GitHub parser provides enhanced formatting for these event types:

+ +
+
+
+
+
📤 Push Events
+

Displays branch, commit count, and individual commit messages with authors.

+
+
+
+
+
+
+
🔀 Pull Request Events
+

Shows PR number, title, state, branches, and direct link to the PR.

+
+
+
+
+
+
+
📝 Issue Events
+

Displays issue number, title, state, and link to the issue.

+
+
+
+
+
+
+
🚀 Release Events
+

Shows release tag, name, and direct link to the release page.

+
+
+
+
+ +
+ All Events Supported: Even if an event isn't specifically formatted above, the parser will display it in a clean, structured format showing all webhook data. +
+ +

Setting Up in GitHub

+

To configure a GitHub webhook:

+
    +
  1. Go to your repository settings → Webhooks → Add webhook
  2. +
  3. Set the Payload URL to: https:///your%40email.com/github
  4. +
  5. Set Content type to: application/json
  6. +
  7. Choose which events to receive (or select "Send me everything")
  8. +
  9. Click "Add webhook"
  10. +
+ +
+ Subject Line: GitHub webhook emails arrive with event-specific subject lines like "📤 GitHub push - username/repo" or "🔀 GitHub pull_request (opened) - username/repo" for easy filtering. +
+ +
+ Tip: Use email plus addressing like you+github%40example.com to easily filter GitHub webhooks in your inbox. +
+
+

Common Use Cases

diff --git a/index.php b/index.php index c2f9f76..e1645d3 100755 --- a/index.php +++ b/index.php @@ -90,10 +90,10 @@ $rawBodySafe = htmlspecialchars($rawBody, ENT_QUOTES, 'UTF-8'); // Compile all PHP superglobal variables into an array for debugging purposes // This provides visibility of all data received with the request $variables = [ - '_GET' => $_GET, - '_POST' => $_POST, - '_REQUEST' => $_REQUEST, - '_FILES' => $_FILES, + 'GET Variables' => $_GET, + 'POST Variables' => $_POST, + 'Raw Request Body' => $_REQUEST, + 'Uploaded Files' => $_FILES, ]; // Convert the variables array to a human-readable string format @@ -109,21 +109,31 @@ $ipAddressSafe = htmlspecialchars($ipAddress, ENT_QUOTES, 'UTF-8'); $requestMethodSafe = htmlspecialchars($requestMethod, ENT_QUOTES, 'UTF-8'); $contentTypeSafe = htmlspecialchars($contentType, ENT_QUOTES, 'UTF-8'); -// Check if a parser is specified and exists +// Load parser helpers +require_once __DIR__ . '/parsers/helpers.php'; + +// Check if a parser is specified and load it $html = ''; $subject = ''; -if ($parserName && function_exists($parserName . 'Parse')) { - $parserFunction = $parserName . 'Parse'; - $parsed = $parserFunction($rawBody, $headersArray, [ - 'date' => $dateReceived, - 'ip' => $ipAddress, - 'method' => $requestMethod, - 'contentType' => $contentType - ]); - - if ($parsed !== false) { - $html = $parsed['html']; - $subject = $parsed['subject']; +if ($parserName) { + $parserFile = __DIR__ . '/parsers/' . $parserName . '.php'; + if (file_exists($parserFile)) { + require_once $parserFile; + $parserFunction = $parserName . 'Parse'; + + if (function_exists($parserFunction)) { + $parsed = $parserFunction($rawBody, $headersArray, [ + 'date' => $dateReceived, + 'ip' => $ipAddress, + 'method' => $requestMethod, + 'contentType' => $contentType + ]); + + if ($parsed !== false) { + $html = $parsed['html']; + $subject = $parsed['subject']; + } + } } } @@ -304,1093 +314,6 @@ HTML; $subject = "‼️ Webhook Request Received - $dateReceived"; } -/** - * Helper function to render nested arrays/objects as HTML tables - * - * @param array $data The data to render - * @param int $depth Current recursion depth - * @return string HTML representation of the data - */ -function renderDataAsTable($data, $depth = 0) { - if ($depth > 10) { - return '
Max depth reached
'; - } - - $html = ''; - - if (!is_array($data)) { - $safe = htmlspecialchars((string)$data, ENT_QUOTES, 'UTF-8'); - return "
$safe
"; - } - - // Check if it's an indexed array of similar items (like commits) - $isIndexedArray = array_keys($data) === range(0, count($data) - 1); - - if ($isIndexedArray && count($data) > 0) { - // Render as a list of items - foreach ($data as $index => $item) { - if (is_array($item)) { - $html .= '
'; - $html .= '
Item ' . ($index + 1) . '
'; - $html .= ''; - $html .= '
'; - } else { - $valueSafe = htmlspecialchars((string)$item, ENT_QUOTES, 'UTF-8'); - $html .= '
• ' . $valueSafe . '
'; - } - } - } else { - // Render as key-value table - $html .= ''; - $html .= '
'; - $html .= '
' . $keySafe . '
'; - $html .= renderDataAsTable($value, $depth + 1); - $html .= '
'; - $html .= ''; - } - - return $html; -} - -/** - * GitHub Webhook Parser - * Parses GitHub webhook events into a formatted email - * - * @param string $rawBody The raw request body - * @param array $headers The request headers - * @param array $metadata Request metadata (date, ip, method, contentType) - * @return array|false Array with 'html' and 'subject' keys, or false on failure - */ -function githubParse($rawBody, $headers, $metadata) { - $payload = json_decode($rawBody, true); - if (!$payload) { - return false; - } - - // Determine event type from headers - $event = $headers['X-GitHub-Event'] ?? $headers['X-Github-Event'] ?? 'Unknown'; - $delivery = $headers['X-GitHub-Delivery'] ?? $headers['X-Github-Delivery'] ?? 'Unknown'; - - // Extract common data - $action = $payload['action'] ?? null; - $repository = $payload['repository']['full_name'] ?? 'Unknown'; - $sender = $payload['sender']['login'] ?? 'Unknown'; - - // Sanitize for HTML - $eventSafe = htmlspecialchars($event, ENT_QUOTES, 'UTF-8'); - $actionSafe = $action ? htmlspecialchars($action, ENT_QUOTES, 'UTF-8') : null; - $repoSafe = htmlspecialchars($repository, ENT_QUOTES, 'UTF-8'); - $senderSafe = htmlspecialchars($sender, ENT_QUOTES, 'UTF-8'); - $dateSafe = htmlspecialchars($metadata['date'], ENT_QUOTES, 'UTF-8'); - $ipSafe = htmlspecialchars($metadata['ip'], ENT_QUOTES, 'UTF-8'); - - // Build event-specific content - $eventContent = ''; - $eventIcon = '🔔'; - - switch ($event) { - case 'push': - $eventIcon = '📤'; - $ref = htmlspecialchars($payload['ref'] ?? 'Unknown', ENT_QUOTES, 'UTF-8'); - $commits = $payload['commits'] ?? []; - $commitCount = count($commits); - - $eventContent = << -
Push Details
- -
-HTML; - - if ($commitCount > 0) { - $eventContent .= '
Commits
'; - foreach (array_slice($commits, 0, 10) as $commit) { - $message = htmlspecialchars($commit['message'] ?? '', ENT_QUOTES, 'UTF-8'); - $author = htmlspecialchars($commit['author']['name'] ?? 'Unknown', ENT_QUOTES, 'UTF-8'); - $sha = htmlspecialchars(substr($commit['id'] ?? '', 0, 7), ENT_QUOTES, 'UTF-8'); - $eventContent .= "
$sha $message by $author
"; - } - $eventContent .= '
'; - } - break; - - case 'pull_request': - $eventIcon = '🔀'; - $pr = $payload['pull_request'] ?? []; - $prNumber = htmlspecialchars($pr['number'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $prTitle = htmlspecialchars($pr['title'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $prUrl = htmlspecialchars($pr['html_url'] ?? '#', ENT_QUOTES, 'UTF-8'); - $prState = htmlspecialchars($pr['state'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $prBase = htmlspecialchars($pr['base']['ref'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $prHead = htmlspecialchars($pr['head']['ref'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - - $eventContent = << -
Pull Request Details
-
- - - - - -
- -HTML; - break; - - case 'issues': - $eventIcon = '📝'; - $issue = $payload['issue'] ?? []; - $issueNumber = htmlspecialchars($issue['number'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $issueTitle = htmlspecialchars($issue['title'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $issueUrl = htmlspecialchars($issue['html_url'] ?? '#', ENT_QUOTES, 'UTF-8'); - $issueState = htmlspecialchars($issue['state'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - - $eventContent = << -
Issue Details
-
- - - - -
- -HTML; - break; - - case 'release': - $eventIcon = '🚀'; - $release = $payload['release'] ?? []; - $tagName = htmlspecialchars($release['tag_name'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $releaseName = htmlspecialchars($release['name'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $releaseUrl = htmlspecialchars($release['html_url'] ?? '#', ENT_QUOTES, 'UTF-8'); - - $eventContent = << -
Release Details
-
- - - -
- -HTML; - break; - - default: - // Generic event display - render as structured tables - $eventContent = '
Event Details
'; - $eventContent .= renderDataAsTable($payload); - $eventContent .= '
'; - } - - $actionDisplay = $actionSafe ? " ($actionSafe)" : ''; - - $html = << - - - - - GitHub Webhook - $eventSafe - - - -
-
-

$eventIcon GitHub: $eventSafe$actionDisplay

-

$dateSafe

-
- -
-
📋 Repository Information
- -
- - $eventContent - - -
- - -HTML; - - $subject = "$eventIcon GitHub $eventSafe$actionDisplay - $repoSafe"; - - return [ - 'html' => $html, - 'subject' => $subject - ]; -} - -/** - * Webex Interact Webhook Parser - * Parses Webex Interact SMS webhook events into a formatted email - * - * @param string $rawBody The raw request body - * @param array $headers The request headers - * @param array $metadata Request metadata (date, ip, method, contentType) - * @return array|false Array with 'html' and 'subject' keys, or false on failure - */ -function wxinteractParse($rawBody, $headers, $metadata) { - $payload = json_decode($rawBody, true); - if (!$payload || !isset($payload['data'])) { - return false; - } - - $data = $payload['data']; - $status = $data['status'] ?? 'Unknown'; - - // Sanitize common fields - $statusSafe = htmlspecialchars($status, ENT_QUOTES, 'UTF-8'); - $dateSafe = htmlspecialchars($metadata['date'], ENT_QUOTES, 'UTF-8'); - $ipSafe = htmlspecialchars($metadata['ip'], ENT_QUOTES, 'UTF-8'); - - // Determine event type and icon - $eventIcon = '📱'; - $eventTitle = 'SMS Event'; - $eventContent = ''; - - switch ($status) { - case 'submitted': - $eventIcon = '📤'; - $eventTitle = 'Outbound SMS - Submitted'; - $eventContent = buildOutboundSmsSubmittedContent($data); - break; - - case 'delivered': - $eventIcon = '✅'; - $eventTitle = 'Outbound SMS - Delivered'; - $eventContent = buildOutboundSmsDeliveredContent($data); - break; - - case 'failed': - $eventIcon = '❌'; - $eventTitle = 'Outbound SMS - Failed'; - $eventContent = buildOutboundSmsFailedContent($data); - break; - - case 'shortlink_clicked': - $eventIcon = '🔗'; - $eventTitle = 'Shortlink Clicked'; - $eventContent = buildShortlinkClickedContent($data); - break; - - case 'received': - $eventIcon = '📥'; - $eventTitle = 'Inbound SMS Received'; - $eventContent = buildInboundSmsContent($data); - break; - - case 'opt_out': - $eventIcon = '🚫'; - $eventTitle = 'SMS Opt Out Received'; - $eventContent = buildOptOutContent($data); - break; - - case 'contact_created': - case 'contact_updated': - $eventIcon = '👤'; - $eventTitle = 'Contacts Callback'; - $eventContent = buildContactsCallbackContent($data); - break; - - default: - // Unknown event type - display all data - $eventIcon = '📱'; - $eventTitle = "SMS Event: $statusSafe"; - $eventContent = '
Event Details
'; - $eventContent .= renderDataAsTable($data); - $eventContent .= '
'; - } - - $eventTitleSafe = htmlspecialchars($eventTitle, ENT_QUOTES, 'UTF-8'); - - $html = << - - - - - Webex Interact - $eventTitleSafe - - - -
-
-

$eventIcon Webex Interact: $eventTitleSafe

-

$dateSafe

-
- -
-
📋 Event Information
- -
- - $eventContent - - -
- - -HTML; - - $subject = "$eventIcon Webex Interact: $eventTitle"; - - return [ - 'html' => $html, - 'subject' => $subject - ]; -} - -/** - * Build content for Outbound SMS Submitted event - */ -function buildOutboundSmsSubmittedContent($data) { - $messageId = htmlspecialchars($data['message_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $to = htmlspecialchars($data['to'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $from = htmlspecialchars($data['from'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $message = htmlspecialchars($data['message'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $submittedAt = htmlspecialchars($data['submitted_at'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $campaignId = htmlspecialchars($data['campaign_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - - return << -
📤 SMS Details
-
- - - - - -
- - -
-
💬 Message Content
-
$message
-
-HTML; -} - -/** - * Build content for Outbound SMS Delivered event - */ -function buildOutboundSmsDeliveredContent($data) { - $messageId = htmlspecialchars($data['message_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $to = htmlspecialchars($data['to'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $from = htmlspecialchars($data['from'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $deliveredAt = htmlspecialchars($data['delivered_at'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $campaignId = htmlspecialchars($data['campaign_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - - return << -
✅ Delivery Details
-
- - - - - -
- -HTML; -} - -/** - * Build content for Outbound SMS Failed event - */ -function buildOutboundSmsFailedContent($data) { - $messageId = htmlspecialchars($data['message_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $to = htmlspecialchars($data['to'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $from = htmlspecialchars($data['from'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $failedAt = htmlspecialchars($data['failed_at'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $errorCode = htmlspecialchars($data['error_code'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $errorMessage = htmlspecialchars($data['error_message'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $campaignId = htmlspecialchars($data['campaign_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - - return << -
❌ Failure Details
-
- - - - - -
- - -
-
⚠️ Error Information
-
- Error Code: $errorCode
- Error Message: $errorMessage -
-
-HTML; -} - -/** - * Build content for Shortlink Clicked event - */ -function buildShortlinkClickedContent($data) { - $shortlink = htmlspecialchars($data['shortlink'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $originalUrl = htmlspecialchars($data['original_url'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $clickedAt = htmlspecialchars($data['clicked_at'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $phoneNumber = htmlspecialchars($data['phone_number'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $messageId = htmlspecialchars($data['message_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $campaignId = htmlspecialchars($data['campaign_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $userAgent = htmlspecialchars($data['user_agent'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $ipAddress = htmlspecialchars($data['ip_address'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - - return << -
🔗 Click Details
-
- - - - - - - -
- - -
-
🌐 Browser Information
-
$userAgent
-
-HTML; -} - -/** - * Build content for Inbound SMS Received event - */ -function buildInboundSmsContent($data) { - $messageId = htmlspecialchars($data['message_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $from = htmlspecialchars($data['from'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $to = htmlspecialchars($data['to'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $message = htmlspecialchars($data['message'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $receivedAt = htmlspecialchars($data['received_at'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $keyword = htmlspecialchars($data['keyword'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - - return << -
📥 Inbound SMS Details
-
- - - - - -
- - -
-
💬 Message Content
-
$message
-
-HTML; -} - -/** - * Build content for SMS Opt Out event - */ -function buildOptOutContent($data) { - $phoneNumber = htmlspecialchars($data['phone_number'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $optOutAt = htmlspecialchars($data['opt_out_at'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $keyword = htmlspecialchars($data['keyword'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $messageId = htmlspecialchars($data['message_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $from = htmlspecialchars($data['from'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - - return << -
🚫 Opt Out Details
-
- - - - - -
- -HTML; -} - -/** - * Build content for Contacts Callback event - */ -function buildContactsCallbackContent($data) { - $contactId = htmlspecialchars($data['contact_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $phoneNumber = htmlspecialchars($data['phone_number'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $firstName = htmlspecialchars($data['first_name'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $lastName = htmlspecialchars($data['last_name'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $email = htmlspecialchars($data['email'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $status = htmlspecialchars($data['status'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $createdAt = htmlspecialchars($data['created_at'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - $updatedAt = htmlspecialchars($data['updated_at'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); - - $customFieldsHtml = ''; - if (isset($data['custom_fields']) && is_array($data['custom_fields']) && !empty($data['custom_fields'])) { - $customFieldsHtml = '
🏷️ Custom Fields
-HTML; - } - $customFieldsHtml .= '
'; - } - - return << -
👤 Contact Details
-
- - - - - - - - -
- - - $customFieldsHtml -HTML; -} - // Build email headers to ensure proper HTML rendering and sender information // MIME-Version declares email format capabilities $emailHeaders = "MIME-Version: 1.0\r\n"; diff --git a/parsers/github.php b/parsers/github.php new file mode 100644 index 0000000..146ccfc --- /dev/null +++ b/parsers/github.php @@ -0,0 +1,396 @@ + +
Push Details
+
+ + + +
+ +HTML; + + if ($commitCount > 0) { + $eventContent .= '
Commits
'; + foreach (array_slice($commits, 0, 10) as $commit) { + $message = htmlspecialchars($commit['message'] ?? '', ENT_QUOTES, 'UTF-8'); + $author = htmlspecialchars($commit['author']['name'] ?? 'Unknown', ENT_QUOTES, 'UTF-8'); + $sha = htmlspecialchars(substr($commit['id'] ?? '', 0, 7), ENT_QUOTES, 'UTF-8'); + $eventContent .= "
$sha $message by $author
"; + } + $eventContent .= '
'; + } + break; + + case 'pull_request': + $eventIcon = '🔀'; + $pr = $payload['pull_request'] ?? []; + $prNumber = htmlspecialchars($pr['number'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $prTitle = htmlspecialchars($pr['title'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $prUrl = htmlspecialchars($pr['html_url'] ?? '#', ENT_QUOTES, 'UTF-8'); + $prState = htmlspecialchars($pr['state'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $prBase = htmlspecialchars($pr['base']['ref'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $prHead = htmlspecialchars($pr['head']['ref'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + + $eventContent = << +
Pull Request Details
+
+ + + + + +
+ +HTML; + break; + + case 'issues': + $eventIcon = '📝'; + $issue = $payload['issue'] ?? []; + $issueNumber = htmlspecialchars($issue['number'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $issueTitle = htmlspecialchars($issue['title'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $issueUrl = htmlspecialchars($issue['html_url'] ?? '#', ENT_QUOTES, 'UTF-8'); + $issueState = htmlspecialchars($issue['state'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + + $eventContent = << +
Issue Details
+
+ + + + +
+ +HTML; + break; + + case 'release': + $eventIcon = '🚀'; + $release = $payload['release'] ?? []; + $tagName = htmlspecialchars($release['tag_name'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $releaseName = htmlspecialchars($release['name'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $releaseUrl = htmlspecialchars($release['html_url'] ?? '#', ENT_QUOTES, 'UTF-8'); + + $eventContent = << +
Release Details
+
+ + + +
+ +HTML; + break; + + default: + // Generic event display - render as structured tables + $eventContent = '
Event Details
'; + $eventContent .= renderDataAsTable($payload); + $eventContent .= '
'; + } + + $actionDisplay = $actionSafe ? " ($actionSafe)" : ''; + + $html = << + + + + + GitHub Webhook - $eventSafe + + + +
+
+

$eventIcon GitHub: $eventSafe$actionDisplay

+

$dateSafe

+
+ +
+
📋 Repository Information
+ +
+ + $eventContent + + +
+ + +HTML; + + $subject = "$eventIcon GitHub $eventSafe$actionDisplay - $repoSafe"; + + return [ + 'html' => $html, + 'subject' => $subject + ]; +} diff --git a/parsers/helpers.php b/parsers/helpers.php new file mode 100644 index 0000000..23c5a06 --- /dev/null +++ b/parsers/helpers.php @@ -0,0 +1,91 @@ + 10) { + return '
Max depth reached
'; + } + + $html = ''; + + if (!is_array($data)) { + $safe = htmlspecialchars((string)$data, ENT_QUOTES, 'UTF-8'); + return "
$safe
"; + } + + // Check if it's an indexed array of similar items (like commits) + $isIndexedArray = array_keys($data) === range(0, count($data) - 1); + + if ($isIndexedArray && count($data) > 0) { + // Render as a list of items + foreach ($data as $index => $item) { + if (is_array($item)) { + $html .= '
'; + $html .= '
Item ' . ($index + 1) . '
'; + $html .= ''; + $html .= '
'; + } else { + $valueSafe = htmlspecialchars((string)$item, ENT_QUOTES, 'UTF-8'); + $html .= '
• ' . $valueSafe . '
'; + } + } + } else { + // Render as key-value table + $html .= '
'; + foreach ($data as $key => $value) { + $keySafe = htmlspecialchars($key, ENT_QUOTES, 'UTF-8'); + + if (is_array($value) && !empty($value)) { + // Close current table and start a subsection + $html .= '
'; + $html .= '
'; + $html .= '
' . $keySafe . '
'; + $html .= renderDataAsTable($value, $depth + 1); + $html .= '
'; + $html .= '
'; + } else if (is_array($value) && empty($value)) { + $html .= ''; + } else { + $valueSafe = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); + $html .= ''; + } + } + $html .= '
'; + } + + return $html; +} diff --git a/parsers/wxinteract.php b/parsers/wxinteract.php new file mode 100644 index 0000000..9107ccb --- /dev/null +++ b/parsers/wxinteract.php @@ -0,0 +1,604 @@ +
Event Details
'; + $eventContent .= renderDataAsTable($data); + $eventContent .= ''; + } + + $eventTitleSafe = htmlspecialchars($eventTitle, ENT_QUOTES, 'UTF-8'); + + $html = << + + + + + Webex Interact - $eventTitleSafe + + + +
+
+

$eventIcon Webex Interact: $eventTitleSafe

+

$dateSafe

+
+ +
+
📋 Event Information
+ +
+ + $eventContent + + +
+ + +HTML; + + $subject = "$eventIcon Webex Interact: $eventTitle"; + + return [ + 'html' => $html, + 'subject' => $subject + ]; +} + +/** + * Build content for Outbound SMS Submitted event + */ +function buildOutboundSmsSubmittedContent($data) { + $messageId = htmlspecialchars($data['message_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $to = htmlspecialchars($data['to'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $from = htmlspecialchars($data['from'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $message = htmlspecialchars($data['message'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $submittedAt = htmlspecialchars($data['submitted_at'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $campaignId = htmlspecialchars($data['campaign_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + + return << +
📤 SMS Details
+
+ + + + + +
+ + +
+
💬 Message Content
+
$message
+
+HTML; +} + +/** + * Build content for Outbound SMS Delivered event + */ +function buildOutboundSmsDeliveredContent($data) { + $messageId = htmlspecialchars($data['message_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $to = htmlspecialchars($data['to'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $from = htmlspecialchars($data['from'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $deliveredAt = htmlspecialchars($data['delivered_at'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $campaignId = htmlspecialchars($data['campaign_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + + return << +
✅ Delivery Details
+
+ + + + + +
+ +HTML; +} + +/** + * Build content for Outbound SMS Failed event + */ +function buildOutboundSmsFailedContent($data) { + $messageId = htmlspecialchars($data['message_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $to = htmlspecialchars($data['to'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $from = htmlspecialchars($data['from'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $failedAt = htmlspecialchars($data['failed_at'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $errorCode = htmlspecialchars($data['error_code'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $errorMessage = htmlspecialchars($data['error_message'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $campaignId = htmlspecialchars($data['campaign_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + + return << +
❌ Failure Details
+
+ + + + + +
+ + +
+
⚠️ Error Information
+
+ Error Code: $errorCode
+ Error Message: $errorMessage +
+
+HTML; +} + +/** + * Build content for Shortlink Clicked event + */ +function buildShortlinkClickedContent($data) { + $shortlink = htmlspecialchars($data['shortlink'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $originalUrl = htmlspecialchars($data['original_url'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $clickedAt = htmlspecialchars($data['clicked_at'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $phoneNumber = htmlspecialchars($data['phone_number'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $messageId = htmlspecialchars($data['message_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $campaignId = htmlspecialchars($data['campaign_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $userAgent = htmlspecialchars($data['user_agent'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $ipAddress = htmlspecialchars($data['ip_address'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + + return << +
🔗 Click Details
+
+ + + + + + + +
+ + +
+
🌐 Browser Information
+
$userAgent
+
+HTML; +} + +/** + * Build content for Inbound SMS Received event + */ +function buildInboundSmsContent($data) { + $messageId = htmlspecialchars($data['message_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $from = htmlspecialchars($data['from'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $to = htmlspecialchars($data['to'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $message = htmlspecialchars($data['message'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $receivedAt = htmlspecialchars($data['received_at'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $keyword = htmlspecialchars($data['keyword'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + + return << +
📥 Inbound SMS Details
+
+ + + + + +
+ + +
+
💬 Message Content
+
$message
+
+HTML; +} + +/** + * Build content for SMS Opt Out event + */ +function buildOptOutContent($data) { + $phoneNumber = htmlspecialchars($data['phone_number'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $optOutAt = htmlspecialchars($data['opt_out_at'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $keyword = htmlspecialchars($data['keyword'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $messageId = htmlspecialchars($data['message_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $from = htmlspecialchars($data['from'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + + return << +
🚫 Opt Out Details
+
+ + + + + +
+ +HTML; +} + +/** + * Build content for Contacts Callback event + */ +function buildContactsCallbackContent($data) { + $contactId = htmlspecialchars($data['contact_id'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $phoneNumber = htmlspecialchars($data['phone_number'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $firstName = htmlspecialchars($data['first_name'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $lastName = htmlspecialchars($data['last_name'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $email = htmlspecialchars($data['email'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $status = htmlspecialchars($data['status'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $createdAt = htmlspecialchars($data['created_at'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + $updatedAt = htmlspecialchars($data['updated_at'] ?? 'N/A', ENT_QUOTES, 'UTF-8'); + + $customFieldsHtml = ''; + if (isset($data['custom_fields']) && is_array($data['custom_fields']) && !empty($data['custom_fields'])) { + $customFieldsHtml = '
🏷️ Custom Fields
+HTML; + } + $customFieldsHtml .= '
'; + } + + return << +
👤 Contact Details
+
+ + + + + + + + +
+ + + $customFieldsHtml +HTML; +}