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:
@@ -95,7 +95,7 @@ class TWP_Agent_Manager {
|
||||
/**
|
||||
* Set agent status
|
||||
*/
|
||||
public static function set_agent_status($user_id, $status, $call_sid = null) {
|
||||
public static function set_agent_status($user_id, $status, $call_sid = null, $auto_set = false) {
|
||||
global $wpdb;
|
||||
$table_name = $wpdb->prefix . 'twp_agent_status';
|
||||
|
||||
@@ -108,6 +108,18 @@ class TWP_Agent_Manager {
|
||||
$is_logged_in = $existing ? $existing->is_logged_in : 0;
|
||||
$logged_in_at = $existing ? $existing->logged_in_at : null;
|
||||
|
||||
// Set auto_busy_at timestamp if automatically setting to busy
|
||||
$auto_busy_at = null;
|
||||
if ($auto_set && $status === 'busy') {
|
||||
$auto_busy_at = current_time('mysql');
|
||||
} elseif ($status !== 'busy') {
|
||||
// Clear auto_busy_at when changing from busy to any other status
|
||||
$auto_busy_at = null;
|
||||
} else {
|
||||
// Preserve existing auto_busy_at if manually setting to busy or not changing
|
||||
$auto_busy_at = $existing ? $existing->auto_busy_at : null;
|
||||
}
|
||||
|
||||
if ($existing) {
|
||||
return $wpdb->update(
|
||||
$table_name,
|
||||
@@ -116,10 +128,11 @@ class TWP_Agent_Manager {
|
||||
'current_call_sid' => $call_sid,
|
||||
'last_activity' => current_time('mysql'),
|
||||
'is_logged_in' => $is_logged_in,
|
||||
'logged_in_at' => $logged_in_at
|
||||
'logged_in_at' => $logged_in_at,
|
||||
'auto_busy_at' => $auto_busy_at
|
||||
),
|
||||
array('user_id' => $user_id),
|
||||
array('%s', '%s', '%s', '%d', '%s'),
|
||||
array('%s', '%s', '%s', '%d', '%s', '%s'),
|
||||
array('%d')
|
||||
);
|
||||
} else {
|
||||
@@ -131,9 +144,10 @@ class TWP_Agent_Manager {
|
||||
'current_call_sid' => $call_sid,
|
||||
'last_activity' => current_time('mysql'),
|
||||
'is_logged_in' => 0,
|
||||
'logged_in_at' => null
|
||||
'logged_in_at' => null,
|
||||
'auto_busy_at' => $auto_busy_at
|
||||
),
|
||||
array('%d', '%s', '%s', '%s', '%d', '%s')
|
||||
array('%d', '%s', '%s', '%s', '%d', '%s', '%s')
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -172,10 +186,11 @@ class TWP_Agent_Manager {
|
||||
'is_logged_in' => $is_logged_in ? 1 : 0,
|
||||
'logged_in_at' => $logged_in_at,
|
||||
'last_activity' => current_time('mysql'),
|
||||
'status' => $is_logged_in ? 'available' : 'offline'
|
||||
'status' => $is_logged_in ? 'available' : 'offline',
|
||||
'auto_busy_at' => null // Clear auto_busy_at when changing login status
|
||||
),
|
||||
array('user_id' => $user_id),
|
||||
array('%d', '%s', '%s', '%s'),
|
||||
array('%d', '%s', '%s', '%s', '%s'),
|
||||
array('%d')
|
||||
);
|
||||
} else {
|
||||
@@ -186,9 +201,10 @@ class TWP_Agent_Manager {
|
||||
'status' => $is_logged_in ? 'available' : 'offline',
|
||||
'is_logged_in' => $is_logged_in ? 1 : 0,
|
||||
'logged_in_at' => $logged_in_at,
|
||||
'last_activity' => current_time('mysql')
|
||||
'last_activity' => current_time('mysql'),
|
||||
'auto_busy_at' => null
|
||||
),
|
||||
array('%d', '%s', '%d', '%s', '%s')
|
||||
array('%d', '%s', '%d', '%s', '%s', '%s')
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -316,7 +332,7 @@ class TWP_Agent_Manager {
|
||||
);
|
||||
|
||||
// Set agent status to busy
|
||||
self::set_agent_status($user_id, 'busy', $call->call_sid);
|
||||
self::set_agent_status($user_id, 'busy', $call->call_sid, true);
|
||||
|
||||
// Make a new call to the agent with proper caller ID
|
||||
$twilio = new TWP_Twilio_API();
|
||||
@@ -362,15 +378,23 @@ class TWP_Agent_Manager {
|
||||
// For browser mode, redirect the existing call to the browser client
|
||||
$current_user = get_userdata($user_id);
|
||||
// Twilio requires alphanumeric characters only - must match generate_capability_token
|
||||
$clean_name = preg_replace('/[^a-zA-Z0-9]/', '', $current_user->display_name);
|
||||
// Use user_login for consistency with capability token generation
|
||||
$clean_name = preg_replace('/[^a-zA-Z0-9]/', '', $current_user->user_login);
|
||||
if (empty($clean_name)) {
|
||||
$clean_name = 'user';
|
||||
}
|
||||
$client_name = 'agent' . $user_id . $clean_name;
|
||||
|
||||
error_log("TWP Accept: Redirecting call {$call->call_sid} to browser client '{$client_name}' for user {$user_id}");
|
||||
|
||||
// Create TwiML to redirect call to browser client
|
||||
$twiml = new \Twilio\TwiML\VoiceResponse();
|
||||
$twiml->say('Connecting you to an agent.', ['voice' => 'alice']);
|
||||
|
||||
// Use TTS helper for ElevenLabs integration
|
||||
require_once plugin_dir_path(__FILE__) . 'class-twp-tts-helper.php';
|
||||
$tts_helper = TWP_TTS_Helper::get_instance();
|
||||
$tts_helper->add_tts_to_twiml($twiml, 'Connecting you to an agent.');
|
||||
|
||||
$dial = $twiml->dial();
|
||||
$dial->setAttribute('timeout', 30);
|
||||
$dial->client($client_name);
|
||||
@@ -594,6 +618,57 @@ class TWP_Agent_Manager {
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check and revert agents from auto-busy to available after 1 minute
|
||||
*/
|
||||
public static function revert_auto_busy_agents() {
|
||||
global $wpdb;
|
||||
$table_name = $wpdb->prefix . 'twp_agent_status';
|
||||
|
||||
// Find agents who have been auto-busy for more than 1 minute and are still logged in
|
||||
$cutoff_time = date('Y-m-d H:i:s', strtotime('-1 minute'));
|
||||
|
||||
$auto_busy_agents = $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT user_id, current_call_sid FROM $table_name
|
||||
WHERE status = 'busy'
|
||||
AND auto_busy_at IS NOT NULL
|
||||
AND auto_busy_at < %s
|
||||
AND is_logged_in = 1",
|
||||
$cutoff_time
|
||||
));
|
||||
|
||||
foreach ($auto_busy_agents as $agent) {
|
||||
// Verify the call is actually finished before reverting
|
||||
$call_sid = $agent->current_call_sid;
|
||||
$call_active = false;
|
||||
|
||||
if ($call_sid) {
|
||||
// Check if call is still active using Twilio API
|
||||
try {
|
||||
$api = new TWP_Twilio_API();
|
||||
$call_status = $api->get_call_status($call_sid);
|
||||
|
||||
// If call is still in progress, don't revert yet
|
||||
if (in_array($call_status, ['queued', 'ringing', 'in-progress'])) {
|
||||
$call_active = true;
|
||||
error_log("TWP Auto-Revert: Call {$call_sid} still active for user {$agent->user_id}, keeping busy");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
error_log("TWP Auto-Revert: Could not check call status for {$call_sid}: " . $e->getMessage());
|
||||
// If we can't check call status, assume it's finished and proceed with revert
|
||||
}
|
||||
}
|
||||
|
||||
// Only revert if call is not active
|
||||
if (!$call_active) {
|
||||
error_log("TWP Auto-Revert: Reverting user {$agent->user_id} from auto-busy to available");
|
||||
self::set_agent_status($agent->user_id, 'available', null, false);
|
||||
}
|
||||
}
|
||||
|
||||
return count($auto_busy_agents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate phone number format
|
||||
*/
|
||||
|
Reference in New Issue
Block a user