account_sid = get_option('twp_twilio_account_sid');
$this->auth_token = get_option('twp_twilio_auth_token');
$this->phone_number = get_option('twp_twilio_phone_number');
}
/**
* Make a phone call
*/
public function make_call($to_number, $twiml_url, $status_callback = null, $from_number = null) {
$url = $this->api_base . '/Accounts/' . $this->account_sid . '/Calls.json';
$data = array(
'To' => $to_number,
'From' => $from_number ?: $this->phone_number,
'Url' => $twiml_url
);
if ($status_callback) {
$data['StatusCallback'] = $status_callback;
$data['StatusCallbackEvent'] = array('initiated', 'ringing', 'answered', 'completed');
}
return $this->make_request('POST', $url, $data);
}
/**
* Forward a call
*/
public function forward_call($call_sid, $to_number) {
$twiml = new SimpleXMLElement('');
$dial = $twiml->addChild('Dial');
$dial->addChild('Number', $to_number);
return $this->update_call($call_sid, array(
'Twiml' => $twiml->asXML()
));
}
/**
* Update an active call
*/
public function update_call($call_sid, $params) {
$url = $this->api_base . '/Accounts/' . $this->account_sid . '/Calls/' . $call_sid . '.json';
return $this->make_request('POST', $url, $params);
}
/**
* Get call details
*/
public function get_call($call_sid) {
$url = $this->api_base . '/Accounts/' . $this->account_sid . '/Calls/' . $call_sid . '.json';
return $this->make_request('GET', $url);
}
/**
* Create TwiML for queue
*/
public function create_queue_twiml($queue_name, $wait_url = null, $wait_message = null) {
$twiml = new SimpleXMLElement('');
if ($wait_message) {
$say = $twiml->addChild('Say', $wait_message);
$say->addAttribute('voice', 'alice');
}
$enqueue = $twiml->addChild('Enqueue', $queue_name);
if ($wait_url) {
$enqueue->addAttribute('waitUrl', $wait_url);
}
return $twiml->asXML();
}
/**
* Create TwiML for IVR menu
*/
public function create_ivr_twiml($message, $options = array()) {
$twiml = new SimpleXMLElement('');
$gather = $twiml->addChild('Gather');
$gather->addAttribute('numDigits', '1');
$gather->addAttribute('timeout', '10');
if (!empty($options['action_url'])) {
$gather->addAttribute('action', $options['action_url']);
}
$say = $gather->addChild('Say', $message);
$say->addAttribute('voice', 'alice');
// Fallback if no input
if (!empty($options['no_input_message'])) {
$say_fallback = $twiml->addChild('Say', $options['no_input_message']);
$say_fallback->addAttribute('voice', 'alice');
}
return $twiml->asXML();
}
/**
* Send SMS
*/
public function send_sms($to_number, $message) {
$url = $this->api_base . '/Accounts/' . $this->account_sid . '/Messages.json';
$data = array(
'To' => $to_number,
'From' => $this->phone_number,
'Body' => $message
);
return $this->make_request('POST', $url, $data);
}
/**
* Get available phone numbers
*/
public function get_phone_numbers() {
$url = $this->api_base . '/Accounts/' . $this->account_sid . '/IncomingPhoneNumbers.json';
return $this->make_request('GET', $url);
}
/**
* Search for available phone numbers
*/
public function search_available_numbers($country_code = 'US', $area_code = null, $contains = null, $limit = 20) {
$url = $this->api_base . '/Accounts/' . $this->account_sid . '/AvailablePhoneNumbers/' . $country_code . '/Local.json';
$params = array('Limit' => $limit);
if ($area_code) {
$params['AreaCode'] = $area_code;
}
if ($contains) {
$params['Contains'] = $contains;
}
$url .= '?' . http_build_query($params);
return $this->make_request('GET', $url);
}
/**
* Purchase a phone number
*/
public function purchase_phone_number($phone_number, $voice_url = null, $sms_url = null) {
$url = $this->api_base . '/Accounts/' . $this->account_sid . '/IncomingPhoneNumbers.json';
$data = array(
'PhoneNumber' => $phone_number
);
if ($voice_url) {
$data['VoiceUrl'] = $voice_url;
$data['VoiceMethod'] = 'POST';
}
if ($sms_url) {
$data['SmsUrl'] = $sms_url;
$data['SmsMethod'] = 'POST';
}
return $this->make_request('POST', $url, $data);
}
/**
* Release a phone number
*/
public function release_phone_number($phone_number_sid) {
$url = $this->api_base . '/Accounts/' . $this->account_sid . '/IncomingPhoneNumbers/' . $phone_number_sid . '.json';
return $this->make_request('DELETE', $url);
}
/**
* Configure phone number webhook
*/
public function configure_phone_number($phone_sid, $voice_url, $sms_url = null) {
$url = $this->api_base . '/Accounts/' . $this->account_sid . '/IncomingPhoneNumbers/' . $phone_sid . '.json';
$data = array(
'VoiceUrl' => $voice_url,
'VoiceMethod' => 'POST'
);
if ($sms_url) {
$data['SmsUrl'] = $sms_url;
$data['SmsMethod'] = 'POST';
}
return $this->make_request('POST', $url, $data);
}
/**
* Make API request
*/
private function make_request($method, $url, $data = array()) {
$args = array(
'method' => $method,
'headers' => array(
'Authorization' => 'Basic ' . base64_encode($this->account_sid . ':' . $this->auth_token),
'Content-Type' => 'application/x-www-form-urlencoded'
),
'timeout' => 30
);
if ($method === 'POST' && !empty($data)) {
$args['body'] = $data;
}
if ($method === 'GET') {
$response = wp_remote_get($url, $args);
} else {
$response = wp_remote_post($url, $args);
}
if (is_wp_error($response)) {
return array(
'success' => false,
'error' => $response->get_error_message()
);
}
$body = wp_remote_retrieve_body($response);
$decoded = json_decode($body, true);
$status_code = wp_remote_retrieve_response_code($response);
if ($status_code >= 200 && $status_code < 300) {
return array(
'success' => true,
'data' => $decoded
);
} else {
return array(
'success' => false,
'error' => isset($decoded['message']) ? $decoded['message'] : 'API request failed',
'code' => $status_code
);
}
}
/**
* Validate webhook signature
*/
public function validate_webhook_signature($url, $params, $signature) {
$data = $url;
if (is_array($params) && !empty($params)) {
ksort($params);
foreach ($params as $key => $value) {
$data .= $key . $value;
}
}
$computed_signature = base64_encode(hash_hmac('sha1', $data, $this->auth_token, true));
return hash_equals($signature, $computed_signature);
}
}