progress made

This commit is contained in:
2025-08-11 20:31:48 -07:00
parent 805af2f199
commit 304b5de40b
15 changed files with 4028 additions and 404 deletions

View File

@@ -32,7 +32,13 @@ class TWP_Call_Queue {
array('%d', '%s', '%s', '%s', '%d', '%s')
);
return $result !== false ? $position : false;
if ($result !== false) {
// Notify agents via SMS when a new call enters the queue
self::notify_agents_for_queue($queue_id, $call_data['from_number']);
return $position;
}
return false;
}
/**
@@ -120,10 +126,17 @@ class TWP_Call_Queue {
$table_name = $wpdb->prefix . 'twp_queued_calls';
$queue_table = $wpdb->prefix . 'twp_call_queues';
error_log('TWP Queue Process: Starting queue processing');
// Get all active queues
$queues = $wpdb->get_results("SELECT * FROM $queue_table");
foreach ($queues as $queue) {
error_log('TWP Queue Process: Processing queue ' . $queue->queue_name . ' (ID: ' . $queue->id . ')');
// First, try to assign agents to waiting calls
$this->assign_agents_to_waiting_calls($queue);
// Check for timed out calls
$timeout_time = date('Y-m-d H:i:s', strtotime('-' . $queue->timeout_seconds . ' seconds'));
@@ -137,6 +150,7 @@ class TWP_Call_Queue {
));
foreach ($timed_out_calls as $call) {
error_log('TWP Queue Process: Handling timeout for call ' . $call->call_sid);
// Handle timeout
$this->handle_timeout($call, $queue);
}
@@ -144,6 +158,8 @@ class TWP_Call_Queue {
// Update caller positions and play position messages
$this->update_queue_positions($queue->id);
}
error_log('TWP Queue Process: Finished queue processing');
}
/**
@@ -170,13 +186,152 @@ class TWP_Call_Queue {
$twilio = new TWP_Twilio_API();
$twilio->update_call($call->call_sid, array(
'Twiml' => $callback_twiml
'twiml' => $callback_twiml
));
// Reorder queue
self::reorder_queue($queue->id);
}
/**
* Assign agents to waiting calls
*/
private function assign_agents_to_waiting_calls($queue) {
global $wpdb;
$table_name = $wpdb->prefix . 'twp_queued_calls';
// Get waiting calls in order
$waiting_calls = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM $table_name
WHERE queue_id = %d AND status = 'waiting'
ORDER BY position ASC",
$queue->id
));
if (empty($waiting_calls)) {
return;
}
error_log('TWP Queue Process: Found ' . count($waiting_calls) . ' waiting calls in queue ' . $queue->queue_name);
// Get available agents for this queue
$available_agents = $this->get_available_agents_for_queue($queue);
if (empty($available_agents)) {
error_log('TWP Queue Process: No available agents for queue ' . $queue->queue_name);
return;
}
error_log('TWP Queue Process: Found ' . count($available_agents) . ' available agents');
// Assign agents to calls (one agent per call)
$assignments = 0;
foreach ($waiting_calls as $call) {
if ($assignments >= count($available_agents)) {
break; // No more agents available
}
$agent = $available_agents[$assignments];
error_log('TWP Queue Process: Attempting to assign call ' . $call->call_sid . ' to agent ' . $agent['phone']);
// Try to bridge the call to the agent
if ($this->bridge_call_to_agent($call, $agent, $queue)) {
$assignments++;
error_log('TWP Queue Process: Successfully initiated bridge for call ' . $call->call_sid);
} else {
error_log('TWP Queue Process: Failed to bridge call ' . $call->call_sid . ' to agent');
}
}
error_log('TWP Queue Process: Made ' . $assignments . ' call assignments');
}
/**
* Get available agents for a queue
*/
private function get_available_agents_for_queue($queue) {
// If queue has assigned agent groups, get agents from those groups
if (!empty($queue->agent_groups)) {
$group_ids = explode(',', $queue->agent_groups);
$agents = array();
foreach ($group_ids as $group_id) {
$group_agents = TWP_Agent_Manager::get_available_agents(intval($group_id));
if ($group_agents) {
$agents = array_merge($agents, $group_agents);
}
}
return $agents;
}
// Fallback to all available agents
return TWP_Agent_Manager::get_available_agents();
}
/**
* Bridge call to agent
*/
private function bridge_call_to_agent($call, $agent, $queue) {
$twilio = new TWP_Twilio_API();
try {
// Create a new call to the agent
$agent_call_data = array(
'to' => $agent['phone'],
'from' => $queue->caller_id ?: $call->to_number, // Use queue caller ID or original number
'url' => home_url('/wp-json/twilio-webhook/v1/agent-connect?' . http_build_query(array(
'customer_call_sid' => $call->call_sid,
'customer_number' => $call->from_number,
'queue_id' => $queue->id,
'agent_phone' => $agent['phone'],
'queued_call_id' => $call->id
))),
'method' => 'POST',
'timeout' => 20,
'statusCallback' => home_url('/wp-json/twilio-webhook/v1/agent-call-status'),
'statusCallbackEvent' => array('answered', 'completed', 'busy', 'no-answer'),
'statusCallbackMethod' => 'POST'
);
error_log('TWP Queue Bridge: Creating agent call with data: ' . json_encode($agent_call_data));
$agent_call_response = $twilio->create_call($agent_call_data);
if ($agent_call_response['success']) {
// Update call status to indicate agent is being contacted
global $wpdb;
$table_name = $wpdb->prefix . 'twp_queued_calls';
$updated = $wpdb->update(
$table_name,
array(
'status' => 'connecting',
'agent_phone' => $agent['phone'],
'agent_call_sid' => $agent_call_response['data']['sid']
),
array('call_sid' => $call->call_sid),
array('%s', '%s', '%s'),
array('%s')
);
if ($updated) {
error_log('TWP Queue Bridge: Updated call status to connecting');
return true;
} else {
error_log('TWP Queue Bridge: Failed to update call status');
}
} else {
error_log('TWP Queue Bridge: Failed to create agent call: ' . ($agent_call_response['error'] ?? 'Unknown error'));
}
} catch (Exception $e) {
error_log('TWP Queue Bridge: Exception bridging call: ' . $e->getMessage());
}
return false;
}
/**
* Update queue positions
*/
@@ -241,7 +396,7 @@ class TWP_Call_Queue {
}
$twilio->update_call($call->call_sid, array(
'Twiml' => $twiml->asXML()
'twiml' => $twiml->asXML()
));
}
}
@@ -278,16 +433,58 @@ class TWP_Call_Queue {
global $wpdb;
$table_name = $wpdb->prefix . 'twp_call_queues';
return $wpdb->insert(
$insert_data = array(
'queue_name' => sanitize_text_field($data['queue_name']),
'phone_number' => !empty($data['phone_number']) ? sanitize_text_field($data['phone_number']) : '',
'agent_group_id' => !empty($data['agent_group_id']) ? intval($data['agent_group_id']) : null,
'max_size' => intval($data['max_size']),
'wait_music_url' => esc_url_raw($data['wait_music_url']),
'tts_message' => sanitize_textarea_field($data['tts_message']),
'timeout_seconds' => intval($data['timeout_seconds'])
);
$insert_format = array('%s', '%s');
if ($insert_data['agent_group_id'] === null) {
$insert_format[] = null;
} else {
$insert_format[] = '%d';
}
$insert_format = array_merge($insert_format, array('%d', '%s', '%s', '%d'));
return $wpdb->insert($table_name, $insert_data, $insert_format);
}
/**
* Update queue
*/
public static function update_queue($queue_id, $data) {
global $wpdb;
$table_name = $wpdb->prefix . 'twp_call_queues';
$update_data = array(
'queue_name' => sanitize_text_field($data['queue_name']),
'phone_number' => !empty($data['phone_number']) ? sanitize_text_field($data['phone_number']) : '',
'agent_group_id' => !empty($data['agent_group_id']) ? intval($data['agent_group_id']) : null,
'max_size' => intval($data['max_size']),
'wait_music_url' => esc_url_raw($data['wait_music_url']),
'tts_message' => sanitize_textarea_field($data['tts_message']),
'timeout_seconds' => intval($data['timeout_seconds'])
);
$update_format = array('%s', '%s');
if ($update_data['agent_group_id'] === null) {
$update_format[] = null;
} else {
$update_format[] = '%d';
}
$update_format = array_merge($update_format, array('%d', '%s', '%s', '%d'));
return $wpdb->update(
$table_name,
array(
'queue_name' => sanitize_text_field($data['queue_name']),
'max_size' => intval($data['max_size']),
'wait_music_url' => esc_url_raw($data['wait_music_url']),
'tts_message' => sanitize_textarea_field($data['tts_message']),
'timeout_seconds' => intval($data['timeout_seconds'])
),
array('%s', '%d', '%s', '%s', '%d')
$update_data,
array('id' => intval($queue_id)),
$update_format,
array('%d')
);
}
@@ -358,4 +555,56 @@ class TWP_Call_Queue {
return $status;
}
/**
* Notify agents via SMS when a call enters the queue
*/
private static function notify_agents_for_queue($queue_id, $caller_number) {
global $wpdb;
// Get queue information including assigned agent group and phone number
$queue_table = $wpdb->prefix . 'twp_call_queues';
$queue = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM $queue_table WHERE id = %d",
$queue_id
));
if (!$queue || !$queue->agent_group_id) {
error_log("TWP: No agent group assigned to queue {$queue_id}, skipping SMS notifications");
return;
}
// Get members of the assigned agent group
require_once dirname(__FILE__) . '/class-twp-agent-groups.php';
$members = TWP_Agent_Groups::get_group_members($queue->agent_group_id);
if (empty($members)) {
error_log("TWP: No members found in agent group {$queue->agent_group_id} for queue {$queue_id}");
return;
}
$twilio = new TWP_Twilio_API();
// Use the queue's phone number as the from number, or fall back to default
$from_number = !empty($queue->phone_number) ? $queue->phone_number : TWP_Twilio_API::get_sms_from_number();
if (empty($from_number)) {
error_log("TWP: No SMS from number available for queue notifications");
return;
}
$message = "Call waiting in queue '{$queue->queue_name}' from {$caller_number}. Text '1' to this number to receive the next available call.";
foreach ($members as $member) {
$agent_phone = get_user_meta($member->user_id, 'twp_phone_number', true);
if (!empty($agent_phone)) {
// Send SMS notification using the queue's phone number
$twilio->send_sms($agent_phone, $message, $from_number);
// Log the notification
error_log("TWP: Queue SMS notification sent to agent {$member->user_id} at {$agent_phone} from {$from_number} for queue {$queue_id}");
}
}
}
}