code push
This commit is contained in:
37
CLAUDE.md
37
CLAUDE.md
@@ -18,7 +18,7 @@ This is a comprehensive WordPress plugin for Twilio voice and SMS integration, f
|
|||||||
### Core Classes (`includes/` directory)
|
### Core Classes (`includes/` directory)
|
||||||
- **TWP_Core**: Main plugin initialization and hook registration
|
- **TWP_Core**: Main plugin initialization and hook registration
|
||||||
- **TWP_Activator**: Database table creation and plugin activation
|
- **TWP_Activator**: Database table creation and plugin activation
|
||||||
- **TWP_Twilio_API**: Twilio REST API wrapper (custom implementation)
|
- **TWP_Twilio_API**: Official Twilio PHP SDK wrapper (requires SDK v8.7.0)
|
||||||
- **TWP_Webhooks**: Handles all Twilio webhook endpoints
|
- **TWP_Webhooks**: Handles all Twilio webhook endpoints
|
||||||
- **TWP_Scheduler**: Business hours and schedule management
|
- **TWP_Scheduler**: Business hours and schedule management
|
||||||
- **TWP_Workflow**: Call flow processing and TwiML generation
|
- **TWP_Workflow**: Call flow processing and TwiML generation
|
||||||
@@ -131,25 +131,28 @@ Agent phone numbers stored as user meta:
|
|||||||
|
|
||||||
## Twilio Integration
|
## Twilio Integration
|
||||||
|
|
||||||
### Current Implementation
|
### Current Implementation (SDK-Only)
|
||||||
- **Custom API Wrapper**: `TWP_Twilio_API` class using `wp_remote_post()`
|
- **Official Twilio SDK**: Uses `twilio/sdk` v8.7.0 for all operations
|
||||||
- **TwiML Generation**: String-based XML construction
|
- **TwiML Generation**: Uses `\Twilio\TwiML\VoiceResponse` classes
|
||||||
- **Response Handling**: Custom parsing of Twilio responses
|
- **Response Handling**: Native Twilio SDK response objects
|
||||||
|
- **Error Handling**: Proper `\Twilio\Exceptions\TwilioException` handling
|
||||||
|
|
||||||
### Recommended Migration to Twilio PHP SDK
|
### Installation Requirements
|
||||||
**URL**: https://www.twilio.com/docs/libraries/reference/twilio-php/
|
**IMPORTANT**: The Twilio PHP SDK v8.7.0 is **REQUIRED** for this plugin to function.
|
||||||
|
|
||||||
**Benefits**:
|
**Installation Methods**:
|
||||||
- Official SDK with better error handling
|
1. **Script Installation** (Recommended):
|
||||||
- Built-in TwiML generation classes
|
```bash
|
||||||
- Automatic retries and rate limiting
|
chmod +x install-twilio-sdk.sh
|
||||||
- Type safety and IDE support
|
./install-twilio-sdk.sh
|
||||||
|
```
|
||||||
|
|
||||||
**Migration Strategy**:
|
2. **Composer Installation**:
|
||||||
1. Install via Composer: `composer require twilio/sdk`
|
```bash
|
||||||
2. Replace `TWP_Twilio_API` methods with SDK calls
|
composer install
|
||||||
3. Update TwiML generation to use SDK classes
|
```
|
||||||
4. Maintain existing method signatures for compatibility
|
|
||||||
|
**PHP Requirements**: PHP 8.0+ required for SDK compatibility
|
||||||
|
|
||||||
### API Response Structure
|
### API Response Structure
|
||||||
Current Twilio API responses follow this pattern:
|
Current Twilio API responses follow this pattern:
|
||||||
|
134
README.md
134
README.md
@@ -1,2 +1,134 @@
|
|||||||
# twilio-wp-plugin
|
# Twilio WordPress Plugin
|
||||||
|
|
||||||
|
A comprehensive WordPress plugin for Twilio voice and SMS integration with advanced call center functionality.
|
||||||
|
|
||||||
|
## ⚠️ IMPORTANT: SDK Required
|
||||||
|
|
||||||
|
This plugin **requires** the Twilio PHP SDK v8.7.0 to function. The plugin will not work without it.
|
||||||
|
|
||||||
|
## Quick Installation
|
||||||
|
|
||||||
|
1. **Install the Twilio SDK** (Required):
|
||||||
|
```bash
|
||||||
|
chmod +x install-twilio-sdk.sh
|
||||||
|
./install-twilio-sdk.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Test the SDK installation**:
|
||||||
|
```bash
|
||||||
|
php test-sdk.php
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Configure Twilio Credentials** in WordPress admin:
|
||||||
|
- Account SID
|
||||||
|
- Auth Token
|
||||||
|
- Phone Number
|
||||||
|
|
||||||
|
4. **Test the installation** with a sample call.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- **PHP 8.0+** (required for Twilio SDK v8.7.0)
|
||||||
|
- **WordPress 5.0+**
|
||||||
|
- **Twilio Account** with active phone number
|
||||||
|
- **curl** and **tar** (for SDK installation)
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
- 📞 **Call Center Operations**: Agent groups, queues, call distribution
|
||||||
|
- 🕒 **Business Hours Management**: Automated routing based on schedules
|
||||||
|
- 📱 **Outbound Calling**: Click-to-call with proper caller ID
|
||||||
|
- 💬 **SMS Integration**: Agent notifications and command system
|
||||||
|
- 🎛️ **Workflow Builder**: Visual call flow creation
|
||||||
|
- 🎤 **Voicemail System**: Recording, transcription, and notifications
|
||||||
|
- 📊 **Real-time Dashboard**: Queue management and statistics
|
||||||
|
|
||||||
|
## Installation Methods
|
||||||
|
|
||||||
|
### Option 1: Installation Script (Recommended)
|
||||||
|
```bash
|
||||||
|
# Run in plugin directory
|
||||||
|
./install-twilio-sdk.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: Composer (For Development)
|
||||||
|
```bash
|
||||||
|
composer install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 3: Manual Installation
|
||||||
|
1. Download Twilio SDK v8.7.0 from GitHub
|
||||||
|
2. Extract to `vendor/twilio/sdk/`
|
||||||
|
3. Create autoloader (see install script for reference)
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
The plugin uses:
|
||||||
|
- **Official Twilio PHP SDK v8.7.0** for all API operations
|
||||||
|
- **Native TwiML classes** for response generation
|
||||||
|
- **WordPress hooks and filters** for integration
|
||||||
|
- **Custom database tables** for call management
|
||||||
|
- **REST API endpoints** for webhooks
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
1. Install the SDK using the provided script
|
||||||
|
2. Configure Twilio credentials in WordPress admin
|
||||||
|
3. Set up phone numbers and webhook URLs
|
||||||
|
4. Create agent groups and workflows
|
||||||
|
5. Test with sample calls
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "Twilio SDK classes not available" Error
|
||||||
|
|
||||||
|
1. **Run the installation script**:
|
||||||
|
```bash
|
||||||
|
chmod +x install-twilio-sdk.sh
|
||||||
|
./install-twilio-sdk.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Test SDK installation**:
|
||||||
|
```bash
|
||||||
|
php test-sdk.php
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Check file permissions**:
|
||||||
|
```bash
|
||||||
|
ls -la vendor/
|
||||||
|
ls -la vendor/twilio/sdk/
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Verify directory structure**:
|
||||||
|
```
|
||||||
|
vendor/
|
||||||
|
├── autoload.php
|
||||||
|
└── twilio/
|
||||||
|
└── sdk/
|
||||||
|
├── Rest/Client.php
|
||||||
|
├── TwiML/VoiceResponse.php
|
||||||
|
└── ... (other SDK files)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Plugin Shows 500 Error
|
||||||
|
|
||||||
|
- Check WordPress error logs
|
||||||
|
- Enable WP_DEBUG in wp-config.php
|
||||||
|
- Look for TWP Plugin error messages in logs
|
||||||
|
|
||||||
|
### SDK Installation Fails
|
||||||
|
|
||||||
|
- Ensure `curl` and `tar` are installed
|
||||||
|
- Check internet connection
|
||||||
|
- Try manual installation (see Installation Methods)
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
- Check `CLAUDE.md` for detailed technical documentation
|
||||||
|
- Review `TWILIO_SDK_MIGRATION.md` for migration details
|
||||||
|
- Enable WordPress debug logging for troubleshooting
|
||||||
|
- Use Twilio Console debugger for webhook testing
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This plugin integrates with Twilio services and requires a Twilio account.
|
||||||
|
240
TWILIO_SDK_MIGRATION.md
Normal file
240
TWILIO_SDK_MIGRATION.md
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
# Twilio PHP SDK Migration Guide
|
||||||
|
|
||||||
|
This document outlines the migration of the Twilio WordPress Plugin from a custom API wrapper to the official Twilio PHP SDK v8.7.0.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The plugin now supports both the official Twilio PHP SDK and falls back to a custom implementation when the SDK is not available. This provides better error handling, type safety, and access to the latest Twilio features while maintaining backward compatibility.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Option 1: Using the Installation Script (Recommended)
|
||||||
|
|
||||||
|
1. Navigate to your plugin directory
|
||||||
|
2. Run the installation script:
|
||||||
|
```bash
|
||||||
|
chmod +x install-twilio-sdk.sh
|
||||||
|
./install-twilio-sdk.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: Manual Installation with Composer
|
||||||
|
|
||||||
|
1. Ensure Composer is installed on your system
|
||||||
|
2. Run in the plugin directory:
|
||||||
|
```bash
|
||||||
|
composer install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 3: Manual Download
|
||||||
|
|
||||||
|
1. Download Twilio SDK v8.7.0 from: https://github.com/twilio/twilio-php/releases
|
||||||
|
2. Extract to `vendor/twilio/sdk/`
|
||||||
|
3. Create autoloader file (see installation script for reference)
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
### Auto-Detection
|
||||||
|
|
||||||
|
The plugin automatically detects if the Twilio SDK is available by:
|
||||||
|
1. Checking for `vendor/autoload.php`
|
||||||
|
2. Verifying the `Twilio\Rest\Client` class exists
|
||||||
|
3. Attempting to initialize the client
|
||||||
|
|
||||||
|
### Dual-Mode Operation
|
||||||
|
|
||||||
|
**SDK Mode (When Available):**
|
||||||
|
- Uses official Twilio PHP SDK classes
|
||||||
|
- Better error handling with specific Twilio exceptions
|
||||||
|
- Type safety and IDE support
|
||||||
|
- Automatic retries and rate limiting
|
||||||
|
- Official TwiML generation classes
|
||||||
|
|
||||||
|
**Fallback Mode (Default):**
|
||||||
|
- Uses custom WordPress HTTP API calls
|
||||||
|
- String-based TwiML generation
|
||||||
|
- Maintains all existing functionality
|
||||||
|
- No external dependencies required
|
||||||
|
|
||||||
|
## Code Changes
|
||||||
|
|
||||||
|
### API Class Updates
|
||||||
|
|
||||||
|
The `TWP_Twilio_API` class now:
|
||||||
|
- Detects SDK availability in the constructor
|
||||||
|
- Routes all methods through SDK when available
|
||||||
|
- Falls back to custom implementation seamlessly
|
||||||
|
- Maintains identical method signatures for compatibility
|
||||||
|
|
||||||
|
### TwiML Generation
|
||||||
|
|
||||||
|
**New Unified Approach:**
|
||||||
|
```php
|
||||||
|
// Works with both SDK and fallback
|
||||||
|
$api = new TWP_Twilio_API();
|
||||||
|
$response = $api->create_twiml();
|
||||||
|
$response->say('Hello world', ['voice' => 'alice']);
|
||||||
|
$response->dial('+1234567890', ['timeout' => 30]);
|
||||||
|
$twiml = $response->asXML();
|
||||||
|
```
|
||||||
|
|
||||||
|
**Old Approach (Now Deprecated):**
|
||||||
|
```php
|
||||||
|
// String concatenation - now handled automatically
|
||||||
|
$twiml = '<?xml version="1.0" encoding="UTF-8"?>';
|
||||||
|
$twiml .= '<Response>';
|
||||||
|
$twiml .= '<Say voice="alice">Hello world</Say>';
|
||||||
|
$twiml .= '</Response>';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Updated Classes
|
||||||
|
|
||||||
|
1. **TWP_Twilio_API**: Core API wrapper with SDK integration
|
||||||
|
2. **TWP_Webhooks**: All webhook handlers updated to use new TwiML builder
|
||||||
|
3. **TWP_Callback_Manager**: Outbound calling and callback management
|
||||||
|
4. **TWP_Workflow**: Call flow TwiML generation
|
||||||
|
5. **TWP_Agent_Manager**: Agent call routing
|
||||||
|
|
||||||
|
## Benefits
|
||||||
|
|
||||||
|
### With SDK Available
|
||||||
|
- **Better Error Handling**: Specific exception types for different error scenarios
|
||||||
|
- **Type Safety**: IDE support and parameter validation
|
||||||
|
- **Official Support**: Direct access to latest Twilio features
|
||||||
|
- **Reliability**: Built-in retry logic and rate limiting
|
||||||
|
- **Performance**: Optimized for high-volume operations
|
||||||
|
|
||||||
|
### Without SDK (Fallback)
|
||||||
|
- **No Dependencies**: Works on any PHP installation
|
||||||
|
- **Lightweight**: Minimal memory footprint
|
||||||
|
- **Compatibility**: Works with older PHP versions
|
||||||
|
- **Reliability**: Battle-tested custom implementation
|
||||||
|
|
||||||
|
## Supported PHP Versions
|
||||||
|
|
||||||
|
- **With SDK**: PHP 8.0+ (SDK v8.7.0 supports PHP 8.4)
|
||||||
|
- **Without SDK**: PHP 7.2+ (fallback implementation)
|
||||||
|
|
||||||
|
## Feature Support
|
||||||
|
|
||||||
|
### API Operations
|
||||||
|
- ✅ Make calls
|
||||||
|
- ✅ Send SMS
|
||||||
|
- ✅ Get call details
|
||||||
|
- ✅ Update active calls
|
||||||
|
- ✅ Manage phone numbers
|
||||||
|
- ✅ Search available numbers
|
||||||
|
- ✅ Purchase/release numbers
|
||||||
|
- ✅ Configure webhooks
|
||||||
|
|
||||||
|
### TwiML Generation
|
||||||
|
- ✅ Say (text-to-speech)
|
||||||
|
- ✅ Dial (with caller ID, timeout)
|
||||||
|
- ✅ Gather (digit collection with prompts)
|
||||||
|
- ✅ Enqueue (queue management)
|
||||||
|
- ✅ Record (voicemail recording)
|
||||||
|
- ✅ Redirect (call flow control)
|
||||||
|
- ✅ Hangup
|
||||||
|
|
||||||
|
### Advanced Features
|
||||||
|
- ✅ Webhook signature validation
|
||||||
|
- ✅ Conference calling
|
||||||
|
- ✅ Queue management
|
||||||
|
- ✅ Agent group routing
|
||||||
|
- ✅ Callback requests
|
||||||
|
- ✅ Voicemail transcription
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
### Verifying SDK Installation
|
||||||
|
|
||||||
|
Check the admin dashboard or logs for:
|
||||||
|
```
|
||||||
|
TWP Plugin: Twilio SDK v8.7.0 loaded successfully
|
||||||
|
```
|
||||||
|
|
||||||
|
### API Testing
|
||||||
|
|
||||||
|
Use the plugin's test call feature to verify:
|
||||||
|
1. Outbound calling works
|
||||||
|
2. TwiML generation is valid
|
||||||
|
3. Webhooks receive proper responses
|
||||||
|
4. Error handling functions correctly
|
||||||
|
|
||||||
|
### Manual Testing
|
||||||
|
|
||||||
|
1. **Test Call Flow**: Make a test call to verify TwiML generation
|
||||||
|
2. **Webhook Testing**: Use Twilio's webhook debugger
|
||||||
|
3. **Error Scenarios**: Test invalid numbers, network failures
|
||||||
|
4. **Queue Operations**: Test agent groups and callbacks
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### SDK Not Loading
|
||||||
|
|
||||||
|
**Check autoloader exists:**
|
||||||
|
```bash
|
||||||
|
ls -la vendor/autoload.php
|
||||||
|
```
|
||||||
|
|
||||||
|
**Check SDK files:**
|
||||||
|
```bash
|
||||||
|
ls -la vendor/twilio/sdk/
|
||||||
|
```
|
||||||
|
|
||||||
|
**PHP Version:**
|
||||||
|
```bash
|
||||||
|
php -v # Should be 8.0+ for SDK support
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. **"Class 'Twilio\Rest\Client' not found"**
|
||||||
|
- SDK not properly installed
|
||||||
|
- Run installation script again
|
||||||
|
|
||||||
|
2. **"Call to undefined method"**
|
||||||
|
- Fallback mode active
|
||||||
|
- Check SDK installation
|
||||||
|
- Verify PHP version compatibility
|
||||||
|
|
||||||
|
3. **"Permission denied" on installation script**
|
||||||
|
- Run: `chmod +x install-twilio-sdk.sh`
|
||||||
|
|
||||||
|
### Fallback Mode Indicators
|
||||||
|
|
||||||
|
The plugin uses fallback mode when:
|
||||||
|
- No `vendor/autoload.php` file
|
||||||
|
- Twilio classes not available
|
||||||
|
- SDK initialization fails
|
||||||
|
- PHP version incompatibility
|
||||||
|
|
||||||
|
## Migration Timeline
|
||||||
|
|
||||||
|
- ✅ **Phase 1**: Core API wrapper updated
|
||||||
|
- ✅ **Phase 2**: Webhook classes migrated
|
||||||
|
- ✅ **Phase 3**: TwiML generation unified
|
||||||
|
- ✅ **Phase 4**: Error handling improved
|
||||||
|
- ⏳ **Phase 5**: Testing and validation
|
||||||
|
- 📋 **Phase 6**: Documentation complete
|
||||||
|
|
||||||
|
## Rollback Plan
|
||||||
|
|
||||||
|
If issues arise, the plugin automatically falls back to the custom implementation. No manual intervention required.
|
||||||
|
|
||||||
|
To completely disable SDK usage:
|
||||||
|
1. Remove or rename `vendor/` directory
|
||||||
|
2. Plugin will automatically use fallback mode
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
For issues related to:
|
||||||
|
- **Plugin functionality**: Check WordPress error logs
|
||||||
|
- **Twilio API errors**: Check Twilio Console debugger
|
||||||
|
- **SDK-specific issues**: Refer to [Twilio PHP SDK documentation](https://www.twilio.com/docs/libraries/reference/twilio-php/)
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [Twilio PHP SDK GitHub](https://github.com/twilio/twilio-php)
|
||||||
|
- [Twilio API Documentation](https://www.twilio.com/docs/usage/api)
|
||||||
|
- [TwiML Reference](https://www.twilio.com/docs/voice/twiml)
|
||||||
|
- [WordPress HTTP API](https://developer.wordpress.org/reference/classes/wp_http/)
|
29
composer.json
Normal file
29
composer.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "twilio-wp/twilio-wp-plugin",
|
||||||
|
"description": "WordPress plugin for Twilio voice and SMS integration - REQUIRES Twilio SDK",
|
||||||
|
"type": "wordpress-plugin",
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.0",
|
||||||
|
"twilio/sdk": "^8.7"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"classmap": [
|
||||||
|
"includes/",
|
||||||
|
"admin/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"optimize-autoloader": true,
|
||||||
|
"platform": {
|
||||||
|
"php": "8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"post-install-cmd": [
|
||||||
|
"echo 'Twilio SDK installed successfully!'"
|
||||||
|
],
|
||||||
|
"install-sdk": [
|
||||||
|
"./install-twilio-sdk.sh"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
83
debug-phone-numbers.php
Normal file
83
debug-phone-numbers.php
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Debug script to see what Twilio SDK actually returns
|
||||||
|
* Run this to debug phone number issues
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Load WordPress (adjust path as needed)
|
||||||
|
$wp_load_path = dirname(dirname(dirname(dirname(__FILE__)))) . '/wp-load.php';
|
||||||
|
if (!file_exists($wp_load_path)) {
|
||||||
|
echo "WordPress not found. Please adjust the path in this script.\n";
|
||||||
|
echo "Looking for: $wp_load_path\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once $wp_load_path;
|
||||||
|
|
||||||
|
echo "Debug: Twilio Phone Numbers\n";
|
||||||
|
echo "===========================\n\n";
|
||||||
|
|
||||||
|
// Load Twilio SDK
|
||||||
|
$autoloader = __DIR__ . '/vendor/autoload.php';
|
||||||
|
if (!file_exists($autoloader)) {
|
||||||
|
echo "ERROR: SDK not found. Run ./install-twilio-sdk.sh first.\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once $autoloader;
|
||||||
|
|
||||||
|
// Get Twilio credentials from WordPress
|
||||||
|
$account_sid = get_option('twp_twilio_account_sid');
|
||||||
|
$auth_token = get_option('twp_twilio_auth_token');
|
||||||
|
|
||||||
|
if (empty($account_sid) || empty($auth_token)) {
|
||||||
|
echo "ERROR: Twilio credentials not configured in WordPress.\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Account SID: " . substr($account_sid, 0, 10) . "...\n";
|
||||||
|
echo "Auth Token: " . substr($auth_token, 0, 10) . "...\n\n";
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create Twilio client
|
||||||
|
$client = new \Twilio\Rest\Client($account_sid, $auth_token);
|
||||||
|
echo "✅ Twilio client created successfully\n\n";
|
||||||
|
|
||||||
|
// Get phone numbers
|
||||||
|
echo "Fetching phone numbers...\n";
|
||||||
|
$numbers = $client->incomingPhoneNumbers->read([], 10);
|
||||||
|
|
||||||
|
if (empty($numbers)) {
|
||||||
|
echo "No phone numbers found in your Twilio account.\n";
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Found " . count($numbers) . " phone number(s):\n\n";
|
||||||
|
|
||||||
|
foreach ($numbers as $i => $number) {
|
||||||
|
echo "=== Phone Number " . ($i + 1) . " ===\n";
|
||||||
|
echo "SID: " . $number->sid . "\n";
|
||||||
|
echo "Phone Number: " . $number->phoneNumber . "\n";
|
||||||
|
echo "Friendly Name: " . ($number->friendlyName ?: '[Not set]') . "\n";
|
||||||
|
echo "Voice URL: " . ($number->voiceUrl ?: '[Not set]') . "\n";
|
||||||
|
echo "SMS URL: " . ($number->smsUrl ?: '[Not set]') . "\n";
|
||||||
|
echo "Account SID: " . $number->accountSid . "\n";
|
||||||
|
|
||||||
|
// Debug capabilities object
|
||||||
|
echo "\nCapabilities (raw object):\n";
|
||||||
|
var_dump($number->capabilities);
|
||||||
|
|
||||||
|
echo "\nCapabilities (properties):\n";
|
||||||
|
echo "- Voice: " . ($number->capabilities->voice ? 'YES' : 'NO') . "\n";
|
||||||
|
echo "- SMS: " . ($number->capabilities->sms ? 'YES' : 'NO') . "\n";
|
||||||
|
echo "- MMS: " . ($number->capabilities->mms ? 'YES' : 'NO') . "\n";
|
||||||
|
echo "\n" . str_repeat('-', 40) . "\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "ERROR: " . $e->getMessage() . "\n";
|
||||||
|
echo "Class: " . get_class($e) . "\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Debug complete!\n";
|
@@ -239,8 +239,7 @@ class TWP_Agent_Manager {
|
|||||||
|
|
||||||
// Create TwiML to redirect the call
|
// Create TwiML to redirect the call
|
||||||
$twiml = new \Twilio\TwiML\VoiceResponse();
|
$twiml = new \Twilio\TwiML\VoiceResponse();
|
||||||
$dial = $twiml->dial();
|
$twiml->dial($phone_number, [
|
||||||
$dial->number($phone_number, [
|
|
||||||
'statusCallback' => home_url('/wp-json/twilio-webhook/v1/call-status'),
|
'statusCallback' => home_url('/wp-json/twilio-webhook/v1/call-status'),
|
||||||
'statusCallbackEvent' => array('completed')
|
'statusCallbackEvent' => array('completed')
|
||||||
]);
|
]);
|
||||||
@@ -307,20 +306,17 @@ class TWP_Agent_Manager {
|
|||||||
// Play a message while dialing
|
// Play a message while dialing
|
||||||
$twiml->say('Please wait while we connect your call...', ['voice' => 'alice']);
|
$twiml->say('Please wait while we connect your call...', ['voice' => 'alice']);
|
||||||
|
|
||||||
// Create a dial with simultaneous ring
|
// Create a dial with simultaneous ring to all group members
|
||||||
$dial = $twiml->dial([
|
$dial = $twiml->dial([
|
||||||
'timeout' => 30,
|
'timeout' => 30,
|
||||||
'action' => home_url('/wp-json/twilio-webhook/v1/dial-status'),
|
'action' => home_url('/wp-json/twilio-webhook/v1/dial-status'),
|
||||||
'method' => 'POST'
|
'method' => 'POST'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Add each member's number to the dial
|
// Add each member's number to the dial for simultaneous ring
|
||||||
foreach ($members as $member) {
|
foreach ($members as $member) {
|
||||||
if ($member['phone_number']) {
|
if ($member['phone_number']) {
|
||||||
$dial->number($member['phone_number'], [
|
$dial->number($member['phone_number']);
|
||||||
'statusCallback' => home_url('/wp-json/twilio-webhook/v1/member-status'),
|
|
||||||
'statusCallbackEvent' => array('answered', 'completed')
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -232,17 +232,13 @@ class TWP_Callback_Manager {
|
|||||||
* Handle outbound agent answered
|
* Handle outbound agent answered
|
||||||
*/
|
*/
|
||||||
public static function handle_outbound_agent_answered($target_number, $agent_call_sid) {
|
public static function handle_outbound_agent_answered($target_number, $agent_call_sid) {
|
||||||
$twilio = new TWP_Twilio_API();
|
|
||||||
|
|
||||||
// Create TwiML to call the target number
|
// Create TwiML to call the target number
|
||||||
$twiml = new \Twilio\TwiML\VoiceResponse();
|
$twiml = new \Twilio\TwiML\VoiceResponse();
|
||||||
$twiml->say('Connecting your call...', ['voice' => 'alice']);
|
$twiml->say('Connecting your call...', ['voice' => 'alice']);
|
||||||
|
$twiml->dial($target_number, [
|
||||||
$dial = $twiml->dial([
|
'callerId' => get_option('twp_caller_id_number', ''),
|
||||||
'callerId' => get_option('twp_caller_id_number', ''), // Use configured caller ID
|
|
||||||
'timeout' => 30
|
'timeout' => 30
|
||||||
]);
|
]);
|
||||||
$dial->number($target_number);
|
|
||||||
|
|
||||||
// If no answer, leave a message
|
// If no answer, leave a message
|
||||||
$twiml->say('The number you called is not available. Please try again later.', ['voice' => 'alice']);
|
$twiml->say('The number you called is not available. Please try again later.', ['voice' => 'alice']);
|
||||||
@@ -259,7 +255,10 @@ class TWP_Callback_Manager {
|
|||||||
$gather = $twiml->gather([
|
$gather = $twiml->gather([
|
||||||
'numDigits' => 1,
|
'numDigits' => 1,
|
||||||
'timeout' => 10,
|
'timeout' => 10,
|
||||||
'action' => home_url('/wp-json/twilio-webhook/v1/callback-choice'),
|
'action' => home_url('/wp-json/twilio-webhook/v1/callback-choice?' . http_build_query([
|
||||||
|
'queue_id' => $queue_id,
|
||||||
|
'phone_number' => $caller_number
|
||||||
|
])),
|
||||||
'method' => 'POST'
|
'method' => 'POST'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -1,276 +1,474 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* Twilio API integration class
|
* Twilio API integration class using official Twilio PHP SDK
|
||||||
*/
|
*/
|
||||||
class TWP_Twilio_API {
|
class TWP_Twilio_API {
|
||||||
|
|
||||||
private $account_sid;
|
private $client;
|
||||||
private $auth_token;
|
|
||||||
private $phone_number;
|
private $phone_number;
|
||||||
private $api_base = 'https://api.twilio.com/2010-04-01';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->account_sid = get_option('twp_twilio_account_sid');
|
$this->init_sdk_client();
|
||||||
$this->auth_token = get_option('twp_twilio_auth_token');
|
|
||||||
$this->phone_number = get_option('twp_twilio_phone_number');
|
$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
|
* Make a phone call
|
||||||
*/
|
*/
|
||||||
public function make_call($to_number, $twiml_url, $status_callback = null, $from_number = null) {
|
public function make_call($to_number, $twiml_url, $status_callback = null, $from_number = null) {
|
||||||
$url = $this->api_base . '/Accounts/' . $this->account_sid . '/Calls.json';
|
try {
|
||||||
|
$params = [
|
||||||
$data = array(
|
'url' => $twiml_url,
|
||||||
'To' => $to_number,
|
'from' => $from_number ?: $this->phone_number,
|
||||||
'From' => $from_number ?: $this->phone_number,
|
'to' => $to_number
|
||||||
'Url' => $twiml_url
|
];
|
||||||
);
|
|
||||||
|
|
||||||
if ($status_callback) {
|
if ($status_callback) {
|
||||||
$data['StatusCallback'] = $status_callback;
|
$params['statusCallback'] = $status_callback;
|
||||||
$data['StatusCallbackEvent'] = array('initiated', 'ringing', 'answered', 'completed');
|
$params['statusCallbackEvent'] = ['initiated', 'ringing', 'answered', 'completed'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->make_request('POST', $url, $data);
|
$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
|
* Forward a call
|
||||||
*/
|
*/
|
||||||
public function forward_call($call_sid, $to_number) {
|
public function forward_call($call_sid, $to_number) {
|
||||||
$twiml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><Response></Response>');
|
try {
|
||||||
$dial = $twiml->addChild('Dial');
|
$twiml = new \Twilio\TwiML\VoiceResponse();
|
||||||
$dial->addChild('Number', $to_number);
|
$twiml->dial($to_number);
|
||||||
|
|
||||||
return $this->update_call($call_sid, array(
|
$call = $this->client->calls($call_sid)->update([
|
||||||
'Twiml' => $twiml->asXML()
|
'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
|
* Update an active call
|
||||||
*/
|
*/
|
||||||
public function update_call($call_sid, $params) {
|
public function update_call($call_sid, $params) {
|
||||||
$url = $this->api_base . '/Accounts/' . $this->account_sid . '/Calls/' . $call_sid . '.json';
|
try {
|
||||||
return $this->make_request('POST', $url, $params);
|
$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
|
* Get call details
|
||||||
*/
|
*/
|
||||||
public function get_call($call_sid) {
|
public function get_call($call_sid) {
|
||||||
$url = $this->api_base . '/Accounts/' . $this->account_sid . '/Calls/' . $call_sid . '.json';
|
try {
|
||||||
return $this->make_request('GET', $url);
|
$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
|
* Create TwiML for queue
|
||||||
*/
|
*/
|
||||||
public function create_queue_twiml($queue_name, $wait_url = null, $wait_message = null) {
|
public function create_queue_twiml($queue_name, $wait_url = null, $wait_message = null) {
|
||||||
$twiml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><Response></Response>');
|
try {
|
||||||
|
$response = new \Twilio\TwiML\VoiceResponse();
|
||||||
|
|
||||||
if ($wait_message) {
|
if ($wait_message) {
|
||||||
$say = $twiml->addChild('Say', $wait_message);
|
$response->say($wait_message, ['voice' => 'alice']);
|
||||||
$say->addAttribute('voice', 'alice');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$enqueue = $twiml->addChild('Enqueue', $queue_name);
|
$enqueue = $response->enqueue($queue_name);
|
||||||
|
|
||||||
if ($wait_url) {
|
if ($wait_url) {
|
||||||
$enqueue->addAttribute('waitUrl', $wait_url);
|
$enqueue->waitUrl($wait_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $twiml->asXML();
|
return $response->asXML();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('TWP Plugin: Failed to create queue TwiML: ' . $e->getMessage());
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create TwiML for IVR menu
|
* Create TwiML for IVR menu
|
||||||
*/
|
*/
|
||||||
public function create_ivr_twiml($message, $options = array()) {
|
public function create_ivr_twiml($message, $options = array()) {
|
||||||
$twiml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><Response></Response>');
|
try {
|
||||||
|
$response = new \Twilio\TwiML\VoiceResponse();
|
||||||
|
|
||||||
$gather = $twiml->addChild('Gather');
|
$gather = $response->gather([
|
||||||
$gather->addAttribute('numDigits', '1');
|
'numDigits' => 1,
|
||||||
$gather->addAttribute('timeout', '10');
|
'timeout' => 10,
|
||||||
|
'action' => isset($options['action_url']) ? $options['action_url'] : null
|
||||||
|
]);
|
||||||
|
|
||||||
if (!empty($options['action_url'])) {
|
$gather->say($message, ['voice' => 'alice']);
|
||||||
$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'])) {
|
if (!empty($options['no_input_message'])) {
|
||||||
$say_fallback = $twiml->addChild('Say', $options['no_input_message']);
|
$response->say($options['no_input_message'], ['voice' => 'alice']);
|
||||||
$say_fallback->addAttribute('voice', 'alice');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $twiml->asXML();
|
return $response->asXML();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('TWP Plugin: Failed to create IVR TwiML: ' . $e->getMessage());
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send SMS
|
* Send SMS
|
||||||
*/
|
*/
|
||||||
public function send_sms($to_number, $message) {
|
public function send_sms($to_number, $message, $from_number = null) {
|
||||||
$url = $this->api_base . '/Accounts/' . $this->account_sid . '/Messages.json';
|
try {
|
||||||
|
$sms = $this->client->messages->create(
|
||||||
$data = array(
|
$to_number,
|
||||||
'To' => $to_number,
|
[
|
||||||
'From' => $this->phone_number,
|
'from' => $from_number ?: $this->phone_number,
|
||||||
'Body' => $message
|
'body' => $message
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
return $this->make_request('POST', $url, $data);
|
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
|
* Get available phone numbers
|
||||||
*/
|
*/
|
||||||
public function get_phone_numbers() {
|
public function get_phone_numbers() {
|
||||||
$url = $this->api_base . '/Accounts/' . $this->account_sid . '/IncomingPhoneNumbers.json';
|
try {
|
||||||
return $this->make_request('GET', $url);
|
$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
|
* Search for available phone numbers
|
||||||
*/
|
*/
|
||||||
public function search_available_numbers($country_code = 'US', $area_code = null, $contains = null, $limit = 20) {
|
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';
|
try {
|
||||||
|
$params = ['limit' => $limit];
|
||||||
$params = array('Limit' => $limit);
|
|
||||||
|
|
||||||
if ($area_code) {
|
if ($area_code) {
|
||||||
$params['AreaCode'] = $area_code;
|
$params['areaCode'] = $area_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($contains) {
|
if ($contains) {
|
||||||
$params['Contains'] = $contains;
|
$params['contains'] = $contains;
|
||||||
}
|
}
|
||||||
|
|
||||||
$url .= '?' . http_build_query($params);
|
$numbers = $this->client->availablePhoneNumbers($country_code)
|
||||||
|
->local
|
||||||
|
->read($params, $limit);
|
||||||
|
|
||||||
return $this->make_request('GET', $url);
|
$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
|
* Purchase a phone number
|
||||||
*/
|
*/
|
||||||
public function purchase_phone_number($phone_number, $voice_url = null, $sms_url = null) {
|
public function purchase_phone_number($phone_number, $voice_url = null, $sms_url = null) {
|
||||||
$url = $this->api_base . '/Accounts/' . $this->account_sid . '/IncomingPhoneNumbers.json';
|
try {
|
||||||
|
$params = ['phoneNumber' => $phone_number];
|
||||||
$data = array(
|
|
||||||
'PhoneNumber' => $phone_number
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($voice_url) {
|
if ($voice_url) {
|
||||||
$data['VoiceUrl'] = $voice_url;
|
$params['voiceUrl'] = $voice_url;
|
||||||
$data['VoiceMethod'] = 'POST';
|
$params['voiceMethod'] = 'POST';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($sms_url) {
|
if ($sms_url) {
|
||||||
$data['SmsUrl'] = $sms_url;
|
$params['smsUrl'] = $sms_url;
|
||||||
$data['SmsMethod'] = 'POST';
|
$params['smsMethod'] = 'POST';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->make_request('POST', $url, $data);
|
$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
|
* Release a phone number
|
||||||
*/
|
*/
|
||||||
public function release_phone_number($phone_number_sid) {
|
public function release_phone_number($phone_number_sid) {
|
||||||
$url = $this->api_base . '/Accounts/' . $this->account_sid . '/IncomingPhoneNumbers/' . $phone_number_sid . '.json';
|
try {
|
||||||
return $this->make_request('DELETE', $url);
|
$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
|
* Configure phone number webhook
|
||||||
*/
|
*/
|
||||||
public function configure_phone_number($phone_sid, $voice_url, $sms_url = null) {
|
public function configure_phone_number($phone_sid, $voice_url, $sms_url = null) {
|
||||||
$url = $this->api_base . '/Accounts/' . $this->account_sid . '/IncomingPhoneNumbers/' . $phone_sid . '.json';
|
try {
|
||||||
|
$params = [
|
||||||
$data = array(
|
'voiceUrl' => $voice_url,
|
||||||
'VoiceUrl' => $voice_url,
|
'voiceMethod' => 'POST'
|
||||||
'VoiceMethod' => 'POST'
|
];
|
||||||
);
|
|
||||||
|
|
||||||
if ($sms_url) {
|
if ($sms_url) {
|
||||||
$data['SmsUrl'] = $sms_url;
|
$params['smsUrl'] = $sms_url;
|
||||||
$data['SmsMethod'] = 'POST';
|
$params['smsMethod'] = 'POST';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->make_request('POST', $url, $data);
|
$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()
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make API request
|
* Create TwiML helper - returns SDK VoiceResponse
|
||||||
*/
|
*/
|
||||||
private function make_request($method, $url, $data = array()) {
|
public function create_twiml() {
|
||||||
$args = array(
|
return new \Twilio\TwiML\VoiceResponse();
|
||||||
'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);
|
* Get the Twilio client instance
|
||||||
} else {
|
*/
|
||||||
$response = wp_remote_post($url, $args);
|
public function get_client() {
|
||||||
}
|
return $this->client;
|
||||||
|
|
||||||
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
|
* Validate webhook signature
|
||||||
*/
|
*/
|
||||||
public function validate_webhook_signature($url, $params, $signature) {
|
public function validate_webhook_signature($url, $params, $signature) {
|
||||||
$data = $url;
|
$validator = new \Twilio\Security\RequestValidator(get_option('twp_twilio_auth_token'));
|
||||||
|
return $validator->validate($signature, $url, $params);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -658,11 +658,10 @@ class TWP_Webhooks {
|
|||||||
* Send default response
|
* Send default response
|
||||||
*/
|
*/
|
||||||
private function send_default_response() {
|
private function send_default_response() {
|
||||||
$twiml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><Response></Response>');
|
$response = new \Twilio\TwiML\VoiceResponse();
|
||||||
$say = $twiml->addChild('Say', 'Thank you for calling. Goodbye.');
|
$response->say('Thank you for calling. Goodbye.', ['voice' => 'alice']);
|
||||||
$say->addAttribute('voice', 'alice');
|
$response->hangup();
|
||||||
$twiml->addChild('Hangup');
|
echo $response->asXML();
|
||||||
echo $twiml->asXML();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1074,17 +1073,13 @@ class TWP_Webhooks {
|
|||||||
error_log('TWP Outbound Webhook - All params: ' . print_r($params, true));
|
error_log('TWP Outbound Webhook - All params: ' . print_r($params, true));
|
||||||
|
|
||||||
if ($target_number && $from_number) {
|
if ($target_number && $from_number) {
|
||||||
// Create TwiML to call the target number with the specified from number
|
// Create TwiML using SDK directly
|
||||||
$twiml = '<?xml version="1.0" encoding="UTF-8"?>';
|
$response = new \Twilio\TwiML\VoiceResponse();
|
||||||
$twiml .= '<Response>';
|
$response->say('Connecting your outbound call...', ['voice' => 'alice']);
|
||||||
$twiml .= '<Say voice="alice">Connecting your outbound call...</Say>';
|
$response->dial($target_number, ['callerId' => $from_number, 'timeout' => 30]);
|
||||||
$twiml .= '<Dial callerId="' . esc_attr($from_number) . '" timeout="30">';
|
|
||||||
$twiml .= '<Number>' . esc_html($target_number) . '</Number>';
|
|
||||||
$twiml .= '</Dial>';
|
|
||||||
$twiml .= '<Say voice="alice">The number you called is not available. Please try again later.</Say>';
|
|
||||||
$twiml .= '</Response>';
|
|
||||||
|
|
||||||
return $this->send_twiml_response($twiml);
|
// If call isn't answered, the TwiML will handle the fallback
|
||||||
|
return $this->send_twiml_response($response->asXML());
|
||||||
} else {
|
} else {
|
||||||
// Enhanced error message with debugging info
|
// Enhanced error message with debugging info
|
||||||
$error_msg = 'Unable to process outbound call.';
|
$error_msg = 'Unable to process outbound call.';
|
||||||
@@ -1097,16 +1092,18 @@ class TWP_Webhooks {
|
|||||||
|
|
||||||
error_log('TWP Outbound Error: ' . $error_msg . ' Params: ' . json_encode($params));
|
error_log('TWP Outbound Error: ' . $error_msg . ' Params: ' . json_encode($params));
|
||||||
|
|
||||||
$twiml = '<?xml version="1.0" encoding="UTF-8"?>';
|
$error_response = new \Twilio\TwiML\VoiceResponse();
|
||||||
$twiml .= '<Response><Say voice="alice">' . esc_html($error_msg) . '</Say><Hangup/></Response>';
|
$error_response->say($error_msg, ['voice' => 'alice']);
|
||||||
return $this->send_twiml_response($twiml);
|
$error_response->hangup();
|
||||||
|
return $this->send_twiml_response($error_response->asXML());
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
error_log('TWP Outbound Webhook Exception: ' . $e->getMessage());
|
error_log('TWP Outbound Webhook Exception: ' . $e->getMessage());
|
||||||
$twiml = '<?xml version="1.0" encoding="UTF-8"?>';
|
$exception_response = new \Twilio\TwiML\VoiceResponse();
|
||||||
$twiml .= '<Response><Say voice="alice">Technical error occurred. Please try again.</Say><Hangup/></Response>';
|
$exception_response->say('Technical error occurred. Please try again.', ['voice' => 'alice']);
|
||||||
return $this->send_twiml_response($twiml);
|
$exception_response->hangup();
|
||||||
|
return $this->send_twiml_response($exception_response->asXML());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -341,9 +341,8 @@ class TWP_Workflow {
|
|||||||
} else if ($routing['action'] === 'forward' && $routing['data']['forward_number']) {
|
} else if ($routing['action'] === 'forward' && $routing['data']['forward_number']) {
|
||||||
// Forward call
|
// Forward call
|
||||||
$twiml = new \Twilio\TwiML\VoiceResponse();
|
$twiml = new \Twilio\TwiML\VoiceResponse();
|
||||||
$dial = $twiml->dial();
|
$twiml->dial($routing['data']['forward_number']);
|
||||||
$dial->number($routing['data']['forward_number']);
|
return $twiml->asXML();
|
||||||
return $twiml;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to legacy behavior if new routing doesn't work
|
// Fallback to legacy behavior if new routing doesn't work
|
||||||
|
136
install-twilio-sdk.sh
Normal file
136
install-twilio-sdk.sh
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script to install Twilio PHP SDK without Composer
|
||||||
|
# This is REQUIRED for the plugin to work properly
|
||||||
|
|
||||||
|
echo "Installing Twilio PHP SDK v8.7.0..."
|
||||||
|
echo "IMPORTANT: This SDK is required for the plugin to function."
|
||||||
|
|
||||||
|
# Check if we can download files
|
||||||
|
if ! command -v curl &> /dev/null; then
|
||||||
|
echo "ERROR: curl is required to download the SDK"
|
||||||
|
echo "Please install curl and try again"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v tar &> /dev/null; then
|
||||||
|
echo "ERROR: tar is required to extract the SDK"
|
||||||
|
echo "Please install tar and try again"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create vendor directory if it doesn't exist
|
||||||
|
mkdir -p vendor/twilio/sdk
|
||||||
|
|
||||||
|
# Download the latest release (8.7.0)
|
||||||
|
echo "Downloading Twilio SDK from GitHub..."
|
||||||
|
if ! curl -L https://github.com/twilio/twilio-php/archive/refs/tags/8.7.0.tar.gz -o twilio-sdk.tar.gz; then
|
||||||
|
echo "ERROR: Failed to download Twilio SDK"
|
||||||
|
echo "Please check your internet connection and try again"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract the archive
|
||||||
|
echo "Extracting SDK files..."
|
||||||
|
if ! tar -xzf twilio-sdk.tar.gz; then
|
||||||
|
echo "ERROR: Failed to extract SDK files"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if the extracted directory exists
|
||||||
|
if [ ! -d "twilio-php-8.7.0/src" ]; then
|
||||||
|
echo "ERROR: Extracted SDK directory structure is unexpected"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create the Twilio directory structure
|
||||||
|
echo "Setting up SDK directory structure..."
|
||||||
|
mkdir -p vendor/twilio
|
||||||
|
|
||||||
|
# Remove existing SDK if it exists
|
||||||
|
if [ -d "vendor/twilio/sdk" ]; then
|
||||||
|
rm -rf vendor/twilio/sdk
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Move the entire src directory to be the sdk
|
||||||
|
echo "Installing SDK files..."
|
||||||
|
if ! mv twilio-php-8.7.0/src vendor/twilio/sdk; then
|
||||||
|
echo "ERROR: Failed to move SDK files"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create a comprehensive autoloader
|
||||||
|
cat > vendor/autoload.php << 'EOF'
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Twilio SDK v8.7.0 Autoloader
|
||||||
|
* This file loads the Twilio PHP SDK classes
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Prevent multiple registrations
|
||||||
|
if (!defined('TWILIO_AUTOLOADER_REGISTERED')) {
|
||||||
|
define('TWILIO_AUTOLOADER_REGISTERED', true);
|
||||||
|
|
||||||
|
// Register the autoloader
|
||||||
|
spl_autoload_register(function ($class) {
|
||||||
|
// Only handle Twilio classes
|
||||||
|
if (strpos($class, 'Twilio\\') !== 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert class name to file path
|
||||||
|
$relative_class = substr($class, 7); // Remove 'Twilio\'
|
||||||
|
$file = __DIR__ . '/twilio/sdk/' . str_replace('\\', '/', $relative_class) . '.php';
|
||||||
|
|
||||||
|
if (file_exists($file)) {
|
||||||
|
require_once $file;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Try to load the SDK's own autoloader if it exists
|
||||||
|
$sdk_autoloader = __DIR__ . '/twilio/sdk/autoload.php';
|
||||||
|
if (file_exists($sdk_autoloader)) {
|
||||||
|
require_once $sdk_autoloader;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load essential Twilio classes manually to ensure they're available
|
||||||
|
$essential_classes = [
|
||||||
|
__DIR__ . '/twilio/sdk/Rest/Client.php',
|
||||||
|
__DIR__ . '/twilio/sdk/TwiML/VoiceResponse.php',
|
||||||
|
__DIR__ . '/twilio/sdk/Exceptions/TwilioException.php',
|
||||||
|
__DIR__ . '/twilio/sdk/Security/RequestValidator.php'
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($essential_classes as $class_file) {
|
||||||
|
if (file_exists($class_file)) {
|
||||||
|
require_once $class_file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
rm -rf twilio-php-8.7.0
|
||||||
|
rm twilio-sdk.tar.gz
|
||||||
|
|
||||||
|
# Verify installation
|
||||||
|
echo ""
|
||||||
|
echo "Verifying installation..."
|
||||||
|
if [ -f "vendor/autoload.php" ] && [ -d "vendor/twilio/sdk" ]; then
|
||||||
|
echo "✓ Twilio SDK v8.7.0 installed successfully!"
|
||||||
|
echo "✓ Autoloader created"
|
||||||
|
echo "✓ Installation complete"
|
||||||
|
echo ""
|
||||||
|
echo "The Twilio WordPress Plugin is now ready to use."
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo "1. Configure your Twilio credentials in WordPress admin"
|
||||||
|
echo "2. Set up your phone numbers and webhooks"
|
||||||
|
echo "3. Test the installation with a sample call"
|
||||||
|
else
|
||||||
|
echo "✗ Installation failed - files not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
34
test-ajax-response.php
Normal file
34
test-ajax-response.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Test what the AJAX method actually returns
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Load WordPress
|
||||||
|
$wp_load_path = dirname(dirname(dirname(dirname(__FILE__)))) . '/wp-load.php';
|
||||||
|
require_once $wp_load_path;
|
||||||
|
|
||||||
|
// Load the plugin
|
||||||
|
require_once __DIR__ . '/includes/class-twp-twilio-api.php';
|
||||||
|
|
||||||
|
// Create API instance
|
||||||
|
$api = new TWP_Twilio_API();
|
||||||
|
|
||||||
|
// Get phone numbers
|
||||||
|
$response = $api->get_phone_numbers();
|
||||||
|
|
||||||
|
echo "API Response:\n";
|
||||||
|
echo "=============\n";
|
||||||
|
print_r($response);
|
||||||
|
|
||||||
|
if ($response['success'] && !empty($response['data']['incoming_phone_numbers'])) {
|
||||||
|
echo "\nFirst phone number data:\n";
|
||||||
|
echo "========================\n";
|
||||||
|
$first = $response['data']['incoming_phone_numbers'][0];
|
||||||
|
foreach ($first as $key => $value) {
|
||||||
|
if (is_array($value)) {
|
||||||
|
echo "$key: " . json_encode($value) . "\n";
|
||||||
|
} else {
|
||||||
|
echo "$key: $value\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
test-capabilities.php
Normal file
37
test-capabilities.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Test script to check PhoneNumberCapabilities methods
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Load the autoloader
|
||||||
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
// Create a mock capabilities object to test available methods
|
||||||
|
echo "Testing PhoneNumberCapabilities methods:\n";
|
||||||
|
echo "========================================\n";
|
||||||
|
|
||||||
|
// We'll check what methods are available
|
||||||
|
$reflection = new ReflectionClass('Twilio\Base\PhoneNumberCapabilities');
|
||||||
|
$methods = $reflection->getMethods(ReflectionMethod::IS_PUBLIC);
|
||||||
|
|
||||||
|
echo "Public methods available:\n";
|
||||||
|
foreach ($methods as $method) {
|
||||||
|
if (!$method->isConstructor() && !$method->isDestructor()) {
|
||||||
|
echo "- " . $method->getName() . "()\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\nProperties:\n";
|
||||||
|
$properties = $reflection->getProperties();
|
||||||
|
foreach ($properties as $property) {
|
||||||
|
echo "- " . $property->getName() . " (" . ($property->isPublic() ? 'public' : ($property->isProtected() ? 'protected' : 'private')) . ")\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we can access via array notation
|
||||||
|
echo "\nTesting array access:\n";
|
||||||
|
try {
|
||||||
|
// This won't work, but let's see what happens
|
||||||
|
echo "ArrayAccess interface: " . (in_array('ArrayAccess', class_implements('Twilio\Base\PhoneNumberCapabilities')) ? 'YES' : 'NO') . "\n";
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "Error: " . $e->getMessage() . "\n";
|
||||||
|
}
|
71
test-sdk.php
Normal file
71
test-sdk.php
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Simple test script to verify Twilio SDK installation
|
||||||
|
* Run this from command line: php test-sdk.php
|
||||||
|
*/
|
||||||
|
|
||||||
|
echo "Twilio SDK Installation Test\n";
|
||||||
|
echo "===========================\n\n";
|
||||||
|
|
||||||
|
// Check if autoloader exists
|
||||||
|
$autoloader = __DIR__ . '/vendor/autoload.php';
|
||||||
|
echo "1. Checking autoloader...\n";
|
||||||
|
echo " Path: $autoloader\n";
|
||||||
|
|
||||||
|
if (!file_exists($autoloader)) {
|
||||||
|
echo " ❌ FAIL: Autoloader not found\n";
|
||||||
|
echo " Please run: ./install-twilio-sdk.sh\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo " ✅ OK: Autoloader exists\n\n";
|
||||||
|
|
||||||
|
// Load autoloader
|
||||||
|
echo "2. Loading autoloader...\n";
|
||||||
|
require_once $autoloader;
|
||||||
|
echo " ✅ OK: Autoloader loaded\n\n";
|
||||||
|
|
||||||
|
// Check for Twilio classes
|
||||||
|
echo "3. Checking Twilio classes...\n";
|
||||||
|
|
||||||
|
$classes_to_check = [
|
||||||
|
'Twilio\Rest\Client',
|
||||||
|
'Twilio\TwiML\VoiceResponse',
|
||||||
|
'Twilio\Exceptions\TwilioException',
|
||||||
|
'Twilio\Security\RequestValidator'
|
||||||
|
];
|
||||||
|
|
||||||
|
$all_good = true;
|
||||||
|
foreach ($classes_to_check as $class) {
|
||||||
|
if (class_exists($class)) {
|
||||||
|
echo " ✅ OK: $class\n";
|
||||||
|
} else {
|
||||||
|
echo " ❌ FAIL: $class not found\n";
|
||||||
|
$all_good = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\n";
|
||||||
|
|
||||||
|
if ($all_good) {
|
||||||
|
echo "🎉 SUCCESS: Twilio SDK is properly installed!\n";
|
||||||
|
echo "\nYou can now use the WordPress plugin.\n";
|
||||||
|
echo "Don't forget to configure your Twilio credentials in WordPress admin.\n";
|
||||||
|
} else {
|
||||||
|
echo "❌ FAILURE: SDK installation incomplete\n";
|
||||||
|
echo "\nPlease run: ./install-twilio-sdk.sh\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to create a simple TwiML response
|
||||||
|
echo "\n4. Testing TwiML generation...\n";
|
||||||
|
try {
|
||||||
|
$response = new \Twilio\TwiML\VoiceResponse();
|
||||||
|
$response->say('Hello from Twilio SDK test!');
|
||||||
|
echo " ✅ OK: TwiML generation works\n";
|
||||||
|
echo " Generated: " . substr(str_replace(["\n", "\r"], '', $response->asXML()), 0, 100) . "...\n";
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo " ❌ FAIL: TwiML generation failed: " . $e->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\nInstallation test complete!\n";
|
@@ -28,6 +28,45 @@ function twp_activate() {
|
|||||||
TWP_Activator::activate();
|
TWP_Activator::activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if Twilio SDK is installed and show admin notice if not
|
||||||
|
*/
|
||||||
|
function twp_check_sdk_installation() {
|
||||||
|
$autoloader_path = TWP_PLUGIN_DIR . 'vendor/autoload.php';
|
||||||
|
$sdk_installed = false;
|
||||||
|
|
||||||
|
if (file_exists($autoloader_path)) {
|
||||||
|
// Try to load autoloader and check for classes
|
||||||
|
require_once $autoloader_path;
|
||||||
|
$sdk_installed = class_exists('Twilio\Rest\Client');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$sdk_installed) {
|
||||||
|
add_action('admin_notices', 'twp_sdk_missing_notice');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display admin notice for missing SDK
|
||||||
|
*/
|
||||||
|
function twp_sdk_missing_notice() {
|
||||||
|
?>
|
||||||
|
<div class="notice notice-error is-dismissible">
|
||||||
|
<h3>Twilio WordPress Plugin - SDK Required</h3>
|
||||||
|
<p><strong>The Twilio PHP SDK is required for this plugin to work.</strong></p>
|
||||||
|
<p>To install the SDK, run this command in your plugin directory:</p>
|
||||||
|
<code>chmod +x install-twilio-sdk.sh && ./install-twilio-sdk.sh</code>
|
||||||
|
<p>Or install via Composer: <code>composer install</code></p>
|
||||||
|
<p><em>Plugin path: <?php echo TWP_PLUGIN_DIR; ?></em></p>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check SDK installation on admin pages
|
||||||
|
if (is_admin()) {
|
||||||
|
add_action('admin_init', 'twp_check_sdk_installation');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin deactivation hook
|
* Plugin deactivation hook
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user