This update adds two major features: 1. Queue Timeout Voicemail - Callers can now leave voicemail when queue timeout is reached - Configurable per-queue voicemail prompts with TTS support - Automatic transcription and urgent keyword detection - Admin setting to choose between voicemail or callback on timeout 2. Amazon SNS SMS Provider - Alternative SMS provider to Twilio for sending text messages - Useful when Twilio SMS approval is difficult to obtain - Provider abstraction layer allows switching between Twilio/SNS - Full AWS SNS configuration in admin settings - Supports custom sender IDs in compatible countries - Lower cost per SMS compared to Twilio New Files: - includes/class-twp-voicemail-handler.php - Voicemail recording handler - includes/interface-twp-sms-provider.php - SMS provider interface - includes/class-twp-sms-provider-twilio.php - Twilio SMS implementation - includes/class-twp-sms-provider-sns.php - Amazon SNS implementation - includes/class-twp-sms-manager.php - SMS provider abstraction manager - QUEUE_VOICEMAIL_SMS_FEATURES.md - Complete feature documentation Modified Files: - includes/class-twp-call-queue.php - Added voicemail option to timeout handler - includes/class-twp-twilio-api.php - Updated send_sms() to use provider abstraction - admin/class-twp-admin.php - Added SMS provider and timeout action settings - composer.json - Added AWS SDK dependency 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
227 lines
8.0 KiB
PHP
227 lines
8.0 KiB
PHP
<?php
|
|
/**
|
|
* Voicemail Handler
|
|
*
|
|
* Handles voicemail recording prompts and processing
|
|
*/
|
|
class TWP_Voicemail_Handler {
|
|
|
|
/**
|
|
* Create TwiML for voicemail prompt and recording
|
|
*
|
|
* @param string $caller_number Caller's phone number
|
|
* @param int $queue_id Queue ID that timed out
|
|
* @param string $custom_prompt Optional custom voicemail prompt
|
|
* @return string TwiML XML
|
|
*/
|
|
public static function create_voicemail_twiml($caller_number, $queue_id = null, $custom_prompt = null) {
|
|
global $wpdb;
|
|
|
|
// Get queue information if provided
|
|
$queue = null;
|
|
if ($queue_id) {
|
|
$queue_table = $wpdb->prefix . 'twp_call_queues';
|
|
$queue = $wpdb->get_row($wpdb->prepare(
|
|
"SELECT * FROM $queue_table WHERE id = %d",
|
|
$queue_id
|
|
));
|
|
}
|
|
|
|
// Determine the prompt message
|
|
$prompt_message = $custom_prompt;
|
|
|
|
if (!$prompt_message && $queue && !empty($queue->voicemail_prompt)) {
|
|
$prompt_message = $queue->voicemail_prompt;
|
|
}
|
|
|
|
if (!$prompt_message) {
|
|
$prompt_message = "We're sorry, but all our agents are currently unavailable. Please leave a message after the tone, and we'll get back to you as soon as possible.";
|
|
}
|
|
|
|
// Generate TTS for the prompt
|
|
require_once dirname(__FILE__) . '/class-twp-tts-helper.php';
|
|
$tts_result = TWP_TTS_Helper::text_to_speech($prompt_message);
|
|
|
|
// Build TwiML response
|
|
$response = new \Twilio\TwiML\VoiceResponse();
|
|
|
|
if ($tts_result['success'] && !empty($tts_result['file_url'])) {
|
|
// Use generated TTS audio
|
|
$response->play($tts_result['file_url']);
|
|
} else {
|
|
// Fallback to Twilio's Say
|
|
$response->say($prompt_message, ['voice' => 'alice']);
|
|
}
|
|
|
|
// Record the voicemail
|
|
$record_params = [
|
|
'action' => home_url('/wp-json/twilio-webhook/v1/voicemail-complete'),
|
|
'recordingStatusCallback' => home_url('/wp-json/twilio-webhook/v1/voicemail-callback?' . http_build_query([
|
|
'from' => $caller_number,
|
|
'queue_id' => $queue_id,
|
|
'source' => 'queue_timeout'
|
|
])),
|
|
'recordingStatusCallbackMethod' => 'POST',
|
|
'maxLength' => 300, // 5 minutes max
|
|
'playBeep' => true,
|
|
'finishOnKey' => '#',
|
|
'transcribe' => true,
|
|
'transcribeCallback' => home_url('/wp-json/twilio-webhook/v1/voicemail-transcription')
|
|
];
|
|
|
|
$response->record($record_params);
|
|
|
|
// Thank you message after recording
|
|
$response->say('Thank you for your message. Goodbye.', ['voice' => 'alice']);
|
|
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Save voicemail to database
|
|
*
|
|
* @param array $voicemail_data Voicemail data
|
|
* @return int|false Voicemail ID or false on failure
|
|
*/
|
|
public static function save_voicemail($voicemail_data) {
|
|
global $wpdb;
|
|
$table_name = $wpdb->prefix . 'twp_voicemails';
|
|
|
|
$insert_data = array(
|
|
'call_sid' => sanitize_text_field($voicemail_data['call_sid']),
|
|
'from_number' => sanitize_text_field($voicemail_data['from_number']),
|
|
'to_number' => !empty($voicemail_data['to_number']) ? sanitize_text_field($voicemail_data['to_number']) : '',
|
|
'recording_url' => esc_url_raw($voicemail_data['recording_url']),
|
|
'recording_duration' => intval($voicemail_data['recording_duration']),
|
|
'workflow_id' => !empty($voicemail_data['workflow_id']) ? intval($voicemail_data['workflow_id']) : null,
|
|
'queue_id' => !empty($voicemail_data['queue_id']) ? intval($voicemail_data['queue_id']) : null,
|
|
'source' => !empty($voicemail_data['source']) ? sanitize_text_field($voicemail_data['source']) : 'workflow',
|
|
'status' => 'new',
|
|
'received_at' => current_time('mysql')
|
|
);
|
|
|
|
$result = $wpdb->insert($table_name, $insert_data);
|
|
|
|
if ($result) {
|
|
$voicemail_id = $wpdb->insert_id;
|
|
|
|
// Log the voicemail
|
|
if (class_exists('TWP_Call_Logger')) {
|
|
TWP_Call_Logger::log_action(
|
|
$voicemail_data['call_sid'],
|
|
'Voicemail recorded from queue timeout (' . $voicemail_data['recording_duration'] . 's)'
|
|
);
|
|
}
|
|
|
|
return $voicemail_id;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Update voicemail transcription
|
|
*
|
|
* @param int $voicemail_id Voicemail ID
|
|
* @param string $transcription Transcription text
|
|
* @param string $transcription_status Transcription status
|
|
* @return bool Success
|
|
*/
|
|
public static function update_transcription($voicemail_id, $transcription, $transcription_status = 'completed') {
|
|
global $wpdb;
|
|
$table_name = $wpdb->prefix . 'twp_voicemails';
|
|
|
|
$result = $wpdb->update(
|
|
$table_name,
|
|
array(
|
|
'transcription' => sanitize_textarea_field($transcription),
|
|
'transcription_status' => $transcription_status
|
|
),
|
|
array('id' => $voicemail_id),
|
|
array('%s', '%s'),
|
|
array('%d')
|
|
);
|
|
|
|
// Check for urgent keywords in transcription
|
|
if ($result && !empty($transcription)) {
|
|
self::check_urgent_keywords($voicemail_id, $transcription);
|
|
}
|
|
|
|
return $result !== false;
|
|
}
|
|
|
|
/**
|
|
* Check transcription for urgent keywords
|
|
*
|
|
* @param int $voicemail_id Voicemail ID
|
|
* @param string $transcription Transcription text
|
|
*/
|
|
private static function check_urgent_keywords($voicemail_id, $transcription) {
|
|
global $wpdb;
|
|
|
|
// Get urgent keywords from settings
|
|
$urgent_keywords = get_option('twp_urgent_voicemail_keywords', array('urgent', 'emergency', 'asap', 'critical'));
|
|
if (is_string($urgent_keywords)) {
|
|
$urgent_keywords = array_map('trim', explode(',', $urgent_keywords));
|
|
}
|
|
|
|
// Check if transcription contains any urgent keywords
|
|
$transcription_lower = strtolower($transcription);
|
|
$found_keyword = null;
|
|
|
|
foreach ($urgent_keywords as $keyword) {
|
|
if (stripos($transcription_lower, strtolower(trim($keyword))) !== false) {
|
|
$found_keyword = $keyword;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($found_keyword) {
|
|
// Mark voicemail as urgent
|
|
$table_name = $wpdb->prefix . 'twp_voicemails';
|
|
$wpdb->update(
|
|
$table_name,
|
|
array('is_urgent' => 1),
|
|
array('id' => $voicemail_id),
|
|
array('%d'),
|
|
array('%d')
|
|
);
|
|
|
|
// Send urgent notification
|
|
$voicemail = $wpdb->get_row($wpdb->prepare(
|
|
"SELECT * FROM $table_name WHERE id = %d",
|
|
$voicemail_id
|
|
));
|
|
|
|
if ($voicemail && class_exists('TWP_Notifications')) {
|
|
TWP_Notifications::send_call_notification('urgent_voicemail', array(
|
|
'type' => 'urgent_voicemail',
|
|
'from_number' => $voicemail->from_number,
|
|
'keyword' => $found_keyword,
|
|
'transcription' => $transcription,
|
|
'voicemail_id' => $voicemail_id,
|
|
'admin_url' => admin_url('admin.php?page=twilio-wp-voicemails&voicemail_id=' . $voicemail_id)
|
|
));
|
|
}
|
|
|
|
error_log("TWP Voicemail: Urgent keyword '$found_keyword' detected in voicemail $voicemail_id");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get voicemail by ID
|
|
*
|
|
* @param int $voicemail_id Voicemail ID
|
|
* @return object|null Voicemail object
|
|
*/
|
|
public static function get_voicemail($voicemail_id) {
|
|
global $wpdb;
|
|
$table_name = $wpdb->prefix . 'twp_voicemails';
|
|
|
|
return $wpdb->get_row($wpdb->prepare(
|
|
"SELECT * FROM $table_name WHERE id = %d",
|
|
$voicemail_id
|
|
));
|
|
}
|
|
}
|