Plugin Directory


Ignore:
Timestamp:
10/28/2026 09:22:17 PM (7 days ago)
Author:
meumouse
Message:

Release 2.6.0

File:
1 edited

Legend:

Unmodified
Added
Removed
  • joinotify/trunk/admin/src/Core/Functions.php

    r3722220 r3771693  
    88
    99use MeuMouse\Joinotify\Api\Transport;
     10use MeuMouse\Joinotify\Api\Contact_Window;
     11use MeuMouse\Joinotify\Api\Template_Repository;
    1012use MeuMouse\Joinotify\Api\Extensions;
    1113use MeuMouse\Joinotify\Admin\Admin;
    … …  
    1618use MeuMouse\Joinotify\Core\Message_History;
    1719use MeuMouse\Joinotify\Core\Logger;
     20use MeuMouse\Joinotify\Core\Workflow_Processor;
    1821use MeuMouse\Joinotify\Notifications\Channel_Manager;
    1922use MeuMouse\Joinotify\Notifications\Notification_Message;
    … …  
    8083
    8184   return $response;
     85}
     86
     87
     88/**
     89 * Send an approved WhatsApp template and say what happened.
     90 *
     91 * Meant for integrations that need the outcome right away — a login code, a
     92 * transactional notice sent from another plugin. The send goes through the
     93 * same path as the workflow actions (sender check, one-line parameters, message
     94 * history, debug log), but in a single attempt by default: a code that expires
     95 * in minutes is worse than useless when a retry delivers it an hour later.
     96 *
     97 * `$args`:
     98 * - `sender` — origin number in digits; defaults to the first connected sender.
     99 * - `queue_on_failure` — put a send refused for a transient reason (rate limit,
     100 *   API unavailable) in the retry queue; default false.
     101 * - `context` — dispatch context for the message history while this send runs,
     102 *   e.g. `array( 'source' => 'otp' )`, which masks the template values (login
     103 *   codes) before they are stored. The context set before the call is restored.
     104 *
     105 * @since 2.6.0
     106 * @param string $receiver      | Recipient phone, with or without the country code.
     107 * @param string $template_name | Approved template name.
     108 * @param string $language      | Template language code (e.g. pt_BR).
     109 * @param array  $components    | Meta components with the parameters already resolved.
     110 * @param array  $args          | Options: sender, queue_on_failure, context.
     111 * @return array{message_id:string,status:string}|WP_Error `status` is `sent`, or `queued`
     112 *         (with an empty `message_id`) when `queue_on_failure` put it in the retry queue. The
     113 *         error data carries `status` (HTTP status to answer with), `response_code` (the
     114 *         Joinotify API's, 0 when not reached), `reason`, `detail`, `meta_code`, `retryable`
     115 *         and, for a 429, `retry_after` in seconds.
     116 */
     117function joinotify_send_whatsapp_template( string $receiver, string $template_name, string $language = 'pt_BR', array $components = array(), array $args = array() ) {
     118        $args = wp_parse_args( $args, array(
     119                'sender' => '',
     120                'queue_on_failure' => false,
     121                'context' => array(),
     122        ) );
     123
     124        $sender = is_scalar( $args['sender'] ) ? preg_replace( '/\D/', '', (string) $args['sender'] ) : '';
     125
     126        if ( '' === $sender ) {
     127                $sender = preg_replace( '/\D/', '', (string) joinotify_get_first_sender() );
     128        }
     129
     130        if ( '' === $sender ) {
     131                return new WP_Error( 'joinotify_no_sender', __( 'No WhatsApp number is connected to Joinotify to send from.', 'joinotify' ), array(
     132                        'status' => 503,
     133                        'response_code' => 0,
     134                        'reason' => 'no_sender',
     135                        'detail' => '',
     136                        'meta_code' => 0,
     137                        'retryable' => false,
     138                ) );
     139        }
     140
     141        $context = is_array( $args['context'] ) ? $args['context'] : array();
     142        $previous_context = Message_History::get_context();
     143
     144        if ( ! empty( $context ) ) {
     145                Message_History::set_context( $context );
     146        }
     147
     148        try {
     149                $details = Transport::send_message_template( $sender, $receiver, $template_name, $language, $components, 0, (bool) $args['queue_on_failure'], true );
     150        } finally {
     151                if ( ! empty( $context ) ) {
     152                        Message_History::set_context( $previous_context );
     153                }
     154        }
     155
     156        return Transport::template_send_result( $details );
     157}
     158
     159
     160/**
     161 * List the WhatsApp message templates of the connected account.
     162 *
     163 * Served from a 15-minute local cache of the platform's own mirror; `force`
     164 * asks the platform to read Meta again. Every template says what it expects:
     165 * `header_format` (`TEXT`, `IMAGE`, `VIDEO`, `DOCUMENT`, `LOCATION` or empty),
     166 * `buttons` and `variables`, one entry per value in the order Meta wants them
     167 * (`component`, `sub_type`, `index`, `key`, `label`, `format` and, for a URL
     168 * button, the button's `url` with its token). A value with no token has an
     169 * empty `key`: a media header (`format` `image`, `video`, `document`), a
     170 * copy-code button (`sub_type` `copy_code`) or the login-code button of an
     171 * AUTHENTICATION template (`format` `otp`). A location header is four
     172 * variables (`format` `location`), an expiring offer one `limited_time_offer`
     173 * variable, and the variables of a carousel card carry its position in `card`
     174 * (-1 outside a carousel), as `cards` describes each card.
     175 *
     176 * @since 2.6.0
     177 * @param array $args | Optional `status`, `waba_id` and `force`.
     178 * @return array|WP_Error `templates`, `total`, `waba_id`, `synced_at`, `stale`, `sync_error`.
     179 */
     180function joinotify_get_whatsapp_templates( array $args = array() ) {
     181        return Template_Repository::get_templates( $args );
     182}
     183
     184
     185/**
     186 * Find one WhatsApp message template by name.
     187 *
     188 * Reads the local copy first and loads the listing once when nothing local
     189 * knows the template, so it is safe on a send path.
     190 *
     191 * @since 2.6.0
     192 * @param string $name     | Template name as approved on Meta.
     193 * @param string $language | Language to prefer (e.g. pt_BR); empty for any.
     194 * @return array|null Normalized template (as in joinotify_get_whatsapp_templates()), or null.
     195 */
     196function joinotify_get_whatsapp_template( string $name, string $language = '' ) {
     197        return Template_Repository::find_or_fetch( $name, $language );
     198}
     199
     200
     201/**
     202 * Pack template values already resolved into the `components` of a template send.
     203 *
     204 * Each variable is an entry of the template's `variables` with its `value`
     205 * set; a media header is a `header` entry with `format` (`image`, `video`,
     206 * `document`) and the file link (or a Meta media id) as its value, and a
     207 * `copy_code` button entry takes the coupon. Location, offer expiration and
     208 * carousel card entries are packed as their template lists them (see
     209 * joinotify_get_whatsapp_templates()). Values are not run through the
     210 * Joinotify placeholders: they go out as given, brought to the one line Meta
     211 * accepts, and named variables carry their `parameter_name`.
     212 *
     213 * @since 2.6.0
     214 * @param array $variables | Variables with their resolved `value`.
     215 * @return array Components for joinotify_send_whatsapp_template().
     216 */
     217function joinotify_build_whatsapp_template_components( array $variables ) {
     218        return Workflow_Processor::components_from_values( $variables );
     219}
     220
     221
     222/**
     223 * Write the text a recipient reads for a template and its components.
     224 *
     225 * Header, body and footer with the values in place; a template the site does
     226 * not know is described by its name and values instead.
     227 *
     228 * @since 2.6.0
     229 * @param string $name       | Template name.
     230 * @param string $language   | Template language code.
     231 * @param array  $components | Components as sent.
     232 * @return string
     233 */
     234function joinotify_render_whatsapp_template( string $name, string $language = '', array $components = array() ) {
     235        $rendered = Template_Repository::render( $name, $language, $components );
     236
     237        return (string) ( $rendered['text'] ?? '' );
     238}
     239
     240
     241/**
     242 * Read the customer service window of a contact on one of the account numbers.
     243 *
     244 * Free text and media reach a contact only while the window opened by the
     245 * contact's last message is open (24 hours, 72 hours after a click-to-WhatsApp
     246 * ad); outside it only an approved template goes out. The platform answers for
     247 * the window, webhooks or not. `unknown` means it could not be asked — not
     248 * connected, unreachable, rate limited — so a caller that sends a template on
     249 * anything but `open` is always right.
     250 *
     251 * @since 2.6.0
     252 * @param string $phone  | Contact phone with the country code.
     253 * @param string $sender | Origin number in digits; empty uses the first connected sender.
     254 * @return array{state:string,expires_at:string,remaining_seconds:int}
     255 */
     256function joinotify_get_whatsapp_window( string $phone, string $sender = '' ) {
     257        return Contact_Window::get( $phone, $sender );
    82258}
    83259
    … …  
    423599 * Get a Joinotify plugin setting value.
    424600 *
    425  * @since 2.0.0
     601 * The Joinotify API key (`whatsapp_cloud_api_token`) is stored sealed since 2.6.0
     602 * and may come from the `JOINOTIFY_CLOUD_API_TOKEN` constant, so it is answered
     603 * through Helpers::cloud_api_token(), in the clear, as before.
     604 *
     605 * @since 2.0.0
     606 * @version 2.6.0
    426607 * @param string $key | Setting key.
    427608 * @return mixed Setting value, or false when the key is not set.
    428609 */
    429610function joinotify_get_setting( $key ) {
     611        if ( 'whatsapp_cloud_api_token' === $key ) {
     612                if ( false === Admin::get_setting( $key ) && ! Helpers::cloud_api_token_locked() ) {
     613                        return false;
     614                }
     615
     616                return Helpers::cloud_api_token();
     617        }
     618
    430619        return Admin::get_setting( $key );
    431620}
Note: See TracChangeset for help on using the changeset viewer.