Implement bridged forwarding with agent call control features

Major enhancement to workflow forwarding that solves voicemail issues and adds agent call control capabilities.

Key improvements:
- Converted direct forwarding to bridged forwarding to avoid self-call voicemail issues
- Added DTMF-based agent features during calls:
  * *9 - Hold/Unhold customer
  * *0 - Start/Stop call recording
  * *5 - Transfer to another extension
  * *1 - Mute/Unmute (placeholder for future conference mode)
- Added webhook handlers for agent features and forward results
- Agent features URL attached to Number elements for DTMF detection
- Continuous DTMF listening during active calls
- Proper call leg detection for hold/resume operations

Technical details:
- Added /agent-features webhook for DTMF capture
- Added /agent-action webhook for processing commands
- Added /forward-result webhook for handling dial outcomes
- Modified append_twiml_element to preserve Number attributes (url, method)
- Enhanced logging throughout for debugging

This eliminates the issue where calling yourself would go straight to voicemail and provides professional call control features for agents.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-18 18:29:20 -07:00
parent 0ee8210fef
commit e475e68a5f
5 changed files with 310 additions and 840 deletions

View File

@@ -214,16 +214,19 @@ class TWP_Workflow {
// Add child Number elements
foreach ($element->children() as $child) {
$child_name = $child->getName();
$child_attrs = self::get_attributes($child);
if ($child_name === 'Number') {
$dial->number((string) $child, self::get_attributes($child));
// Number can have url, method attributes for agent features
$dial->number((string) $child, $child_attrs);
} elseif ($child_name === 'Client') {
$dial->client((string) $child, self::get_attributes($child));
$dial->client((string) $child, $child_attrs);
} elseif ($child_name === 'Queue') {
$dial->queue((string) $child, self::get_attributes($child));
$dial->queue((string) $child, $child_attrs);
} elseif ($child_name === 'Conference') {
$dial->conference((string) $child, self::get_attributes($child));
$dial->conference((string) $child, $child_attrs);
} elseif ($child_name === 'Sip') {
$dial->sip((string) $child, self::get_attributes($child));
$dial->sip((string) $child, $child_attrs);
}
}
break;
@@ -591,10 +594,27 @@ class TWP_Workflow {
error_log('TWP Workflow Forward: No To number found for caller ID, will use account default');
}
// Add all forward numbers
// Check if we should use bridging with agent features
$use_bridge_features = isset($step_data['enable_agent_features']) ? $step_data['enable_agent_features'] : true;
// Add action URL to handle dial result (for tracking and potential fallback)
$action_url = home_url('/wp-json/twilio-webhook/v1/forward-result');
$dial->addAttribute('action', $action_url);
$dial->addAttribute('method', 'POST');
// Add all forward numbers with agent features if enabled
foreach ($forward_numbers as $number) {
error_log('TWP Workflow Forward: Adding number to Dial: ' . $number);
$dial->addChild('Number', $number);
$number_element = $dial->addChild('Number', $number);
if ($use_bridge_features) {
// Add URL to handle agent-side features (DTMF detection)
$agent_url = home_url('/wp-json/twilio-webhook/v1/agent-features');
$number_element->addAttribute('url', $agent_url);
$number_element->addAttribute('method', 'POST');
error_log('TWP Workflow Forward: Added agent features URL for DTMF detection');
}
}
$result = $twiml->asXML();