Files
twilio-wp-plugin/includes/class-twp-twilio-api.php
2025-08-07 15:24:29 -07:00

474 lines
15 KiB
PHP

<?php
/**
* Twilio API integration class using official Twilio PHP SDK
*/
class TWP_Twilio_API {
private $client;
private $phone_number;
/**
* Constructor
*/
public function __construct() {
$this->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);
}
}