'', 'endpoint_url' => 'https://v3.api.beon.chat/api/partner/messages/whatsapp/template', 'templates_url' => 'https://v3.api.beon.chat/api/partner/templates', 'country_code' => '20', 'admin_phones' => '', 'use_queue' => 1, 'debug_log' => 1, 'log_days' => 30, 'woo_rules' => array(), 'form_rules' => array(), 'form_sources' => null, ); $saved = get_option( 'beon_notify_settings', array() ); $s = wp_parse_args( is_array( $saved ) ? $saved : array(), $defaults ); // The first builds guessed the list address; BeOn's partner API lists templates at /api/partner/templates. if ( 'https://v3.api.beon.chat/api/v3/template' === $s['templates_url'] ) { $s['templates_url'] = $defaults['templates_url']; } $s['woo_rules'] = beon_notify_rule_list( $s['woo_rules'] ); // Form rules ("source:id"). Rules saved as `nf_rules` (Ninja Forms only) become "nf:{id}". $form_rules = (array) $s['form_rules']; if ( ! $form_rules && ! empty( $saved['nf_rules'] ) ) { foreach ( (array) $saved['nf_rules'] as $rule ) { $rule = (array) $rule; $rule['form'] = 'nf:' . (int) ( $rule['form_id'] ?? 0 ); $form_rules[] = $rule; } } $s['form_rules'] = array_values( array_map( static function ( $rule ) { return wp_parse_args( array_intersect_key( (array) $rule, array_flip( array( 'id', 'form', 'phone_field', 'name_field', 'recipients', 'template_id', 'variables', 'enabled' ) ) ), array( 'enabled' => 1, 'id' => '', 'form' => '', 'phone_field' => '', 'name_field' => '', 'recipients' => 'visitor', 'template_id' => 0, 'variables' => '', ) ); }, $form_rules ) ); // Connected form plugins. Never chosen yet (older settings): the plugins that already have rules. if ( ! is_array( $s['form_sources'] ) ) { $s['form_sources'] = array(); foreach ( $s['form_rules'] as $rule ) { $s['form_sources'][] = strtok( (string) $rule['form'], ':' ); } } $s['form_sources'] = array_values( array_unique( array_filter( array_map( 'sanitize_key', $s['form_sources'] ) ) ) ); return $s; } /** * WooCommerce rules as a list of {id, event, recipients, template_id, variables}. * * Also reads the 1.0 format (one rule per event, keyed by event, with an * `enabled` flag): enabled rules keep the event as their id, so orders already * messaged under 1.0 aren't messaged again. */ function beon_notify_rule_list( $rules ) { $list = array(); foreach ( (array) $rules as $key => $rule ) { $rule = (array) $rule; if ( ! isset( $rule['event'] ) ) { if ( empty( $rule['enabled'] ) || ! is_string( $key ) ) { continue; } $rule['event'] = $key; $rule['id'] = $key; } $list[] = array( 'id' => (string) ( $rule['id'] ?? '' ), 'event' => (string) $rule['event'], 'recipients' => (string) ( $rule['recipients'] ?? 'customer' ), 'template_id' => (int) ( $rule['template_id'] ?? 0 ), 'variables' => (string) ( $rule['variables'] ?? '' ), ); } return $list; } /** * Normalises a phone number to international digits without "+" (e.g. 201001234567). * * Local numbers starting with a single 0 (Egypt: 01xxxxxxxxx) get the default * country code; "00" prefixes are treated as international. */ function beon_notify_normalize_phone( $phone, $country_code = null ) { $digits = preg_replace( '/\D+/', '', (string) $phone ); if ( '' === $digits ) { return ''; } if ( null === $country_code ) { $country_code = beon_notify_settings()['country_code']; } $country_code = preg_replace( '/\D+/', '', (string) $country_code ); if ( 0 === strpos( $digits, '00' ) ) { $digits = substr( $digits, 2 ); } elseif ( '' !== $country_code && 0 === strpos( $digits, '0' ) ) { $digits = $country_code . substr( $digits, 1 ); } elseif ( '' !== $country_code && strlen( $digits ) <= 10 && 0 !== strpos( $digits, $country_code ) ) { $digits = $country_code . $digits; } if ( strlen( $digits ) < 8 || strlen( $digits ) > 15 ) { return ''; } return $digits; } /** * Splits a textarea (one entry per line) into trimmed non-empty lines. */ function beon_notify_lines( $text ) { $lines = preg_split( '/\r\n|\r|\n/', (string) $text ); return array_values( array_filter( array_map( 'trim', $lines ), 'strlen' ) ); } /** * Replaces {placeholders} in a string from a key => value map. */ function beon_notify_fill( $text, array $vars ) { return preg_replace_callback( '/\{([a-z0-9_:\-]+)\}/i', static function ( $m ) use ( $vars ) { return array_key_exists( $m[1], $vars ) ? (string) $vars[ $m[1] ] : ''; }, (string) $text ); } /** * Writes to the WooCommerce log when available, else the PHP error log. */ function beon_notify_log( $message, $level = 'info' ) { if ( empty( beon_notify_settings()['debug_log'] ) && 'error' !== $level ) { return; } if ( function_exists( 'wc_get_logger' ) ) { wc_get_logger()->log( $level, $message, array( 'source' => 'beon-chat' ) ); return; } error_log( '[beon-chat] ' . $level . ': ' . $message ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions }