HEX
Server: Apache/2.4.65 (Ubuntu)
System: Linux ielts-store-v2 6.8.0-1036-gcp #38~22.04.1-Ubuntu SMP Thu Aug 14 01:19:18 UTC 2025 x86_64
User: root (0)
PHP: 7.2.34-54+ubuntu20.04.1+deb.sury.org+1
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,
Upload Files
File: /var/www/html/ielts-store/wp-content/plugins/automatewoo/includes/Actions/Send_SMS_Twilio.php
<?php
// phpcs:ignoreFile

namespace AutomateWoo;

if ( ! defined( 'ABSPATH' ) ) exit;

/**
 * @class Action_Send_SMS_Twilio
 */
class Action_Send_SMS_Twilio extends Action {


	function load_admin_details() {
		$this->title = __( 'Send SMS (Twilio)', 'automatewoo' );
		$this->group = __( 'SMS', 'automatewoo' );
		$this->description = __( 'It is recommended to include an unsubscribe link by using the variable {{ customer.unsubscribe_url }} in the SMS body.', 'automatewoo' );

		if ( AW()->options()->bitly_api && AW()->options()->bitly_shorten_sms_links ) {
			$bitly_text = __( 'Links in the SMS body will be shortened with the <%1$s>Bitly integration<%2$s>.', 'automatewoo' );
		} else {
			$bitly_text = __( 'To shorten links in the SMS body the <%1$s>Bitly integration<%2$s> must be enabled.', 'automatewoo' );
		}

		$this->description .= sprintf(
			' ' . $bitly_text,
			'a href="' . Admin::page_url( 'settings-bitly' ) . '" target="_blank"',
			'/a'
		);
	}


	function load_fields() {
		$sms_recipient = ( new Fields\Text() )
			->set_name( 'sms_recipient' )
			->set_title( __( 'SMS recipients', 'automatewoo' ) )
			->set_description( __( 'Multiple recipient numbers must be separated by commas. When using the {{ customer.phone }} variable the country code will be added automatically, if not already entered by the customer, by referencing the billing country.', 'automatewoo' ) )
			->set_variable_validation()
			->set_required();

		$sms_body = ( new Fields\Text_Area() )
			->set_name( 'sms_body' )
			->set_title( __( 'SMS body', 'automatewoo' ) )
			->set_rows(4)
			->set_variable_validation()
			->set_required();

		$this->add_field( $sms_recipient );
		$this->add_field( $sms_body );
	}


	/**
	 * @throws \Exception
	 */
	function run() {
		$recipients = Clean::comma_delimited_string( $this->get_option( 'sms_recipient' ) );
		$message = $this->get_option( 'sms_body', true );

		if ( empty( $message ) ) {
			throw new \Exception( __( 'Empty message body', 'automatewoo') );
		}

		$message = $this->process_urls_in_sms( $message );

		$valid_recipients_count = 0;
		foreach ( $recipients as $recipient_string ) {
			$recipient = $this->workflow->variable_processor()->process_field( $recipient_string );
			// Do not send SMS for a variable that resolved to an empty number.
			if( $recipient ) {
				$this->send_sms( $recipient, $recipient_string, $message );
				$valid_recipients_count++;
			}
		}
		if ( $valid_recipients_count == 0 ) {
			throw new \Exception( __( 'No valid recipients', 'automatewoo') );
		}
	}


	/**
	 * Sends an SMS to one recipient.
	 *
	 * @since 4.3.2
	 *
	 * @param string $recipient_phone The phone number of the SMS recipient.
	 * @param string $recipient_string Unprocessed recipient string.
	 * @param string $message         The body of the SMS
	 */
	public function send_sms( $recipient_phone, $recipient_string, $message ) {
		$twilio = Integrations::get_twilio();

		if ( ! $twilio ) {
			return;
		}

		$is_sms_to_customer = $this->is_recipient_the_primary_customer( $recipient_string );

		// check if this SMS is going to the workflow's primary customer
		if ( $is_sms_to_customer ) {
			$customer = $this->workflow->data_layer()->get_customer();

			// check if the customer is unsubscribed
			if ( $this->workflow->is_customer_unsubscribed( $customer ) ) {
				$error = new \WP_Error( 'unsubscribed', __( "The recipient is not opted-in to this workflow.", 'automatewoo' ) );
				$this->workflow->log_action_email_error( $error, $this );
				return;
			}

			// because the SMS is to the primary customer, use the customer's country to parse the phone number
			$recipient_phone = Phone_Numbers::parse( $recipient_phone, $customer->get_billing_country() );
		}
		else {
			$recipient_phone = Phone_Numbers::parse( $recipient_phone );
		}

		$request = $twilio->send_sms( $recipient_phone, $message );

		if ( $request->is_successful() ) {
			$this->workflow->log_action_note( $this, __( 'SMS successfully sent.', 'automatewoo' ) );
		}
		else {
			// don't throw exception since the error is only for one recipient
			$this->workflow->log_action_error( $this, $twilio->get_request_error_message( $request ) );
		}
	}


	/**
	 * Determines if a recipient is the primary customer for the workflow.
	 *
	 * Must be used before the $recipient_phone has variables processed.
	 *
	 * @param string $recipient_field
	 *
	 * @return bool
	 */
	public function is_recipient_the_primary_customer( $recipient_field ) {
		if ( ! $recipient_field ) {
			return false;
		}

		$is_primary_customer = false;

		if ( stristr( $recipient_field, 'customer.phone' ) || stristr( $recipient_field, 'order.billing_phone' ) ) {
			$is_primary_customer = true;
		}

		return apply_filters( 'automatewoo/sms/is_recipient_primary_customer', $is_primary_customer, $this );
	}


	/**
	 * Process the URLs in an SMS body.
	 * - Maybe converts URLs to trackable URLs
	 * - Maybe shortens URL
	 *
	 * @since 4.3.2
	 *
	 * @param string $sms
	 *
	 * @return string
	 */
	public function process_urls_in_sms( $sms ) {
		$replacer = new Replace_Helper( $sms, [ $this, 'callback_process_url' ], 'text_urls' );
		$processed = $replacer->process();
		return $processed;
	}


	/**
	 * Processes a single URL in the SMS body.
	 *
	 * @param string $url
	 *
	 * @return string
	 */
	public function callback_process_url( $url ) {
		$url = html_entity_decode( $url );

		// make URL trackable if enabled
		if ( $this->workflow->is_tracking_enabled() ) {
			// don't track unsubscribe clicks
			if ( ! strstr( $url, 'aw-action=unsubscribe' ) ) {
				$url = $this->workflow->append_ga_tracking_to_url( $url );
				$url = Tracking::get_click_tracking_url( $this->workflow, $url );
			}
		}

		// shorten links if enabled
		if ( AW()->options()->bitly_shorten_sms_links ) {
			$bitly = Integrations::get_bitly();

			if ( $bitly ) {
				$short_url = $bitly->shorten_url( $url );

				// Default to full url if bitly fails for any reason (e.g. exceeded rate limit).
				// https://github.com/woocommerce/automatewoo/issues/895
				if ( $short_url ) {
					$url = $short_url;
				}
			}
		}

		return $url;
	}


}