This commit adds comprehensive mobile app support to enable a native Android app that won't timeout or sleep when the screen goes dark.
New Features:
- JWT-based authentication system (no WordPress session dependency)
- REST API endpoints for mobile app (agent status, queue management, call control)
- Server-Sent Events (SSE) for real-time updates to mobile app
- Firebase Cloud Messaging (FCM) integration for push notifications
- Gitea-based automatic plugin updates
- Mobile app admin settings page
New Files:
- includes/class-twp-mobile-auth.php - JWT authentication with login/refresh/logout
- includes/class-twp-mobile-api.php - REST API endpoints under /twilio-mobile/v1
- includes/class-twp-mobile-sse.php - Real-time event streaming
- includes/class-twp-fcm.php - Push notification handling
- includes/class-twp-auto-updater.php - Gitea-based auto-updates
- admin/mobile-app-settings.php - Admin configuration page
Modified Files:
- includes/class-twp-activator.php - Added twp_mobile_sessions table
- includes/class-twp-core.php - Load and initialize mobile classes
- admin/class-twp-admin.php - Added Mobile App menu item and settings page
Database Changes:
- New table: twp_mobile_sessions (stores JWT refresh tokens and FCM tokens)
API Endpoints:
- POST /twilio-mobile/v1/auth/login
- POST /twilio-mobile/v1/auth/refresh
- POST /twilio-mobile/v1/auth/logout
- GET/POST /twilio-mobile/v1/agent/status
- GET /twilio-mobile/v1/queues/state
- POST /twilio-mobile/v1/calls/{call_sid}/accept
- GET /twilio-mobile/v1/stream/events (SSE)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
215 lines
6.2 KiB
PHP
215 lines
6.2 KiB
PHP
<?php
|
|
/**
|
|
* Firebase Cloud Messaging (FCM) Integration
|
|
*
|
|
* Handles push notifications to mobile devices via FCM
|
|
*/
|
|
class TWP_FCM {
|
|
|
|
private $server_key;
|
|
private $fcm_url = 'https://fcm.googleapis.com/fcm/send';
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public function __construct() {
|
|
$this->server_key = get_option('twp_fcm_server_key', '');
|
|
}
|
|
|
|
/**
|
|
* Send push notification to user's devices
|
|
*/
|
|
public function send_notification($user_id, $title, $body, $data = array()) {
|
|
if (empty($this->server_key)) {
|
|
error_log('TWP FCM: Server key not configured');
|
|
return false;
|
|
}
|
|
|
|
// Get user's FCM tokens
|
|
$tokens = $this->get_user_tokens($user_id);
|
|
|
|
if (empty($tokens)) {
|
|
error_log("TWP FCM: No tokens found for user $user_id");
|
|
return false;
|
|
}
|
|
|
|
$success_count = 0;
|
|
$failed_tokens = array();
|
|
|
|
foreach ($tokens as $token) {
|
|
$result = $this->send_to_token($token, $title, $body, $data);
|
|
|
|
if ($result['success']) {
|
|
$success_count++;
|
|
} else {
|
|
$failed_tokens[] = $token;
|
|
|
|
// If token is invalid, remove it from database
|
|
if ($result['error'] === 'invalid_token') {
|
|
$this->remove_invalid_token($token);
|
|
}
|
|
}
|
|
}
|
|
|
|
error_log("TWP FCM: Sent notification to $success_count/" . count($tokens) . " devices for user $user_id");
|
|
|
|
return $success_count > 0;
|
|
}
|
|
|
|
/**
|
|
* Send notification to specific token
|
|
*/
|
|
private function send_to_token($token, $title, $body, $data = array()) {
|
|
$notification = array(
|
|
'title' => $title,
|
|
'body' => $body,
|
|
'sound' => 'default',
|
|
'priority' => 'high',
|
|
'click_action' => 'FLUTTER_NOTIFICATION_CLICK'
|
|
);
|
|
|
|
$payload = array(
|
|
'to' => $token,
|
|
'notification' => $notification,
|
|
'data' => array_merge($data, array(
|
|
'title' => $title,
|
|
'body' => $body,
|
|
'timestamp' => time()
|
|
)),
|
|
'priority' => 'high'
|
|
);
|
|
|
|
$headers = array(
|
|
'Authorization: key=' . $this->server_key,
|
|
'Content-Type: application/json'
|
|
);
|
|
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $this->fcm_url);
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
|
|
|
$response = curl_exec($ch);
|
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($http_code !== 200) {
|
|
error_log("TWP FCM: Failed to send notification. HTTP $http_code: $response");
|
|
|
|
// Check if token is invalid
|
|
$response_data = json_decode($response, true);
|
|
if (isset($response_data['results'][0]['error']) &&
|
|
in_array($response_data['results'][0]['error'], array('InvalidRegistration', 'NotRegistered'))) {
|
|
return array('success' => false, 'error' => 'invalid_token');
|
|
}
|
|
|
|
return array('success' => false, 'error' => 'http_error');
|
|
}
|
|
|
|
return array('success' => true);
|
|
}
|
|
|
|
/**
|
|
* Get all active FCM tokens for a user
|
|
*/
|
|
private function get_user_tokens($user_id) {
|
|
global $wpdb;
|
|
$table = $wpdb->prefix . 'twp_mobile_sessions';
|
|
|
|
return $wpdb->get_col($wpdb->prepare(
|
|
"SELECT fcm_token FROM $table
|
|
WHERE user_id = %d
|
|
AND is_active = 1
|
|
AND fcm_token IS NOT NULL
|
|
AND fcm_token != ''
|
|
AND expires_at > NOW()",
|
|
$user_id
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Remove invalid FCM token from database
|
|
*/
|
|
private function remove_invalid_token($token) {
|
|
global $wpdb;
|
|
$table = $wpdb->prefix . 'twp_mobile_sessions';
|
|
|
|
$wpdb->update(
|
|
$table,
|
|
array('fcm_token' => null),
|
|
array('fcm_token' => $token),
|
|
array('%s'),
|
|
array('%s')
|
|
);
|
|
|
|
error_log("TWP FCM: Removed invalid token from database");
|
|
}
|
|
|
|
/**
|
|
* Send incoming call notification
|
|
*/
|
|
public function notify_incoming_call($user_id, $from_number, $queue_name, $call_sid) {
|
|
$title = 'Incoming Call';
|
|
$body = "Call from $from_number in $queue_name queue";
|
|
|
|
$data = array(
|
|
'type' => 'incoming_call',
|
|
'call_sid' => $call_sid,
|
|
'from_number' => $from_number,
|
|
'queue_name' => $queue_name
|
|
);
|
|
|
|
return $this->send_notification($user_id, $title, $body, $data);
|
|
}
|
|
|
|
/**
|
|
* Send queue timeout notification
|
|
*/
|
|
public function notify_queue_timeout($user_id, $queue_name, $waiting_count) {
|
|
$title = 'Queue Alert';
|
|
$body = "$queue_name has $waiting_count waiting call" . ($waiting_count > 1 ? 's' : '');
|
|
|
|
$data = array(
|
|
'type' => 'queue_timeout',
|
|
'queue_name' => $queue_name,
|
|
'waiting_count' => $waiting_count
|
|
);
|
|
|
|
return $this->send_notification($user_id, $title, $body, $data);
|
|
}
|
|
|
|
/**
|
|
* Send agent status change notification
|
|
*/
|
|
public function notify_status_change($user_id, $old_status, $new_status) {
|
|
$title = 'Status Changed';
|
|
$body = "Your status changed from $old_status to $new_status";
|
|
|
|
$data = array(
|
|
'type' => 'status_change',
|
|
'old_status' => $old_status,
|
|
'new_status' => $new_status
|
|
);
|
|
|
|
return $this->send_notification($user_id, $title, $body, $data);
|
|
}
|
|
|
|
/**
|
|
* Test notification (for settings page)
|
|
*/
|
|
public function send_test_notification($user_id) {
|
|
$title = 'Test Notification';
|
|
$body = 'This is a test notification from Twilio WordPress Plugin';
|
|
|
|
$data = array(
|
|
'type' => 'test',
|
|
'test' => true
|
|
);
|
|
|
|
return $this->send_notification($user_id, $title, $body, $data);
|
|
}
|
|
}
|