Fix call hold/resume and recording issues, add configurable hold music
## Hold/Resume Fixes - Fixed call disconnection issue when resuming from hold - Changed resume logic from webhook redirect to empty TwiML continuation - This allows browser phone calls to continue normally after hold ## Recording Stop Improvements - Enhanced error handling for recording stop functionality - Check recording status in database before attempting Twilio API call - Handle cases where recording is already stopped or completed - Graceful fallback if Twilio API call fails - Better logging for debugging recording issues ## Configurable Hold Music - Added "Hold Music URL" setting in admin Settings page - Allows admins to specify custom hold music instead of default cowbell - Supports MP3 and WAV files from any publicly accessible URL - Default remains Twilio's cowbell.mp3 for backwards compatibility - Setting automatically saved/loaded through WordPress settings API ## Technical Details - Hold music URL retrieved via get_option() with fallback - Recording stop checks database state before API calls - Improved error messages and logging throughout - All changes maintain backwards compatibility 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
		@@ -398,6 +398,20 @@ class TWP_Admin {
 | 
			
		||||
                    </tr>
 | 
			
		||||
                </table>
 | 
			
		||||
                
 | 
			
		||||
                <h2>Call Settings</h2>
 | 
			
		||||
                <table class="form-table">
 | 
			
		||||
                    <tr>
 | 
			
		||||
                        <th scope="row">Hold Music URL</th>
 | 
			
		||||
                        <td>
 | 
			
		||||
                            <input type="url" name="twp_hold_music_url" 
 | 
			
		||||
                                   value="<?php echo esc_attr(get_option('twp_hold_music_url', 'https://api.twilio.com/cowbell.mp3')); ?>" 
 | 
			
		||||
                                   class="regular-text" />
 | 
			
		||||
                            <p class="description">URL to audio file to play when calls are placed on hold. Must be publicly accessible MP3 or WAV file.</p>
 | 
			
		||||
                            <p class="description"><strong>Suggested sources:</strong> Upload to your Media Library or use a service like Freesound.org for royalty-free music.</p>
 | 
			
		||||
                        </td>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                </table>
 | 
			
		||||
                
 | 
			
		||||
                <h2>Default Queue Settings</h2>
 | 
			
		||||
                <table class="form-table">
 | 
			
		||||
                    <tr>
 | 
			
		||||
@@ -3005,6 +3019,7 @@ class TWP_Admin {
 | 
			
		||||
        register_setting('twilio-wp-settings-group', 'twp_elevenlabs_api_key');
 | 
			
		||||
        register_setting('twilio-wp-settings-group', 'twp_elevenlabs_voice_id');
 | 
			
		||||
        register_setting('twilio-wp-settings-group', 'twp_elevenlabs_model_id');
 | 
			
		||||
        register_setting('twilio-wp-settings-group', 'twp_hold_music_url');
 | 
			
		||||
        register_setting('twilio-wp-settings-group', 'twp_default_queue_timeout');
 | 
			
		||||
        register_setting('twilio-wp-settings-group', 'twp_default_queue_size');
 | 
			
		||||
        register_setting('twilio-wp-settings-group', 'twp_urgent_keywords');
 | 
			
		||||
@@ -6890,17 +6905,20 @@ class TWP_Admin {
 | 
			
		||||
            
 | 
			
		||||
            if ($hold) {
 | 
			
		||||
                // Place call on hold with music
 | 
			
		||||
                $hold_music_url = get_option('twp_hold_music_url', 'https://api.twilio.com/cowbell.mp3');
 | 
			
		||||
                $twiml = new \Twilio\TwiML\VoiceResponse();
 | 
			
		||||
                $twiml->play('https://api.twilio.com/cowbell.mp3', ['loop' => 0]);
 | 
			
		||||
                $twiml->play($hold_music_url, ['loop' => 0]);
 | 
			
		||||
                
 | 
			
		||||
                $call = $client->calls($call_sid)->update([
 | 
			
		||||
                    'twiml' => $twiml->asXML()
 | 
			
		||||
                ]);
 | 
			
		||||
            } else {
 | 
			
		||||
                // Resume call by redirecting back to conference or original context
 | 
			
		||||
                // Resume call - for browser phone, just provide empty TwiML to continue
 | 
			
		||||
                $twiml = new \Twilio\TwiML\VoiceResponse();
 | 
			
		||||
                // Empty TwiML allows the call to continue normally
 | 
			
		||||
                
 | 
			
		||||
                $call = $client->calls($call_sid)->update([
 | 
			
		||||
                    'url' => home_url('/wp-json/twilio-webhook/v1/resume-call'),
 | 
			
		||||
                    'method' => 'POST'
 | 
			
		||||
                    'twiml' => $twiml->asXML()
 | 
			
		||||
                ]);
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
@@ -7065,19 +7083,44 @@ class TWP_Admin {
 | 
			
		||||
        $call_sid = sanitize_text_field($_POST['call_sid']);
 | 
			
		||||
        $recording_sid = sanitize_text_field($_POST['recording_sid']);
 | 
			
		||||
        
 | 
			
		||||
        if (empty($recording_sid)) {
 | 
			
		||||
            wp_send_json_error('Recording SID is required');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        global $wpdb;
 | 
			
		||||
        $recordings_table = $wpdb->prefix . 'twp_call_recordings';
 | 
			
		||||
        
 | 
			
		||||
        // Check if recording exists and is active
 | 
			
		||||
        $recording_info = $wpdb->get_row($wpdb->prepare(
 | 
			
		||||
            "SELECT * FROM $recordings_table WHERE recording_sid = %s",
 | 
			
		||||
            $recording_sid
 | 
			
		||||
        ));
 | 
			
		||||
        
 | 
			
		||||
        if (!$recording_info) {
 | 
			
		||||
            wp_send_json_error('Recording not found in database');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if ($recording_info->status === 'completed') {
 | 
			
		||||
            // Already stopped, just update UI
 | 
			
		||||
            wp_send_json_success(['message' => 'Recording already stopped']);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        try {
 | 
			
		||||
            $twilio = new TWP_Twilio_API();
 | 
			
		||||
            $client = $twilio->get_client();
 | 
			
		||||
            
 | 
			
		||||
            // Stop the recording
 | 
			
		||||
            $recording = $client->calls($call_sid)
 | 
			
		||||
                ->recordings($recording_sid)
 | 
			
		||||
                ->update(['status' => 'stopped']);
 | 
			
		||||
            
 | 
			
		||||
            // Update database
 | 
			
		||||
            global $wpdb;
 | 
			
		||||
            $recordings_table = $wpdb->prefix . 'twp_call_recordings';
 | 
			
		||||
            // Try to stop the recording in Twilio
 | 
			
		||||
            try {
 | 
			
		||||
                $recording = $client->recordings($recording_sid)->update(['status' => 'stopped']);
 | 
			
		||||
            } catch (Exception $twilio_error) {
 | 
			
		||||
                // Recording might already be stopped or completed on Twilio's side
 | 
			
		||||
                error_log('TWP: Could not stop recording in Twilio (may already be stopped): ' . $twilio_error->getMessage());
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // Update database regardless
 | 
			
		||||
            $wpdb->update(
 | 
			
		||||
                $recordings_table,
 | 
			
		||||
                [
 | 
			
		||||
@@ -7089,6 +7132,7 @@ class TWP_Admin {
 | 
			
		||||
            
 | 
			
		||||
            wp_send_json_success(['message' => 'Recording stopped']);
 | 
			
		||||
        } catch (Exception $e) {
 | 
			
		||||
            error_log('TWP: Error stopping recording: ' . $e->getMessage());
 | 
			
		||||
            wp_send_json_error('Failed to stop recording: ' . $e->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user