1, 'bot_name' => __( 'Support Assistant', 'avi-support-assistant' ), 'subtitle' => __( 'We are here to help', 'avi-support-assistant' ), 'tooltip' => __( 'Chat with us', 'avi-support-assistant' ), // Appearance. 'launcher' => 'robot', 'bot_icon' => '', 'color' => '#0b63ce', 'theme' => 'light', 'position' => 'left', // Conversation. 'welcome' => __( 'Hi there! 👋 Ask me a question or pick one below.', 'avi-support-assistant' ), 'fallback' => __( "Sorry, I don't have an answer for that yet. Please try asking in a different way, or pick one of the questions below.", 'avi-support-assistant' ), 'max_suggestions' => 0, 'quick_links' => '', 'faqs' => array( array( 'question' => __( 'What are your business hours?', 'avi-support-assistant' ), 'keywords' => __( 'hours, timing, open, close, working time', 'avi-support-assistant' ), 'answer' => __( 'We are open Monday to Saturday, 9:30 AM to 6:30 PM.', 'avi-support-assistant' ), ), array( 'question' => __( 'Where are you located?', 'avi-support-assistant' ), 'keywords' => __( 'address, location, office, where', 'avi-support-assistant' ), 'answer' => __( 'Please update this answer with your office address.', 'avi-support-assistant' ), ), array( 'question' => __( 'How can I get a quote?', 'avi-support-assistant' ), 'keywords' => __( 'quote, price, pricing, cost, rate, charges', 'avi-support-assistant' ), 'answer' => __( 'Share your requirements through our contact page and we will send a quote within one working day.', 'avi-support-assistant' ), ), ), // Search & leads. 'site_search' => 1, 'search_types' => array( 'post', 'page' ), 'lead_form' => 1, 'lead_phone' => 1, 'lead_prompt' => __( "Couldn't find what you need? Leave your details and our team will get back to you.", 'avi-support-assistant' ), 'lead_button' => __( 'Leave my details', 'avi-support-assistant' ), 'lead_thanks' => __( 'Thank you! Our team will get back to you soon.', 'avi-support-assistant' ), 'lead_email' => '', 'log_unanswered' => 1, // Contact options. 'call_number' => '', 'call_label' => __( 'Call us', 'avi-support-assistant' ), 'callback' => 0, 'callback_label' => __( 'Request a callback', 'avi-support-assistant' ), 'whatsapp_number' => '', 'whatsapp_label' => __( 'Chat on WhatsApp', 'avi-support-assistant' ), 'whatsapp_text' => __( 'Hello! I have a question.', 'avi-support-assistant' ), 'contact_show' => 'always', // Display rules. 'auto_open' => 0, 'devices' => 'all', 'exclude_pages' => '', 'delete_data' => 0, ); } /** * Get plugin settings merged with defaults. * * @return array */ function avca_get_settings() { $saved = get_option( AVCA_OPTION, array() ); $settings = wp_parse_args( is_array( $saved ) ? $saved : array(), avca_get_defaults() ); return array_intersect_key( $settings, avca_get_defaults() ); } /** * Get one setting. * * @param string $key Setting key. * @return mixed */ function avca_get_setting( $key ) { $settings = avca_get_settings(); return isset( $settings[ $key ] ) ? $settings[ $key ] : null; } /** * Allowed values for choice settings. * * @return array */ function avca_get_choices() { return array( 'launcher' => array( 'robot' => __( 'Robot mascot', 'avi-support-assistant' ), 'bubble' => __( 'Round chat bubble', 'avi-support-assistant' ), 'pill' => __( 'Button with label', 'avi-support-assistant' ), ), 'theme' => array( 'light' => __( 'Light', 'avi-support-assistant' ), 'dark' => __( 'Dark', 'avi-support-assistant' ), ), 'position' => array( 'left' => __( 'Bottom left', 'avi-support-assistant' ), 'right' => __( 'Bottom right', 'avi-support-assistant' ), ), 'contact_show' => array( 'always' => __( 'Always, with the suggested questions', 'avi-support-assistant' ), 'no_answer' => __( 'Only when the bot has no answer', 'avi-support-assistant' ), ), 'devices' => array( 'all' => __( 'All devices', 'avi-support-assistant' ), 'desktop' => __( 'Desktop and tablet only', 'avi-support-assistant' ), 'mobile' => __( 'Mobile only', 'avi-support-assistant' ), ), ); } /** * Public post types that site search can use. * * @return array Post type name => label. */ function avca_get_searchable_post_types() { $types = array(); foreach ( get_post_types( array( 'public' => true ), 'objects' ) as $type ) { if ( 'attachment' === $type->name ) { continue; } $types[ $type->name ] = $type->labels->name; } return $types; } /** * Sanitize settings input. * * @param array $input Raw input. * @return array Sanitized settings. */ function avca_sanitize_settings( $input ) { $input = is_array( $input ) ? $input : array(); $defaults = avca_get_defaults(); $sanitized = array(); foreach ( array( 'enabled', 'site_search', 'lead_form', 'lead_phone', 'log_unanswered', 'delete_data', 'callback' ) as $key ) { $sanitized[ $key ] = empty( $input[ $key ] ) ? 0 : 1; } foreach ( avca_get_choices() as $key => $choices ) { $value = isset( $input[ $key ] ) ? sanitize_key( $input[ $key ] ) : ''; $sanitized[ $key ] = isset( $choices[ $value ] ) ? $value : $defaults[ $key ]; } foreach ( array( 'bot_name', 'subtitle', 'tooltip', 'lead_button', 'call_label', 'callback_label', 'whatsapp_label' ) as $key ) { $sanitized[ $key ] = isset( $input[ $key ] ) ? sanitize_text_field( $input[ $key ] ) : ''; } foreach ( array( 'welcome', 'fallback', 'lead_prompt', 'lead_thanks', 'whatsapp_text' ) as $key ) { $sanitized[ $key ] = isset( $input[ $key ] ) ? sanitize_textarea_field( $input[ $key ] ) : ''; } // Phone numbers: digits and a leading + only. $sanitized['call_number'] = isset( $input['call_number'] ) ? avca_sanitize_phone( $input['call_number'], true ) : ''; $sanitized['whatsapp_number'] = isset( $input['whatsapp_number'] ) ? avca_sanitize_phone( $input['whatsapp_number'], false ) : ''; if ( '' !== $sanitized['whatsapp_number'] && ( strlen( $sanitized['whatsapp_number'] ) < 8 || strlen( $sanitized['whatsapp_number'] ) > 15 ) ) { add_settings_error( AVCA_OPTION, 'avca_invalid_whatsapp', __( 'Please enter the WhatsApp number with country code, digits only (for example 919876543210).', 'avi-support-assistant' ) ); } $color = isset( $input['color'] ) ? sanitize_hex_color( $input['color'] ) : ''; $sanitized['color'] = $color ? $color : $defaults['color']; $sanitized['bot_icon'] = isset( $input['bot_icon'] ) ? esc_url_raw( $input['bot_icon'] ) : ''; $sanitized['max_suggestions'] = isset( $input['max_suggestions'] ) ? min( 50, absint( $input['max_suggestions'] ) ) : 0; $sanitized['auto_open'] = isset( $input['auto_open'] ) ? min( 600, absint( $input['auto_open'] ) ) : 0; $email = isset( $input['lead_email'] ) ? sanitize_email( $input['lead_email'] ) : ''; $sanitized['lead_email'] = is_email( $email ) ? $email : ''; if ( isset( $input['lead_email'] ) && '' !== trim( (string) $input['lead_email'] ) && '' === $sanitized['lead_email'] ) { add_settings_error( AVCA_OPTION, 'avca_invalid_email', __( 'The lead notification email address is not valid, so leads will be sent to the site admin email.', 'avi-support-assistant' ) ); } $allowed_types = array_keys( avca_get_searchable_post_types() ); $types = isset( $input['search_types'] ) && is_array( $input['search_types'] ) ? array_map( 'sanitize_key', $input['search_types'] ) : array(); $sanitized['search_types'] = array_values( array_intersect( $types, $allowed_types ) ); // Page IDs, comma separated. $ids = isset( $input['exclude_pages'] ) ? wp_parse_id_list( $input['exclude_pages'] ) : array(); $sanitized['exclude_pages'] = implode( ', ', array_filter( $ids ) ); $sanitized['quick_links'] = isset( $input['quick_links'] ) ? avca_sanitize_quick_links( $input['quick_links'] ) : ''; $sanitized['faqs'] = array(); $is_pro = function_exists( 'avca_pro_active' ) && avca_pro_active(); $max_allowed = $is_pro ? AVCA_MAX_FAQS : 3; if ( ! empty( $input['faqs'] ) && is_array( $input['faqs'] ) ) { foreach ( $input['faqs'] as $faq ) { if ( ! is_array( $faq ) ) { continue; } $question = isset( $faq['question'] ) ? sanitize_text_field( $faq['question'] ) : ''; $answer = isset( $faq['answer'] ) ? sanitize_textarea_field( $faq['answer'] ) : ''; // Skip incomplete rows. if ( '' === $question || '' === $answer ) { continue; } $sanitized['faqs'][] = array( 'question' => $question, 'keywords' => isset( $faq['keywords'] ) ? sanitize_text_field( $faq['keywords'] ) : '', 'answer' => $answer, ); if ( count( $sanitized['faqs'] ) >= $max_allowed ) { if ( ! $is_pro && count( $input['faqs'] ) > 3 ) { add_settings_error( AVCA_OPTION, 'avca_faq_limit', __( 'Free Plan Limit: Saved the first 3 FAQs. Upgrade to Pro for unlimited FAQs and AI answers.', 'avi-support-assistant' ), 'info' ); } break; } } } return $sanitized; } /** * Sanitize a phone number. * * @param string $raw Raw number. * @param bool $allow_plus Keep a leading +. * @return string */ function avca_sanitize_phone( $raw, $allow_plus ) { $raw = trim( (string) $raw ); $plus = $allow_plus && 0 === strpos( $raw, '+' ) ? '+' : ''; $digits = preg_replace( '/\D+/', '', $raw ); return '' === $digits ? '' : $plus . $digits; } /** * Sanitize the quick links textarea: one "Label | URL" per line. * * @param string $raw Raw textarea value. * @return string */ function avca_sanitize_quick_links( $raw ) { $lines = array(); foreach ( avca_parse_quick_links( (string) $raw ) as $link ) { $lines[] = $link['label'] . ' | ' . $link['url']; } return implode( "\n", $lines ); } /** * Parse quick links text into label/URL pairs. * * @param string $raw "Label | URL" per line. * @return array[] Each item has label and url. */ function avca_parse_quick_links( $raw ) { $links = array(); foreach ( preg_split( '/\r\n|\r|\n/', (string) $raw ) as $line ) { $parts = array_map( 'trim', explode( '|', $line, 2 ) ); if ( 2 !== count( $parts ) ) { continue; } $label = sanitize_text_field( $parts[0] ); $url = esc_url_raw( $parts[1] ); if ( '' === $label || '' === $url ) { continue; } $links[] = array( 'label' => $label, 'url' => $url, ); if ( count( $links ) >= 10 ) { break; } } return $links; } /** * Set default options on activation. */ function avca_activate() { if ( false === get_option( AVCA_OPTION ) ) { add_option( AVCA_OPTION, avca_get_defaults() ); } if ( false === get_option( AVCA_UNANSWERED_OPTION ) ) { add_option( AVCA_UNANSWERED_OPTION, array(), '', false ); } }