Fix recording playback authentication and phone number display

Recording Playback Fixes:
- Added proxy_recording_audio endpoint to handle authenticated playback
- Recordings now play through WordPress proxy like voicemails
- Updated Play/Download buttons to use proxy URLs instead of direct Twilio URLs
- No more Twilio login prompts when playing recordings

Phone Number Display Fixes:
- Fixed outbound call recording display to show customer numbers properly
- For browser phone outbound calls, customer number now shows in "From" field
- Added logic to detect client: calls and swap number display appropriately
- Enhanced logging for debugging outbound call number handling

The recording system now works consistently with the voicemail system,
providing seamless authenticated playback without exposing Twilio credentials.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-08-30 16:54:19 -07:00
parent cf37cbd3cc
commit ae9c6f5e8c
2 changed files with 89 additions and 4 deletions

View File

@@ -2217,8 +2217,9 @@ class TWP_Admin {
html += '<td>' + formatDuration(recording.duration) + '</td>'; html += '<td>' + formatDuration(recording.duration) + '</td>';
html += '<td>'; html += '<td>';
if (recording.has_recording) { if (recording.has_recording) {
html += '<button class="button button-small" onclick="playRecording(\'' + recording.recording_url + '\')">Play</button> '; var proxyUrl = '<?php echo home_url('/wp-json/twilio-webhook/v1/recording-audio/'); ?>' + recording.id;
html += '<a href="' + recording.recording_url + '" class="button button-small" download>Download</a>'; html += '<button class="button button-small" onclick="playRecording(\'' + proxyUrl + '\')">Play</button> ';
html += '<a href="' + proxyUrl + '" class="button button-small" download>Download</a>';
<?php if (current_user_can('manage_options')): ?> <?php if (current_user_can('manage_options')): ?>
html += ' <button class="button button-small button-link-delete" onclick="deleteRecording(' + recording.id + ')">Delete</button>'; html += ' <button class="button button-small button-link-delete" onclick="deleteRecording(' + recording.id + ')">Delete</button>';
<?php endif; ?> <?php endif; ?>
@@ -7415,11 +7416,24 @@ class TWP_Admin {
global $wpdb; global $wpdb;
$recordings_table = $wpdb->prefix . 'twp_call_recordings'; $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, [ $insert_result = $wpdb->insert($recordings_table, [
'call_sid' => $call_sid, 'call_sid' => $call_sid,
'recording_sid' => $recording->sid, 'recording_sid' => $recording->sid,
'from_number' => $call->from, 'from_number' => $from_number,
'to_number' => $call->to, 'to_number' => $to_number,
'agent_id' => $user_id, 'agent_id' => $user_id,
'status' => 'recording', 'status' => 'recording',
'started_at' => current_time('mysql') 'started_at' => current_time('mysql')

View File

@@ -159,6 +159,23 @@ class TWP_Webhooks {
), ),
)); ));
// Call recording audio proxy endpoint
register_rest_route('twilio-webhook/v1', '/recording-audio/(?P<id>\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 // Transcription webhook
register_rest_route('twilio-webhook/v1', '/transcription', array( register_rest_route('twilio-webhook/v1', '/transcription', array(
'methods' => 'POST', 'methods' => 'POST',
@@ -1175,6 +1192,60 @@ class TWP_Webhooks {
exit; 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 * Handle voicemail callback
*/ */