init_sdk_client(); $this->phone_number = get_option('twp_twilio_phone_number'); } /** * Initialize Twilio SDK client */ private function init_sdk_client() { // Check if autoloader exists $autoloader_path = TWP_PLUGIN_DIR . 'vendor/autoload.php'; if (!file_exists($autoloader_path)) { error_log('TWP Plugin: Autoloader not found at: ' . $autoloader_path); throw new Exception('Twilio SDK not found. Please run: ./install-twilio-sdk.sh'); } // Load the autoloader require_once $autoloader_path; // Give more detailed error information if (!class_exists('Twilio\Rest\Client')) { $sdk_path = TWP_PLUGIN_DIR . 'vendor/twilio/sdk'; $client_file = $sdk_path . '/Rest/Client.php'; error_log('TWP Plugin: Twilio SDK classes not found.'); error_log('TWP Plugin: Looking for SDK at: ' . $sdk_path); error_log('TWP Plugin: Client.php exists: ' . (file_exists($client_file) ? 'YES' : 'NO')); error_log('TWP Plugin: SDK directory contents: ' . print_r(scandir($sdk_path), true)); throw new Exception('Twilio SDK classes not available. Please reinstall with: ./install-twilio-sdk.sh'); } $account_sid = get_option('twp_twilio_account_sid'); $auth_token = get_option('twp_twilio_auth_token'); if (empty($account_sid) || empty($auth_token)) { throw new Exception('Twilio credentials not configured. Please check your WordPress admin settings.'); } try { $this->client = new \Twilio\Rest\Client($account_sid, $auth_token); error_log('TWP Plugin: Twilio SDK initialized successfully'); } catch (Exception $e) { error_log('TWP Plugin: Failed to initialize Twilio client: ' . $e->getMessage()); throw new Exception('Failed to initialize Twilio SDK: ' . $e->getMessage()); } } /** * Make a phone call */ public function make_call($to_number, $twiml_url, $status_callback = null, $from_number = null) { try { $params = [ 'url' => $twiml_url, 'from' => $from_number ?: $this->phone_number, 'to' => $to_number ]; if ($status_callback) { $params['statusCallback'] = $status_callback; $params['statusCallbackEvent'] = ['initiated', 'ringing', 'answered', 'completed']; } $call = $this->client->calls->create( $to_number, $from_number ?: $this->phone_number, $params ); return [ 'success' => true, 'data' => [ 'sid' => $call->sid, 'status' => $call->status, 'from' => $call->from, 'to' => $call->to, 'direction' => $call->direction, 'price' => $call->price, 'priceUnit' => $call->priceUnit ] ]; } catch (\Twilio\Exceptions\TwilioException $e) { return [ 'success' => false, 'error' => $e->getMessage(), 'code' => $e->getCode() ]; } } /** * Forward a call */ public function forward_call($call_sid, $to_number) { try { $twiml = new \Twilio\TwiML\VoiceResponse(); $twiml->dial($to_number); $call = $this->client->calls($call_sid)->update([ 'twiml' => $twiml->asXML() ]); return [ 'success' => true, 'data' => [ 'sid' => $call->sid, 'status' => $call->status ] ]; } catch (\Twilio\Exceptions\TwilioException $e) { return [ 'success' => false, 'error' => $e->getMessage(), 'code' => $e->getCode() ]; } } /** * Update an active call */ public function update_call($call_sid, $params) { try { $call = $this->client->calls($call_sid)->update($params); return [ 'success' => true, 'data' => [ 'sid' => $call->sid, 'status' => $call->status, 'from' => $call->from, 'to' => $call->to ] ]; } catch (\Twilio\Exceptions\TwilioException $e) { return [ 'success' => false, 'error' => $e->getMessage(), 'code' => $e->getCode() ]; } } /** * Get call details */ public function get_call($call_sid) { try { $call = $this->client->calls($call_sid)->fetch(); return [ 'success' => true, 'data' => [ 'sid' => $call->sid, 'status' => $call->status, 'from' => $call->from, 'to' => $call->to, 'direction' => $call->direction, 'duration' => $call->duration, 'price' => $call->price, 'priceUnit' => $call->priceUnit ] ]; } catch (\Twilio\Exceptions\TwilioException $e) { return [ 'success' => false, 'error' => $e->getMessage(), 'code' => $e->getCode() ]; } } /** * Create TwiML for queue */ public function create_queue_twiml($queue_name, $wait_url = null, $wait_message = null) { try { $response = new \Twilio\TwiML\VoiceResponse(); if ($wait_message) { $response->say($wait_message, ['voice' => 'alice']); } $enqueue = $response->enqueue($queue_name); if ($wait_url) { $enqueue->waitUrl($wait_url); } return $response->asXML(); } catch (Exception $e) { error_log('TWP Plugin: Failed to create queue TwiML: ' . $e->getMessage()); throw $e; } } /** * Create TwiML for IVR menu */ public function create_ivr_twiml($message, $options = array()) { try { $response = new \Twilio\TwiML\VoiceResponse(); $gather = $response->gather([ 'numDigits' => 1, 'timeout' => 10, 'action' => isset($options['action_url']) ? $options['action_url'] : null ]); $gather->say($message, ['voice' => 'alice']); if (!empty($options['no_input_message'])) { $response->say($options['no_input_message'], ['voice' => 'alice']); } return $response->asXML(); } catch (Exception $e) { error_log('TWP Plugin: Failed to create IVR TwiML: ' . $e->getMessage()); throw $e; } } /** * Send SMS */ public function send_sms($to_number, $message, $from_number = null) { try { $sms = $this->client->messages->create( $to_number, [ 'from' => $from_number ?: $this->phone_number, 'body' => $message ] ); return [ 'success' => true, 'data' => [ 'sid' => $sms->sid, 'status' => $sms->status, 'from' => $sms->from, 'to' => $sms->to, 'body' => $sms->body, 'price' => $sms->price, 'priceUnit' => $sms->priceUnit ] ]; } catch (\Twilio\Exceptions\TwilioException $e) { return [ 'success' => false, 'error' => $e->getMessage(), 'code' => $e->getCode() ]; } } /** * Get available phone numbers */ public function get_phone_numbers() { try { $numbers = $this->client->incomingPhoneNumbers->read([], 50); $numbers_data = []; foreach ($numbers as $number) { $numbers_data[] = [ 'sid' => $number->sid ?: '', 'phoneNumber' => $number->phoneNumber ?: '', 'friendlyName' => $number->friendlyName ?: $number->phoneNumber ?: 'Unknown', 'voiceUrl' => $number->voiceUrl ?: '', 'smsUrl' => $number->smsUrl ?: '', 'capabilities' => [ 'voice' => $number->capabilities ? (bool)$number->capabilities->getVoice() : false, 'sms' => $number->capabilities ? (bool)$number->capabilities->getSms() : false, 'mms' => $number->capabilities ? (bool)$number->capabilities->getMms() : false ] ]; } return [ 'success' => true, 'data' => [ 'incoming_phone_numbers' => $numbers_data ] ]; } catch (\Twilio\Exceptions\TwilioException $e) { return [ 'success' => false, 'error' => $e->getMessage(), 'code' => $e->getCode() ]; } } /** * Search for available phone numbers */ public function search_available_numbers($country_code = 'US', $area_code = null, $contains = null, $limit = 20) { try { $params = ['limit' => $limit]; if ($area_code) { $params['areaCode'] = $area_code; } if ($contains) { $params['contains'] = $contains; } $numbers = $this->client->availablePhoneNumbers($country_code) ->local ->read($params, $limit); $numbers_data = []; foreach ($numbers as $number) { $numbers_data[] = [ 'phoneNumber' => $number->phoneNumber ?: '', 'friendlyName' => $number->friendlyName ?: $number->phoneNumber ?: 'Available Number', 'locality' => $number->locality ?: '', 'region' => $number->region ?: '', 'postalCode' => $number->postalCode ?: '', 'capabilities' => [ 'voice' => $number->capabilities ? (bool)$number->capabilities->getVoice() : false, 'sms' => $number->capabilities ? (bool)$number->capabilities->getSms() : false, 'mms' => $number->capabilities ? (bool)$number->capabilities->getMms() : false ] ]; } return [ 'success' => true, 'data' => [ 'available_phone_numbers' => $numbers_data ] ]; } catch (\Twilio\Exceptions\TwilioException $e) { return [ 'success' => false, 'error' => $e->getMessage(), 'code' => $e->getCode() ]; } } /** * Purchase a phone number */ public function purchase_phone_number($phone_number, $voice_url = null, $sms_url = null) { try { $params = ['phoneNumber' => $phone_number]; if ($voice_url) { $params['voiceUrl'] = $voice_url; $params['voiceMethod'] = 'POST'; } if ($sms_url) { $params['smsUrl'] = $sms_url; $params['smsMethod'] = 'POST'; } $number = $this->client->incomingPhoneNumbers->create($params); return [ 'success' => true, 'data' => [ 'sid' => $number->sid, 'phoneNumber' => $number->phoneNumber, 'friendlyName' => $number->friendlyName, 'voiceUrl' => $number->voiceUrl, 'smsUrl' => $number->smsUrl ] ]; } catch (\Twilio\Exceptions\TwilioException $e) { return [ 'success' => false, 'error' => $e->getMessage(), 'code' => $e->getCode() ]; } } /** * Release a phone number */ public function release_phone_number($phone_number_sid) { try { $this->client->incomingPhoneNumbers($phone_number_sid)->delete(); return [ 'success' => true, 'data' => [ 'message' => 'Phone number released successfully' ] ]; } catch (\Twilio\Exceptions\TwilioException $e) { return [ 'success' => false, 'error' => $e->getMessage(), 'code' => $e->getCode() ]; } } /** * Configure phone number webhook */ public function configure_phone_number($phone_sid, $voice_url, $sms_url = null) { try { $params = [ 'voiceUrl' => $voice_url, 'voiceMethod' => 'POST' ]; if ($sms_url) { $params['smsUrl'] = $sms_url; $params['smsMethod'] = 'POST'; } $number = $this->client->incomingPhoneNumbers($phone_sid)->update($params); return [ 'success' => true, 'data' => [ 'sid' => $number->sid, 'phoneNumber' => $number->phoneNumber, 'voiceUrl' => $number->voiceUrl, 'smsUrl' => $number->smsUrl ] ]; } catch (\Twilio\Exceptions\TwilioException $e) { return [ 'success' => false, 'error' => $e->getMessage(), 'code' => $e->getCode() ]; } } /** * Create TwiML helper - returns SDK VoiceResponse */ public function create_twiml() { return new \Twilio\TwiML\VoiceResponse(); } /** * Get the Twilio client instance */ public function get_client() { return $this->client; } /** * Validate webhook signature */ public function validate_webhook_signature($url, $params, $signature) { $validator = new \Twilio\Security\RequestValidator(get_option('twp_twilio_auth_token')); return $validator->validate($signature, $url, $params); } }