$message, 'embeds' => array( array( 'title' => self::get_notification_title($data), 'color' => self::get_notification_color($data), 'fields' => self::get_discord_fields($data), 'timestamp' => date('c'), 'footer' => array( 'text' => 'Twilio WP Plugin' ) ) ) ); self::send_webhook_request($webhook_url, $payload, 'Discord'); } /** * Send notification to Slack */ public static function send_slack_notification($webhook_url, $data, $message = null) { $payload = array( 'text' => self::get_notification_title($data), 'attachments' => array( array( 'color' => self::get_slack_color($data), 'fields' => self::get_slack_fields($data), 'footer' => 'Twilio WP Plugin', 'ts' => time() ) ) ); self::send_webhook_request($webhook_url, $payload, 'Slack'); } /** * Get notification title */ private static function get_notification_title($data) { $type = isset($data['type']) ? $data['type'] : 'call_event'; switch ($type) { case 'incoming_call': return '📞 Incoming Call'; case 'queue_timeout': return '⏰ Queue Timeout Alert'; case 'missed_call': return '❌ Missed Call'; case 'urgent_voicemail': return '🚨 URGENT Voicemail Alert'; default: return '📋 Call Event'; } } /** * Get notification color for Discord (decimal) */ private static function get_notification_color($data) { $type = isset($data['type']) ? $data['type'] : 'call_event'; switch ($type) { case 'incoming_call': return 3447003; // Blue case 'queue_timeout': return 16776960; // Yellow case 'missed_call': return 15158332; // Red case 'urgent_voicemail': return 16711680; // Bright Red default: return 9807270; // Gray } } /** * Get notification color for Slack (hex) */ private static function get_slack_color($data) { $type = isset($data['type']) ? $data['type'] : 'call_event'; switch ($type) { case 'incoming_call': return '#36a64f'; // Green case 'queue_timeout': return '#ffcc00'; // Yellow case 'missed_call': return '#ff0000'; // Red case 'urgent_voicemail': return '#ff0000'; // Bright Red default: return '#666666'; // Gray } } /** * Get Discord fields */ private static function get_discord_fields($data) { $fields = array(); if (isset($data['caller'])) { $fields[] = array( 'name' => 'Caller', 'value' => $data['caller'], 'inline' => true ); } if (isset($data['queue'])) { $fields[] = array( 'name' => 'Queue', 'value' => $data['queue'], 'inline' => true ); } if (isset($data['duration'])) { $fields[] = array( 'name' => 'Duration', 'value' => $data['duration'] . ' seconds', 'inline' => true ); } if (isset($data['workflow_number'])) { $fields[] = array( 'name' => 'Number Called', 'value' => $data['workflow_number'], 'inline' => true ); } // Urgent voicemail specific fields if (isset($data['type']) && $data['type'] === 'urgent_voicemail') { if (isset($data['from_number'])) { $fields[] = array( 'name' => '📞 From', 'value' => $data['from_number'], 'inline' => true ); } if (isset($data['keyword'])) { $fields[] = array( 'name' => '🔴 Keyword Detected', 'value' => strtoupper($data['keyword']), 'inline' => true ); } if (isset($data['transcription'])) { // Truncate transcription if too long $transcription = $data['transcription']; if (strlen($transcription) > 500) { $transcription = substr($transcription, 0, 497) . '...'; } $fields[] = array( 'name' => '📝 Transcription', 'value' => $transcription, 'inline' => false ); } if (isset($data['admin_url'])) { $fields[] = array( 'name' => '🔗 Action', 'value' => '[Listen to Voicemail](' . $data['admin_url'] . ')', 'inline' => false ); } } return $fields; } /** * Get Slack fields */ private static function get_slack_fields($data) { $fields = array(); if (isset($data['caller'])) { $fields[] = array( 'title' => 'Caller', 'value' => $data['caller'], 'short' => true ); } if (isset($data['queue'])) { $fields[] = array( 'title' => 'Queue', 'value' => $data['queue'], 'short' => true ); } if (isset($data['duration'])) { $fields[] = array( 'title' => 'Duration', 'value' => $data['duration'] . ' seconds', 'short' => true ); } if (isset($data['workflow_number'])) { $fields[] = array( 'title' => 'Number Called', 'value' => $data['workflow_number'], 'short' => true ); } // Urgent voicemail specific fields if (isset($data['type']) && $data['type'] === 'urgent_voicemail') { if (isset($data['from_number'])) { $fields[] = array( 'title' => 'From', 'value' => $data['from_number'], 'short' => true ); } if (isset($data['keyword'])) { $fields[] = array( 'title' => '🔴 Keyword Detected', 'value' => strtoupper($data['keyword']), 'short' => true ); } if (isset($data['transcription'])) { // Truncate transcription if too long $transcription = $data['transcription']; if (strlen($transcription) > 500) { $transcription = substr($transcription, 0, 497) . '...'; } $fields[] = array( 'title' => 'Transcription', 'value' => $transcription, 'short' => false ); } if (isset($data['admin_url'])) { $fields[] = array( 'title' => 'Action', 'value' => '<' . $data['admin_url'] . '|Listen to Voicemail>', 'short' => false ); } } return $fields; } /** * Send webhook request */ private static function send_webhook_request($webhook_url, $payload, $service) { // Send notification immediately without blocking $response = wp_remote_post($webhook_url, array( 'headers' => array( 'Content-Type' => 'application/json', ), 'body' => json_encode($payload), 'timeout' => 10, // Reduce timeout for faster processing 'blocking' => false, // Non-blocking request for immediate processing )); if (is_wp_error($response)) { error_log("TWP {$service} Notification Error: " . $response->get_error_message()); return false; } // For non-blocking requests, we can't check response code immediately error_log("TWP {$service} notification sent (non-blocking)"); return true; } /** * Monitor queue timeouts */ public static function check_queue_timeouts() { global $wpdb; $threshold = get_option('twp_queue_timeout_threshold', 300); // Default 5 minutes $calls_table = $wpdb->prefix . 'twp_queued_calls'; $queues_table = $wpdb->prefix . 'twp_call_queues'; // Find calls that have been waiting too long $timeout_calls = $wpdb->get_results($wpdb->prepare(" SELECT qc.*, q.queue_name FROM {$calls_table} qc LEFT JOIN {$queues_table} q ON qc.queue_id = q.id WHERE qc.status = 'waiting' AND TIMESTAMPDIFF(SECOND, qc.joined_at, NOW()) > %d AND qc.notified_timeout IS NULL ", $threshold)); foreach ($timeout_calls as $call) { // Send timeout notification self::send_call_notification('queue_timeout', array( 'type' => 'queue_timeout', 'caller' => $call->from_number, 'queue' => $call->queue_name, 'duration' => time() - strtotime($call->joined_at), 'workflow_number' => $call->to_number )); // Mark as notified to avoid duplicate notifications $wpdb->update( $calls_table, array('notified_timeout' => current_time('mysql')), array('id' => $call->id), array('%s'), array('%d') ); } } }