diff --git a/admin/class-twp-admin.php b/admin/class-twp-admin.php index 2218722..6999247 100644 --- a/admin/class-twp-admin.php +++ b/admin/class-twp-admin.php @@ -2217,8 +2217,9 @@ class TWP_Admin { html += '' + formatDuration(recording.duration) + ''; html += ''; if (recording.has_recording) { - html += ' '; - html += 'Download'; + var proxyUrl = '' + recording.id; + html += ' '; + html += 'Download'; html += ' '; @@ -7415,11 +7416,24 @@ class TWP_Admin { global $wpdb; $recordings_table = $wpdb->prefix . 'twp_call_recordings'; + // For outbound calls from browser phone, swap the numbers for better display + $from_number = $call->from; + $to_number = $call->to; + + // If this is a browser phone call (from contains 'client:'), then the customer is the 'to' number + if (strpos($call->from, 'client:') === 0) { + // This is an outbound call from browser phone + // Store the customer number as 'from' for display consistency + $from_number = $call->to; // Customer number + $to_number = $call->from; // Browser phone client + error_log("TWP: Outbound browser call - Customer: {$call->to}, Agent: {$call->from}"); + } + $insert_result = $wpdb->insert($recordings_table, [ 'call_sid' => $call_sid, 'recording_sid' => $recording->sid, - 'from_number' => $call->from, - 'to_number' => $call->to, + 'from_number' => $from_number, + 'to_number' => $to_number, 'agent_id' => $user_id, 'status' => 'recording', 'started_at' => current_time('mysql') diff --git a/includes/class-twp-webhooks.php b/includes/class-twp-webhooks.php index 5dd2bad..7090602 100644 --- a/includes/class-twp-webhooks.php +++ b/includes/class-twp-webhooks.php @@ -159,6 +159,23 @@ class TWP_Webhooks { ), )); + // Call recording audio proxy endpoint + register_rest_route('twilio-webhook/v1', '/recording-audio/(?P\d+)', array( + 'methods' => 'GET', + 'callback' => array($this, 'proxy_recording_audio'), + 'permission_callback' => function() { + // Check if user is logged in with proper permissions + return is_user_logged_in() && current_user_can('manage_options'); + }, + 'args' => array( + 'id' => array( + 'validate_callback' => function($param, $request, $key) { + return is_numeric($param); + } + ), + ), + )); + // Transcription webhook register_rest_route('twilio-webhook/v1', '/transcription', array( 'methods' => 'POST', @@ -1175,6 +1192,60 @@ class TWP_Webhooks { exit; } + /** + * Proxy call recording audio through WordPress + */ + public function proxy_recording_audio($request) { + // Permission already checked by REST API permission_callback + $recording_id = intval($request->get_param('id')); + + global $wpdb; + $table_name = $wpdb->prefix . 'twp_call_recordings'; + + $recording = $wpdb->get_row($wpdb->prepare( + "SELECT recording_url FROM $table_name WHERE id = %d", + $recording_id + )); + + if (!$recording || !$recording->recording_url) { + header('HTTP/1.0 404 Not Found'); + exit('Recording not found'); + } + + // Fetch the audio from Twilio using authenticated request + $twilio = new TWP_Twilio_API(); + $account_sid = get_option('twp_twilio_account_sid'); + $auth_token = get_option('twp_twilio_auth_token'); + + // Add .mp3 to the URL if not present + $audio_url = $recording->recording_url; + if (strpos($audio_url, '.mp3') === false && strpos($audio_url, '.wav') === false) { + $audio_url .= '.mp3'; + } + + // Fetch audio with authentication + $response = wp_remote_get($audio_url, array( + 'headers' => array( + 'Authorization' => 'Basic ' . base64_encode($account_sid . ':' . $auth_token) + ), + 'timeout' => 30 + )); + + if (is_wp_error($response)) { + return new WP_Error('fetch_error', 'Unable to fetch recording', array('status' => 500)); + } + + $body = wp_remote_retrieve_body($response); + $content_type = wp_remote_retrieve_header($response, 'content-type') ?: 'audio/mpeg'; + + // Return audio with proper headers + header('Content-Type: ' . $content_type); + header('Content-Length: ' . strlen($body)); + header('Cache-Control: private, max-age=3600'); + echo $body; + exit; + } + /** * Handle voicemail callback */