diff --git a/includes/class-twp-call-queue.php b/includes/class-twp-call-queue.php index ee71b0c..87e6955 100644 --- a/includes/class-twp-call-queue.php +++ b/includes/class-twp-call-queue.php @@ -577,6 +577,69 @@ class TWP_Call_Queue { return $status; } + /** + * Cron callback: re-send FCM queue alerts every minute for calls still waiting. + * Only alerts for calls that have been waiting > 60 seconds (initial alert + * already sent on entry). Skips re-alerting for the same call within 55 seconds + * using a short transient to avoid overlap with the 60-second cron. + */ + public static function send_queue_reminders() { + global $wpdb; + $calls_table = $wpdb->prefix . 'twp_queued_calls'; + $queue_table = $wpdb->prefix . 'twp_call_queues'; + + // Find calls waiting longer than 60 seconds + $waiting_calls = $wpdb->get_results( + "SELECT c.*, q.queue_name, q.user_id AS queue_owner_id, q.agent_group_id + FROM $calls_table c + JOIN $queue_table q ON q.id = c.queue_id + WHERE c.status = 'waiting' + AND c.joined_at <= DATE_SUB(NOW(), INTERVAL 60 SECOND)" + ); + + if (empty($waiting_calls)) { + return; + } + + require_once dirname(__FILE__) . '/class-twp-fcm.php'; + $fcm = new TWP_FCM(); + + foreach ($waiting_calls as $call) { + // Throttle: skip if we reminded for this call within the last 55 seconds + $transient_key = 'twp_queue_remind_' . $call->call_sid; + if (get_transient($transient_key)) { + continue; + } + set_transient($transient_key, 1, 55); + + $waiting_minutes = max(1, round((time() - strtotime($call->joined_at)) / 60)); + $title = 'Call Still Waiting'; + $body = "Call from {$call->from_number} waiting {$waiting_minutes}m in {$call->queue_name}"; + + $notified_users = array(); + + // Notify queue owner + if (!empty($call->queue_owner_id)) { + $fcm->notify_queue_alert($call->queue_owner_id, $call->from_number, $call->queue_name, $call->call_sid); + $notified_users[] = $call->queue_owner_id; + } + + // Notify agent group members + if (!empty($call->agent_group_id)) { + require_once dirname(__FILE__) . '/class-twp-agent-groups.php'; + $members = TWP_Agent_Groups::get_group_members($call->agent_group_id); + foreach ($members as $member) { + if (!in_array($member->user_id, $notified_users)) { + $fcm->notify_queue_alert($member->user_id, $call->from_number, $call->queue_name, $call->call_sid); + $notified_users[] = $member->user_id; + } + } + } + + error_log("TWP Queue Reminder: Re-alerted " . count($notified_users) . " user(s) for call {$call->call_sid} waiting {$waiting_minutes}m"); + } + } + /** * Notify agents via SMS when a call enters the queue */ diff --git a/includes/class-twp-core.php b/includes/class-twp-core.php index 96e1085..99d0b01 100644 --- a/includes/class-twp-core.php +++ b/includes/class-twp-core.php @@ -265,6 +265,9 @@ class TWP_Core { $queue = new TWP_Call_Queue(); $this->loader->add_action('twp_process_queue', $queue, 'process_waiting_calls'); + + // Queue reminder alerts (re-send FCM every minute for waiting calls) + add_action('twp_queue_reminders', array('TWP_Call_Queue', 'send_queue_reminders')); // Callback processing $this->loader->add_action('twp_process_callbacks', 'TWP_Callback_Manager', 'process_callbacks'); @@ -281,6 +284,10 @@ class TWP_Core { wp_schedule_event(time(), 'twp_every_30_seconds', 'twp_process_queue'); } + if (!wp_next_scheduled('twp_queue_reminders')) { + wp_schedule_event(time(), 'twp_every_minute', 'twp_queue_reminders'); + } + if (!wp_next_scheduled('twp_process_callbacks')) { wp_schedule_event(time(), 'twp_every_minute', 'twp_process_callbacks'); } diff --git a/includes/class-twp-deactivator.php b/includes/class-twp-deactivator.php index a1fcd74..687b8a5 100644 --- a/includes/class-twp-deactivator.php +++ b/includes/class-twp-deactivator.php @@ -11,6 +11,7 @@ class TWP_Deactivator { // Clear scheduled events wp_clear_scheduled_hook('twp_check_schedules'); wp_clear_scheduled_hook('twp_process_queue'); + wp_clear_scheduled_hook('twp_queue_reminders'); wp_clear_scheduled_hook('twp_auto_revert_agents'); // Flush rewrite rules diff --git a/includes/class-twp-mobile-phone-page.php b/includes/class-twp-mobile-phone-page.php index 6e727bc..740439f 100644 --- a/includes/class-twp-mobile-phone-page.php +++ b/includes/class-twp-mobile-phone-page.php @@ -55,17 +55,32 @@ class TWP_Mobile_Phone_Page { $user_id, $fcm_token )); - if (!$existing) { + if ($existing) { + // Refresh the expiry on existing session + $wpdb->update($table, + array('expires_at' => date('Y-m-d H:i:s', time() + 7 * DAY_IN_SECONDS)), + array('id' => $existing->id), + array('%s'), + array('%d') + ); + } else { $wpdb->insert($table, array( - 'user_id' => $user_id, - 'fcm_token' => $fcm_token, - 'device_info' => 'WebView Mobile App', - 'is_active' => 1, - 'created_at' => current_time('mysql'), - 'expires_at' => date('Y-m-d H:i:s', time() + 7 * DAY_IN_SECONDS), + 'user_id' => $user_id, + 'refresh_token' => 'webview-' . wp_generate_password(32, false), + 'fcm_token' => $fcm_token, + 'device_info' => 'WebView Mobile App', + 'is_active' => 1, + 'created_at' => current_time('mysql'), + 'expires_at' => date('Y-m-d H:i:s', time() + 7 * DAY_IN_SECONDS), )); + + if ($wpdb->last_error) { + error_log('TWP FCM: Failed to insert token: ' . $wpdb->last_error); + wp_send_json_error('Failed to store token'); + } } + error_log("TWP FCM: Token registered for user $user_id"); wp_send_json_success('FCM token registered'); } diff --git a/includes/class-twp-webhooks.php b/includes/class-twp-webhooks.php index aec5f5c..4e11c3a 100644 --- a/includes/class-twp-webhooks.php +++ b/includes/class-twp-webhooks.php @@ -1276,10 +1276,24 @@ class TWP_Webhooks { if ($updated) { error_log('TWP Queue Action: Updated call status to ' . $status); + + // Cancel FCM queue alerts when call leaves the queue for any reason + if (in_array($status, array('answered', 'hangup', 'transferred', 'timeout', 'completed'))) { + $queued_call = $wpdb->get_row($wpdb->prepare( + "SELECT queue_id FROM $table_name WHERE call_sid = %s", + $call_sid + )); + if ($queued_call) { + require_once plugin_dir_path(__FILE__) . 'class-twp-fcm.php'; + $fcm = new TWP_FCM(); + $fcm->cancel_queue_alert_for_queue($queued_call->queue_id, $call_sid); + error_log('TWP Queue Action: Sent FCM cancel for call ' . $call_sid); + } + } } else { error_log('TWP Queue Action: No call found to update with SID ' . $call_sid); } - + // Return empty response - this is just for tracking return $this->send_twiml_response(''); }