Add comprehensive call control features and web phone transfer capabilities

## New Call Control Features
- Call hold/unhold with music playback
- Call transfer with agent selection dialog
- Call requeue to different queues
- Call recording with start/stop controls
- Real-time recording status tracking

## Enhanced Transfer System
- Transfer to agents with cell phones (direct)
- Transfer to web phone agents via personal queues
- Automatic queue creation for each user
- Real-time agent availability status
- Visual agent selection with status indicators (📱 phone, 💻 web)

## Call Recordings Management
- New database table for call recordings
- Recordings tab in voicemail interface
- Play/download recordings functionality
- Admin-only delete capability
- Integration with Twilio recording webhooks

## Agent Queue System
- Personal queues (agent_[user_id]) for web phone transfers
- Automatic polling for incoming transfers
- Transfer notifications with browser alerts
- Agent status tracking (available/busy/offline)

## Technical Enhancements
- 8 new AJAX endpoints for call controls
- Recording status webhooks
- Enhanced transfer dialogs with agent selection
- Improved error handling and user feedback
- Mobile-responsive call control interface

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-08-30 11:52:50 -07:00
parent 7398f97f24
commit dc3c12e006
8 changed files with 1721 additions and 33 deletions

View File

@@ -100,6 +100,20 @@ class TWP_Webhooks {
'permission_callback' => '__return_true'
));
// Recording status webhook
register_rest_route('twilio-webhook/v1', '/recording-status', array(
'methods' => 'POST',
'callback' => array($this, 'handle_recording_status'),
'permission_callback' => '__return_true'
));
// Resume call webhook (for unhold)
register_rest_route('twilio-webhook/v1', '/resume-call', array(
'methods' => 'POST',
'callback' => array($this, 'handle_resume_call'),
'permission_callback' => '__return_true'
));
// Smart routing webhook (checks user preference)
register_rest_route('twilio-webhook/v1', '/smart-routing', array(
'methods' => 'POST',
@@ -2251,4 +2265,78 @@ class TWP_Webhooks {
// Optionally: Try to assign to another available agent
// $this->try_assign_to_next_agent($queued_call->queue_id, $queued_call_id);
}
/**
* Handle recording status callback
*/
public function handle_recording_status($request) {
$params = $request->get_params();
error_log('TWP Recording Status: ' . print_r($params, true));
$recording_sid = isset($params['RecordingSid']) ? $params['RecordingSid'] : '';
$recording_url = isset($params['RecordingUrl']) ? $params['RecordingUrl'] : '';
$recording_status = isset($params['RecordingStatus']) ? $params['RecordingStatus'] : '';
$recording_duration = isset($params['RecordingDuration']) ? intval($params['RecordingDuration']) : 0;
$call_sid = isset($params['CallSid']) ? $params['CallSid'] : '';
if ($recording_sid && $recording_status === 'completed') {
global $wpdb;
$recordings_table = $wpdb->prefix . 'twp_call_recordings';
// Update recording with URL and duration
$wpdb->update(
$recordings_table,
[
'recording_url' => $recording_url,
'duration' => $recording_duration,
'status' => 'completed',
'ended_at' => current_time('mysql')
],
['recording_sid' => $recording_sid]
);
error_log("TWP: Recording completed - SID: $recording_sid, Duration: $recording_duration seconds");
}
// Return empty response
$response = new \Twilio\TwiML\VoiceResponse();
return new WP_REST_Response($response->asXML(), 200, array('Content-Type' => 'text/xml'));
}
/**
* Handle resume call (unhold)
*/
public function handle_resume_call($request) {
$params = $request->get_params();
error_log('TWP Resume Call: ' . print_r($params, true));
$call_sid = isset($params['CallSid']) ? $params['CallSid'] : '';
// Return empty TwiML to continue the call
$response = new \Twilio\TwiML\VoiceResponse();
// Check if this is a conference call
global $wpdb;
$call_log_table = $wpdb->prefix . 'twp_call_log';
$call_info = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM $call_log_table WHERE call_sid = %s",
$call_sid
));
if ($call_info && strpos($call_info->call_type, 'conference') !== false) {
// Rejoin conference
$dial = $response->dial();
$dial->conference('Room_' . $call_sid, [
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true
]);
} else {
// Just continue the call
$response->say('Call resumed');
}
return new WP_REST_Response($response->asXML(), 200, array('Content-Type' => 'text/xml'));
}
}