Fix extension transfer system and browser phone compatibility
Major Fixes: - Fixed extension transfers going directly to voicemail for available agents - Resolved browser phone call disconnections during transfers - Fixed voicemail transcription placeholder text issue - Added Firefox compatibility with automatic media permissions Extension Transfer Improvements: - Changed from active client dialing to proper queue-based system - Fixed client name generation consistency (user_login vs display_name) - Added 2-minute timeout with automatic voicemail fallback - Enhanced agent availability detection for browser phone users Browser Phone Enhancements: - Added automatic microphone/speaker permission requests - Improved Firefox compatibility with explicit getUserMedia calls - Fixed client naming consistency across capability tokens and call acceptance - Added comprehensive error handling for permission denials Database & System Updates: - Added auto_busy_at column for automatic agent status reversion - Implemented 1-minute auto-revert system for busy agents with cron job - Updated database version to 1.6.2 for automatic migration - Fixed voicemail user_id association for extension voicemails Call Statistics & Logging: - Fixed browser phone calls not appearing in agent statistics - Enhanced call logging with proper agent_id association in JSON format - Improved customer number detection for complex call topologies - Added comprehensive debugging for call leg detection Voicemail & Transcription: - Replaced placeholder transcription with real Twilio API integration - Added manual transcription request capability for existing voicemails - Enhanced voicemail callback handling with user_id support - Fixed transcription webhook processing for extension voicemails Technical Improvements: - Standardized client name generation across all components - Added ElevenLabs TTS integration to agent connection messages - Enhanced error handling and logging throughout transfer system - Fixed TwiML generation syntax errors in dial() methods 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -5036,23 +5036,53 @@ class TWP_Admin {
|
||||
wp_send_json_error('Voicemail not found');
|
||||
}
|
||||
|
||||
// For now, we'll use a placeholder transcription since we'd need a speech-to-text service
|
||||
// In a real implementation, you'd send the recording URL to a transcription service
|
||||
$placeholder_transcription = "This is a placeholder transcription. In a production environment, this would be generated using a speech-to-text service like Google Cloud Speech-to-Text, Amazon Transcribe, or Twilio's built-in transcription service.";
|
||||
|
||||
$result = $wpdb->update(
|
||||
$table_name,
|
||||
array('transcription' => $placeholder_transcription),
|
||||
array('id' => $voicemail_id),
|
||||
array('%s'),
|
||||
array('%d')
|
||||
);
|
||||
|
||||
if ($result !== false) {
|
||||
wp_send_json_success(array('transcription' => $placeholder_transcription));
|
||||
} else {
|
||||
wp_send_json_error('Error generating transcription');
|
||||
// Check if voicemail already has a transcription
|
||||
if (!empty($voicemail->transcription) && $voicemail->transcription !== 'Transcription pending...') {
|
||||
wp_send_json_success(array(
|
||||
'message' => 'Transcription already exists',
|
||||
'transcription' => $voicemail->transcription
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to request transcription from Twilio
|
||||
if (!empty($voicemail->recording_url)) {
|
||||
try {
|
||||
$api = new TWP_Twilio_API();
|
||||
$client = $api->get_client();
|
||||
|
||||
// Extract recording SID from URL
|
||||
preg_match('/Recordings\/([A-Za-z0-9]+)/', $voicemail->recording_url, $matches);
|
||||
$recording_sid = $matches[1] ?? '';
|
||||
|
||||
if ($recording_sid) {
|
||||
// Create transcription request
|
||||
$transcription = $client->transcriptions->create($recording_sid);
|
||||
|
||||
// Update status to pending
|
||||
$wpdb->update(
|
||||
$table_name,
|
||||
array('transcription' => 'Transcription in progress...'),
|
||||
array('id' => $voicemail_id),
|
||||
array('%s'),
|
||||
array('%d')
|
||||
);
|
||||
|
||||
wp_send_json_success(array(
|
||||
'message' => 'Transcription requested successfully',
|
||||
'transcription' => 'Transcription in progress...'
|
||||
));
|
||||
return;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
error_log('TWP Transcription Error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback - manual transcription not available
|
||||
wp_send_json_error(array(
|
||||
'message' => 'Unable to request transcription. Automatic transcription should occur when voicemails are recorded.'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -5268,15 +5298,33 @@ class TWP_Admin {
|
||||
$groups_table = $wpdb->prefix . 'twp_group_members';
|
||||
$queues_table = $wpdb->prefix . 'twp_call_queues';
|
||||
|
||||
// Verify user is a member of this queue's agent group
|
||||
$is_member = $wpdb->get_var($wpdb->prepare("
|
||||
SELECT COUNT(*)
|
||||
FROM $groups_table gm
|
||||
JOIN $queues_table q ON gm.group_id = q.agent_group_id
|
||||
WHERE gm.user_id = %d AND q.id = %d
|
||||
", $user_id, $queue_id));
|
||||
// Check if this is a user's personal or hold queue first
|
||||
$queue_info = $wpdb->get_row($wpdb->prepare("
|
||||
SELECT * FROM $queues_table WHERE id = %d
|
||||
", $queue_id));
|
||||
|
||||
if (!$is_member) {
|
||||
$is_authorized = false;
|
||||
|
||||
// Check if it's the user's own personal or hold queue
|
||||
if ($queue_info && $queue_info->user_id == $user_id &&
|
||||
($queue_info->queue_type == 'personal' || $queue_info->queue_type == 'hold')) {
|
||||
$is_authorized = true;
|
||||
error_log("TWP: User {$user_id} authorized for their own {$queue_info->queue_type} queue {$queue_id}");
|
||||
} else {
|
||||
// For regular queues, verify user is a member of this queue's agent group
|
||||
$is_member = $wpdb->get_var($wpdb->prepare("
|
||||
SELECT COUNT(*)
|
||||
FROM $groups_table gm
|
||||
JOIN $queues_table q ON gm.group_id = q.agent_group_id
|
||||
WHERE gm.user_id = %d AND q.id = %d
|
||||
", $user_id, $queue_id));
|
||||
|
||||
if ($is_member) {
|
||||
$is_authorized = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$is_authorized) {
|
||||
wp_send_json_error('You are not authorized to accept calls from this queue');
|
||||
return;
|
||||
}
|
||||
@@ -5984,7 +6032,7 @@ class TWP_Admin {
|
||||
$call_sid = isset($agent_call_result['data']['sid']) ? $agent_call_result['data']['sid'] : null;
|
||||
|
||||
// Set agent to busy
|
||||
TWP_Agent_Manager::set_agent_status(get_current_user_id(), 'busy', $call_sid);
|
||||
TWP_Agent_Manager::set_agent_status(get_current_user_id(), 'busy', $call_sid, true);
|
||||
|
||||
// Log the outbound call
|
||||
TWP_Call_Logger::log_call(array(
|
||||
@@ -6701,11 +6749,41 @@ class TWP_Admin {
|
||||
$current_user_id
|
||||
));
|
||||
}
|
||||
// Get agent status and stats
|
||||
$agent_status = TWP_Agent_Manager::get_agent_status($current_user_id);
|
||||
$agent_stats = TWP_Agent_Manager::get_agent_stats($current_user_id);
|
||||
$is_logged_in = TWP_Agent_Manager::is_agent_logged_in($current_user_id);
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1>Browser Phone</h1>
|
||||
<p>Make and receive calls directly from your browser using Twilio Client.</p>
|
||||
|
||||
<!-- Agent Status Bar -->
|
||||
<div class="agent-status-bar">
|
||||
<div class="status-info">
|
||||
<strong>Extension:</strong>
|
||||
<span class="extension-badge"><?php echo $extension_data ? esc_html($extension_data->extension) : 'Not Assigned'; ?></span>
|
||||
|
||||
<strong>Login Status:</strong>
|
||||
<button id="login-toggle-btn" class="button <?php echo $is_logged_in ? 'button-secondary' : 'button-primary'; ?>" onclick="toggleAgentLogin()">
|
||||
<?php echo $is_logged_in ? 'Log Out' : 'Log In'; ?>
|
||||
</button>
|
||||
|
||||
<strong>Your Status:</strong>
|
||||
<select id="agent-status-select" onchange="updateAgentStatus(this.value)" <?php echo !$is_logged_in ? 'disabled' : ''; ?>>
|
||||
<option value="available" <?php selected($agent_status->status ?? '', 'available'); ?>>Available</option>
|
||||
<option value="busy" <?php selected($agent_status->status ?? '', 'busy'); ?>>Busy</option>
|
||||
<option value="offline" <?php selected($agent_status->status ?? 'offline', 'offline'); ?>>Offline</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="agent-stats">
|
||||
<span>Calls Today: <strong><?php echo $agent_stats['calls_today']; ?></strong></span>
|
||||
<span>Total Calls: <strong><?php echo $agent_stats['total_calls']; ?></strong></span>
|
||||
<span>Avg Duration: <strong><?php echo round($agent_stats['avg_duration'] ?? 0); ?>s</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="browser-phone-container">
|
||||
<div class="phone-interface">
|
||||
<div class="phone-display">
|
||||
@@ -7189,6 +7267,42 @@ class TWP_Admin {
|
||||
});
|
||||
}
|
||||
|
||||
// Request microphone and speaker permissions
|
||||
async function requestMediaPermissions() {
|
||||
try {
|
||||
console.log('Requesting media permissions...');
|
||||
|
||||
// Request microphone permission
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: true,
|
||||
video: false
|
||||
});
|
||||
|
||||
// Stop the stream immediately as we just needed permission
|
||||
stream.getTracks().forEach(track => track.stop());
|
||||
|
||||
console.log('Media permissions granted');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Media permission denied or not available:', error);
|
||||
|
||||
// Show user-friendly error message
|
||||
let errorMessage = 'Microphone access is required for browser phone functionality. ';
|
||||
|
||||
if (error.name === 'NotAllowedError') {
|
||||
errorMessage += 'Please allow microphone access in your browser settings and refresh the page.';
|
||||
} else if (error.name === 'NotFoundError') {
|
||||
errorMessage += 'No microphone found. Please connect a microphone and try again.';
|
||||
} else {
|
||||
errorMessage += 'Please check your browser settings and try again.';
|
||||
}
|
||||
|
||||
$('#browser-phone-error').show().find('.notice-message').text(errorMessage);
|
||||
$('#browser-phone-status').text('Permission denied').removeClass('online').addClass('offline');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function setupTwilioDevice(token) {
|
||||
try {
|
||||
// Check if Twilio SDK is available
|
||||
@@ -7196,6 +7310,12 @@ class TWP_Admin {
|
||||
throw new Error('Twilio Voice SDK not loaded');
|
||||
}
|
||||
|
||||
// Request media permissions before setting up device
|
||||
const hasPermissions = await requestMediaPermissions();
|
||||
if (!hasPermissions) {
|
||||
return; // Stop setup if permissions denied
|
||||
}
|
||||
|
||||
// Clean up existing device if any
|
||||
if (device) {
|
||||
await device.destroy();
|
||||
@@ -8258,6 +8378,50 @@ class TWP_Admin {
|
||||
notice.fadeOut();
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
// Agent status functions for the status bar
|
||||
function toggleAgentLogin() {
|
||||
$.ajax({
|
||||
url: ajaxurl,
|
||||
method: 'POST',
|
||||
data: {
|
||||
action: 'twp_toggle_agent_login',
|
||||
nonce: '<?php echo wp_create_nonce('twp_ajax_nonce'); ?>'
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
showNotice('Failed to change login status: ' + response.data, 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showNotice('Failed to change login status', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateAgentStatus(status) {
|
||||
$.ajax({
|
||||
url: ajaxurl,
|
||||
method: 'POST',
|
||||
data: {
|
||||
action: 'twp_update_agent_status',
|
||||
status: status,
|
||||
nonce: '<?php echo wp_create_nonce('twp_ajax_nonce'); ?>'
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showNotice('Status updated to ' + status, 'success');
|
||||
} else {
|
||||
showNotice('Failed to update status: ' + response.data, 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showNotice('Failed to update status', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
@@ -8436,6 +8600,20 @@ class TWP_Admin {
|
||||
|
||||
// Use the Hold Queue system to properly hold the call
|
||||
require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-twp-user-queue-manager.php';
|
||||
|
||||
// Check if user has queues, create them if not
|
||||
$extension_data = TWP_User_Queue_Manager::get_user_extension_data($current_user_id);
|
||||
if (!$extension_data || !$extension_data['hold_queue_id']) {
|
||||
error_log("TWP: User doesn't have queues, creating them now");
|
||||
$queue_creation = TWP_User_Queue_Manager::create_user_queues($current_user_id);
|
||||
if (!$queue_creation['success']) {
|
||||
error_log("TWP: Failed to create user queues - " . $queue_creation['error']);
|
||||
wp_send_json_error('Failed to create hold queue: ' . $queue_creation['error']);
|
||||
return;
|
||||
}
|
||||
$extension_data = TWP_User_Queue_Manager::get_user_extension_data($current_user_id);
|
||||
}
|
||||
|
||||
$queue_result = TWP_User_Queue_Manager::transfer_to_hold_queue($current_user_id, $target_call_sid);
|
||||
|
||||
if ($queue_result['success']) {
|
||||
@@ -8503,6 +8681,20 @@ class TWP_Admin {
|
||||
|
||||
// Use the Hold Queue system to properly resume the call
|
||||
require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-twp-user-queue-manager.php';
|
||||
|
||||
// Check if user has queues, create them if not
|
||||
$extension_data = TWP_User_Queue_Manager::get_user_extension_data($current_user_id);
|
||||
if (!$extension_data || !$extension_data['hold_queue_id']) {
|
||||
error_log("TWP: User doesn't have queues for resume, creating them now");
|
||||
$queue_creation = TWP_User_Queue_Manager::create_user_queues($current_user_id);
|
||||
if (!$queue_creation['success']) {
|
||||
error_log("TWP: Failed to create user queues - " . $queue_creation['error']);
|
||||
wp_send_json_error('Failed to create queues: ' . $queue_creation['error']);
|
||||
return;
|
||||
}
|
||||
$extension_data = TWP_User_Queue_Manager::get_user_extension_data($current_user_id);
|
||||
}
|
||||
|
||||
$queue_result = TWP_User_Queue_Manager::resume_from_hold($current_user_id, $target_call_sid);
|
||||
|
||||
if ($queue_result['success']) {
|
||||
@@ -8519,7 +8711,10 @@ class TWP_Admin {
|
||||
|
||||
// If it's a personal queue, try to connect directly to agent
|
||||
if ($queue->queue_type === 'personal') {
|
||||
$twiml->say('Resuming your call.', ['voice' => 'alice']);
|
||||
// Use TTS helper for ElevenLabs support
|
||||
require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-twp-tts-helper.php';
|
||||
$tts_helper = TWP_TTS_Helper::get_instance();
|
||||
$tts_helper->add_tts_to_twiml($twiml, 'Resuming your call.');
|
||||
|
||||
// Get the agent's phone number
|
||||
$agent_number = get_user_meta($current_user_id, 'twp_phone_number', true);
|
||||
@@ -8527,7 +8722,8 @@ class TWP_Admin {
|
||||
$dial = $twiml->dial(['timeout' => 30]);
|
||||
$dial->number($agent_number);
|
||||
} else {
|
||||
$twiml->say('Unable to locate agent. Please try again.', ['voice' => 'alice']);
|
||||
// Use TTS helper for error message
|
||||
$tts_helper->add_tts_to_twiml($twiml, 'Unable to locate agent. Please try again.');
|
||||
$twiml->hangup();
|
||||
}
|
||||
} else {
|
||||
@@ -8669,6 +8865,8 @@ class TWP_Admin {
|
||||
// It's an extension, find the user's queue
|
||||
$user_id = TWP_User_Queue_Manager::get_user_by_extension($target);
|
||||
|
||||
error_log("TWP Transfer: Looking up extension {$target}, found user_id: " . ($user_id ?: 'none'));
|
||||
|
||||
if (!$user_id) {
|
||||
wp_send_json_error('Extension not found');
|
||||
return;
|
||||
@@ -8677,31 +8875,143 @@ class TWP_Admin {
|
||||
$extension_data = TWP_User_Queue_Manager::get_user_extension_data($user_id);
|
||||
$target_queue_id = $extension_data['personal_queue_id'];
|
||||
|
||||
// Move call to new queue using our queue system
|
||||
// Find customer call leg for transfer FIRST (important for outbound calls)
|
||||
$customer_call_sid = $this->find_customer_call_leg($call_sid, $twilio);
|
||||
error_log("TWP Transfer: Using customer call leg {$customer_call_sid} for extension transfer (original: {$call_sid})");
|
||||
|
||||
// Move call to new queue using the CUSTOMER call SID for proper tracking
|
||||
$next_position = $wpdb->get_var($wpdb->prepare(
|
||||
"SELECT COALESCE(MAX(position), 0) + 1 FROM {$wpdb->prefix}twp_queued_calls
|
||||
WHERE queue_id = %d AND status = 'waiting'",
|
||||
$target_queue_id
|
||||
));
|
||||
|
||||
$result = $wpdb->update(
|
||||
$wpdb->prefix . 'twp_queued_calls',
|
||||
array(
|
||||
// First check if call already exists in queue table
|
||||
$existing_call = $wpdb->get_row($wpdb->prepare(
|
||||
"SELECT * FROM {$wpdb->prefix}twp_queued_calls WHERE call_sid = %s",
|
||||
$customer_call_sid
|
||||
));
|
||||
|
||||
if ($existing_call) {
|
||||
// Update existing call record
|
||||
$result = $wpdb->update(
|
||||
$wpdb->prefix . 'twp_queued_calls',
|
||||
array(
|
||||
'queue_id' => $target_queue_id,
|
||||
'position' => $next_position,
|
||||
'status' => 'waiting'
|
||||
),
|
||||
array('call_sid' => $customer_call_sid),
|
||||
array('%d', '%d', '%s'),
|
||||
array('%s')
|
||||
);
|
||||
} else {
|
||||
// Get call details from Twilio for new record
|
||||
$client = $twilio->get_client();
|
||||
try {
|
||||
$call = $client->calls($customer_call_sid)->fetch();
|
||||
$from_number = $call->from;
|
||||
$to_number = $call->to;
|
||||
} catch (Exception $e) {
|
||||
error_log("TWP Transfer: Could not fetch call details: " . $e->getMessage());
|
||||
$from_number = '';
|
||||
$to_number = '';
|
||||
}
|
||||
|
||||
// Insert new call record
|
||||
$insert_data = array(
|
||||
'queue_id' => $target_queue_id,
|
||||
'position' => $next_position
|
||||
),
|
||||
array('call_sid' => $call_sid),
|
||||
array('%d', '%d'),
|
||||
array('%s')
|
||||
);
|
||||
'call_sid' => $customer_call_sid,
|
||||
'from_number' => $from_number,
|
||||
'to_number' => $to_number,
|
||||
'position' => $next_position,
|
||||
'status' => 'waiting'
|
||||
);
|
||||
|
||||
// Check if enqueued_at column exists
|
||||
$calls_table = $wpdb->prefix . 'twp_queued_calls';
|
||||
$columns = $wpdb->get_col("DESCRIBE $calls_table");
|
||||
if (in_array('enqueued_at', $columns)) {
|
||||
$insert_data['enqueued_at'] = current_time('mysql');
|
||||
} else {
|
||||
$insert_data['joined_at'] = current_time('mysql');
|
||||
}
|
||||
|
||||
$result = $wpdb->insert($calls_table, $insert_data);
|
||||
}
|
||||
|
||||
if ($result !== false) {
|
||||
// Update call with new queue wait URL
|
||||
$twilio->update_call($call_sid, array(
|
||||
'url' => site_url('/wp-json/twilio-webhook/v1/queue-wait?queue_id=' . $target_queue_id)
|
||||
|
||||
// Check if target user is logged in and available using proper agent manager
|
||||
require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-twp-agent-manager.php';
|
||||
$is_logged_in = TWP_Agent_Manager::is_agent_logged_in($user_id);
|
||||
$agent_status = TWP_Agent_Manager::get_agent_status($user_id);
|
||||
$is_available = $is_logged_in && ($agent_status && $agent_status->status === 'available');
|
||||
|
||||
error_log("TWP Transfer: Extension {$target} to User {$user_id} - Logged in: " . ($is_logged_in ? 'yes' : 'no') . ", Status: " . ($agent_status ? $agent_status->status : 'unknown') . ", Available: " . ($is_available ? 'yes' : 'no'));
|
||||
|
||||
// Get target user details
|
||||
$target_user = get_user_by('id', $user_id);
|
||||
$agent_phone = get_user_meta($user_id, 'twp_phone_number', true);
|
||||
|
||||
// Create TwiML for extension transfer with timeout and voicemail
|
||||
$twiml = new \Twilio\TwiML\VoiceResponse();
|
||||
|
||||
// Use TTS helper for ElevenLabs support
|
||||
require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-twp-tts-helper.php';
|
||||
$tts_helper = TWP_TTS_Helper::get_instance();
|
||||
$tts_helper->add_tts_to_twiml($twiml, 'Transferring to extension ' . $target . '. Please hold.');
|
||||
|
||||
if ($is_available || $is_logged_in) {
|
||||
// Agent is logged in - place call in their personal queue with 2-minute timeout
|
||||
error_log("TWP Transfer: Agent {$user_id} is logged in, placing call in personal queue with timeout");
|
||||
|
||||
// Redirect to queue wait with timeout
|
||||
$queue_wait_url = home_url('/wp-json/twilio-webhook/v1/queue-wait');
|
||||
$queue_wait_url = add_query_arg(array(
|
||||
'queue_id' => $target_queue_id,
|
||||
'call_sid' => $customer_call_sid,
|
||||
'timeout' => 120, // 2 minutes
|
||||
'timeout_action' => home_url('/wp-json/twilio-webhook/v1/extension-voicemail?user_id=' . $user_id . '&extension=' . $target)
|
||||
), $queue_wait_url);
|
||||
|
||||
$twiml->redirect($queue_wait_url, ['method' => 'POST']);
|
||||
} else {
|
||||
// Agent is offline or no phone configured - go straight to voicemail
|
||||
error_log("TWP Transfer: Agent {$user_id} is offline or has no phone, sending to voicemail");
|
||||
|
||||
// Get voicemail prompt from personal queue settings
|
||||
$personal_queue = $wpdb->get_row($wpdb->prepare(
|
||||
"SELECT * FROM {$wpdb->prefix}twp_call_queues WHERE id = %d",
|
||||
$target_queue_id
|
||||
));
|
||||
|
||||
$voicemail_prompt = $personal_queue && $personal_queue->voicemail_prompt
|
||||
? $personal_queue->voicemail_prompt
|
||||
: sprintf('%s is not available. Please leave a message after the tone.', $target_user->display_name);
|
||||
|
||||
$tts_helper->add_tts_to_twiml($twiml, $voicemail_prompt);
|
||||
|
||||
// Record voicemail with proper callback to save to database
|
||||
$twiml->record([
|
||||
'action' => home_url('/wp-json/twilio-webhook/v1/voicemail-callback?user_id=' . $user_id),
|
||||
'maxLength' => 120, // 2 minutes max
|
||||
'playBeep' => true,
|
||||
'transcribe' => true,
|
||||
'transcribeCallback' => home_url('/wp-json/twilio-webhook/v1/transcription?user_id=' . $user_id)
|
||||
]);
|
||||
}
|
||||
|
||||
// Update the customer call with proper TwiML
|
||||
$result = $twilio->update_call($customer_call_sid, array(
|
||||
'twiml' => $twiml->asXML()
|
||||
));
|
||||
|
||||
wp_send_json_success(['message' => 'Call transferred to extension ' . $target]);
|
||||
if ($result['success']) {
|
||||
wp_send_json_success(['message' => 'Call transferred to extension ' . $target]);
|
||||
} else {
|
||||
wp_send_json_error('Failed to transfer call: ' . $result['error']);
|
||||
}
|
||||
} else {
|
||||
wp_send_json_error('Failed to transfer call to queue');
|
||||
}
|
||||
@@ -8733,14 +9043,35 @@ class TWP_Admin {
|
||||
$customer_call_sid = $this->find_customer_call_leg($call_sid, $twilio);
|
||||
error_log("TWP Transfer: Using customer call leg {$customer_call_sid} for queue transfer (original: {$call_sid})");
|
||||
|
||||
// Update customer call with new queue wait URL
|
||||
$twilio->update_call($customer_call_sid, array(
|
||||
'url' => site_url('/wp-json/twilio-webhook/v1/queue-wait?queue_id=' . $target_queue_id)
|
||||
// Create TwiML to redirect call to queue
|
||||
$twiml = new \Twilio\TwiML\VoiceResponse();
|
||||
|
||||
// Use TTS helper for ElevenLabs support
|
||||
require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-twp-tts-helper.php';
|
||||
$tts_helper = TWP_TTS_Helper::get_instance();
|
||||
$tts_helper->add_tts_to_twiml($twiml, 'Transferring your call. Please hold.');
|
||||
|
||||
// Redirect to queue wait endpoint
|
||||
$queue_wait_url = home_url('/wp-json/twilio-webhook/v1/queue-wait');
|
||||
$queue_wait_url = add_query_arg(array(
|
||||
'queue_id' => $target_queue_id,
|
||||
'call_sid' => $customer_call_sid
|
||||
), $queue_wait_url);
|
||||
|
||||
$twiml->redirect($queue_wait_url, ['method' => 'POST']);
|
||||
|
||||
// Update the customer call with proper TwiML
|
||||
$result = $twilio->update_call($customer_call_sid, array(
|
||||
'twiml' => $twiml->asXML()
|
||||
));
|
||||
|
||||
wp_send_json_success(['message' => 'Call transferred to queue']);
|
||||
if ($result['success']) {
|
||||
wp_send_json_success(['message' => 'Call transferred to queue']);
|
||||
} else {
|
||||
wp_send_json_error('Failed to transfer call: ' . $result['error']);
|
||||
}
|
||||
} else {
|
||||
wp_send_json_error('Failed to transfer call to queue');
|
||||
wp_send_json_error('Failed to update queue database');
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -8753,7 +9084,11 @@ class TWP_Admin {
|
||||
|
||||
// Create TwiML for client transfer
|
||||
$twiml = new \Twilio\TwiML\VoiceResponse();
|
||||
$twiml->say('Transferring your call to ' . $agent_name . '. Please hold.');
|
||||
|
||||
// Use TTS helper for ElevenLabs support
|
||||
require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-twp-tts-helper.php';
|
||||
$tts_helper = TWP_TTS_Helper::get_instance();
|
||||
$tts_helper->add_tts_to_twiml($twiml, 'Transferring your call to ' . $agent_name . '. Please hold.');
|
||||
|
||||
// Use Dial with client endpoint
|
||||
$dial = $twiml->dial();
|
||||
@@ -8776,7 +9111,11 @@ class TWP_Admin {
|
||||
} elseif (preg_match('/^\+?[1-9]\d{1,14}$/', $target)) {
|
||||
// Transfer to phone number
|
||||
$twiml = new \Twilio\TwiML\VoiceResponse();
|
||||
$twiml->say('Transferring your call. Please hold.');
|
||||
|
||||
// Use TTS helper for ElevenLabs support
|
||||
require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-twp-tts-helper.php';
|
||||
$tts_helper = TWP_TTS_Helper::get_instance();
|
||||
$tts_helper->add_tts_to_twiml($twiml, 'Transferring your call. Please hold.');
|
||||
$twiml->dial($target);
|
||||
$twiml_xml = $twiml->asXML();
|
||||
|
||||
@@ -8845,13 +9184,26 @@ class TWP_Admin {
|
||||
$customer_call_sid = $this->find_customer_call_leg($call_sid, $twilio);
|
||||
error_log("TWP Requeue: Using customer call leg {$customer_call_sid} for requeue (original: {$call_sid})");
|
||||
|
||||
// Create TwiML using the TWP_Twilio_API method that works
|
||||
$wait_url = home_url('/wp-json/twilio-webhook/v1/queue-wait');
|
||||
$twiml_xml = $twilio->create_queue_twiml($queue->queue_name, 'Placing you back in the queue. Please hold.', $wait_url);
|
||||
// Create proper TwiML using VoiceResponse
|
||||
$twiml = new \Twilio\TwiML\VoiceResponse();
|
||||
|
||||
// Use TTS helper for ElevenLabs support
|
||||
require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-twp-tts-helper.php';
|
||||
$tts_helper = TWP_TTS_Helper::get_instance();
|
||||
$tts_helper->add_tts_to_twiml($twiml, 'Placing you back in the queue. Please hold.');
|
||||
|
||||
// Redirect to queue wait endpoint with proper parameters
|
||||
$queue_wait_url = home_url('/wp-json/twilio-webhook/v1/queue-wait');
|
||||
$queue_wait_url = add_query_arg(array(
|
||||
'queue_id' => $queue_id,
|
||||
'call_sid' => $customer_call_sid
|
||||
), $queue_wait_url);
|
||||
|
||||
$twiml->redirect($queue_wait_url, ['method' => 'POST']);
|
||||
|
||||
// Update the customer call with the requeue TwiML
|
||||
$call = $client->calls($customer_call_sid)->update([
|
||||
'twiml' => $twiml_xml
|
||||
'twiml' => $twiml->asXML()
|
||||
]);
|
||||
|
||||
// Add call to our database queue tracking
|
||||
|
Reference in New Issue
Block a user