<?php

/*
 * ==========================================================
 * WHATSAPP APP POST FILE
 * ==========================================================
 *
 * WhatsApp app post file to receive messages sent by Twilio. © 2017-2025 board.support. All rights reserved.
 *
 */

if (isset($_GET['hub_mode']) && $_GET['hub_mode'] == 'subscribe') {
    require('../../include/functions.php');
    sb_cloud_load_by_url();
    if ($_GET['hub_verify_token'] == sb_get_multi_setting('whatsapp-cloud', 'whatsapp-cloud-key')) {
        echo $_GET['hub_challenge'];
    }
    die();
}
$raw = file_get_contents('php://input');
flush();
if (function_exists('fastcgi_finish_request')) {
    fastcgi_finish_request();
}
if ($raw) {
    require('../../include/functions.php');
    if (sb_is_cloud()) {
        sb_cloud_load_by_url();
        sb_cloud_membership_validation(true);
    }
    $provider = sb_whatsapp_provider();
    $twilio = $provider == 'twilio';
    $response = [];
    if ($twilio) {
        $items = explode('&', urldecode($raw));
        for ($i = 0; $i < count($items); $i++) {
            $value = explode('=', $items[$i]);
            $response[$value[0]] = str_replace('\/', '/', $value[1]);
        }
    } else {
        $response = json_decode($raw, true);
        if (isset($response['entry']) && isset($response['entry'][0]['changes'])) {
            $response = $response['entry'][0]['changes'][0]['value'];
        }
    }
    $error = $twilio ? sb_isset($response, 'ErrorCode') : (isset($response['statuses']) && is_array($response['statuses']) && count($response['statuses']) && isset($response['statuses'][0]['errors']) ? $response['statuses'][0]['errors'][0]['code'] : false);
    $is_echo = isset($response['message_echoes']);
    $call = sb_isset($response, ['calls', 0]);
    $call_permission_request = sb_isset($response, ['messages', 0, 'interactive', 'call_permission_reply']);
    $call_response = sb_isset($response, ['statuses', 0, 'type']) == 'call' ? $response['statuses'][0] : false;
    if ($call_permission_request || $call_response) {
        $call = true;
    }
    if ($is_echo) {
        $response['messages'] = $response['message_echoes'];
    }
    if (($twilio && isset($response['From']) && !$error) || (!$twilio && (isset($response['messages']) || $call))) {
        if ($twilio) {
            if (!isset($response['Body']) && !isset($response['MediaContentType0'])) {
                die();
            }
            if (strpos($response['From'], 'whatsapp:') === false) {
                require('../../include/api.php');
                return;
            }
        }
        $GLOBALS['SB_FORCE_ADMIN'] = true;
        $user_id = false;
        $conversation_id = false;
        $phone = '+';
        if ($twilio) {
            $phone = str_replace('whatsapp:', '', $response['From']);
        } else if ($is_echo) {
            $phone = $response['messages'][0]['to'];
        } else if ($call_response) {
            $phone = $call_response['recipient_id'];
        } else if ($call && !$call_permission_request) {
            $phone = sb_isset($call, sb_isset($call, ['session', 'sdp_type']) == 'answer' || sb_isset($call, 'direction') == 'BUSINESS_INITIATED' ? 'to' : 'from');
        } else {
            $phone = $response['contacts'][0]['wa_id'];
        }
        $user = sb_get_user_by('phone', $phone);
        $phone_number_id = isset($response['metadata']) ? sb_isset($response['metadata'], 'phone_number_id') : false;
        $department = false;
        $tags = false;
        $delayed_message = false;
        $payload = '';
        $message = '';
        $waid = false;
        $new_conversation = false;
        $flow = false;
        $is_new_user = false;
        if ($provider == 'official' && $phone_number_id) {
            $phone_number_settings = sb_whatsapp_cloud_get_phone_numbers($phone_number_id);
            if (!$phone_number_settings) {
                sb_error('phone-number-not-found', 'post.php', 'Phone number not found. Phone number ID: ' . $phone_number_id, true);
                die();
            }
            $department = sb_isset($phone_number_settings, 'whatsapp-cloud-numbers-department'); ; // Deprecated. Replace with: sb_isset($phone_number_settings, ['whatsapp-cloud-numbers-department', 0])
            $department = is_array($department) ? $department[0] : $department; // Deprecated
            $tags = sb_isset($phone_number_settings, 'whatsapp-cloud-numbers-tags');
        }
        if ($twilio) {
            $message = $response['Body'];
        } else if (!$call) {
            $message_ = $response['messages'][0];
            $message_type = $message_['type'];
            if ($message_type == 'button') {
                $message = $message_['button']['text'];
                $message_type = 'text';
            } else if ($message_type == 'text') {
                $message = $message_['text']['body'];
                $referral = sb_isset($message_, 'referral');
                if ($referral && sb_isset($referral, 'source_type') == 'post') {
                    $message .= PHP_EOL . PHP_EOL . '__*' . $referral['headline'] . '*' . PHP_EOL . $referral['body'] . PHP_EOL . $referral['source_url'] . '__';
                }
            } else if ($message_type == 'interactive') {
                $flow = json_decode(sb_isset($message_, ['interactive', 'nfm_reply', 'response_json']), true);
            }
            $waid = $message_['id'];
            $payload = ['waid' => $waid];
            $waids = sb_get_external_setting('waids', []);
            if (!in_array($waid, $waids)) {
                array_push($waids, $waid);
                if (count($waids) > 20) {
                    array_shift($waids);
                }
                sb_save_external_setting('waids', $waids);
            } else {
                die();
            }
        }

        // User and conversation
        if (!$user) {
            $name = sb_split_name($twilio ? $response['ProfileName'] : ($is_echo ? $phone : $response['contacts'][0]['profile']['name']));
            $extra = ['phone' => [$phone, 'Phone']];
            if (defined('SB_DIALOGFLOW')) {
                $extra['language'] = sb_google_language_detection_get_user_extra($message);
            }
            $user_id = sb_add_user([], $extra);
            $is_new_user = true;
            sb_update_user($user_id, ['first_name' => $name[0], 'last_name' => $name[1], 'user_type' => 'user']);
            $user = sb_get_user($user_id);
        } else {
            $user_id = $user['id'];
            $conversation_id = sb_whatsapp_get_conversation_id($user_id, $phone_number_id);
        }
        $GLOBALS['SB_LOGIN'] = $user;
        $is_routing = sb_routing_is_active();
        if (!$conversation_id) {
            $conversation_id = sb_isset(sb_new_conversation($user_id, 2, '', $department, $is_routing ? sb_routing_find_best_agent($department) : -1, 'wa', $phone_number_id, false, $call ? 'call' : false, $tags), 'details', [])['id'];
            $new_conversation = true;
            if (!$call && empty($user['email']) && in_array(sb_get_setting('registration-required'), ['registration', 'registration-login'])) {
                $response_wa = sb_whatsapp_cloud_flow_send_builtin($phone, $phone_number_id, 'registration');
                if (empty($response_wa['error'])) {
                    $delayed_message = '[registration]';
                }
            }
        } else {
            if ($payload && sb_isset(sb_db_get('SELECT COUNT(*) AS `count` FROM sb_messages WHERE conversation_id =  ' . $conversation_id . ' AND payload LIKE "%' . sb_db_json_escape($payload) . '%"'), 'count') != 0) {
                die();
            }
            if ($is_routing && sb_isset(sb_db_get('SELECT status_code FROM sb_conversations WHERE id = ' . $conversation_id), 'status_code') == 3) {
                sb_update_conversation_agent($conversation_id, sb_routing_find_best_agent($department));
            }
        }
        if (!$call) {
            $context = sb_isset($message_, ['context', 'id']);
            if ($context) {
                $reply_to_id = sb_db_get('SELECT id FROM sb_messages WHERE conversation_id = ' . $conversation_id . ' AND payload LIKE "%' . sb_db_escape($context) . '%" LIMIT 1');
                if ($reply_to_id) {
                    $payload['reply'] = $reply_to_id['id'];
                }
            }
        } else if ($call_permission_request) {
            return sb_send_message($user_id, $conversation_id, '', false, 2, ['event' => 'call_permission_request', 'response' => $call_permission_request['response'], 'preview' => sb_('Call permission request') . ' ' . sb_($call_permission_request['response'] == 'accept' ? 'accepted' : 'rejected')]);
        }
        if ($is_new_user) {
            sb_update_user($user_id, ['profile_image' => sb_get_avatar($name[0], $name[1])]);
        }

        // Attachments
        $attachments = [];
        if ($twilio) {
            $extension = sb_isset($response, 'MediaContentType0');
            if ($extension) {
                $extension = sb_whatsapp_get_extension($extension);
                if ($extension) {
                    $file_name = strtolower(basename($response['MediaUrl0'])) . ($extension == '.ogg' ? '_voice_message' : '') . $extension;
                    $settings = sb_get_setting('whatsapp-twilio');
                    array_push($attachments, [$file_name, sb_download_file($response['MediaUrl0'], $file_name, false, ['Authorization: Basic ' . base64_encode($settings['whatsapp-twilio-user'] . ':' . $settings['whatsapp-twilio-token'])])]);
                }
            }
        } else if (!$call && $message_type != 'text' && $message_type != 'unsupported') {
            $file_data = $message_[$message_type];
            switch ($message_type) {
                case 'location':
                    $message = 'https://www.google.com/maps/place/' . $message_[$message_type]['latitude'] . ',' . $message_[$message_type]['longitude'];
                    break;
                case 'reaction':
                    $message = $file_data['emoji'];
                    break;
                case 'contacts':
                    for ($i = 0; $i < count($file_data); $i++) {
                        $message .= $file_data[$i]['phones'][0]['phone'] . PHP_EOL;
                    }
                    break;
                case 'interactive':
                    $message = $file_data[$file_data['type']]['title'];
                    break;
                case 'order':
                    $total = 0;
                    $products = $file_data['product_items'];
                    for ($i = 0; $i < count($products); $i++) {
                        $price = intval($products[$i]['item_price']);
                        $quantity = intval($products[$i]['quantity']);
                        $message .= '*' . $price . $products[$i]['currency'] . '* ' . $products[$i]['product_retailer_id'] . ($quantity > 1 ? ' __x' . $quantity . '__' : '') . PHP_EOL;
                        $total += $price;
                    }
                    $message = '`' . sb_('New order') . '` ' . $products[0]['currency'] . ' ' . $total . PHP_EOL . $message;
                    $url = sb_get_setting('whatsapp-order-webhook');
                    if ($url) {
                        sb_curl($url, $raw, ['Content-Type: application/json', 'Content-Length: ' . strlen($raw)]);
                    }
                    if (defined('SB_WOOCOMMERCE')) {
                        $woocommerce_wa_carts = sb_get_external_setting('wc-whatsapp-carts', []);
                        $products['expiration'] = time() + 2600000;
                        $woocommerce_wa_carts[trim(str_replace('+', '', $phone))] = $products;
                        sb_save_external_setting('wc-whatsapp-carts', $woocommerce_wa_carts);
                    }
                    break;
                default:
                    $sticker = $message_type == 'sticker';
                    $mime = sb_isset($file_data, 'mime_type');
                    $is_audio = $mime == 'audio/ogg; codecs=opus';
                    $file_name = sb_isset($file_data, 'filename', $file_data['id']) . ($is_audio ? '_voice_message.ogg' : ($sticker ? '.webp' : ''));
                    $url = false;
                    if ($provider == '360') {
                        $url = sb_download_file('https://waba-v2.360dialog.io/media/' . $file_data['id'], rand(9999999, 99999999999) . '_' . $file_name, $mime, ['D360-API-KEY: ' . sb_get_multi_setting('whatsapp-360', 'whatsapp-360-key')]);
                    } else {
                        $media_url = sb_isset(sb_whatsapp_cloud_curl($file_data['id'], false, $phone_number_id, 'GET'), 'url');
                        if ($media_url) {
                            $url = sb_download_file($media_url, ($sticker ? 'sticker_' : '') . rand(9999999, 99999999999) . '_' . $file_name, $mime, ['Authorization: Bearer ' . sb_whatsapp_cloud_get_token($phone_number_id)]);
                        }
                    }
                    if ($url) {
                        array_push($attachments, [sb_string_slug(basename($url)), $url]);
                    }
                    if (isset($file_data['caption']) && $file_data['caption'] != $file_name) {
                        $message = $file_data['caption'];
                    }
            }
        }

        // Flows response
        if ($flow && in_array(sb_isset($flow, 'sb_type'), ['registration', 'email'])) {
            unset($flow['flow_token']);
            unset($flow['sb_type']);
            $flow = array_merge($user, $flow);
            sb_update_user($user_id, $flow, $flow, true, true);
        }

        // Response
        if ($is_echo) {
            $response = sb_send_message(sb_get_bot_ID(), $conversation_id, $message, $attachments, false, $payload);
        } else if ($call || $call_response) {
            if (!empty(sb_get_multi_setting('whatsapp-call', 'whatsapp-call-disable'))) {
                die();
            }
            $call_data = $call_response ? $call_response : $call;
            $event = $call_response ? 'call_response' : $call_data['event'];
            $is_connect = $event == 'connect' && sb_isset($call, 'direction') == 'BUSINESS_INITIATED';
            $call_data['phone_number_id'] = $phone_number_id;
            $call_payload = ['conversation_id' => $conversation_id];
            $call_payload[$call_response ? $event : ($is_connect ? 'connect_call' : ($event == 'terminate' ? 'terminate_call' : 'incoming_call'))] = $call_data;
            sb_pusher_trigger('private-user-' . $user_id, 'new-message', $call_payload);
            if ($call_response) {
                $payload_ = ['event' => $event, 'response' => $call_response['status'], 'preview' => '<i class="sb-icon-phone-call-' . ($call_response['status'] == 'ACCEPTED' ? '1' : '2') . '"></i> ' . sb_('Call') . ' ' . sb_($call_response['status'] == 'ACCEPTED' ? 'accepted' : 'rejected')];
            } else if ($event == 'terminate') {
                $duration = sb_isset($call_data, 'duration');
                $payload_ = ['event' => 'terminate_call', 'call_id' => $call_data['id'], 'duration' => $duration, 'preview' => '<i class="sb-icon-phone-call-' . ($duration ? '1' : '2') . '"></i> ' . sb_($duration ? 'Voice call' : 'Call not answered')];
            } else {
                $payload_ = ['event' => $is_connect ? 'connect_call' : 'incoming_call', 'call_id' => $call_data['id'], 'sdp' => $call_data['session']['sdp'], 'phone_number_id' => $phone_number_id, 'preview' => $is_connect ? '' : '<i class="sb-icon-phone"></i> ' . sb_('Incoming voice call') . '...'];
            }
            $response = sb_send_message($user_id, $conversation_id, '', false, 2, $payload_);
        } else {

            // Send message
            $response = sb_send_message($user_id, $conversation_id, $message, $attachments, false, $payload);

            // Dialogflow, Notifications, Bot messages
            $response_extarnal = sb_messaging_platforms_functions($conversation_id, $message, $attachments, $user, ['source' => 'wa', 'platform_value' => $phone, 'new_conversation' => $new_conversation, 'extra' => $phone_number_id]);

            // Queue
            sb_queue_check_and_run($conversation_id, $department, 'wa');

            // Miscellaneous
            sb_update_users_last_activity($user_id);
            if ($delayed_message) {
                sb_send_message(sb_get_bot_ID(), $conversation_id, $delayed_message);
            }
        }
        $GLOBALS['SB_FORCE_ADMIN'] = false;
    } else if ($error) {
        if (!$twilio) {
            $response = $response['statuses'][0];
        }
        $phone = $twilio ? str_replace('whatsapp:', '', $response['To']) : $response['recipient_id'];
        $user = sb_get_user_by('phone', $phone);
        if (!isset($response['ErrorMessage']) && isset($response['MessageStatus'])) {
            $response['ErrorMessage'] = $response['MessageStatus'];
        }
        if ($user) {
            $agents_ids = sb_get_agents_ids();
            $message = sb_db_get('SELECT id, message, attachments, conversation_id FROM sb_messages WHERE user_id IN (' . implode(',', $agents_ids) . ') AND conversation_id IN (SELECT id FROM sb_conversations WHERE source = "wa" AND user_id = ' . $user['id'] . ') ORDER BY id DESC LIMIT 1');
            if ($message) {
                $GLOBALS['SB_FORCE_ADMIN'] = true;
                $conversation_id = $message['conversation_id'];
                $user_language = sb_get_user_language($user['id']);
                $user_name = sb_get_user_name($user);
                $user_email = sb_isset($user, 'email', '');
                $conversation_url_parameter = $conversation_id && $user ? ('?conversation=' . $conversation_id . '&token=' . $user['token']) : '';

                // SMS
                if (sb_get_multi_setting('whatsapp-sms', 'whatsapp-sms-active')) {
                    $template = sb_get_multi_setting('whatsapp-sms', 'whatsapp-sms-template');
                    $message_sms = $template ? str_replace('{message}', $message['message'], sb_t($template, $user_language)) : $message['message'];
                    $message_sms = str_replace(['{conversation_url_parameter}', '{recipient_name}', '{recipient_email}'], [$conversation_url_parameter, $user_name, $user_email], $message_sms);
                    $response_sms = sb_send_sms($message_sms, $phone, false, $conversation_id, empty($message['attachments']) ? [] : json_decode($message['attachments']));
                    if ($response_sms['status'] == 'sent' || $response_sms['status'] == 'queued') {
                        $response = ['whatsapp-templates' => true];
                    }
                }

                // WhatsApp Template
                $phone_number_id = $conversation_id && !$twilio && is_array(sb_get_setting('whatsapp-cloud-numbers')) ? sb_isset(sb_db_get('SELECT extra FROM sb_conversations WHERE id = ' . sb_db_escape($conversation_id, true)), 'extra') : false;
                $response_template = sb_whatsapp_send_template($phone, $user_language, $conversation_url_parameter, $user_name, $user_email, false, $phone_number_id);
                if (($twilio && ($response_template['status'] == 'sent' || $response_template['status'] == 'queued')) || (!$twilio && $response_template && isset($response_template['messages']))) {
                    if (isset($response['whatsapp-templates'])) {
                        $response['whatsapp-template-fallback'] = true;
                    } else {
                        $response = ['whatsapp-template-fallback' => true];
                    }
                } else if (!$twilio && isset($response_template['errors'])) {
                    $response = ['ErrorCode' => true, 'ErrorMessage' => $response_template['errors'][0]['details']];
                }
                $response['delivery_failed'] = 'wa';
                sb_update_message($message['id'], false, false, $response);
                $GLOBALS['SB_FORCE_ADMIN'] = false;
            }
        }
    }
}

function sb_whatsapp_get_extension($mime_type) {
    switch ($mime_type) {
        case 'video/mp4':
            return '.mp4';
        case 'image/gif':
            return '.gif';
        case 'image/png':
            return '.png';
        case 'image/jpg':
        case 'image/jpeg':
            return '.jpg';
        case 'image/webp':
            return '.webp';
        case 'audio/ogg':
            return '.ogg';
        case 'audio/mpeg':
            return '.mp3';
        case 'audio/amr':
            return '.amr';
        case 'application/pdf':
            return '.pdf';
    }
    return false;
}

die();

?>