From ce6da47bab645492811bb4bd5402ee2509246b64 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 12 Nov 2025 19:23:42 +0000
Subject: [PATCH 01/11] Initial plan
From 1719412039b1baa62e6c381f68e64915c5660e0d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 12 Nov 2025 19:27:04 +0000
Subject: [PATCH 02/11] Add Facebook webhook integration for live video
detection
Co-authored-by: jkrrv <1238391+jkrrv@users.noreply.github.com>
---
shimmer.php | 3 +
shimmer/FACEBOOK_WEBHOOK.md | 77 ++++++++++++++
shimmer/FacebookWebhook.php | 205 ++++++++++++++++++++++++++++++++++++
3 files changed, 285 insertions(+)
create mode 100644 shimmer/FACEBOOK_WEBHOOK.md
create mode 100644 shimmer/FacebookWebhook.php
diff --git a/shimmer.php b/shimmer.php
index 14ff761..3a70733 100644
--- a/shimmer.php
+++ b/shimmer.php
@@ -12,9 +12,11 @@
*/
use tp\Shimmer\SessionMatters;
+use tp\Shimmer\FacebookWebhook;
use tp\TouchPointWP\TouchPointWP;
require_once __DIR__ . "/shimmer/SessionMatters.php";
+require_once __DIR__ . "/shimmer/FacebookWebhook.php";
function tenth_involvementClasses()
{
@@ -400,6 +402,7 @@ function tenth_formatTimeString($string, $t = null): string
add_filter('tp_adjust_time_string', 'tenth_formatTimeString');
SessionMatters::load();
+FacebookWebhook::load();
/**
diff --git a/shimmer/FACEBOOK_WEBHOOK.md b/shimmer/FACEBOOK_WEBHOOK.md
new file mode 100644
index 0000000..0dffa27
--- /dev/null
+++ b/shimmer/FACEBOOK_WEBHOOK.md
@@ -0,0 +1,77 @@
+# Facebook Webhook Configuration
+
+This plugin now includes Facebook webhook functionality to detect when the Facebook page `/tenth` has a new live video.
+
+## Setup Instructions
+
+### 1. Facebook App Configuration
+
+1. Go to [Facebook Developers](https://developers.facebook.com/)
+2. Create a new app or use an existing one
+3. Add the "Webhooks" product to your app
+4. In your app settings, note down:
+ - App Secret (found in Settings > Basic)
+ - Create a Verify Token (a random string you create)
+
+### 2. WordPress Configuration
+
+Add the following constants to your `wp-config.php` file:
+
+```php
+// Facebook App Configuration
+define('FACEBOOK_APP_SECRET', 'your_app_secret_here');
+define('FACEBOOK_WEBHOOK_VERIFY_TOKEN', 'your_verify_token_here');
+```
+
+### 3. Webhook Subscription Setup
+
+1. In your Facebook App dashboard, go to Webhooks
+2. Click "Add Callback URL"
+3. Enter your webhook URL: `https://your-site.com/wp-json/shimmer/v1/facebook-webhook`
+4. Enter the Verify Token (the same one you defined in wp-config.php)
+5. Click "Verify and Save"
+
+### 4. Subscribe to Page Events
+
+1. In the Webhooks section, find your Page subscription
+2. Click "Add Subscriptions"
+3. Select the `live_videos` field
+4. Save the subscription
+
+### 5. Grant Permissions
+
+1. Your Facebook App needs permission to access the page
+2. Go to your Facebook Page settings
+3. Add your app with appropriate permissions
+
+## How It Works
+
+When a live video is started on the Facebook page `/tenth`:
+
+1. Facebook sends a POST request to the webhook endpoint
+2. The request is verified using the app secret
+3. The webhook payload is parsed to extract the video ID
+4. The `FacebookWebhook::handleLiveVideo($videoId)` method is called with the video ID
+
+## Customization
+
+The stub method `FacebookWebhook::handleLiveVideo($videoId)` currently just logs the video ID. You can customize this method in `/shimmer/FacebookWebhook.php` to:
+
+- Create a WordPress post
+- Send notifications
+- Update custom fields
+- Trigger other actions
+
+## Testing
+
+To test the webhook:
+
+1. Use Facebook's webhook testing tool in the App dashboard
+2. Send a test event for `live_videos`
+3. Check your WordPress error logs for the message: "Facebook Live Video Detected - Video ID: {id}"
+
+## Security
+
+- All webhook requests are verified using HMAC signature validation
+- The verify token prevents unauthorized webhook subscriptions
+- Both GET (verification) and POST (notifications) endpoints are properly secured
diff --git a/shimmer/FacebookWebhook.php b/shimmer/FacebookWebhook.php
new file mode 100644
index 0000000..e147cab
--- /dev/null
+++ b/shimmer/FacebookWebhook.php
@@ -0,0 +1,205 @@
+ 'GET',
+ 'callback' => [self::class, 'handleWebhookVerification'],
+ 'permission_callback' => '__return_true', // Facebook needs to access this
+ ],
+ [
+ 'methods' => 'POST',
+ 'callback' => [self::class, 'handleWebhookNotification'],
+ 'permission_callback' => '__return_true', // Facebook needs to access this
+ ],
+ ]);
+ }
+
+ /**
+ * Handle webhook verification requests from Facebook (GET)
+ *
+ * Facebook sends a GET request with hub.mode, hub.verify_token, and hub.challenge
+ * to verify the webhook endpoint during subscription setup.
+ *
+ * @param \WP_REST_Request $request
+ * @return \WP_REST_Response
+ */
+ public static function handleWebhookVerification(\WP_REST_Request $request): \WP_REST_Response
+ {
+ $mode = $request->get_param('hub_mode');
+ $token = $request->get_param('hub_verify_token');
+ $challenge = $request->get_param('hub_challenge');
+
+ // Verify that the mode and token match
+ if ($mode === 'subscribe' && $token === self::getVerifyToken()) {
+ // Respond with the challenge token to complete verification
+ return new \WP_REST_Response($challenge, 200, ['Content-Type' => 'text/plain']);
+ }
+
+ return new \WP_REST_Response('Forbidden', 403);
+ }
+
+ /**
+ * Handle incoming webhook notifications from Facebook (POST)
+ *
+ * @param \WP_REST_Request $request
+ * @return \WP_REST_Response
+ */
+ public static function handleWebhookNotification(\WP_REST_Request $request): \WP_REST_Response
+ {
+ // Verify the request signature
+ if (!self::verifySignature($request)) {
+ return new \WP_REST_Response('Forbidden', 403);
+ }
+
+ $body = $request->get_json_params();
+
+ // Process each entry in the webhook payload
+ if (isset($body['entry']) && is_array($body['entry'])) {
+ foreach ($body['entry'] as $entry) {
+ self::processEntry($entry);
+ }
+ }
+
+ // Always return 200 OK to acknowledge receipt
+ return new \WP_REST_Response('EVENT_RECEIVED', 200);
+ }
+
+ /**
+ * Verify the signature of the webhook request
+ *
+ * @param \WP_REST_Request $request
+ * @return bool
+ */
+ private static function verifySignature(\WP_REST_Request $request): bool
+ {
+ $signature = $request->get_header('X-Hub-Signature-256');
+
+ if (!$signature) {
+ // Fallback to older signature header
+ $signature = $request->get_header('X-Hub-Signature');
+ if (!$signature) {
+ return false;
+ }
+ $hashAlgorithm = 'sha1';
+ } else {
+ $hashAlgorithm = 'sha256';
+ }
+
+ $appSecret = self::getAppSecret();
+ if (!$appSecret) {
+ error_log('Facebook webhook: App secret not configured');
+ return false;
+ }
+
+ $body = $request->get_body();
+ $expectedSignature = $hashAlgorithm . '=' . hash_hmac($hashAlgorithm, $body, $appSecret);
+
+ return hash_equals($expectedSignature, $signature);
+ }
+
+ /**
+ * Process a webhook entry to detect live video events
+ *
+ * @param array $entry
+ */
+ private static function processEntry(array $entry): void
+ {
+ if (!isset($entry['changes']) || !is_array($entry['changes'])) {
+ return;
+ }
+
+ foreach ($entry['changes'] as $change) {
+ if (isset($change['field']) && $change['field'] === 'live_videos') {
+ self::processLiveVideoChange($change);
+ }
+ }
+ }
+
+ /**
+ * Process a live video change notification
+ *
+ * @param array $change
+ */
+ private static function processLiveVideoChange(array $change): void
+ {
+ if (!isset($change['value'])) {
+ return;
+ }
+
+ $value = $change['value'];
+
+ // Check if this is a live video going live
+ if (isset($value['status']) && $value['status'] === 'live') {
+ $videoId = $value['id'] ?? null;
+
+ if ($videoId) {
+ self::handleLiveVideo($videoId);
+ }
+ }
+ }
+
+ /**
+ * Stub method to handle when a live video is detected
+ *
+ * This is the method that will be called with the video ID when
+ * the /tenth Facebook page starts a live video.
+ *
+ * @param string $videoId The Facebook video ID
+ */
+ public static function handleLiveVideo(string $videoId): void
+ {
+ // TODO: Implement actual handling logic
+ // This is a stub method that receives the video ID
+ // You can add your custom logic here to process the live video
+
+ error_log("Facebook Live Video Detected - Video ID: {$videoId}");
+
+ // Example: You could create a WordPress post, send notifications, etc.
+ // For now, this is just a placeholder that logs the video ID
+ }
+
+ /**
+ * Get the Facebook app verify token from configuration
+ *
+ * @return string|null
+ */
+ private static function getVerifyToken(): ?string
+ {
+ return defined('FACEBOOK_WEBHOOK_VERIFY_TOKEN') ? FACEBOOK_WEBHOOK_VERIFY_TOKEN : null;
+ }
+
+ /**
+ * Get the Facebook app secret from configuration
+ *
+ * @return string|null
+ */
+ private static function getAppSecret(): ?string
+ {
+ return defined('FACEBOOK_APP_SECRET') ? FACEBOOK_APP_SECRET : null;
+ }
+}
From f679a8fa971f2e71295555eb6dd015b6a5e7b53e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 12 Nov 2025 19:28:28 +0000
Subject: [PATCH 03/11] Add test and example files for Facebook webhook
Co-authored-by: jkrrv <1238391+jkrrv@users.noreply.github.com>
---
shimmer/FacebookWebhookTest.php | 157 +++++++++++++++++++++++++++
shimmer/facebook-webhook-example.php | 88 +++++++++++++++
2 files changed, 245 insertions(+)
create mode 100644 shimmer/FacebookWebhookTest.php
create mode 100644 shimmer/facebook-webhook-example.php
diff --git a/shimmer/FacebookWebhookTest.php b/shimmer/FacebookWebhookTest.php
new file mode 100644
index 0000000..b62461b
--- /dev/null
+++ b/shimmer/FacebookWebhookTest.php
@@ -0,0 +1,157 @@
+get_routes();
+ return isset($routes['/shimmer/v1/facebook-webhook']);
+ }
+
+ /**
+ * Test webhook verification with correct token
+ */
+ public static function testVerificationSuccess(): array
+ {
+ $request = new \WP_REST_Request('GET', '/shimmer/v1/facebook-webhook');
+ $request->set_param('hub_mode', 'subscribe');
+ $request->set_param('hub_verify_token', FACEBOOK_WEBHOOK_VERIFY_TOKEN ?? 'test_token');
+ $request->set_param('hub_challenge', 'test_challenge_123');
+
+ $response = rest_do_request($request);
+
+ return [
+ 'status' => $response->get_status(),
+ 'data' => $response->get_data(),
+ 'expected_status' => 200,
+ 'expected_data' => 'test_challenge_123'
+ ];
+ }
+
+ /**
+ * Test webhook verification with incorrect token
+ */
+ public static function testVerificationFailure(): array
+ {
+ $request = new \WP_REST_Request('GET', '/shimmer/v1/facebook-webhook');
+ $request->set_param('hub_mode', 'subscribe');
+ $request->set_param('hub_verify_token', 'wrong_token');
+ $request->set_param('hub_challenge', 'test_challenge_123');
+
+ $response = rest_do_request($request);
+
+ return [
+ 'status' => $response->get_status(),
+ 'data' => $response->get_data(),
+ 'expected_status' => 403
+ ];
+ }
+
+ /**
+ * Create a mock webhook payload for testing
+ */
+ public static function createMockLiveVideoPayload(string $videoId = 'test_video_123'): array
+ {
+ return [
+ 'object' => 'page',
+ 'entry' => [
+ [
+ 'id' => '123456789',
+ 'time' => time(),
+ 'changes' => [
+ [
+ 'field' => 'live_videos',
+ 'value' => [
+ 'id' => $videoId,
+ 'status' => 'live',
+ 'stream_url' => 'rtmps://example.com/stream',
+ ]
+ ]
+ ]
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * Test signature verification
+ */
+ public static function testSignatureVerification(): bool
+ {
+ $appSecret = FACEBOOK_APP_SECRET ?? 'test_secret';
+ $payload = json_encode(self::createMockLiveVideoPayload());
+
+ $signature = 'sha256=' . hash_hmac('sha256', $payload, $appSecret);
+
+ // In a real test, you would create a request with this signature
+ // and verify it's accepted
+ return !empty($signature);
+ }
+
+ /**
+ * Example of what the handleLiveVideo method receives
+ */
+ public static function exampleHandleLiveVideo(): string
+ {
+ $videoId = 'test_video_123';
+
+ // This would be called by the webhook handler when a live video is detected
+ // \tp\Shimmer\FacebookWebhook::handleLiveVideo($videoId);
+
+ return "Video ID received: {$videoId}";
+ }
+
+ /**
+ * Run all tests
+ */
+ public static function runAll(): array
+ {
+ $results = [];
+
+ $results['endpoint_registered'] = self::testEndpointRegistered();
+
+ if (defined('FACEBOOK_WEBHOOK_VERIFY_TOKEN')) {
+ $results['verification_success'] = self::testVerificationSuccess();
+ $results['verification_failure'] = self::testVerificationFailure();
+ }
+
+ if (defined('FACEBOOK_APP_SECRET')) {
+ $results['signature_check'] = self::testSignatureVerification();
+ }
+
+ $results['example_payload'] = self::createMockLiveVideoPayload();
+ $results['example_handler'] = self::exampleHandleLiveVideo();
+
+ return $results;
+ }
+}
+
+/**
+ * Example usage (would be called from WordPress admin or a test script):
+ *
+ * $results = \tp\Shimmer\Tests\FacebookWebhookTest::runAll();
+ * print_r($results);
+ */
diff --git a/shimmer/facebook-webhook-example.php b/shimmer/facebook-webhook-example.php
new file mode 100644
index 0000000..6923841
--- /dev/null
+++ b/shimmer/facebook-webhook-example.php
@@ -0,0 +1,88 @@
+ 'page',
+ 'entry' => [
+ [
+ 'id' => '123456789', // Page ID
+ 'time' => 1699824264,
+ 'changes' => [
+ [
+ 'field' => 'live_videos',
+ 'value' => [
+ 'id' => '987654321', // Video ID
+ 'status' => 'live',
+ 'title' => 'Sunday Morning Service',
+ 'description' => 'Live worship service',
+ 'stream_url' => 'rtmps://live-api-s.facebook.com/...',
+ 'secure_stream_url' => 'rtmps://live-api-s.facebook.com/...',
+ 'dash_ingest_url' => 'https://live-api-s.facebook.com/...',
+ 'dash_ingest_secure_url' => 'https://live-api-s.facebook.com/...',
+ ]
+ ]
+ ]
+ ]
+ ]
+];
+
+echo json_encode($examplePayload, JSON_PRETTY_PRINT) . "\n\n";
+
+echo "3. WEBHOOK PROCESSING\n";
+echo " - Plugin validates the request signature using HMAC\n";
+echo " - Extracts the video ID from the payload\n";
+echo " - Calls: FacebookWebhook::handleLiveVideo('987654321')\n\n";
+
+echo "4. CUSTOM HANDLING (in handleLiveVideo method)\n";
+echo " Currently the stub method just logs:\n";
+echo " 'Facebook Live Video Detected - Video ID: 987654321'\n\n";
+echo " You can customize this to:\n";
+echo " - Create a WordPress post\n";
+echo " - Send email notifications\n";
+echo " - Update custom fields\n";
+echo " - Trigger other integrations\n";
+echo " - Start recording/archiving\n";
+echo " - Update digital signage\n";
+echo " - Etc.\n\n";
+
+echo "5. RESPONSE TO FACEBOOK\n";
+echo " - Plugin returns: 200 OK with 'EVENT_RECEIVED'\n";
+echo " - Facebook marks the event as delivered\n\n";
+
+echo "=== Configuration Required ===\n";
+echo "In wp-config.php:\n";
+echo "define('FACEBOOK_APP_SECRET', 'your_app_secret');\n";
+echo "define('FACEBOOK_WEBHOOK_VERIFY_TOKEN', 'your_verify_token');\n\n";
+
+echo "=== Facebook App Setup ===\n";
+echo "1. Create Facebook App at developers.facebook.com\n";
+echo "2. Add Webhooks product\n";
+echo "3. Configure callback URL: https://your-site.com/wp-json/shimmer/v1/facebook-webhook\n";
+echo "4. Subscribe to page events\n";
+echo "5. Select 'live_videos' field\n";
+echo "6. Grant app access to the /tenth page\n\n";
+
+echo "=== Webhook URL ===\n";
+echo "https://your-wordpress-site.com/wp-json/shimmer/v1/facebook-webhook\n\n";
From 4e24039e8decc1cfe202204e6d1eecd9a3c4fc82 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 12 Nov 2025 19:29:51 +0000
Subject: [PATCH 04/11] Add implementation summary documentation
Co-authored-by: jkrrv <1238391+jkrrv@users.noreply.github.com>
---
IMPLEMENTATION_SUMMARY.md | 116 ++++++++++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
create mode 100644 IMPLEMENTATION_SUMMARY.md
diff --git a/IMPLEMENTATION_SUMMARY.md b/IMPLEMENTATION_SUMMARY.md
new file mode 100644
index 0000000..6fcdcee
--- /dev/null
+++ b/IMPLEMENTATION_SUMMARY.md
@@ -0,0 +1,116 @@
+# Facebook Webhook Implementation Summary
+
+## Overview
+This implementation adds Facebook webhook functionality to the Shimmer WordPress plugin to detect when the Facebook page `/tenth` has a new live video online.
+
+## Files Added/Modified
+
+### New Files
+1. **shimmer/FacebookWebhook.php** - Main webhook handler class
+2. **shimmer/FACEBOOK_WEBHOOK.md** - Setup and configuration documentation
+3. **shimmer/FacebookWebhookTest.php** - Test utilities for verification
+4. **shimmer/facebook-webhook-example.php** - Example demonstrating webhook flow
+
+### Modified Files
+1. **shimmer.php** - Added 3 lines to load and initialize FacebookWebhook class
+
+## Requirements Met
+
+✅ **Requirement 1**: Use webhook API to detect when Facebook page /tenth has new live video
+- Implemented via REST API endpoint at `/wp-json/shimmer/v1/facebook-webhook`
+- Subscribes to `live_videos` field events
+- Processes webhook notifications when status changes to 'live'
+
+✅ **Requirement 2**: Get video ID and provide it to stub method
+- Extracts video ID from webhook payload
+- Calls `FacebookWebhook::handleLiveVideo($videoId)` with the video ID
+- Stub method currently logs the video ID (ready for customization)
+
+✅ **Requirement 3**: Smoothly handle all auth
+- Implements webhook verification (GET endpoint) for Facebook app setup
+- Uses HMAC-SHA256 signature verification for all webhook notifications
+- Supports fallback to SHA1 for older implementations
+- Uses `hash_equals()` for timing-attack-safe comparison
+- Credentials stored as WordPress constants (not in code)
+
+## Technical Implementation
+
+### Webhook Verification (GET)
+When Facebook sets up the webhook subscription:
+1. Facebook sends GET request with `hub.mode`, `hub.verify_token`, and `hub.challenge`
+2. Plugin verifies the token matches `FACEBOOK_WEBHOOK_VERIFY_TOKEN`
+3. Returns the challenge to confirm subscription
+
+### Webhook Notifications (POST)
+When a live video starts on /tenth:
+1. Facebook sends POST request with signed payload
+2. Plugin verifies HMAC signature using `FACEBOOK_APP_SECRET`
+3. Parses payload to extract video information
+4. Calls `handleLiveVideo($videoId)` when status is 'live'
+5. Returns 200 OK to acknowledge receipt
+
+### Security Features
+- ✅ HMAC signature verification (SHA256 or SHA1)
+- ✅ Timing-safe string comparison (`hash_equals()`)
+- ✅ Verify token validation
+- ✅ No hardcoded credentials
+- ✅ Proper error handling
+- ✅ No SQL injection vulnerabilities
+- ✅ No XSS vulnerabilities
+- ✅ Public endpoint only accessible via proper authentication
+
+## Configuration
+
+Add to `wp-config.php`:
+```php
+define('FACEBOOK_APP_SECRET', 'your_app_secret_here');
+define('FACEBOOK_WEBHOOK_VERIFY_TOKEN', 'your_verify_token_here');
+```
+
+## Webhook URL
+```
+https://your-wordpress-site.com/wp-json/shimmer/v1/facebook-webhook
+```
+
+## Next Steps for Users
+
+1. Create Facebook App at developers.facebook.com
+2. Add Webhooks product to the app
+3. Configure the callback URL with the webhook endpoint
+4. Subscribe to Page events, specifically the `live_videos` field
+5. Grant the app access to the /tenth Facebook page
+6. Customize `handleLiveVideo()` method for specific business logic
+
+## Code Quality
+
+- ✅ PHP syntax validated
+- ✅ Follows WordPress REST API conventions
+- ✅ Proper namespacing (`tp\Shimmer\FacebookWebhook`)
+- ✅ Comprehensive documentation
+- ✅ Test utilities included
+- ✅ Security best practices applied
+- ✅ Minimal changes to existing code
+- ✅ All functionality in new files
+
+## Testing
+
+Test utilities provided in `FacebookWebhookTest.php`:
+- Endpoint registration verification
+- Successful webhook verification test
+- Failed webhook verification test
+- Signature generation example
+- Mock payload creation
+
+Example script in `facebook-webhook-example.php` demonstrates the complete flow.
+
+## Extensibility
+
+The `handleLiveVideo($videoId)` stub method can be customized to:
+- Create WordPress posts
+- Send email/SMS notifications
+- Update custom fields
+- Trigger recording/archiving
+- Update digital signage
+- Integrate with other systems
+
+Current implementation just logs the video ID for verification.
From 4596f4ddd7c631083f0c13c902874a818e17e918 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 12 Nov 2025 19:33:13 +0000
Subject: [PATCH 05/11] Use WordPress settings for Facebook credentials instead
of constants
Co-authored-by: jkrrv <1238391+jkrrv@users.noreply.github.com>
---
IMPLEMENTATION_SUMMARY.md | 11 +-
shimmer/FACEBOOK_WEBHOOK.md | 22 +++-
shimmer/FacebookWebhook.php | 175 ++++++++++++++++++++++++++-
shimmer/FacebookWebhookTest.php | 30 ++++-
shimmer/facebook-webhook-example.php | 11 +-
5 files changed, 231 insertions(+), 18 deletions(-)
diff --git a/IMPLEMENTATION_SUMMARY.md b/IMPLEMENTATION_SUMMARY.md
index 6fcdcee..dc11d9d 100644
--- a/IMPLEMENTATION_SUMMARY.md
+++ b/IMPLEMENTATION_SUMMARY.md
@@ -61,12 +61,21 @@ When a live video starts on /tenth:
## Configuration
-Add to `wp-config.php`:
+**Method 1: WordPress Settings Page (Recommended)**
+1. Go to WordPress Admin > Settings > Facebook Webhook
+2. Enter your Facebook App Secret
+3. Enter your Webhook Verify Token
+4. Click "Save Settings"
+
+**Method 2: wp-config.php (Alternative)**
+Add to `wp-config.php` (for backward compatibility):
```php
define('FACEBOOK_APP_SECRET', 'your_app_secret_here');
define('FACEBOOK_WEBHOOK_VERIFY_TOKEN', 'your_verify_token_here');
```
+Note: Settings page values take precedence over constants.
+
## Webhook URL
```
https://your-wordpress-site.com/wp-json/shimmer/v1/facebook-webhook
diff --git a/shimmer/FACEBOOK_WEBHOOK.md b/shimmer/FACEBOOK_WEBHOOK.md
index 0dffa27..c674115 100644
--- a/shimmer/FACEBOOK_WEBHOOK.md
+++ b/shimmer/FACEBOOK_WEBHOOK.md
@@ -9,26 +9,35 @@ This plugin now includes Facebook webhook functionality to detect when the Faceb
1. Go to [Facebook Developers](https://developers.facebook.com/)
2. Create a new app or use an existing one
3. Add the "Webhooks" product to your app
-4. In your app settings, note down:
- - App Secret (found in Settings > Basic)
- - Create a Verify Token (a random string you create)
+4. In your app settings (Settings > Basic), note down your App Secret
### 2. WordPress Configuration
-Add the following constants to your `wp-config.php` file:
+**Method 1: Using WordPress Settings (Recommended)**
+
+1. In WordPress admin, go to **Settings > Facebook Webhook**
+2. Enter your Facebook App Secret
+3. Create and enter a custom Verify Token (any random string you create - remember it for step 3)
+4. Click "Save Settings"
+
+**Method 2: Using wp-config.php (Alternative)**
+
+For backward compatibility, you can still define these as constants in `wp-config.php`:
```php
-// Facebook App Configuration
+// Facebook App Configuration (optional - settings page is preferred)
define('FACEBOOK_APP_SECRET', 'your_app_secret_here');
define('FACEBOOK_WEBHOOK_VERIFY_TOKEN', 'your_verify_token_here');
```
+Note: Settings page values take precedence over constants.
+
### 3. Webhook Subscription Setup
1. In your Facebook App dashboard, go to Webhooks
2. Click "Add Callback URL"
3. Enter your webhook URL: `https://your-site.com/wp-json/shimmer/v1/facebook-webhook`
-4. Enter the Verify Token (the same one you defined in wp-config.php)
+4. Enter the Verify Token (the same one you entered in WordPress settings)
5. Click "Verify and Save"
### 4. Subscribe to Page Events
@@ -75,3 +84,4 @@ To test the webhook:
- All webhook requests are verified using HMAC signature validation
- The verify token prevents unauthorized webhook subscriptions
- Both GET (verification) and POST (notifications) endpoints are properly secured
+- Credentials are stored securely in WordPress options table
diff --git a/shimmer/FacebookWebhook.php b/shimmer/FacebookWebhook.php
index e147cab..4c49a6d 100644
--- a/shimmer/FacebookWebhook.php
+++ b/shimmer/FacebookWebhook.php
@@ -18,6 +18,7 @@ class FacebookWebhook {
public static function load(): void
{
add_action('rest_api_init', [self::class, 'registerWebhookEndpoint']);
+ self::registerSettings();
}
/**
@@ -184,22 +185,188 @@ public static function handleLiveVideo(string $videoId): void
}
/**
- * Get the Facebook app verify token from configuration
+ * Get the Facebook app verify token from WordPress settings
+ * Falls back to constant if setting not found (for backward compatibility)
*
* @return string|null
*/
private static function getVerifyToken(): ?string
{
- return defined('FACEBOOK_WEBHOOK_VERIFY_TOKEN') ? FACEBOOK_WEBHOOK_VERIFY_TOKEN : null;
+ $token = get_option('shimmer_facebook_verify_token');
+ if (empty($token) && defined('FACEBOOK_WEBHOOK_VERIFY_TOKEN')) {
+ $token = FACEBOOK_WEBHOOK_VERIFY_TOKEN;
+ }
+ return !empty($token) ? $token : null;
}
/**
- * Get the Facebook app secret from configuration
+ * Get the Facebook app secret from WordPress settings
+ * Falls back to constant if setting not found (for backward compatibility)
*
* @return string|null
*/
private static function getAppSecret(): ?string
{
- return defined('FACEBOOK_APP_SECRET') ? FACEBOOK_APP_SECRET : null;
+ $secret = get_option('shimmer_facebook_app_secret');
+ if (empty($secret) && defined('FACEBOOK_APP_SECRET')) {
+ $secret = FACEBOOK_APP_SECRET;
+ }
+ return !empty($secret) ? $secret : null;
+ }
+
+ /**
+ * Register settings page in WordPress admin
+ */
+ public static function registerSettings(): void
+ {
+ add_action('admin_menu', [self::class, 'addSettingsPage']);
+ add_action('admin_init', [self::class, 'registerSettingsFields']);
+ }
+
+ /**
+ * Add settings page to WordPress admin menu
+ */
+ public static function addSettingsPage(): void
+ {
+ add_options_page(
+ 'Facebook Webhook Settings',
+ 'Facebook Webhook',
+ 'manage_options',
+ 'shimmer-facebook-webhook',
+ [self::class, 'renderSettingsPage']
+ );
+ }
+
+ /**
+ * Register settings fields
+ */
+ public static function registerSettingsFields(): void
+ {
+ register_setting('shimmer_facebook_webhook', 'shimmer_facebook_app_secret', [
+ 'type' => 'string',
+ 'sanitize_callback' => 'sanitize_text_field',
+ 'default' => '',
+ ]);
+
+ register_setting('shimmer_facebook_webhook', 'shimmer_facebook_verify_token', [
+ 'type' => 'string',
+ 'sanitize_callback' => 'sanitize_text_field',
+ 'default' => '',
+ ]);
+
+ add_settings_section(
+ 'shimmer_facebook_webhook_section',
+ 'Facebook App Credentials',
+ [self::class, 'renderSettingsSection'],
+ 'shimmer-facebook-webhook'
+ );
+
+ add_settings_field(
+ 'shimmer_facebook_app_secret',
+ 'Facebook App Secret',
+ [self::class, 'renderAppSecretField'],
+ 'shimmer-facebook-webhook',
+ 'shimmer_facebook_webhook_section'
+ );
+
+ add_settings_field(
+ 'shimmer_facebook_verify_token',
+ 'Webhook Verify Token',
+ [self::class, 'renderVerifyTokenField'],
+ 'shimmer-facebook-webhook',
+ 'shimmer_facebook_webhook_section'
+ );
+ }
+
+ /**
+ * Render settings section description
+ */
+ public static function renderSettingsSection(): void
+ {
+ echo '
Configure your Facebook App credentials for webhook integration. These settings are required to receive live video notifications from the /tenth Facebook page.
In your Facebook App, go to Settings > Basic and copy your App Secret
+
Paste the App Secret in the field above
+
Create a custom Verify Token (any random string) and enter it above
+
Save these settings
+
In your Facebook App, add the "Webhooks" product
+
Click "Add Callback URL" and enter the Webhook URL shown above
+
Enter the same Verify Token you created in step 4
+
Click "Verify and Save"
+
Subscribe to the live_videos field for your page
+
+
+ set_param('hub_mode', 'subscribe');
- $request->set_param('hub_verify_token', FACEBOOK_WEBHOOK_VERIFY_TOKEN ?? 'test_token');
+
+ // Get token from WordPress settings or fallback to constant
+ $token = get_option('shimmer_facebook_verify_token');
+ if (empty($token) && defined('FACEBOOK_WEBHOOK_VERIFY_TOKEN')) {
+ $token = FACEBOOK_WEBHOOK_VERIFY_TOKEN;
+ }
+ if (empty($token)) {
+ $token = 'test_token';
+ }
+
+ $request->set_param('hub_verify_token', $token);
$request->set_param('hub_challenge', 'test_challenge_123');
$response = rest_do_request($request);
@@ -101,7 +111,15 @@ public static function createMockLiveVideoPayload(string $videoId = 'test_video_
*/
public static function testSignatureVerification(): bool
{
- $appSecret = FACEBOOK_APP_SECRET ?? 'test_secret';
+ // Get app secret from WordPress settings or fallback to constant
+ $appSecret = get_option('shimmer_facebook_app_secret');
+ if (empty($appSecret) && defined('FACEBOOK_APP_SECRET')) {
+ $appSecret = FACEBOOK_APP_SECRET;
+ }
+ if (empty($appSecret)) {
+ $appSecret = 'test_secret';
+ }
+
$payload = json_encode(self::createMockLiveVideoPayload());
$signature = 'sha256=' . hash_hmac('sha256', $payload, $appSecret);
@@ -133,12 +151,16 @@ public static function runAll(): array
$results['endpoint_registered'] = self::testEndpointRegistered();
- if (defined('FACEBOOK_WEBHOOK_VERIFY_TOKEN')) {
+ // Check if credentials are configured (either in settings or constants)
+ $hasVerifyToken = !empty(get_option('shimmer_facebook_verify_token')) || defined('FACEBOOK_WEBHOOK_VERIFY_TOKEN');
+ $hasAppSecret = !empty(get_option('shimmer_facebook_app_secret')) || defined('FACEBOOK_APP_SECRET');
+
+ if ($hasVerifyToken) {
$results['verification_success'] = self::testVerificationSuccess();
$results['verification_failure'] = self::testVerificationFailure();
}
- if (defined('FACEBOOK_APP_SECRET')) {
+ if ($hasAppSecret) {
$results['signature_check'] = self::testSignatureVerification();
}
diff --git a/shimmer/facebook-webhook-example.php b/shimmer/facebook-webhook-example.php
index 6923841..339a4bd 100644
--- a/shimmer/facebook-webhook-example.php
+++ b/shimmer/facebook-webhook-example.php
@@ -72,9 +72,14 @@
echo " - Facebook marks the event as delivered\n\n";
echo "=== Configuration Required ===\n";
-echo "In wp-config.php:\n";
-echo "define('FACEBOOK_APP_SECRET', 'your_app_secret');\n";
-echo "define('FACEBOOK_WEBHOOK_VERIFY_TOKEN', 'your_verify_token');\n\n";
+echo "Method 1 (Recommended): WordPress Admin\n";
+echo " - Go to Settings > Facebook Webhook\n";
+echo " - Enter your Facebook App Secret\n";
+echo " - Enter your Webhook Verify Token\n";
+echo " - Click 'Save Settings'\n\n";
+echo "Method 2 (Alternative): wp-config.php\n";
+echo " define('FACEBOOK_APP_SECRET', 'your_app_secret');\n";
+echo " define('FACEBOOK_WEBHOOK_VERIFY_TOKEN', 'your_verify_token');\n\n";
echo "=== Facebook App Setup ===\n";
echo "1. Create Facebook App at developers.facebook.com\n";
From 21d04b9a5134dad267b6c2c3d1046dcb64e2b541 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 12 Nov 2025 19:34:13 +0000
Subject: [PATCH 06/11] Add settings page layout documentation
Co-authored-by: jkrrv <1238391+jkrrv@users.noreply.github.com>
---
shimmer/SETTINGS_PAGE_LAYOUT.md | 92 +++++++++++++++++++++++++++++++++
1 file changed, 92 insertions(+)
create mode 100644 shimmer/SETTINGS_PAGE_LAYOUT.md
diff --git a/shimmer/SETTINGS_PAGE_LAYOUT.md b/shimmer/SETTINGS_PAGE_LAYOUT.md
new file mode 100644
index 0000000..b46b746
--- /dev/null
+++ b/shimmer/SETTINGS_PAGE_LAYOUT.md
@@ -0,0 +1,92 @@
+# WordPress Settings Page - Facebook Webhook
+
+## Location
+WordPress Admin → Settings → Facebook Webhook
+
+## Settings Page Layout
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ Facebook Webhook Settings │
+├─────────────────────────────────────────────────────────────────┤
+│ │
+│ Configure your Facebook App credentials for webhook │
+│ integration. These settings are required to receive live video │
+│ notifications from the /tenth Facebook page. │
+│ │
+│ ┌────────────────────────────────────────────────────────────┐ │
+│ │ Facebook App Credentials │ │
+│ ├────────────────────────────────────────────────────────────┤ │
+│ │ │ │
+│ │ Facebook App Secret │ │
+│ │ [••••••••••••••••••••••••••••••] │ │
+│ │ Enter your Facebook App Secret (found in Facebook App │ │
+│ │ Settings > Basic) │ │
+│ │ │ │
+│ │ Webhook Verify Token │ │
+│ │ [________________________________] │ │
+│ │ Enter a custom verify token (you create this, and use │ │
+│ │ the same value when configuring the webhook in Facebook) │ │
+│ │ │ │
+│ └────────────────────────────────────────────────────────────┘ │
+│ │
+│ [ Save Settings ] │
+│ │
+├─────────────────────────────────────────────────────────────────┤
+│ Webhook Information │
+├─────────────────────────────────────────────────────────────────┤
+│ │
+│ Webhook URL: │
+│ https://your-site.com/wp-json/shimmer/v1/facebook-webhook │
+│ Use this URL when configuring your Facebook webhook │
+│ │
+├─────────────────────────────────────────────────────────────────┤
+│ Setup Instructions │
+├─────────────────────────────────────────────────────────────────┤
+│ │
+│ 1. Create a Facebook App at developers.facebook.com │
+│ 2. In your Facebook App, go to Settings > Basic and copy │
+│ your App Secret │
+│ 3. Paste the App Secret in the field above │
+│ 4. Create a custom Verify Token (any random string) and │
+│ enter it above │
+│ 5. Save these settings │
+│ 6. In your Facebook App, add the "Webhooks" product │
+│ 7. Click "Add Callback URL" and enter the Webhook URL │
+│ shown above │
+│ 8. Enter the same Verify Token you created in step 4 │
+│ 9. Click "Verify and Save" │
+│ 10. Subscribe to the live_videos field for your page │
+│ │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+## Features
+
+### Security
+- **App Secret Field**: Password type input (masked)
+- **Verify Token Field**: Text input (visible for copying)
+- Values stored in WordPress options table
+- Sanitized on save using `sanitize_text_field()`
+
+### User Experience
+- Clear instructions integrated into the settings page
+- Webhook URL displayed for easy copying
+- Step-by-step setup guide
+- Success message shown after saving
+
+### Access Control
+- Only users with `manage_options` capability can access
+- Typically administrators only
+
+### Backward Compatibility
+- Constants in wp-config.php still work
+- Settings page values take precedence
+- Gradual migration path for existing users
+
+## Usage
+
+After saving settings, the webhook will automatically:
+1. Accept verification requests from Facebook
+2. Process live video notifications
+3. Call the `handleLiveVideo()` stub method with video IDs
From 978b2ab1a12f824a9cb72beb5f880a5202ca2a55 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 12 Nov 2025 19:53:20 +0000
Subject: [PATCH 07/11] Add video title to stub method, move settings to
universal class, and add i18n
Co-authored-by: jkrrv <1238391+jkrrv@users.noreply.github.com>
---
IMPLEMENTATION_SUMMARY.md | 15 +-
shimmer.php | 3 +
shimmer/FACEBOOK_WEBHOOK.md | 15 +-
shimmer/FacebookWebhook.php | 248 +++++++++------------------
shimmer/FacebookWebhookTest.php | 5 +-
shimmer/SETTINGS_PAGE_LAYOUT.md | 55 +++---
shimmer/ShimmerSettings.php | 179 +++++++++++++++++++
shimmer/facebook-webhook-example.php | 9 +-
8 files changed, 311 insertions(+), 218 deletions(-)
create mode 100644 shimmer/ShimmerSettings.php
diff --git a/IMPLEMENTATION_SUMMARY.md b/IMPLEMENTATION_SUMMARY.md
index dc11d9d..021c402 100644
--- a/IMPLEMENTATION_SUMMARY.md
+++ b/IMPLEMENTATION_SUMMARY.md
@@ -62,10 +62,11 @@ When a live video starts on /tenth:
## Configuration
**Method 1: WordPress Settings Page (Recommended)**
-1. Go to WordPress Admin > Settings > Facebook Webhook
-2. Enter your Facebook App Secret
-3. Enter your Webhook Verify Token
-4. Click "Save Settings"
+1. Go to WordPress Admin > Settings > Shimmer
+2. Find the Facebook Webhook section
+3. Enter your Facebook App Secret
+4. Enter your Webhook Verify Token
+5. Click "Save Settings"
**Method 2: wp-config.php (Alternative)**
Add to `wp-config.php` (for backward compatibility):
@@ -88,7 +89,7 @@ https://your-wordpress-site.com/wp-json/shimmer/v1/facebook-webhook
3. Configure the callback URL with the webhook endpoint
4. Subscribe to Page events, specifically the `live_videos` field
5. Grant the app access to the /tenth Facebook page
-6. Customize `handleLiveVideo()` method for specific business logic
+6. Customize `handleLiveVideo($videoId, $videoTitle)` method for specific business logic
## Code Quality
@@ -114,7 +115,7 @@ Example script in `facebook-webhook-example.php` demonstrates the complete flow.
## Extensibility
-The `handleLiveVideo($videoId)` stub method can be customized to:
+The `handleLiveVideo($videoId, $videoTitle)` stub method can be customized to:
- Create WordPress posts
- Send email/SMS notifications
- Update custom fields
@@ -122,4 +123,4 @@ The `handleLiveVideo($videoId)` stub method can be customized to:
- Update digital signage
- Integrate with other systems
-Current implementation just logs the video ID for verification.
+Current implementation just logs the video ID and title for verification.
diff --git a/shimmer.php b/shimmer.php
index 3a70733..b4db1d3 100644
--- a/shimmer.php
+++ b/shimmer.php
@@ -12,10 +12,12 @@
*/
use tp\Shimmer\SessionMatters;
+use tp\Shimmer\ShimmerSettings;
use tp\Shimmer\FacebookWebhook;
use tp\TouchPointWP\TouchPointWP;
require_once __DIR__ . "/shimmer/SessionMatters.php";
+require_once __DIR__ . "/shimmer/ShimmerSettings.php";
require_once __DIR__ . "/shimmer/FacebookWebhook.php";
function tenth_involvementClasses()
@@ -402,6 +404,7 @@ function tenth_formatTimeString($string, $t = null): string
add_filter('tp_adjust_time_string', 'tenth_formatTimeString');
SessionMatters::load();
+ShimmerSettings::load();
FacebookWebhook::load();
diff --git a/shimmer/FACEBOOK_WEBHOOK.md b/shimmer/FACEBOOK_WEBHOOK.md
index c674115..774a2d1 100644
--- a/shimmer/FACEBOOK_WEBHOOK.md
+++ b/shimmer/FACEBOOK_WEBHOOK.md
@@ -15,10 +15,11 @@ This plugin now includes Facebook webhook functionality to detect when the Faceb
**Method 1: Using WordPress Settings (Recommended)**
-1. In WordPress admin, go to **Settings > Facebook Webhook**
-2. Enter your Facebook App Secret
-3. Create and enter a custom Verify Token (any random string you create - remember it for step 3)
-4. Click "Save Settings"
+1. In WordPress admin, go to **Settings > Shimmer**
+2. Find the **Facebook Webhook** section
+3. Enter your Facebook App Secret
+4. Create and enter a custom Verify Token (any random string you create - remember it for step 3)
+5. Click "Save Settings"
**Method 2: Using wp-config.php (Alternative)**
@@ -59,12 +60,12 @@ When a live video is started on the Facebook page `/tenth`:
1. Facebook sends a POST request to the webhook endpoint
2. The request is verified using the app secret
-3. The webhook payload is parsed to extract the video ID
-4. The `FacebookWebhook::handleLiveVideo($videoId)` method is called with the video ID
+3. The webhook payload is parsed to extract the video ID and title
+4. The `FacebookWebhook::handleLiveVideo($videoId, $videoTitle)` method is called with the video ID and title
## Customization
-The stub method `FacebookWebhook::handleLiveVideo($videoId)` currently just logs the video ID. You can customize this method in `/shimmer/FacebookWebhook.php` to:
+The stub method `FacebookWebhook::handleLiveVideo($videoId, $videoTitle)` currently just logs the video ID and title. You can customize this method in `/shimmer/FacebookWebhook.php` to:
- Create a WordPress post
- Send notifications
diff --git a/shimmer/FacebookWebhook.php b/shimmer/FacebookWebhook.php
index 4c49a6d..93093ed 100644
--- a/shimmer/FacebookWebhook.php
+++ b/shimmer/FacebookWebhook.php
@@ -18,7 +18,79 @@ class FacebookWebhook {
public static function load(): void
{
add_action('rest_api_init', [self::class, 'registerWebhookEndpoint']);
- self::registerSettings();
+ add_action('admin_init', [self::class, 'registerSettings'], 5);
+ }
+
+ /**
+ * Register settings with ShimmerSettings
+ */
+ public static function registerSettings(): void
+ {
+ // Register Facebook webhook section
+ ShimmerSettings::registerSection(
+ 'shimmer_facebook_webhook_section',
+ __('Facebook Webhook', 'shimmer'),
+ [self::class, 'renderSettingsSection']
+ );
+
+ // Register Facebook App Secret field
+ ShimmerSettings::registerField(
+ 'shimmer_facebook_app_secret',
+ 'shimmer_facebook_webhook_section',
+ __('Facebook App Secret', 'shimmer'),
+ 'password',
+ [
+ 'description' => __('Enter your Facebook App Secret (found in Facebook App Settings > Basic)', 'shimmer'),
+ 'default' => '',
+ ]
+ );
+
+ // Register Webhook Verify Token field
+ ShimmerSettings::registerField(
+ 'shimmer_facebook_verify_token',
+ 'shimmer_facebook_webhook_section',
+ __('Webhook Verify Token', 'shimmer'),
+ 'text',
+ [
+ 'description' => __('Enter a custom verify token (you create this, and use the same value when configuring the webhook in Facebook)', 'shimmer'),
+ 'default' => '',
+ ]
+ );
+ }
+
+ /**
+ * Render settings section description
+ */
+ public static function renderSettingsSection(): void
+ {
+ $webhookUrl = rest_url('shimmer/v1/facebook-webhook');
+ ?>
+
+
+
Configure your Facebook App credentials for webhook integration. These settings are required to receive live video notifications from the /tenth Facebook page.