testing progress
This commit is contained in:
373
README.md
373
README.md
@@ -20,11 +20,14 @@ This plugin **requires** the Twilio PHP SDK v8.7.0 to function. The plugin will
|
|||||||
```
|
```
|
||||||
|
|
||||||
3. **Configure Twilio Credentials** in WordPress admin:
|
3. **Configure Twilio Credentials** in WordPress admin:
|
||||||
- Account SID
|
- Go to **Twilio** → **Settings**
|
||||||
- Auth Token
|
- Enter Account SID and Auth Token
|
||||||
- Phone Number
|
- Configure default phone numbers
|
||||||
|
|
||||||
4. **Test the installation** with a sample call.
|
4. **Set up Phone Numbers** in Twilio Console:
|
||||||
|
- Configure webhook URLs for voice and SMS
|
||||||
|
- Voice: `https://yoursite.com/wp-json/twilio-webhook/v1/voice`
|
||||||
|
- SMS: `https://yoursite.com/wp-json/twilio-webhook/v1/sms`
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
@@ -35,99 +38,327 @@ This plugin **requires** the Twilio PHP SDK v8.7.0 to function. The plugin will
|
|||||||
|
|
||||||
## Key Features
|
## Key Features
|
||||||
|
|
||||||
- 📞 **Call Center Operations**: Agent groups, queues, call distribution
|
### 📞 Call Center Operations
|
||||||
- 🕒 **Business Hours Management**: Automated routing based on schedules
|
- **Agent Groups**: Organize agents into groups with priority levels
|
||||||
- 📱 **Outbound Calling**: Click-to-call with proper caller ID
|
- **Call Queues**: Manage incoming calls with position announcements
|
||||||
- 💬 **SMS Integration**: Agent notifications and command system
|
- **Smart Routing**: Distribute calls based on availability and schedules
|
||||||
- 🎛️ **Workflow Builder**: Visual call flow creation
|
- **SMS Accept**: Agents can text "1" to accept incoming calls
|
||||||
- 🎤 **Voicemail System**: Recording, transcription, and notifications
|
- **Queue Notifications**: SMS alerts to designated numbers when calls enter queues
|
||||||
- 📊 **Real-time Dashboard**: Queue management and statistics
|
|
||||||
|
|
||||||
## Installation Methods
|
### 🌐 Browser Phone (WebRTC)
|
||||||
|
- **In-Browser Calling**: Make and receive calls directly from WordPress admin
|
||||||
|
- **Twilio Voice SDK v2**: Uses latest SDK for WebRTC functionality
|
||||||
|
- **Visual Dialpad**: Click-to-dial interface with DTMF support
|
||||||
|
- **Call Controls**: Mute, hold indicators, call timer
|
||||||
|
- **Auto-Answer**: Optional automatic call acceptance
|
||||||
|
- **Queue Integration**: Accept calls from specific queues
|
||||||
|
- **Token Management**: Automatic token refresh for uninterrupted service
|
||||||
|
|
||||||
### Option 1: Installation Script (Recommended)
|
### 🕒 Business Hours Management
|
||||||
```bash
|
- **Schedule-based Routing**: Different call flows for business hours vs after-hours
|
||||||
# Run in plugin directory
|
- **Holiday Support**: Define specific dates for holiday routing
|
||||||
./install-twilio-sdk.sh
|
- **Multiple Schedules**: Create different schedules for departments
|
||||||
```
|
- **After-Hours Actions**: Configurable routing when closed
|
||||||
|
|
||||||
### Option 2: Composer (For Development)
|
### 🎛️ Workflow Builder
|
||||||
```bash
|
- **Visual Interface**: Drag-and-drop workflow creation
|
||||||
composer install
|
- **Step Types**:
|
||||||
```
|
- **Greeting**: Welcome messages with multiple voice options
|
||||||
|
- **IVR Menu**: Interactive voice response with digit collection
|
||||||
|
- **Call Queue**: Place callers in queue with hold music
|
||||||
|
- **Forward**: Route calls to specific numbers
|
||||||
|
- **Voicemail**: Record messages with transcription
|
||||||
|
- **Schedule Check**: Route based on business hours
|
||||||
|
- **Voice Options**:
|
||||||
|
- Default Twilio voice (Say)
|
||||||
|
- ElevenLabs text-to-speech integration with voice persistence
|
||||||
|
- Custom audio file URLs
|
||||||
|
- **Smart Voice Loading**: Saved voices display without API calls
|
||||||
|
|
||||||
### Option 3: Manual Installation
|
### 📱 SMS Integration
|
||||||
1. Download Twilio SDK v8.7.0 from GitHub
|
- **Agent Notifications**: Automatic SMS alerts when calls arrive
|
||||||
2. Extract to `vendor/twilio/sdk/`
|
- **Queue Management**: Agents receive queue status updates
|
||||||
3. Create autoloader (see install script for reference)
|
- **Command System**: Text commands to manage availability
|
||||||
|
- **SMS Logging**: Complete history of all SMS interactions
|
||||||
|
|
||||||
## Architecture
|
### 📊 Real-time Dashboard
|
||||||
|
- **Queue Monitor**: Live view of waiting calls
|
||||||
|
- **Agent Status**: Track agent availability
|
||||||
|
- **Call Statistics**: Performance metrics and reporting
|
||||||
|
- **Call Logs**: Detailed history with filtering options
|
||||||
|
- **Active Call Display**: Real-time call count monitoring
|
||||||
|
|
||||||
The plugin uses:
|
### 🎤 Advanced Features
|
||||||
- **Official Twilio PHP SDK v8.7.0** for all API operations
|
- **Voicemail Transcription**: Automatic speech-to-text
|
||||||
- **Native TwiML classes** for response generation
|
- **Callback System**: Offer callbacks instead of long holds
|
||||||
- **WordPress hooks and filters** for integration
|
- **Outbound Calling**: Click-to-call with proper caller ID
|
||||||
- **Custom database tables** for call management
|
- **Multiple Phone Numbers**: Support for multiple business lines
|
||||||
- **REST API endpoints** for webhooks
|
- **Agent Phone Management**: Store and validate agent phone numbers
|
||||||
|
- **Duplicate Prevention**: Ensures unique phone numbers per agent
|
||||||
|
|
||||||
|
## Recent Updates
|
||||||
|
|
||||||
|
### Browser Phone Upgrade (v2.0)
|
||||||
|
- **Migrated to Twilio Voice SDK v2**: Replaced deprecated Client SDK v1.14
|
||||||
|
- **Improved Stability**: Better error handling and automatic recovery
|
||||||
|
- **Token Management**: Auto-refresh tokens before expiration
|
||||||
|
- **Enhanced Performance**: Modern WebRTC implementation
|
||||||
|
|
||||||
|
### Queue System Improvements
|
||||||
|
- **Notification Numbers**: Queues now use notification_number field for SMS alerts
|
||||||
|
- **No Direct Assignment**: Queues are workflow destinations, not directly assigned to numbers
|
||||||
|
- **Better Integration**: Improved queue handling in IVR and workflow steps
|
||||||
|
|
||||||
|
### Voice Configuration Enhancements
|
||||||
|
- **Voice Persistence**: ElevenLabs voices save both ID and name
|
||||||
|
- **No Unnecessary API Calls**: Saved voices display immediately without loading
|
||||||
|
- **Improved UX**: Load voices only when changing selection
|
||||||
|
|
||||||
|
### IVR Fixes
|
||||||
|
- **Form Field Handling**: Fixed IVR option saving and loading
|
||||||
|
- **Queue Selection**: Proper queue routing based on digit selection
|
||||||
|
- **Voice Selection**: Fixed voice dropdown persistence issues
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
### Call Flow
|
||||||
|
1. **Incoming Call** → Twilio webhook triggers
|
||||||
|
2. **Workflow Processing** → System loads assigned workflow
|
||||||
|
3. **Step Execution** → Each workflow step processes sequentially:
|
||||||
|
- Greeting plays welcome message
|
||||||
|
- IVR collects user input
|
||||||
|
- Schedule check determines routing
|
||||||
|
- Queue or forward based on configuration
|
||||||
|
4. **Agent Connection** → Call routed to available agent
|
||||||
|
5. **Logging** → All interactions logged for reporting
|
||||||
|
|
||||||
|
### Queue System
|
||||||
|
- **Queue Assignment**: Calls routed to queues through workflows
|
||||||
|
- **Notification Numbers**: Optional SMS alerts to designated numbers (not agents)
|
||||||
|
- **Agent Groups**: Queues linked to agent groups for distribution
|
||||||
|
- **Wait Experience**: Configurable hold music and position announcements
|
||||||
|
- **Timeout Handling**: Automatic callback offers after timeout
|
||||||
|
- **Browser Phone Integration**: Agents can accept queue calls via browser
|
||||||
|
|
||||||
|
### Agent Management
|
||||||
|
- **Phone Number Storage**: Agent numbers stored in WordPress user profiles
|
||||||
|
- **Status Tracking**: Available/Busy/Offline states
|
||||||
|
- **SMS Commands**: Text "1" to accept calls
|
||||||
|
- **Priority Levels**: Agents have priority within groups
|
||||||
|
- **Browser Phone Mode**: Option to receive calls in browser or cell phone
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
1. Install the SDK using the provided script
|
### Initial Setup
|
||||||
2. Configure Twilio credentials in WordPress admin
|
1. **Install Plugin** and activate in WordPress
|
||||||
3. Set up phone numbers and webhook URLs
|
2. **Install Twilio SDK** using provided script
|
||||||
4. Create agent groups and workflows
|
3. **Configure Credentials**:
|
||||||
5. Test with sample calls
|
- Navigate to **Twilio** → **Settings**
|
||||||
|
- Enter Twilio Account SID and Auth Token
|
||||||
|
- Set default SMS number for notifications
|
||||||
|
- Configure TwiML App SID for browser phone (optional)
|
||||||
|
|
||||||
|
### Phone Number Setup
|
||||||
|
1. **In Twilio Console**:
|
||||||
|
- Purchase or select phone number
|
||||||
|
- Configure Voice webhook: `https://yoursite.com/wp-json/twilio-webhook/v1/voice`
|
||||||
|
- Configure SMS webhook: `https://yoursite.com/wp-json/twilio-webhook/v1/sms`
|
||||||
|
- Set method to HTTP POST
|
||||||
|
|
||||||
|
2. **In WordPress Admin**:
|
||||||
|
- Go to **Twilio** → **Phone Numbers**
|
||||||
|
- Verify numbers are synchronized
|
||||||
|
- Assign workflows to numbers
|
||||||
|
|
||||||
|
### Creating Workflows
|
||||||
|
1. **Navigate to** Twilio → Workflows
|
||||||
|
2. **Create New Workflow**:
|
||||||
|
- Name your workflow
|
||||||
|
- Select phone number to assign
|
||||||
|
- Add steps using the builder
|
||||||
|
|
||||||
|
3. **Configure Steps**:
|
||||||
|
- **Greeting**: Set welcome message and voice
|
||||||
|
- **IVR Menu**: Define options and routing
|
||||||
|
- **Queue**: Select target queue
|
||||||
|
- **Schedule**: Choose business hours schedule
|
||||||
|
|
||||||
|
### Setting Up Queues
|
||||||
|
1. **Create Queue** (Twilio → Queues):
|
||||||
|
- Queue Name: Descriptive name
|
||||||
|
- Notification Number: SMS alerts for queue activity (optional)
|
||||||
|
- Agent Group: Select assigned group
|
||||||
|
- Wait Music: URL for hold music
|
||||||
|
- Timeout: Maximum wait time
|
||||||
|
|
||||||
|
2. **Use in Workflows**:
|
||||||
|
- Add Queue step to workflow
|
||||||
|
- Select queue from dropdown
|
||||||
|
- Configure announcement message
|
||||||
|
|
||||||
|
### Browser Phone Setup
|
||||||
|
1. **Create TwiML App** in Twilio Console:
|
||||||
|
- Voice Request URL: Your server's TwiML endpoint
|
||||||
|
- Copy the Application SID
|
||||||
|
|
||||||
|
2. **Configure in WordPress**:
|
||||||
|
- Go to **Twilio** → **Settings**
|
||||||
|
- Enter TwiML App SID
|
||||||
|
- Save settings
|
||||||
|
|
||||||
|
3. **Access Browser Phone**:
|
||||||
|
- Navigate to **Twilio** → **Browser Phone**
|
||||||
|
- Select caller ID from available numbers
|
||||||
|
- Start making/receiving calls
|
||||||
|
|
||||||
|
### Agent Configuration
|
||||||
|
1. **Create Agent Groups** (Twilio → Agent Groups)
|
||||||
|
2. **Add Agents** to groups with priorities
|
||||||
|
3. **Set Phone Numbers** in user profiles
|
||||||
|
4. **Configure Call Mode**: Browser or cell phone
|
||||||
|
5. **Train Agents** on SMS commands and browser phone
|
||||||
|
|
||||||
|
## Voice Configuration
|
||||||
|
|
||||||
|
### ElevenLabs Integration
|
||||||
|
1. **Get API Key** from ElevenLabs dashboard
|
||||||
|
2. **Configure in Settings**: Add API key
|
||||||
|
3. **Select Voices** in workflow steps:
|
||||||
|
- Click "Load Voices" to fetch available options
|
||||||
|
- Selected voices are saved with both ID and name
|
||||||
|
- Voice names persist across edits without API calls
|
||||||
|
|
||||||
|
### Audio Options per Step
|
||||||
|
- **Say**: Default Twilio text-to-speech
|
||||||
|
- **TTS**: ElevenLabs premium voices with persistence
|
||||||
|
- **Audio**: Custom MP3 file URLs
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### "Twilio SDK classes not available" Error
|
### Common Issues
|
||||||
|
|
||||||
1. **Run the installation script**:
|
#### Browser Phone "Client version not supported"
|
||||||
|
- **Fixed in latest version**: Upgraded to Voice SDK v2
|
||||||
|
- Clear browser cache and reload page
|
||||||
|
- Check TwiML App SID is configured
|
||||||
|
|
||||||
|
#### "Twilio SDK classes not available"
|
||||||
```bash
|
```bash
|
||||||
chmod +x install-twilio-sdk.sh
|
# Reinstall SDK
|
||||||
./install-twilio-sdk.sh
|
./install-twilio-sdk.sh
|
||||||
```
|
# Test installation
|
||||||
|
|
||||||
2. **Test SDK installation**:
|
|
||||||
```bash
|
|
||||||
php test-sdk.php
|
php test-sdk.php
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Check file permissions**:
|
#### Calls Not Routing to Queues
|
||||||
```bash
|
- Verify queue exists and is active
|
||||||
ls -la vendor/
|
- Check agent group has members
|
||||||
ls -la vendor/twilio/sdk/
|
- Ensure agents have valid phone numbers
|
||||||
```
|
- Review workflow step configuration
|
||||||
|
- Check notification_number field (not phone_number)
|
||||||
4. **Verify directory structure**:
|
|
||||||
```
|
|
||||||
vendor/
|
|
||||||
├── autoload.php
|
|
||||||
└── twilio/
|
|
||||||
└── sdk/
|
|
||||||
├── Rest/Client.php
|
|
||||||
├── TwiML/VoiceResponse.php
|
|
||||||
└── ... (other SDK files)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Plugin Shows 500 Error
|
|
||||||
|
|
||||||
|
#### SMS Not Sending from Admin
|
||||||
|
- Test with direct PHP script: `php test-twilio-direct.php send`
|
||||||
|
- Verify Twilio credentials in settings
|
||||||
- Check WordPress error logs
|
- Check WordPress error logs
|
||||||
- Enable WP_DEBUG in wp-config.php
|
- Ensure number is SMS-capable in Twilio
|
||||||
- Look for TWP Plugin error messages in logs
|
|
||||||
|
|
||||||
### SDK Installation Fails
|
#### Voice Selections Not Saving
|
||||||
|
- Voices now save both ID and name automatically
|
||||||
|
- No API call needed to display saved voices
|
||||||
|
- Click "Load Voices" only to change selection
|
||||||
|
- Check browser console for JavaScript errors
|
||||||
|
|
||||||
- Ensure `curl` and `tar` are installed
|
#### IVR Options Not Working
|
||||||
- Check internet connection
|
- Ensure only active form fields are enabled
|
||||||
- Try manual installation (see Installation Methods)
|
- Check queue selections are properly saved
|
||||||
|
- Verify digit mappings in workflow data
|
||||||
|
|
||||||
|
### Debug Mode
|
||||||
|
Enable WordPress debugging in `wp-config.php`:
|
||||||
|
```php
|
||||||
|
define('WP_DEBUG', true);
|
||||||
|
define('WP_DEBUG_LOG', true);
|
||||||
|
define('WP_DEBUG_DISPLAY', false);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing Tools
|
||||||
|
- **Direct SMS Test**: `php test-twilio-direct.php send`
|
||||||
|
- **SDK Test**: `php test-sdk.php`
|
||||||
|
- **Webhook Simulator**: Use Twilio Console debugger
|
||||||
|
- **Call Logs**: Review in WordPress admin
|
||||||
|
- **Browser Phone**: Test WebRTC connectivity
|
||||||
|
|
||||||
|
## Database Structure
|
||||||
|
|
||||||
|
The plugin creates these tables:
|
||||||
|
- `twp_phone_schedules` - Business hours definitions
|
||||||
|
- `twp_call_queues` - Queue configurations (uses notification_number)
|
||||||
|
- `twp_queued_calls` - Active calls in queues
|
||||||
|
- `twp_workflows` - Call flow definitions with voice persistence
|
||||||
|
- `twp_call_log` - Complete call history
|
||||||
|
- `twp_sms_log` - SMS message tracking
|
||||||
|
- `twp_voicemails` - Recordings and transcriptions
|
||||||
|
- `twp_agent_groups` - Agent group definitions
|
||||||
|
- `twp_group_members` - User-to-group relationships
|
||||||
|
- `twp_agent_status` - Real-time agent availability
|
||||||
|
- `twp_callbacks` - Callback request queue
|
||||||
|
|
||||||
|
## Webhook Endpoints
|
||||||
|
|
||||||
|
All webhooks are REST API endpoints under `/wp-json/twilio-webhook/v1/`:
|
||||||
|
- `/voice` - Main incoming call handler
|
||||||
|
- `/sms` - SMS message handler
|
||||||
|
- `/ivr-response` - IVR digit collection (fixed in latest)
|
||||||
|
- `/queue-wait` - Queue hold experience
|
||||||
|
- `/agent-connect` - Agent connection handler
|
||||||
|
- `/callback-request` - Callback system
|
||||||
|
- `/outbound-agent-with-from` - Outbound calling
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- **Twilio PHP SDK v8.7.0** - Server-side API operations
|
||||||
|
- **Twilio Voice SDK v2** - Browser phone WebRTC
|
||||||
|
- **WordPress REST API** - Webhook handling
|
||||||
|
- **jQuery** - Admin interface interactions
|
||||||
|
|
||||||
|
### Browser Compatibility
|
||||||
|
- **Chrome/Edge**: Full support
|
||||||
|
- **Firefox**: Full support
|
||||||
|
- **Safari**: Requires HTTPS for WebRTC
|
||||||
|
- **Mobile Browsers**: Limited WebRTC support
|
||||||
|
|
||||||
|
### Security Considerations
|
||||||
|
- All webhooks use WordPress nonce verification
|
||||||
|
- Phone numbers validated and sanitized
|
||||||
|
- SQL queries use prepared statements
|
||||||
|
- Sensitive data encrypted in database
|
||||||
|
- HTTPS required for production use
|
||||||
|
|
||||||
|
## Version History
|
||||||
|
|
||||||
|
### v2.0.0 (Current)
|
||||||
|
- Upgraded to Twilio Voice SDK v2 for browser phone
|
||||||
|
- Fixed queue notification_number field naming
|
||||||
|
- Enhanced voice selection persistence
|
||||||
|
- Fixed IVR option saving and loading
|
||||||
|
- Improved error handling and logging
|
||||||
|
- Added automatic token refresh
|
||||||
|
|
||||||
|
### v1.3.x
|
||||||
|
- Initial release with full call center features
|
||||||
|
- Browser phone with Client SDK v1.14
|
||||||
|
- Basic workflow builder
|
||||||
|
- Queue management system
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
- Check `CLAUDE.md` for detailed technical documentation
|
- Check `CLAUDE.md` for detailed technical documentation
|
||||||
- Review `TWILIO_SDK_MIGRATION.md` for migration details
|
- Review error logs in `/wp-content/debug.log`
|
||||||
- Enable WordPress debug logging for troubleshooting
|
- Monitor Twilio Console for webhook errors
|
||||||
- Use Twilio Console debugger for webhook testing
|
- Test components individually using provided scripts
|
||||||
|
- Report issues with specific error messages and logs
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
@@ -19,6 +19,20 @@ class TWP_Admin {
|
|||||||
* Register admin menu
|
* Register admin menu
|
||||||
*/
|
*/
|
||||||
public function add_plugin_admin_menu() {
|
public function add_plugin_admin_menu() {
|
||||||
|
// Determine if user has any agent access
|
||||||
|
$has_agent_access = current_user_can('twp_access_voicemails') ||
|
||||||
|
current_user_can('twp_access_call_log') ||
|
||||||
|
current_user_can('twp_access_agent_queue') ||
|
||||||
|
current_user_can('twp_access_sms_inbox') ||
|
||||||
|
current_user_can('twp_access_browser_phone');
|
||||||
|
|
||||||
|
// Only show menu if user is admin or has agent access
|
||||||
|
if (!current_user_can('manage_options') && !$has_agent_access) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main menu - show dashboard for admins, redirect to first available page for agents
|
||||||
|
if (current_user_can('manage_options')) {
|
||||||
add_menu_page(
|
add_menu_page(
|
||||||
'Twilio WP Plugin',
|
'Twilio WP Plugin',
|
||||||
'Twilio Phone',
|
'Twilio Phone',
|
||||||
@@ -37,7 +51,28 @@ class TWP_Admin {
|
|||||||
'twilio-wp-plugin',
|
'twilio-wp-plugin',
|
||||||
array($this, 'display_plugin_dashboard')
|
array($this, 'display_plugin_dashboard')
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
// For agents, determine first available page
|
||||||
|
$first_page = 'twilio-wp-browser-phone'; // Default to browser phone
|
||||||
|
if (current_user_can('twp_access_voicemails')) $first_page = 'twilio-wp-voicemails';
|
||||||
|
elseif (current_user_can('twp_access_call_log')) $first_page = 'twilio-wp-call-logs';
|
||||||
|
elseif (current_user_can('twp_access_agent_queue')) $first_page = 'twilio-wp-agent-queue';
|
||||||
|
elseif (current_user_can('twp_access_sms_inbox')) $first_page = 'twilio-wp-sms-inbox';
|
||||||
|
elseif (current_user_can('twp_access_browser_phone')) $first_page = 'twilio-wp-browser-phone';
|
||||||
|
|
||||||
|
add_menu_page(
|
||||||
|
'Twilio Phone',
|
||||||
|
'Twilio Phone',
|
||||||
|
'read',
|
||||||
|
$first_page,
|
||||||
|
null,
|
||||||
|
'dashicons-phone',
|
||||||
|
30
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Admin-only pages
|
||||||
|
if (current_user_can('manage_options')) {
|
||||||
add_submenu_page(
|
add_submenu_page(
|
||||||
'twilio-wp-plugin',
|
'twilio-wp-plugin',
|
||||||
'Settings',
|
'Settings',
|
||||||
@@ -83,24 +118,6 @@ class TWP_Admin {
|
|||||||
array($this, 'display_numbers_page')
|
array($this, 'display_numbers_page')
|
||||||
);
|
);
|
||||||
|
|
||||||
add_submenu_page(
|
|
||||||
'twilio-wp-plugin',
|
|
||||||
'Voicemails',
|
|
||||||
'Voicemails',
|
|
||||||
'manage_options',
|
|
||||||
'twilio-wp-voicemails',
|
|
||||||
array($this, 'display_voicemails_page')
|
|
||||||
);
|
|
||||||
|
|
||||||
add_submenu_page(
|
|
||||||
'twilio-wp-plugin',
|
|
||||||
'Call Logs',
|
|
||||||
'Call Logs',
|
|
||||||
'manage_options',
|
|
||||||
'twilio-wp-call-logs',
|
|
||||||
array($this, 'display_call_logs_page')
|
|
||||||
);
|
|
||||||
|
|
||||||
add_submenu_page(
|
add_submenu_page(
|
||||||
'twilio-wp-plugin',
|
'twilio-wp-plugin',
|
||||||
'Agent Groups',
|
'Agent Groups',
|
||||||
@@ -109,43 +126,69 @@ class TWP_Admin {
|
|||||||
'twilio-wp-groups',
|
'twilio-wp-groups',
|
||||||
array($this, 'display_groups_page')
|
array($this, 'display_groups_page')
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Agent-accessible pages
|
||||||
|
$menu_parent = current_user_can('manage_options') ? 'twilio-wp-plugin' : null;
|
||||||
|
|
||||||
|
if (current_user_can('manage_options') || current_user_can('twp_access_voicemails')) {
|
||||||
add_submenu_page(
|
add_submenu_page(
|
||||||
'twilio-wp-plugin',
|
$menu_parent,
|
||||||
|
'Voicemails',
|
||||||
|
'Voicemails',
|
||||||
|
current_user_can('manage_options') ? 'manage_options' : 'twp_access_voicemails',
|
||||||
|
'twilio-wp-voicemails',
|
||||||
|
array($this, 'display_voicemails_page')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_user_can('manage_options') || current_user_can('twp_access_call_log')) {
|
||||||
|
add_submenu_page(
|
||||||
|
$menu_parent,
|
||||||
|
'Call Logs',
|
||||||
|
'Call Logs',
|
||||||
|
current_user_can('manage_options') ? 'manage_options' : 'twp_access_call_log',
|
||||||
|
'twilio-wp-call-logs',
|
||||||
|
array($this, 'display_call_logs_page')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_user_can('manage_options') || current_user_can('twp_access_agent_queue')) {
|
||||||
|
add_submenu_page(
|
||||||
|
$menu_parent,
|
||||||
'Agent Queue',
|
'Agent Queue',
|
||||||
'Agent Queue',
|
'Agent Queue',
|
||||||
'manage_options',
|
current_user_can('manage_options') ? 'manage_options' : 'twp_access_agent_queue',
|
||||||
'twilio-wp-agent-queue',
|
'twilio-wp-agent-queue',
|
||||||
array($this, 'display_agent_queue_page')
|
array($this, 'display_agent_queue_page')
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
add_submenu_page(
|
// Outbound Calls page removed - functionality merged into Browser Phone
|
||||||
'twilio-wp-plugin',
|
// Keeping capability 'twp_access_outbound_calls' for backwards compatibility
|
||||||
'Outbound Calls',
|
|
||||||
'Outbound Calls',
|
|
||||||
'manage_options',
|
|
||||||
'twilio-wp-outbound',
|
|
||||||
array($this, 'display_outbound_calls_page')
|
|
||||||
);
|
|
||||||
|
|
||||||
|
if (current_user_can('manage_options') || current_user_can('twp_access_sms_inbox')) {
|
||||||
add_submenu_page(
|
add_submenu_page(
|
||||||
'twilio-wp-plugin',
|
$menu_parent,
|
||||||
'SMS Inbox',
|
'SMS Inbox',
|
||||||
'SMS Inbox',
|
'SMS Inbox',
|
||||||
'manage_options',
|
current_user_can('manage_options') ? 'manage_options' : 'twp_access_sms_inbox',
|
||||||
'twilio-wp-sms-inbox',
|
'twilio-wp-sms-inbox',
|
||||||
array($this, 'display_sms_inbox_page')
|
array($this, 'display_sms_inbox_page')
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_user_can('manage_options') || current_user_can('twp_access_browser_phone')) {
|
||||||
add_submenu_page(
|
add_submenu_page(
|
||||||
'twilio-wp-plugin',
|
$menu_parent,
|
||||||
'Browser Phone',
|
'Browser Phone',
|
||||||
'Browser Phone',
|
'Browser Phone',
|
||||||
'manage_options',
|
current_user_can('manage_options') ? 'manage_options' : 'twp_access_browser_phone',
|
||||||
'twilio-wp-browser-phone',
|
'twilio-wp-browser-phone',
|
||||||
array($this, 'display_browser_phone_page')
|
array($this, 'display_browser_phone_page')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display dashboard
|
* Display dashboard
|
||||||
@@ -5448,19 +5491,34 @@ class TWP_Admin {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script src="https://sdk.twilio.com/js/client/v1.14/twilio.min.js"></script>
|
<!-- Twilio Voice SDK v2 from unpkg CDN -->
|
||||||
|
<script src="https://unpkg.com/@twilio/voice-sdk@2.11.0/dist/twilio.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
jQuery(document).ready(function($) {
|
jQuery(document).ready(function($) {
|
||||||
var device = null;
|
var device = null;
|
||||||
var currentConnection = null;
|
var currentCall = null;
|
||||||
var callTimer = null;
|
var callTimer = null;
|
||||||
var callStartTime = null;
|
var callStartTime = null;
|
||||||
|
|
||||||
|
// Wait for SDK to load
|
||||||
|
function waitForTwilioSDK(callback) {
|
||||||
|
if (typeof Twilio !== 'undefined' && Twilio.Device) {
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
console.log('Waiting for Twilio Voice SDK to load...');
|
||||||
|
setTimeout(function() {
|
||||||
|
waitForTwilioSDK(callback);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize the browser phone
|
// Initialize the browser phone
|
||||||
function initializeBrowserPhone() {
|
function initializeBrowserPhone() {
|
||||||
$('#phone-status').text('Initializing...');
|
$('#phone-status').text('Initializing...');
|
||||||
|
|
||||||
// Get capability token
|
// Wait for SDK before proceeding
|
||||||
|
waitForTwilioSDK(function() {
|
||||||
|
// Get capability token (access token for v2)
|
||||||
$.post(ajaxurl, {
|
$.post(ajaxurl, {
|
||||||
action: 'twp_generate_capability_token',
|
action: 'twp_generate_capability_token',
|
||||||
nonce: '<?php echo wp_create_nonce('twp_ajax_nonce'); ?>'
|
nonce: '<?php echo wp_create_nonce('twp_ajax_nonce'); ?>'
|
||||||
@@ -5474,77 +5532,142 @@ class TWP_Admin {
|
|||||||
}).fail(function() {
|
}).fail(function() {
|
||||||
showError('Failed to connect to server');
|
showError('Failed to connect to server');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupTwilioDevice(token) {
|
async function setupTwilioDevice(token) {
|
||||||
try {
|
try {
|
||||||
// Setup Twilio Device
|
// Check if Twilio SDK is available
|
||||||
Twilio.Device.setup(token, {
|
if (typeof Twilio === 'undefined' || !Twilio.Device) {
|
||||||
debug: true,
|
throw new Error('Twilio Voice SDK not loaded');
|
||||||
codecPreferences: ['opus', 'pcmu']
|
}
|
||||||
|
|
||||||
|
// Clean up existing device if any
|
||||||
|
if (device) {
|
||||||
|
await device.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup Twilio Voice SDK v2 Device
|
||||||
|
// Note: Voice SDK v2 uses Twilio.Device directly, not Twilio.Voice.Device
|
||||||
|
device = new Twilio.Device(token, {
|
||||||
|
logLevel: 1, // 0 = TRACE, 1 = DEBUG
|
||||||
|
codecPreferences: ['opus', 'pcmu'],
|
||||||
|
edge: 'sydney' // Or closest edge location
|
||||||
});
|
});
|
||||||
|
|
||||||
// Use modern EventEmitter interface instead of deprecated callbacks
|
// Set up event handlers BEFORE registering
|
||||||
Twilio.Device.on('ready', function(device) {
|
// Device registered and ready
|
||||||
|
device.on('registered', function() {
|
||||||
|
console.log('Device registered successfully');
|
||||||
$('#phone-status').text('Ready').css('color', '#4CAF50');
|
$('#phone-status').text('Ready').css('color', '#4CAF50');
|
||||||
$('#call-btn').prop('disabled', false);
|
$('#call-btn').prop('disabled', false);
|
||||||
});
|
});
|
||||||
|
|
||||||
Twilio.Device.on('error', function(error) {
|
// Handle errors
|
||||||
|
device.on('error', function(error) {
|
||||||
console.error('Twilio Device Error:', error);
|
console.error('Twilio Device Error:', error);
|
||||||
|
|
||||||
var errorMsg = error.message;
|
var errorMsg = error.message || error.toString();
|
||||||
|
|
||||||
// Provide specific help for common errors
|
// Provide specific help for common errors
|
||||||
if (error.message.includes('valid callerId must be provided')) {
|
if (errorMsg.includes('valid callerId must be provided')) {
|
||||||
errorMsg = 'Caller ID error: Make sure you select a verified Twilio phone number as Caller ID. The number must be purchased through your Twilio account.';
|
errorMsg = 'Caller ID error: Make sure you select a verified Twilio phone number as Caller ID. The number must be purchased through your Twilio account.';
|
||||||
} else if (error.message.includes('TwiML App')) {
|
} else if (errorMsg.includes('TwiML App')) {
|
||||||
errorMsg = 'TwiML App error: Check that your TwiML App SID is correctly configured in Settings.';
|
errorMsg = 'TwiML App error: Check that your TwiML App SID is correctly configured in Settings.';
|
||||||
} else if (error.message.includes('token')) {
|
} else if (errorMsg.includes('token') || errorMsg.includes('Token')) {
|
||||||
errorMsg = 'Token error: ' + error.message + ' - The page will automatically try to refresh the token.';
|
errorMsg = 'Token error: ' + errorMsg + ' - The page will automatically try to refresh the token.';
|
||||||
|
// Try to reinitialize after token error
|
||||||
|
setTimeout(initializeBrowserPhone, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
showError(errorMsg);
|
showError(errorMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
Twilio.Device.on('connect', function(conn) {
|
// Handle incoming calls
|
||||||
currentConnection = conn;
|
device.on('incoming', function(call) {
|
||||||
|
currentCall = call;
|
||||||
|
$('#phone-status').text('Incoming Call').css('color', '#FF9800');
|
||||||
|
$('#phone-number-display').text(call.parameters.From || 'Unknown Number');
|
||||||
|
$('#call-btn').hide();
|
||||||
|
$('#answer-btn').show();
|
||||||
|
|
||||||
|
// Setup call event handlers
|
||||||
|
setupCallHandlers(call);
|
||||||
|
|
||||||
|
if ($('#auto-answer').is(':checked')) {
|
||||||
|
call.accept();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Token about to expire
|
||||||
|
device.on('tokenWillExpire', function() {
|
||||||
|
console.log('Token will expire soon, refreshing...');
|
||||||
|
refreshToken();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register device AFTER setting up event handlers
|
||||||
|
await device.register();
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error setting up Twilio Device:', error);
|
||||||
|
showError('Failed to setup device: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupCallHandlers(call) {
|
||||||
|
// Call accepted/connected
|
||||||
|
call.on('accept', function() {
|
||||||
$('#phone-status').text('Connected').css('color', '#2196F3');
|
$('#phone-status').text('Connected').css('color', '#2196F3');
|
||||||
$('#call-btn').hide();
|
$('#call-btn').hide();
|
||||||
|
$('#answer-btn').hide();
|
||||||
$('#hangup-btn').show();
|
$('#hangup-btn').show();
|
||||||
$('#phone-controls-extra').show();
|
$('#phone-controls-extra').show();
|
||||||
|
|
||||||
startCallTimer();
|
startCallTimer();
|
||||||
});
|
});
|
||||||
|
|
||||||
Twilio.Device.on('disconnect', function(conn) {
|
// Call disconnected
|
||||||
currentConnection = null;
|
call.on('disconnect', function() {
|
||||||
|
currentCall = null;
|
||||||
$('#phone-status').text('Ready').css('color', '#4CAF50');
|
$('#phone-status').text('Ready').css('color', '#4CAF50');
|
||||||
$('#hangup-btn').hide();
|
$('#hangup-btn').hide();
|
||||||
$('#answer-btn').hide();
|
$('#answer-btn').hide();
|
||||||
$('#call-btn').show();
|
$('#call-btn').show();
|
||||||
$('#phone-controls-extra').hide();
|
$('#phone-controls-extra').hide();
|
||||||
$('#call-timer').hide();
|
$('#call-timer').hide();
|
||||||
|
|
||||||
stopCallTimer();
|
stopCallTimer();
|
||||||
});
|
});
|
||||||
|
|
||||||
Twilio.Device.on('incoming', function(conn) {
|
// Call rejected
|
||||||
currentConnection = conn;
|
call.on('reject', function() {
|
||||||
$('#phone-status').text('Incoming Call').css('color', '#FF9800');
|
currentCall = null;
|
||||||
$('#phone-number-display').text(conn.parameters.From || 'Unknown Number');
|
$('#phone-status').text('Ready').css('color', '#4CAF50');
|
||||||
$('#call-btn').hide();
|
$('#answer-btn').hide();
|
||||||
$('#answer-btn').show();
|
$('#call-btn').show();
|
||||||
|
|
||||||
if ($('#auto-answer').is(':checked')) {
|
|
||||||
conn.accept();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
// Call cancelled (by caller before answer)
|
||||||
console.error('Error setting up Twilio Device:', error);
|
call.on('cancel', function() {
|
||||||
showError('Failed to setup device: ' + error.message);
|
currentCall = null;
|
||||||
|
$('#phone-status').text('Missed Call').css('color', '#FF9800');
|
||||||
|
$('#answer-btn').hide();
|
||||||
|
$('#call-btn').show();
|
||||||
|
setTimeout(function() {
|
||||||
|
$('#phone-status').text('Ready').css('color', '#4CAF50');
|
||||||
|
}, 3000);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function refreshToken() {
|
||||||
|
$.post(ajaxurl, {
|
||||||
|
action: 'twp_generate_capability_token',
|
||||||
|
nonce: '<?php echo wp_create_nonce('twp_ajax_nonce'); ?>'
|
||||||
|
}, function(response) {
|
||||||
|
if (response.success && device) {
|
||||||
|
device.updateToken(response.data.token);
|
||||||
|
}
|
||||||
|
}).fail(function() {
|
||||||
|
console.error('Failed to refresh token');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showError(message) {
|
function showError(message) {
|
||||||
@@ -5597,7 +5720,7 @@ class TWP_Admin {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Call button
|
// Call button
|
||||||
$('#call-btn').on('click', function() {
|
$('#call-btn').on('click', async function() {
|
||||||
var phoneNumber = $('#phone-number-input').val().trim();
|
var phoneNumber = $('#phone-number-input').val().trim();
|
||||||
var callerId = $('#caller-id-select').val();
|
var callerId = $('#caller-id-select').val();
|
||||||
|
|
||||||
@@ -5611,6 +5734,11 @@ class TWP_Admin {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!device) {
|
||||||
|
alert('Phone is not initialized. Please refresh the page.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Format phone number
|
// Format phone number
|
||||||
phoneNumber = phoneNumber.replace(/\D/g, '');
|
phoneNumber = phoneNumber.replace(/\D/g, '');
|
||||||
if (phoneNumber.length === 10) {
|
if (phoneNumber.length === 10) {
|
||||||
@@ -5631,7 +5759,8 @@ class TWP_Admin {
|
|||||||
};
|
};
|
||||||
|
|
||||||
console.log('Making call with params:', params);
|
console.log('Making call with params:', params);
|
||||||
currentConnection = Twilio.Device.connect(params);
|
currentCall = await device.connect({params: params});
|
||||||
|
setupCallHandlers(currentCall);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Call error:', error);
|
console.error('Call error:', error);
|
||||||
showError('Failed to make call: ' + error.message);
|
showError('Failed to make call: ' + error.message);
|
||||||
@@ -5641,30 +5770,40 @@ class TWP_Admin {
|
|||||||
|
|
||||||
// Hangup button
|
// Hangup button
|
||||||
$('#hangup-btn').on('click', function() {
|
$('#hangup-btn').on('click', function() {
|
||||||
if (currentConnection) {
|
if (currentCall) {
|
||||||
currentConnection.disconnect();
|
currentCall.disconnect();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Answer button
|
// Answer button
|
||||||
$('#answer-btn').on('click', function() {
|
$('#answer-btn').on('click', function() {
|
||||||
if (currentConnection) {
|
if (currentCall) {
|
||||||
currentConnection.accept();
|
currentCall.accept();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mute button
|
// Mute button
|
||||||
$('#mute-btn').on('click', function() {
|
$('#mute-btn').on('click', function() {
|
||||||
if (currentConnection) {
|
if (currentCall) {
|
||||||
var muted = currentConnection.isMuted();
|
var muted = currentCall.isMuted();
|
||||||
currentConnection.mute(!muted);
|
currentCall.mute(!muted);
|
||||||
$(this).text(muted ? 'Mute' : 'Unmute');
|
$(this).text(muted ? 'Mute' : 'Unmute');
|
||||||
$(this).find('.dashicons').toggleClass('dashicons-microphone dashicons-microphone');
|
$(this).find('.dashicons').toggleClass('dashicons-microphone dashicons-microphone');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialize on page load
|
// Check if SDK loaded and initialize
|
||||||
|
$(window).on('load', function() {
|
||||||
|
setTimeout(function() {
|
||||||
|
if (typeof Twilio === 'undefined') {
|
||||||
|
showError('Twilio Voice SDK failed to load. Please check your internet connection and try refreshing the page.');
|
||||||
|
console.error('Twilio SDK not found. Script may be blocked or failed to load.');
|
||||||
|
} else {
|
||||||
|
console.log('Twilio SDK loaded successfully');
|
||||||
initializeBrowserPhone();
|
initializeBrowserPhone();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
// Refresh token every 50 minutes (tokens expire in 1 hour)
|
// Refresh token every 50 minutes (tokens expire in 1 hour)
|
||||||
setInterval(initializeBrowserPhone, 50 * 60 * 1000);
|
setInterval(initializeBrowserPhone, 50 * 60 * 1000);
|
||||||
|
@@ -14,6 +14,9 @@ class TWP_Activator {
|
|||||||
// Set default options
|
// Set default options
|
||||||
self::set_default_options();
|
self::set_default_options();
|
||||||
|
|
||||||
|
// Create custom user roles
|
||||||
|
self::create_user_roles();
|
||||||
|
|
||||||
// Set the database version
|
// Set the database version
|
||||||
if (defined('TWP_DB_VERSION')) {
|
if (defined('TWP_DB_VERSION')) {
|
||||||
update_option('twp_db_version', TWP_DB_VERSION);
|
update_option('twp_db_version', TWP_DB_VERSION);
|
||||||
@@ -373,4 +376,29 @@ class TWP_Activator {
|
|||||||
add_option('twp_sms_notification_number', '');
|
add_option('twp_sms_notification_number', '');
|
||||||
add_option('twp_default_sms_number', '');
|
add_option('twp_default_sms_number', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create custom user roles for the plugin
|
||||||
|
*/
|
||||||
|
private static function create_user_roles() {
|
||||||
|
// Remove role first if it exists to ensure clean setup
|
||||||
|
remove_role('phone_agent');
|
||||||
|
|
||||||
|
// Add Phone Agent role with limited capabilities
|
||||||
|
add_role('phone_agent', 'Phone Agent', array(
|
||||||
|
// Basic WordPress capabilities
|
||||||
|
'read' => true,
|
||||||
|
|
||||||
|
// Profile management
|
||||||
|
'edit_profile' => true,
|
||||||
|
|
||||||
|
// Phone agent specific capabilities
|
||||||
|
'twp_access_voicemails' => true,
|
||||||
|
'twp_access_call_log' => true,
|
||||||
|
'twp_access_agent_queue' => true,
|
||||||
|
'twp_access_outbound_calls' => true,
|
||||||
|
'twp_access_sms_inbox' => true,
|
||||||
|
'twp_access_browser_phone' => true,
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user