graphClient = new GraphClient(); } /** * Language options for template. * * @return array */ public static function getLanguageOptions(): array { return array( 'en_US' => 'English (US)', 'en_GB' => 'English (UK)', 'es_ES' => 'Spanish (Spain)', 'pt_BR' => 'Portuguese (Brazil)', 'fr' => 'French', 'de' => 'German', 'it' => 'Italian', 'nl' => 'Dutch', 'ar' => 'Arabic', 'hi' => 'Hindi', 'id' => 'Indonesian', 'tr' => 'Turkish', ); } /** * Category options for template. * * @return array */ public static function getCategoryOptions(): array { return array( 'UTILITY' => __( 'Utility', 'afroit-messaging-workflows' ), 'MARKETING' => __( 'Marketing', 'afroit-messaging-workflows' ), 'AUTHENTICATION' => __( 'Authentication', 'afroit-messaging-workflows' ), ); } public static function getButtonTypeOptions(): array { return array( 'QUICK_REPLY' => array( 'label' => __( 'Custom', 'afroit-messaging-workflows' ), 'fields' => array( 'text' ), 'dashicon' => 'undo', ), 'URL' => array( 'label' => __( 'Visit website', 'afroit-messaging-workflows' ), 'fields' => array( 'text', 'url' ), 'dashicon' => 'external', ), 'VOICE_CALL' => array( 'label' => __( 'Call on WhatsApp', 'afroit-messaging-workflows' ), 'fields' => array( 'text' ), 'dashicon' => 'whatsapp', ), 'PHONE_NUMBER' => array( 'label' => __( 'Call phone number', 'afroit-messaging-workflows' ), 'fields' => array( 'text', 'phone' ), 'dashicon' => 'phone', ), /* 'COPY_CODE' => array( 'label' => __( 'Copy offer code', 'afroit-messaging-workflows' ), 'fields' => array( 'example' ), 'dashicon' => 'admin-page', ), */ ); } /** * Get message templates for a WABA with cursor-based pagination. * * @param string $waba_id WhatsApp Business Account ID. * @param array $paging Optional. Cursor params: 'after' or 'before'. * @return array|false Response with 'data' and 'paging', or false on failure. */ public function getMessageTemplates( $waba_id, $paging = array() ) { //return []; $params = array( 'limit' => 20, 'fields' => 'id,name,language,status', ); if ( ! empty( $paging['after'] ) ) { $params['after'] = $paging['after']; } if ( ! empty( $paging['before'] ) ) { $params['before'] = $paging['before']; } $response = $this->graphClient->get( $waba_id . '/message_templates', $params ); return is_array( $response ) ? $response : false; } /** * Summary of getTemplate * @param mixed $id * @return array|false */ public function getTemplate($id) { if (empty($id)) { return false; } $response = $this->graphClient->get($id, [], null, true); $response->throw(); return $response->getBody(); } /** * Delete a message template. * * @param string $waba_id WhatsApp Business Account ID. * @param string $template_id Template ID (hsm_id). * @param string $name Template name. * @return bool True on success, false on failure. */ public function deleteTemplate( $waba_id, $template_id, $name ) { if ( empty( $waba_id ) || empty( $template_id ) || empty( $name ) ) { return false; } return $this->graphClient->delete( $waba_id . '/message_templates', array( 'hsm_id' => $template_id, 'name' => $name ) ); } /** * Send a WhatsApp template message (recommended for transactional). * * @param string $to E.164 phone number. * @param string $name Template name. * @param string $language Language code (e.g. "en_US"). * @param array $components Template components per WA spec. * @return array|false|\WP_Error Graph response. */ public function sendMessageTemplate( $to, $name, $language = 'en_US', $components = array() ) { // Allow "+" at start of phone number before removing non-numeric characters $to = (string) $to; $to = preg_replace('/^\+/', '+', $to); // keep + at start, if any, for now // Only keep digits and optionally a single "+" at the beginning if (strpos($to, '+') === 0) { // Keep leading +, remove non-digits from rest $to = '+' . preg_replace('/\D+/', '', substr($to, 1)); } else { $to = preg_replace('/\D+/', '', $to); } $name = trim( (string) $name ); $language = trim( (string) $language ); $phone_number_id = $this->getPhoneNumberId(); if ( '' === $phone_number_id || '' === $name ) { return false; } $payload = array( 'messaging_product' => 'whatsapp', 'to' => $to, 'recipient_type' => 'individual', 'type' => 'template', 'template' => array( 'name' => $name, 'language' => array( 'code' => $language ), 'components' => array_values( (array) $components ), ), ); Log::info( 'sendMessageTemplate dispatch', array( 'phone_number_id' => $phone_number_id, 'template_name' => $name, 'language' => $language, ) ); $response = ( new GraphClient() )->post( $phone_number_id . '/messages', $payload, null,true ); $response->throw(); return $response->getBody(); //Log::info( 'sendMessageTemplate', array( 'payload' => $payload ) ); //return true; } /** * Get the configured phone number id from secure storage. * * @return string */ private function getPhoneNumberId() { $secure = new SecureOptions(); $business = $secure->get_selected_business(); if ( empty( $business['phone_id'] ) ) { return ''; } return $business['phone_id']; } private function getWabaId() { $secure = new SecureOptions(); $business = $secure->get_selected_business(); if ( empty( $business['waba_id'] ) ) { return ''; } return $business['waba_id']; } public function createTemplate( $name, $language, $category, $header, $body, $footer, $buttons = [] ) { $payload = array( 'name' => $name, 'language' => $language, 'category' => $category, 'parameter_format' => 'named', 'components' => $this->getComponents( $header, $body, $footer, $buttons ), ); Log::info( 'createTemplate dispatch', array( 'payload' => $payload, ) ); $waba_id = $this->getWabaId(); if ( empty( $waba_id ) ) { return false; } $response = $this->graphClient->post( $waba_id . '/message_templates', $payload, null, true ); $response->throw(); return $response->getBody(); } protected function getComponents( $header, $body, $footer, $buttons ) { $wooVariables = new Variables(); $components = []; if (($header_component = $this->getHeaderComponent( $header, $wooVariables )) !== null) { $components[] = $header_component; } if (($body_component = $this->getBodyComponent( $body, $wooVariables )) !== null) { $components[] = $body_component; } if (($footer_component = $this->getFooterComponent( $footer, $wooVariables )) !== null) { $components[] = $footer_component; } if (($buttons_component = $this->getButtonsComponent( $buttons, $wooVariables )) !== null) { $components[] = $buttons_component; } return $components; } protected function getComponentParams( string $text, Variables $wooVariables ) { $matches = array(); preg_match_all('/{{\s*([a-zA-Z0-9_\.]+)\s*}}/', $text, $matches); $variables = isset($matches[1]) ? $matches[1] : array(); $examples = array(); foreach ($variables as $variable) { $examples[] = array( 'param_name' => $variable, 'example' => $wooVariables->get($variable, true), ); } return $examples; } protected function getHeaderComponent(string $header, Variables $wooVariables ) { if ( empty( $header ) ) { return null; } $component = array( 'type' => 'HEADER', 'format' => 'text', 'text' => $header, ); $params = $this->getComponentParams( $header, $wooVariables ); if (!empty($params)) { $component['example']['header_text_named_params'] = $params; } return $component; } protected function getBodyComponent(string $body, Variables $wooVariables ) { if ( empty( $body ) ) { return null; } $component = array( 'type' => 'BODY', 'text' => $body, ); $params = $this->getComponentParams( $body, $wooVariables ); if (!empty($params)) { $component['example']['body_text_named_params'] = $params; } return $component; } protected function getFooterComponent(string $footer, Variables $wooVariables ) { if ( empty( $footer ) ) { return null; } return array( 'type' => 'FOOTER', 'text' => $footer, ); } protected function getButtonsComponent(array $candidates, Variables $wooVariables ) { if ( empty( $candidates ) ) { return null; } $component = array( 'type' => 'BUTTONS' ); $buttons = array(); foreach ($candidates as $candidate) { switch ($candidate['type']) { case 'QUICK_REPLY': $buttons[] = array( 'type' => 'QUICK_REPLY', 'text' => $candidate['text'] ?? '', ); break; case 'URL': $params = $this->getComponentParams( $candidate['url'], $wooVariables ); $button = array( 'type' => 'URL', 'text' => $candidate['text'], 'url' => $candidate['url'] ?? '', ); if (!empty($params)) { $button['example'] = [$params[0]['example']]; } $buttons[] = $button; break; case 'VOICE_CALL': $buttons[] = array( 'type' => 'VOICE_CALL', 'text' => $candidate['text'], ); break; case 'PHONE_NUMBER': $buttons[] = array( 'type' => 'PHONE_NUMBER', 'text' => $candidate['text'], 'phone_number' => $candidate['phone_number'] ?? '', ); break; case 'FLOW': $buttons[] = array( 'type' => 'FLOW', 'text' => $candidate['text'], 'url' => $candidate['url'] ?? '', ); break; case 'COPY_CODE': $buttons[] = array( 'type' => 'COPY_CODE', 'text' => $candidate['text'], 'example' => $candidate['example'] ?? '', ); break; } } $component['buttons'] = $buttons; return $component; } }