Implement Discord and Slack notifications for call events
Settings & Configuration: - Added Discord webhook URL, Slack webhook URL settings in admin - Added notification type toggles (incoming calls, queue timeouts, missed calls) - Added queue timeout threshold setting (30-1800 seconds) - Registered all new settings with WordPress options system Notification System: - Created TWP_Notifications class for Discord/Slack webhook handling - Rich message formatting with embeds/attachments for both platforms - Color-coded notifications (blue=incoming, yellow=timeout, red=missed) - Comprehensive error handling and logging Integration Points: - Incoming calls: Notifications sent when calls enter queues - Queue timeouts: Automated monitoring via cron job (every minute) - Missed calls: Notifications for browser phone and general missed calls - Added notified_timeout column to prevent duplicate timeout notifications Features: - Professional Discord embeds with fields and timestamps - Slack attachments with proper formatting and colors - Automatic cron job setup for queue timeout monitoring - Fallback to SMS notifications while Discord/Slack also work - Configurable notification types and timeout thresholds This provides real-time call notifications to Discord channels and Slack channels, helping teams stay informed about incoming calls and queue issues even when SMS notifications aren't working due to validation delays. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
		@@ -481,6 +481,60 @@ class TWP_Admin {
 | 
			
		||||
                            <p class="description">Default Twilio phone number to use as sender for SMS messages when not in a workflow context.</p>
 | 
			
		||||
                        </td>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                    
 | 
			
		||||
                    <!-- Discord/Slack Notifications Section -->
 | 
			
		||||
                    <tr valign="top">
 | 
			
		||||
                        <td colspan="2">
 | 
			
		||||
                            <h3 style="margin-top: 30px; margin-bottom: 15px;">Discord & Slack Notifications</h3>
 | 
			
		||||
                            <p class="description">Configure webhook URLs to receive call notifications in Discord and/or Slack channels.</p>
 | 
			
		||||
                        </td>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                    
 | 
			
		||||
                    <tr valign="top">
 | 
			
		||||
                        <th scope="row">Discord Webhook URL</th>
 | 
			
		||||
                        <td>
 | 
			
		||||
                            <input type="url" name="twp_discord_webhook_url" value="<?php echo esc_attr(get_option('twp_discord_webhook_url')); ?>" class="regular-text" placeholder="https://discord.com/api/webhooks/..." />
 | 
			
		||||
                            <p class="description">Discord webhook URL for call notifications. <a href="https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks" target="_blank">How to create a Discord webhook</a></p>
 | 
			
		||||
                        </td>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                    
 | 
			
		||||
                    <tr valign="top">
 | 
			
		||||
                        <th scope="row">Slack Webhook URL</th>
 | 
			
		||||
                        <td>
 | 
			
		||||
                            <input type="url" name="twp_slack_webhook_url" value="<?php echo esc_attr(get_option('twp_slack_webhook_url')); ?>" class="regular-text" placeholder="https://hooks.slack.com/services/..." />
 | 
			
		||||
                            <p class="description">Slack webhook URL for call notifications. <a href="https://slack.com/help/articles/115005265063-Incoming-webhooks-for-Slack" target="_blank">How to create a Slack webhook</a></p>
 | 
			
		||||
                        </td>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                    
 | 
			
		||||
                    <tr valign="top">
 | 
			
		||||
                        <th scope="row">Notification Settings</th>
 | 
			
		||||
                        <td>
 | 
			
		||||
                            <fieldset>
 | 
			
		||||
                                <label>
 | 
			
		||||
                                    <input type="checkbox" name="twp_notify_on_incoming_calls" value="1" <?php checked(get_option('twp_notify_on_incoming_calls', 1)); ?> />
 | 
			
		||||
                                    Notify on incoming calls
 | 
			
		||||
                                </label><br>
 | 
			
		||||
                                <label>
 | 
			
		||||
                                    <input type="checkbox" name="twp_notify_on_queue_timeout" value="1" <?php checked(get_option('twp_notify_on_queue_timeout', 1)); ?> />
 | 
			
		||||
                                    Notify when calls stay in queue too long
 | 
			
		||||
                                </label><br>
 | 
			
		||||
                                <label>
 | 
			
		||||
                                    <input type="checkbox" name="twp_notify_on_missed_calls" value="1" <?php checked(get_option('twp_notify_on_missed_calls', 1)); ?> />
 | 
			
		||||
                                    Notify on missed calls
 | 
			
		||||
                                </label>
 | 
			
		||||
                            </fieldset>
 | 
			
		||||
                            <p class="description">Choose which events trigger Discord/Slack notifications.</p>
 | 
			
		||||
                        </td>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                    
 | 
			
		||||
                    <tr valign="top">
 | 
			
		||||
                        <th scope="row">Queue Timeout Threshold</th>
 | 
			
		||||
                        <td>
 | 
			
		||||
                            <input type="number" name="twp_queue_timeout_threshold" value="<?php echo esc_attr(get_option('twp_queue_timeout_threshold', 300)); ?>" min="30" max="1800" />
 | 
			
		||||
                            <span>seconds</span>
 | 
			
		||||
                            <p class="description">Send notification if call stays in queue longer than this time (30-1800 seconds).</p>
 | 
			
		||||
                        </td>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                </table>
 | 
			
		||||
                
 | 
			
		||||
                <?php submit_button(); ?>
 | 
			
		||||
@@ -2725,6 +2779,14 @@ class TWP_Admin {
 | 
			
		||||
        register_setting('twilio-wp-settings-group', 'twp_urgent_keywords');
 | 
			
		||||
        register_setting('twilio-wp-settings-group', 'twp_sms_notification_number');
 | 
			
		||||
        register_setting('twilio-wp-settings-group', 'twp_default_sms_number');
 | 
			
		||||
        
 | 
			
		||||
        // Discord/Slack notification settings
 | 
			
		||||
        register_setting('twilio-wp-settings-group', 'twp_discord_webhook_url');
 | 
			
		||||
        register_setting('twilio-wp-settings-group', 'twp_slack_webhook_url');
 | 
			
		||||
        register_setting('twilio-wp-settings-group', 'twp_notify_on_incoming_calls');
 | 
			
		||||
        register_setting('twilio-wp-settings-group', 'twp_notify_on_queue_timeout');
 | 
			
		||||
        register_setting('twilio-wp-settings-group', 'twp_notify_on_missed_calls');
 | 
			
		||||
        register_setting('twilio-wp-settings-group', 'twp_queue_timeout_threshold');
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -366,6 +366,12 @@ class TWP_Activator {
 | 
			
		||||
        if (empty($status_index_exists)) {
 | 
			
		||||
            $wpdb->query("ALTER TABLE $table_queued_calls ADD INDEX status (status)");
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Add notified_timeout column for Discord/Slack notifications
 | 
			
		||||
        $notified_timeout_exists = $wpdb->get_results("SHOW COLUMNS FROM $table_queued_calls LIKE 'notified_timeout'");
 | 
			
		||||
        if (empty($notified_timeout_exists)) {
 | 
			
		||||
            $wpdb->query("ALTER TABLE $table_queued_calls ADD COLUMN notified_timeout datetime AFTER agent_call_sid");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -574,6 +574,15 @@ class TWP_Call_Queue {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Send Discord/Slack notification for incoming call
 | 
			
		||||
        require_once dirname(__FILE__) . '/class-twp-notifications.php';
 | 
			
		||||
        TWP_Notifications::send_call_notification('incoming_call', array(
 | 
			
		||||
            'type' => 'incoming_call',
 | 
			
		||||
            'caller' => $caller_number,
 | 
			
		||||
            'queue' => $queue->queue_name,
 | 
			
		||||
            'queue_id' => $queue_id
 | 
			
		||||
        ));
 | 
			
		||||
        
 | 
			
		||||
        // Get members of the assigned agent group
 | 
			
		||||
        require_once dirname(__FILE__) . '/class-twp-agent-groups.php';
 | 
			
		||||
        $members = TWP_Agent_Groups::get_group_members($queue->agent_group_id);
 | 
			
		||||
 
 | 
			
		||||
@@ -167,6 +167,10 @@ class TWP_Core {
 | 
			
		||||
        
 | 
			
		||||
        // Agent queue management AJAX
 | 
			
		||||
        $this->loader->add_action('wp_ajax_twp_accept_call', $plugin_admin, 'ajax_accept_call');
 | 
			
		||||
        
 | 
			
		||||
        // Discord/Slack notification system
 | 
			
		||||
        $this->loader->add_action('init', $this, 'setup_notification_cron');
 | 
			
		||||
        $this->loader->add_action('twp_check_queue_timeouts', $this, 'check_queue_timeouts');
 | 
			
		||||
        $this->loader->add_action('wp_ajax_twp_accept_next_queue_call', $plugin_admin, 'ajax_accept_next_queue_call');
 | 
			
		||||
        $this->loader->add_action('wp_ajax_twp_get_waiting_calls', $plugin_admin, 'ajax_get_waiting_calls');
 | 
			
		||||
        $this->loader->add_action('wp_ajax_twp_set_agent_status', $plugin_admin, 'ajax_set_agent_status');
 | 
			
		||||
@@ -385,4 +389,21 @@ class TWP_Core {
 | 
			
		||||
            error_log("TWP Cleanup: Updated {$updated_waiting} old waiting calls to 'timeout' status");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Setup notification cron job
 | 
			
		||||
     */
 | 
			
		||||
    public function setup_notification_cron() {
 | 
			
		||||
        if (!wp_next_scheduled('twp_check_queue_timeouts')) {
 | 
			
		||||
            wp_schedule_event(time(), 'twp_every_minute', 'twp_check_queue_timeouts');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Check for queue timeouts and send notifications
 | 
			
		||||
     */
 | 
			
		||||
    public function check_queue_timeouts() {
 | 
			
		||||
        require_once dirname(__FILE__) . '/class-twp-notifications.php';
 | 
			
		||||
        TWP_Notifications::check_queue_timeouts();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										328
									
								
								includes/class-twp-notifications.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										328
									
								
								includes/class-twp-notifications.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,328 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handle Discord and Slack notifications for call events
 | 
			
		||||
 */
 | 
			
		||||
class TWP_Notifications {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Send notification to Discord and/or Slack
 | 
			
		||||
     */
 | 
			
		||||
    public static function send_call_notification($type, $data) {
 | 
			
		||||
        // Check if notifications are enabled for this type
 | 
			
		||||
        if (!self::is_notification_enabled($type)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $message = self::format_message($type, $data);
 | 
			
		||||
        
 | 
			
		||||
        // Send to Discord if configured
 | 
			
		||||
        $discord_url = get_option('twp_discord_webhook_url');
 | 
			
		||||
        if (!empty($discord_url)) {
 | 
			
		||||
            self::send_discord_notification($discord_url, $message, $data);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Send to Slack if configured
 | 
			
		||||
        $slack_url = get_option('twp_slack_webhook_url');
 | 
			
		||||
        if (!empty($slack_url)) {
 | 
			
		||||
            self::send_slack_notification($slack_url, $message, $data);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if notifications are enabled for a specific type
 | 
			
		||||
     */
 | 
			
		||||
    private static function is_notification_enabled($type) {
 | 
			
		||||
        switch ($type) {
 | 
			
		||||
            case 'incoming_call':
 | 
			
		||||
                return get_option('twp_notify_on_incoming_calls', 1);
 | 
			
		||||
            case 'queue_timeout':
 | 
			
		||||
                return get_option('twp_notify_on_queue_timeout', 1);
 | 
			
		||||
            case 'missed_call':
 | 
			
		||||
                return get_option('twp_notify_on_missed_calls', 1);
 | 
			
		||||
            default:
 | 
			
		||||
                return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Format message based on notification type
 | 
			
		||||
     */
 | 
			
		||||
    private static function format_message($type, $data) {
 | 
			
		||||
        $caller = isset($data['caller']) ? $data['caller'] : 'Unknown';
 | 
			
		||||
        $queue = isset($data['queue']) ? $data['queue'] : 'Unknown';
 | 
			
		||||
        $time = current_time('Y-m-d H:i:s');
 | 
			
		||||
        
 | 
			
		||||
        switch ($type) {
 | 
			
		||||
            case 'incoming_call':
 | 
			
		||||
                return "📞 **Incoming Call**\n" .
 | 
			
		||||
                       "**From:** {$caller}\n" .
 | 
			
		||||
                       "**Queue:** {$queue}\n" .
 | 
			
		||||
                       "**Time:** {$time}";
 | 
			
		||||
                       
 | 
			
		||||
            case 'queue_timeout':
 | 
			
		||||
                $duration = isset($data['duration']) ? $data['duration'] : 'Unknown';
 | 
			
		||||
                return "⏰ **Queue Timeout Alert**\n" .
 | 
			
		||||
                       "**Caller:** {$caller}\n" .
 | 
			
		||||
                       "**Queue:** {$queue}\n" .
 | 
			
		||||
                       "**Wait Time:** {$duration} seconds\n" .
 | 
			
		||||
                       "**Time:** {$time}";
 | 
			
		||||
                       
 | 
			
		||||
            case 'missed_call':
 | 
			
		||||
                return "❌ **Missed Call**\n" .
 | 
			
		||||
                       "**From:** {$caller}\n" .
 | 
			
		||||
                       "**Queue:** {$queue}\n" .
 | 
			
		||||
                       "**Time:** {$time}";
 | 
			
		||||
                       
 | 
			
		||||
            default:
 | 
			
		||||
                return "📋 **Call Event:** {$type}\n" .
 | 
			
		||||
                       "**Details:** " . json_encode($data);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Send notification to Discord
 | 
			
		||||
     */
 | 
			
		||||
    private static function send_discord_notification($webhook_url, $message, $data) {
 | 
			
		||||
        $payload = array(
 | 
			
		||||
            'content' => $message,
 | 
			
		||||
            'embeds' => array(
 | 
			
		||||
                array(
 | 
			
		||||
                    'title' => self::get_notification_title($data),
 | 
			
		||||
                    'color' => self::get_notification_color($data),
 | 
			
		||||
                    'fields' => self::get_discord_fields($data),
 | 
			
		||||
                    'timestamp' => date('c'),
 | 
			
		||||
                    'footer' => array(
 | 
			
		||||
                        'text' => 'Twilio WP Plugin'
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        self::send_webhook_request($webhook_url, $payload, 'Discord');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Send notification to Slack
 | 
			
		||||
     */
 | 
			
		||||
    private static function send_slack_notification($webhook_url, $message, $data) {
 | 
			
		||||
        $payload = array(
 | 
			
		||||
            'text' => self::get_notification_title($data),
 | 
			
		||||
            'attachments' => array(
 | 
			
		||||
                array(
 | 
			
		||||
                    'color' => self::get_slack_color($data),
 | 
			
		||||
                    'fields' => self::get_slack_fields($data),
 | 
			
		||||
                    'footer' => 'Twilio WP Plugin',
 | 
			
		||||
                    'ts' => time()
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        self::send_webhook_request($webhook_url, $payload, 'Slack');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get notification title
 | 
			
		||||
     */
 | 
			
		||||
    private static function get_notification_title($data) {
 | 
			
		||||
        $type = isset($data['type']) ? $data['type'] : 'call_event';
 | 
			
		||||
        
 | 
			
		||||
        switch ($type) {
 | 
			
		||||
            case 'incoming_call':
 | 
			
		||||
                return '📞 Incoming Call';
 | 
			
		||||
            case 'queue_timeout':
 | 
			
		||||
                return '⏰ Queue Timeout Alert';
 | 
			
		||||
            case 'missed_call':
 | 
			
		||||
                return '❌ Missed Call';
 | 
			
		||||
            default:
 | 
			
		||||
                return '📋 Call Event';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get notification color for Discord (decimal)
 | 
			
		||||
     */
 | 
			
		||||
    private static function get_notification_color($data) {
 | 
			
		||||
        $type = isset($data['type']) ? $data['type'] : 'call_event';
 | 
			
		||||
        
 | 
			
		||||
        switch ($type) {
 | 
			
		||||
            case 'incoming_call':
 | 
			
		||||
                return 3447003; // Blue
 | 
			
		||||
            case 'queue_timeout':
 | 
			
		||||
                return 16776960; // Yellow
 | 
			
		||||
            case 'missed_call':
 | 
			
		||||
                return 15158332; // Red
 | 
			
		||||
            default:
 | 
			
		||||
                return 9807270; // Gray
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get notification color for Slack (hex)
 | 
			
		||||
     */
 | 
			
		||||
    private static function get_slack_color($data) {
 | 
			
		||||
        $type = isset($data['type']) ? $data['type'] : 'call_event';
 | 
			
		||||
        
 | 
			
		||||
        switch ($type) {
 | 
			
		||||
            case 'incoming_call':
 | 
			
		||||
                return '#36a64f'; // Green
 | 
			
		||||
            case 'queue_timeout':
 | 
			
		||||
                return '#ffcc00'; // Yellow
 | 
			
		||||
            case 'missed_call':
 | 
			
		||||
                return '#ff0000'; // Red
 | 
			
		||||
            default:
 | 
			
		||||
                return '#666666'; // Gray
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get Discord fields
 | 
			
		||||
     */
 | 
			
		||||
    private static function get_discord_fields($data) {
 | 
			
		||||
        $fields = array();
 | 
			
		||||
        
 | 
			
		||||
        if (isset($data['caller'])) {
 | 
			
		||||
            $fields[] = array(
 | 
			
		||||
                'name' => 'Caller',
 | 
			
		||||
                'value' => $data['caller'],
 | 
			
		||||
                'inline' => true
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (isset($data['queue'])) {
 | 
			
		||||
            $fields[] = array(
 | 
			
		||||
                'name' => 'Queue',
 | 
			
		||||
                'value' => $data['queue'],
 | 
			
		||||
                'inline' => true
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (isset($data['duration'])) {
 | 
			
		||||
            $fields[] = array(
 | 
			
		||||
                'name' => 'Duration',
 | 
			
		||||
                'value' => $data['duration'] . ' seconds',
 | 
			
		||||
                'inline' => true
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (isset($data['workflow_number'])) {
 | 
			
		||||
            $fields[] = array(
 | 
			
		||||
                'name' => 'Number Called',
 | 
			
		||||
                'value' => $data['workflow_number'],
 | 
			
		||||
                'inline' => true
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return $fields;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get Slack fields
 | 
			
		||||
     */
 | 
			
		||||
    private static function get_slack_fields($data) {
 | 
			
		||||
        $fields = array();
 | 
			
		||||
        
 | 
			
		||||
        if (isset($data['caller'])) {
 | 
			
		||||
            $fields[] = array(
 | 
			
		||||
                'title' => 'Caller',
 | 
			
		||||
                'value' => $data['caller'],
 | 
			
		||||
                'short' => true
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (isset($data['queue'])) {
 | 
			
		||||
            $fields[] = array(
 | 
			
		||||
                'title' => 'Queue',
 | 
			
		||||
                'value' => $data['queue'],
 | 
			
		||||
                'short' => true
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (isset($data['duration'])) {
 | 
			
		||||
            $fields[] = array(
 | 
			
		||||
                'title' => 'Duration',
 | 
			
		||||
                'value' => $data['duration'] . ' seconds',
 | 
			
		||||
                'short' => true
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (isset($data['workflow_number'])) {
 | 
			
		||||
            $fields[] = array(
 | 
			
		||||
                'title' => 'Number Called',
 | 
			
		||||
                'value' => $data['workflow_number'],
 | 
			
		||||
                'short' => true
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return $fields;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Send webhook request
 | 
			
		||||
     */
 | 
			
		||||
    private static function send_webhook_request($webhook_url, $payload, $service) {
 | 
			
		||||
        $response = wp_remote_post($webhook_url, array(
 | 
			
		||||
            'headers' => array(
 | 
			
		||||
                'Content-Type' => 'application/json',
 | 
			
		||||
            ),
 | 
			
		||||
            'body' => json_encode($payload),
 | 
			
		||||
            'timeout' => 30,
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        if (is_wp_error($response)) {
 | 
			
		||||
            error_log("TWP {$service} Notification Error: " . $response->get_error_message());
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $response_code = wp_remote_retrieve_response_code($response);
 | 
			
		||||
        if ($response_code >= 200 && $response_code < 300) {
 | 
			
		||||
            error_log("TWP {$service} notification sent successfully");
 | 
			
		||||
            return true;
 | 
			
		||||
        } else {
 | 
			
		||||
            error_log("TWP {$service} notification failed with response code: " . $response_code);
 | 
			
		||||
            error_log("Response body: " . wp_remote_retrieve_body($response));
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Monitor queue timeouts
 | 
			
		||||
     */
 | 
			
		||||
    public static function check_queue_timeouts() {
 | 
			
		||||
        global $wpdb;
 | 
			
		||||
        
 | 
			
		||||
        $threshold = get_option('twp_queue_timeout_threshold', 300); // Default 5 minutes
 | 
			
		||||
        $calls_table = $wpdb->prefix . 'twp_queued_calls';
 | 
			
		||||
        $queues_table = $wpdb->prefix . 'twp_call_queues';
 | 
			
		||||
        
 | 
			
		||||
        // Find calls that have been waiting too long
 | 
			
		||||
        $timeout_calls = $wpdb->get_results($wpdb->prepare("
 | 
			
		||||
            SELECT qc.*, q.queue_name
 | 
			
		||||
            FROM {$calls_table} qc
 | 
			
		||||
            LEFT JOIN {$queues_table} q ON qc.queue_id = q.id
 | 
			
		||||
            WHERE qc.status = 'waiting' 
 | 
			
		||||
            AND TIMESTAMPDIFF(SECOND, qc.joined_at, NOW()) > %d
 | 
			
		||||
            AND qc.notified_timeout IS NULL
 | 
			
		||||
        ", $threshold));
 | 
			
		||||
        
 | 
			
		||||
        foreach ($timeout_calls as $call) {
 | 
			
		||||
            // Send timeout notification
 | 
			
		||||
            self::send_call_notification('queue_timeout', array(
 | 
			
		||||
                'type' => 'queue_timeout',
 | 
			
		||||
                'caller' => $call->from_number,
 | 
			
		||||
                'queue' => $call->queue_name,
 | 
			
		||||
                'duration' => time() - strtotime($call->joined_at),
 | 
			
		||||
                'workflow_number' => $call->to_number
 | 
			
		||||
            ));
 | 
			
		||||
            
 | 
			
		||||
            // Mark as notified to avoid duplicate notifications
 | 
			
		||||
            $wpdb->update(
 | 
			
		||||
                $calls_table,
 | 
			
		||||
                array('notified_timeout' => current_time('mysql')),
 | 
			
		||||
                array('id' => $call->id),
 | 
			
		||||
                array('%s'),
 | 
			
		||||
                array('%d')
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -338,6 +338,15 @@ class TWP_Webhooks {
 | 
			
		||||
     * Send SMS notification to agents about missed browser call
 | 
			
		||||
     */
 | 
			
		||||
    private function send_agent_notification_sms($customer_number, $twilio_number) {
 | 
			
		||||
        // Send Discord/Slack notification for missed browser call
 | 
			
		||||
        require_once dirname(__FILE__) . '/class-twp-notifications.php';
 | 
			
		||||
        TWP_Notifications::send_call_notification('missed_call', array(
 | 
			
		||||
            'type' => 'missed_call',
 | 
			
		||||
            'caller' => $customer_number,
 | 
			
		||||
            'queue' => 'Browser Phone',
 | 
			
		||||
            'workflow_number' => $twilio_number
 | 
			
		||||
        ));
 | 
			
		||||
        
 | 
			
		||||
        // Get agents with phone numbers
 | 
			
		||||
        $agents = get_users(array(
 | 
			
		||||
            'meta_key' => 'twp_phone_number',
 | 
			
		||||
@@ -556,6 +565,15 @@ class TWP_Webhooks {
 | 
			
		||||
     * Send SMS notification to agents about missed call
 | 
			
		||||
     */
 | 
			
		||||
    private function send_missed_call_notification($customer_number, $twilio_number) {
 | 
			
		||||
        // Send Discord/Slack notification for missed call
 | 
			
		||||
        require_once dirname(__FILE__) . '/class-twp-notifications.php';
 | 
			
		||||
        TWP_Notifications::send_call_notification('missed_call', array(
 | 
			
		||||
            'type' => 'missed_call',
 | 
			
		||||
            'caller' => $customer_number,
 | 
			
		||||
            'queue' => 'General',
 | 
			
		||||
            'workflow_number' => $twilio_number
 | 
			
		||||
        ));
 | 
			
		||||
        
 | 
			
		||||
        // Get agents with phone numbers
 | 
			
		||||
        $agents = get_users(array(
 | 
			
		||||
            'meta_key' => 'twp_phone_number',
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user