Automator_Recipe_Process_User

Class Automator_Recipe_Process_User


Source Source

File: src/core/lib/process/class-automator-recipe-process-user.php

class Automator_Recipe_Process_User {
	/**
	 * Automator_Recipe_Process_User constructor.
	 */
	public function __construct() {
	}
	/**
	 *
	 * Matches recipes against trigger meta/code. If a recipe is found and not completed,
	 * add a trigger entry in to the DB and matches number of times.
	 *
	 * @param      $args
	 * @param bool $mark_trigger_complete
	 *
	 * @return array|bool|int|null
	 */
	public function maybe_add_trigger_entry( $args, $mark_trigger_complete = true ) {
		$is_signed_in       = Automator()->is_user_signed_in( $args );
		$check_trigger_code = key_exists( 'code', $args ) ? $args['code'] : null;
		$trigger_meta       = key_exists( 'meta', $args ) ? $args['meta'] : null;
		$post_id            = key_exists( 'post_id', $args ) ? $args['post_id'] : 0;
		$user_id            = key_exists( 'user_id', $args ) ? $args['user_id'] : wp_get_current_user()->ID;
		$matched_recipe_id  = key_exists( 'recipe_to_match', $args ) ? (int) $args['recipe_to_match'] : null;
		$matched_trigger_id = key_exists( 'trigger_to_match', $args ) ? (int) $args['trigger_to_match'] : null;
		$ignore_post_id     = key_exists( 'ignore_post_id', $args ) ? true : false;
		$is_webhook         = key_exists( 'is_webhook', $args ) ? true : false;
		$webhook_recipe     = key_exists( 'webhook_recipe', $args ) ? (int) $args['webhook_recipe'] : null;
		$get_trigger_log_id = null;
		$result             = array();
		if ( is_null( $check_trigger_code ) ) {
			return null;
		}
		$args = [
			'code'             => $check_trigger_code,
			'meta'             => $trigger_meta,
			'post_id'          => $post_id,
			'user_id'          => $user_id,
			'recipe_to_match'  => $matched_recipe_id,
			'trigger_to_match' => $matched_trigger_id,
			'ignore_post_id'   => $ignore_post_id,
			'is_signed_in'     => $is_signed_in,
		];
		if ( $is_webhook ) {
			$recipes = Automator()->get->recipes_from_trigger_code( $check_trigger_code, $webhook_recipe );
		} else {
			$recipes = Automator()->get->recipes_from_trigger_code( $check_trigger_code );
		}
		foreach ( $recipes as $recipe ) {
			//loop only published
			if ( 'publish' !== $recipe['post_status'] ) {
				continue;
			}
			if ( 'user' === (string) $recipe['recipe_type'] && ! $is_signed_in ) {
				//If it's user recipe & user is not logged in.. skip recipe
				continue;
			}
			$recipe_id = absint( $recipe['ID'] );
			/**
			 * if recipe is already completed, bail early
			 * @version 2.5.1
			 * @author  Saad
			 */
			if ( Automator()->is_recipe_completed( $recipe_id, $user_id ) ) {
				continue;
			}
			$maybe_recipe_log    = $this->maybe_create_recipe_log_entry( $recipe_id, $user_id, true, $args, true );
			$maybe_recipe_log_id = (int) $maybe_recipe_log['recipe_log_id'];
			foreach ( $recipe['triggers'] as $trigger ) {
				if ( ! empty( $matched_trigger_id ) && is_numeric( $matched_trigger_id ) && (int) $matched_trigger_id !== (int) $trigger['ID'] ) {
					continue;
				}
				$trigger_id          = absint( $trigger['ID'] );
				$trigger_post_status = $trigger['post_status'];
				if ( 'publish' !== $trigger_post_status ) {
					continue;
				}
				$get_trigger_log_id = $this->get_trigger_id( $args, $trigger, $recipe_id, $maybe_recipe_log_id, $ignore_post_id );
				if ( is_array( $get_trigger_log_id ) && false === $get_trigger_log_id['result'] ) {
					$result[] = $get_trigger_log_id;
					continue;
				}
				if ( ! $maybe_recipe_log['existing'] ) {
					//trigger validated.. add recipe log ID now!
					$recipe_log_details = $this->maybe_create_recipe_log_entry( $recipe_id, $user_id, true, $args );
					$recipe_log_id      = (int) $recipe_log_details['recipe_log_id'];
					//running again--after $recipe_log_id
					$get_trigger_log_id = $this->get_trigger_id( $args, $trigger, $recipe_id, $maybe_recipe_log_id, $ignore_post_id );
				} else {
					$recipe_log_id = $maybe_recipe_log_id;
				}
				$get_trigger_log_id = $get_trigger_log_id['trigger_log_id'];
				$numtimes_arg = [
					'recipe_id'      => $recipe_id,
					'trigger_id'     => $trigger_id,
					'trigger'        => $trigger,
					'user_id'        => $user_id,
					'recipe_log_id'  => $recipe_log_id,
					'trigger_log_id' => $get_trigger_log_id,
					'is_signed_in'   => $is_signed_in,
				];
				$trigger_steps_completed = $this->maybe_trigger_num_times_completed( $numtimes_arg );
				//If -1 / Any option is used, save it's entry for tokens
				if ( ( isset( $trigger['meta'][ $trigger_meta ] ) && intval( '-1' ) === intval( $trigger['meta'][ $trigger_meta ] ) ) && true === $trigger_steps_completed['result'] ) {
					$meta_arg = [
						'recipe_id'      => $recipe_id,
						'trigger_id'     => $trigger_id,
						'user_id'        => $user_id,
						'recipe_log_id'  => $recipe_log_id,
						'trigger_log_id' => $get_trigger_log_id,
						'post_id'        => $post_id,
						'trigger'        => $trigger,
						'is_signed_in'   => $is_signed_in,
						'meta'           => $trigger_meta,
						'run_number'     => Automator()->get->next_run_number( $recipe_id, $user_id, true ),
					];
					// Fix to avoid saving value as 0 when Any option is selected
					if ( 0 !== absint( $post_id ) ) {
						$meta_results = $this->maybe_trigger_add_any_option_meta( $meta_arg, $trigger_meta );
						if ( isset( $meta_results['result'] ) && false === $meta_results['result'] ) {
							Automator()->error->add_error( 'uap_maybe_add_meta_entry', 'ERROR: You are trying to add entry ' . $trigger['meta'][ $trigger_meta ] . ' and post_id = ' . $post_id . '.', $this );
						}
					}
				}
				do_action_deprecated(
					'uap_after_trigger_run',
					array(
						$check_trigger_code,
						$post_id,
						$user_id,
						$trigger_meta,
					),
					'3.0',
					'automator_after_trigger_run'
				);
				do_action( 'automator_after_trigger_run', $check_trigger_code, $post_id, $user_id, $trigger_meta );
				if ( true === $trigger_steps_completed['result'] ) {
					/**
					 * @deprecated  $args['trigger_log_id'] Use $args['trigger_log_id'].
					 * @version 3.0
					 */
					$args['get_trigger_id'] = $get_trigger_log_id;
					$args['trigger_log_id'] = $get_trigger_log_id;
					$args['recipe_id']      = $recipe_id;
					$args['trigger_id']     = $trigger_id;
					$args['recipe_log_id']  = $recipe_log_id;
					$args['post_id']        = $post_id;
					$args['is_signed_in']   = $is_signed_in;
					$args['run_number']     = Automator()->get->next_run_number( $recipe_id, $user_id, true );
					if ( 1 === + $mark_trigger_complete ) {
						$this->maybe_trigger_complete( $args );
					} else {
						$result[] = array( 'result' => true, 'args' => $args );
					}
				}
			}
		}
		return $result;
	}
	/**
	 * @param int $recipe_id
	 * @param int $user_id
	 * @param bool $create_recipe
	 * @param array $args
	 * @param bool $maybe_simulate
	 * @param null $maybe_add_log_id
	 *
	 * @return array
	 * @since  2.0
	 * @author Saad S. on Nov 15th, 2019
	 *
	 * Added $maybe_simulate in order to avoid unnecessary recipe logs in database.
	 * It'll return existing $recipe_log_id if there's one for a user & recipe, or
	 * simulate an ID for the next run.. The reason for simulate is to avoid unnecessary
	 * recipe_logs in the database since we insert recipe log first & check if trigger
	 * is valid after which means, recipe log is added and not used in this run.
	 * Once trigger is validated.. I pass $maybe_simulate ID to $maybe_add_log_id
	 * and insert recipe log at this point.
	 *
	 */
	public function maybe_create_recipe_log_entry( $recipe_id, $user_id, $create_recipe = true, $args = array(), $maybe_simulate = false, $maybe_add_log_id = null ) {
		global $wpdb;
		$recipe_log_id = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT ID
						FROM {$wpdb->prefix}uap_recipe_log
						WHERE completed NOT IN (1,2,5,9)
						AND automator_recipe_id = %d
						AND user_id = %d",
				$recipe_id,
				$user_id
			)
		);
		if ( $recipe_log_id && 0 !== absint( $user_id ) ) {
			return array(
				'existing'      => true,
				'recipe_log_id' => $recipe_log_id,
			);
		} elseif ( true === $maybe_simulate ) {
			/*
			 * @since 2.0
			 * @author Saad S.
			 */
			if ( ! is_null( $maybe_add_log_id ) ) {
				return array(
					'existing'      => false,
					'recipe_log_id' => $this->insert_recipe_log( $recipe_id, $user_id, $maybe_add_log_id ),
				);
			} else {
				/**
				 * Query changed from Table schema to Max(ID) to support wider MySQL settings
				 * Next Auto_Increment in certain environments returned last inserted ID instead of
				 * next one. Manually add 1 to get next insert ID
				 *
				 * @version 2.6.3
				 *
				 * Query changed back to AUTO_INCREMENT but added another query to reset cache
				 * @version 2.9
				 * @author  Saad S.
				 */
				//Check if it's MySQL 8+
				$check_mysql8 = $wpdb->get_results( "SHOW VARIABLES LIKE 'information_schema_stats_expiry'" );
				if ( ! empty( $check_mysql8 ) ) {
					$wpdb->query( 'SET information_schema_stats_expiry = 0;' );
				}
				$recipe_log_id = $wpdb->get_var( "SELECT `AUTO_INCREMENT` FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '{$wpdb->prefix}uap_recipe_log';" );
				return array(
					'existing'      => false,
					'recipe_log_id' => $recipe_log_id,
				);
			}
		} elseif ( true === $create_recipe ) {
			return array(
				'existing'      => false,
				'recipe_log_id' => $this->insert_recipe_log( $recipe_id, $user_id, null ),
			);
		}
		return array(
			'existing'      => false,
			'recipe_log_id' => null,
		);
	}
	/**
	 * @param      $recipe_id
	 * @param      $user_id
	 * @param null $maybe_add_log_id
	 *
	 * @return int
	 */
	public function insert_recipe_log( $recipe_id, $user_id, $maybe_add_log_id = null ) {
		global $wpdb;
		$table_name = $wpdb->prefix . 'uap_recipe_log';
		$results = $wpdb->get_var( $wpdb->prepare(
			"SELECT
				COUNT(completed)
				FROM $table_name
				WHERE 1=1
				AND completed = 1
				AND user_id = %d
				AND automator_recipe_id = %d", $user_id, $recipe_id ) );
		if ( 0 !== absint( $user_id ) ) {
			$num_times_recipe_run = Automator()->utilities->recipe_number_times_completed( $recipe_id, $results );
		} else {
			$num_times_recipe_run = false;
		}

		if ( ! $num_times_recipe_run ) {
			$run_number = Automator()->get->next_run_number( $recipe_id, $user_id );
			$insert = array(
				'date_time'           => '0000-00-00 00:00:00',
				'user_id'             => $user_id,
				'automator_recipe_id' => $recipe_id,
				'completed'           => - 1,
				'run_number'          => $run_number,
			);
			$format = array(
				'%s',
				'%d',
				'%d',
				'%d',
			);
			/*
			 * Force new ID
			 * if ( ! is_null( $maybe_add_log_id ) ) {
				$insert['ID'] = $maybe_add_log_id;
				$format[]     = '%d';
			}*/
			$wpdb->insert( $table_name, $insert, $format );
			$recipe_log_id = (int) $wpdb->insert_id;
			return $recipe_log_id;
		}
		return null;
	}
	/**
	 * @param $args
	 * @param $trigger
	 * @param $recipe_id
	 * @param $maybe_recipe_log_id
	 * @param $ignore_post_id
	 *
	 * @return array
	 */
	public function get_trigger_id( $args, $trigger, $recipe_id, $maybe_recipe_log_id, $ignore_post_id ) {
		if ( $ignore_post_id ) {
			$get_trigger_id = $this->maybe_validate_trigger_without_postid( $args, $trigger, $recipe_id, $maybe_recipe_log_id );
		} else {
			$get_trigger_id = $this->maybe_validate_trigger( $args, $trigger, $recipe_id, $maybe_recipe_log_id );
		}
		return $get_trigger_id;
	}
	/**
	 *
	 * Validate recipe post ID when ignore post id is passed.
	 * This is mostly going to be used when user/dev done validation in trigger
	 * and passes recipe IDs for this to be validated and added to trigger log DB.
	 *
	 * @param array $args
	 * @param null $trigger
	 * @param null $recipe_id
	 * @param null $recipe_log_id
	 *
	 * @return array
	 */
	public function maybe_validate_trigger_without_postid( $args = array(), $trigger = null, $recipe_id = null, $recipe_log_id = null ) {
		if ( empty( $args ) || null === $trigger || null === $recipe_id ) {
			return [
				'result' => false,
				'error'  => __( 'One of the required field is missing.', 'uncanny-automator' ),
			];
		}

		$check_trigger_code  = $args['code'];
		$trigger_meta        = $args['meta'];
		$user_id             = $args['user_id'];
		$matched_recipe_id   = $args['recipe_to_match'];
		$matched_trigger_id  = $args['trigger_to_match'];
		$trigger_id          = is_numeric( $matched_trigger_id ) ? (int) $matched_trigger_id : $trigger['ID'];
		$trigger_code        = $trigger['meta']['code'];
		$trigger_integration = $trigger['meta']['integration'];
		// Skip completion if the plugin is not active
		if ( 0 === Automator()->plugin_status->get( $trigger_integration ) ) {
			// The plugin for this trigger is NOT active
			Automator()->error->add_error( 'uap_do_trigger_log', 'ERROR: You are trying to complete ' . $trigger['meta']['code'] . ' and the plugin ' . $trigger_integration . ' is not active. ', $this );
			return [
				'result' => false,
				'error'  => __( 'Plugin is not active.', 'uncanny-automator' ),
			];
		}
		/*if ( is_null( $recipe_log_id ) || ! is_numeric( $recipe_log_id ) ) {
			$recipe_log_id = $this->maybe_create_recipe_log_entry( $recipe_id, $user_id, true );
		}*/
		// Stop here if the trigger was already completed
		$is_trigger_completed = $this->is_trigger_completed( $user_id, $trigger_id, $recipe_id, $recipe_log_id, $args );
		if ( $is_trigger_completed ) {
			return [
				'result' => false,
				'error'  => __( 'Trigger is completed.', 'uncanny-automator' ),
			];
		}
		// Skip if the executed trigger doesn't match
		if ( (string) $check_trigger_code !== (string) $trigger_code ) {
			return [
				'result' => false,
				'error'  => sprintf( '%s AND %s triggers not matched.', $check_trigger_code, $trigger_code ),
			];
		}
		if ( 0 !== (int) $matched_recipe_id && (int) $recipe_id !== (int) $matched_recipe_id ) {
			return [
				'result' => false,
				'error'  => __( 'Recipe not matched.', 'uncanny-automator' ),
			];
		} elseif ( (int) $recipe_id === (int) $matched_recipe_id ) {
			/**
			 * Added second part of code to check for MAGICBUTTON
			 * since trigger meta of MAGICBUTTON is saved by
			 * `code` instead of `meta`
			 *
			 * @version 2.1.6
			 * @author  Saad
			 */
			if ( ! isset( $trigger['meta'][ $trigger_meta ] ) && ! isset( $trigger['meta'][ $args['code'] ] ) ) {
				return [
					'result' => false,
					'error'  => __( 'Trigger meta not found.', 'uncanny-automator' ),
				];
			}
		}
		return $this->maybe_get_trigger_id( $user_id, $trigger_id, $recipe_id, $recipe_log_id );
	}
	/**
	 * Check if the trigger is completed
	 *
	 * @param       $user_id       null
	 * @param       $trigger_id    null
	 * @param       $recipe_id     null
	 * @param       $recipe_log_id null
	 * @param array $args
	 * @param bool $process_recipe
	 *
	 * @return null|bool
	 * @deprecated 3.0
	 */
	public function is_trigger_completed( $user_id = null, $trigger_id = null, $recipe_id = null, $recipe_log_id = null, $args = array(), $process_recipe = false ) {
		if ( defined( 'AUTOMATOR_DEBUG_MODE' ) && true === AUTOMATOR_DEBUG_MODE ) {
			_doing_it_wrong( 'Automator()->process->user->is_trigger_completed( ...$args )', 'Use Automator()->db->trigger->is_completed( ...$args ) instead.', '3.0' );
		}
		return Automator()->db->trigger->is_completed( $user_id, $trigger_id, $recipe_id, $recipe_log_id, $process_recipe, $args );
	}
	/**
	 *
	 * Record an entry in to DB against a trigger
	 *
	 * @param      $user_id
	 * @param      $trigger_id
	 * @param      $recipe_id
	 * @param null $recipe_log_id
	 *
	 * @return array
	 */
	public function maybe_get_trigger_id( $user_id, $trigger_id, $recipe_id, $recipe_log_id = null ) {
		if ( null === $trigger_id || null === $recipe_id || null === $user_id ) {
			return array(
				'result' => false,
				'error'  => __( 'One of the required field is missing.', 'uncanny-automator' ),
			);
		}
		$get_trigger_id = Automator()->get->trigger_log_id( $user_id, $trigger_id, $recipe_id, $recipe_log_id );
		if ( is_null( $get_trigger_id ) && is_numeric( $recipe_log_id ) ) {
			//Nothing found! Insert
			$get_trigger_id = $this->insert_trigger( $user_id, $trigger_id, $recipe_id, false, $recipe_log_id );
		}
		return array(
			'result'         => true,
			'trigger_log_id' => $get_trigger_id,
		);
	}
	/**
	 * Insert trigger for the user
	 *
	 * @param $user_id
	 * @param $trigger_id
	 * @param $recipe_id
	 * @param $completed
	 * @param $recipe_log_id
	 *
	 * @return int|null
	 * @deprecated 3.0
	 */
	public function insert_trigger( $user_id = null, $trigger_id = null, $recipe_id = null, $completed = false, $recipe_log_id = null ) {
		if ( defined( 'AUTOMATOR_DEBUG_MODE' ) && true === AUTOMATOR_DEBUG_MODE ) {
			_doing_it_wrong( 'Automator()->insert_trigger()', 'User Automator()->db->trigger->add() instead', '3.0' );
		}
		return Automator()->db->trigger->add( $user_id, $trigger_id, $recipe_id, $completed, $recipe_log_id );
	}
	/**
	 *
	 * Validate (int) values trigger v/s (int) trigger['meta'].
	 * If matched add value to trigger log table
	 *
	 * @param array $args
	 * @param null $trigger
	 * @param null $recipe_id
	 * @param null $recipe_log_id
	 *
	 * @return array
	 */
	public function maybe_validate_trigger( $args = array(), $trigger = null, $recipe_id = null, $recipe_log_id = null ) {
		if ( empty( $args ) || null === $trigger || null === $recipe_id ) {
			return [
				'result' => false,
				'error'  => __( 'One of the required field is missing.', 'uncanny-automator' ),
			];
		}
		$check_trigger_code  = $args['code'];
		$trigger_meta        = $args['meta'];
		$post_id             = $args['post_id'];
		$user_id             = $args['user_id'];
		$trigger_id          = $trigger['ID'];
		$trigger_code        = $trigger['meta']['code'];
		$trigger_integration = $trigger['meta']['integration'];
		// Skip completion if the plugin is not active
		if ( 0 === Automator()->plugin_status->get( $trigger_integration ) ) {
			// The plugin for this trigger is NOT active
			Automator()->error->add_error( 'uap_do_trigger_log', 'ERROR: You are trying to complete ' . $trigger['meta']['code'] . ' and the plugin ' . $trigger_integration . ' is not active. ', $this );
			return [
				'result' => false,
				'error'  => __( 'Plugin is not active.', 'uncanny-automator' ),
			];
		}
		// Stop here if the trigger was already completed
		$is_trigger_completed = $this->is_trigger_completed( $user_id, $trigger_id, $recipe_id, $recipe_log_id, $args );
		if ( $is_trigger_completed ) {
			return [
				'result' => false,
				'error'  => __( 'Trigger is completed.', 'uncanny-automator' ),
			];
		}
		// Skip if the executed trigger doesn't match
		if ( $check_trigger_code !== $trigger_code ) {
			return [
				'result' => false,
				'error'  => __( 'Trigger isn\'t matched.', 'uncanny-automator' ),
			];
		}
		// The post ID the current user needs to visit
		if ( key_exists( $trigger_meta, $trigger['meta'] ) ) {
			$trigger_post_id = intval( $trigger['meta'][ $trigger_meta ] );
		} else {
			$trigger_post_id = 0;
		}
		if ( intval( '-1' ) !== intval( $trigger_post_id ) ) {
			if ( is_numeric( $trigger_post_id ) && is_numeric( $post_id ) && absint( $trigger_post_id ) !== absint( $post_id ) ) {
				return [
					'result' => false,
					'error'  => __( 'Trigger not matched.', 'uncanny-automator' ),
				];
			} elseif ( (string) $trigger_post_id != (string) $post_id ) {
				return [
					'result' => false,
					'error'  => __( 'Trigger not matched.', 'uncanny-automator' ),
				];
			}
		}
		return $this->maybe_get_trigger_id( $user_id, $trigger_id, $recipe_id, $recipe_log_id );
	}
	/**
	 * Validate if the number of times of a trigger condition met
	 *
	 * @param $times_args
	 *
	 * @return array
	 */
	public function maybe_trigger_num_times_completed( $times_args ) {
		$recipe_id      = key_exists( 'recipe_id', $times_args ) ? $times_args['recipe_id'] : null;
		$trigger_id     = key_exists( 'trigger_id', $times_args ) ? $times_args['trigger_id'] : null;
		$trigger        = key_exists( 'trigger', $times_args ) ? $times_args['trigger'] : null;
		$user_id        = key_exists( 'user_id', $times_args ) ? $times_args['user_id'] : null;
		$recipe_log_id  = key_exists( 'recipe_log_id', $times_args ) ? $times_args['recipe_log_id'] : null;
		$trigger_log_id = key_exists( 'trigger_log_id', $times_args ) ? $times_args['trigger_log_id'] : null;
		if ( null === $trigger_id || null === $trigger || null === $user_id ) {
			return array(
				'result' => false,
				'error'  => __( 'One of the required field is missing.', 'uncanny-automator' ),
			);
		}
		// The number of times the current user needs to visit the post/page
		$num_times = key_exists( 'NUMTIMES', $trigger['meta'] ) ? absint( $trigger['meta']['NUMTIMES'] ) : 1;
		// Get max run number from trigger logs
		$run_number = Automator()->get->trigger_run_number( $trigger_id, $trigger_log_id, $user_id );
		// How many times has this user triggered this trigger
		$user_num_times = Automator()->get->trigger_meta( $user_id, $trigger['ID'], 'NUMTIMES', $trigger_log_id );
		$args = [
			'user_id'        => $user_id,
			'trigger_id'     => $trigger_id,
			'meta_key'       => 'NUMTIMES',
			'run_number'     => $run_number,
			'trigger_log_id' => $trigger_log_id,
		];
		if ( empty( $user_num_times ) ) {
			//This is first time user visited
			$args['meta_value'] = 1;
			$user_num_times     = 1;
		} else {
			$user_num_times ++;
			$run_number         = $run_number + 1;
			$args['run_number'] = $run_number;
			$args['meta_value'] = 1;
		}
		$this->insert_trigger_meta( $args );
		/**  Moved this from Completed to run number code */
		/**
		 * Provide hook to developers to hook in to and
		 * do what they want to do with it
		 *
		 * @version 2.5.1
		 * @author  Saad
		 *
		 */
		$trigger_data = Automator()->get->trigger_sentence( $trigger_id, 'trigger_detail' );
		do_action( 'automator_complete_trigger_detail', $trigger_data, $args );
		$sentence_human_readable = Automator()->get->trigger_sentence( $trigger_id, 'sentence_human_readable' );
		// Store trigger sentence details for the completion
		if ( ! empty( $sentence_human_readable ) ) {
			$save_meta = array(
				'user_id'        => $user_id,
				'trigger_id'     => $trigger_id,
				'trigger_log_id' => $trigger_log_id,
				'run_number'     => $run_number,
				'meta_key'       => 'sentence_human_readable',
				'meta_value'     => $sentence_human_readable,
			);
			Automator()->process->user->insert_trigger_meta( $save_meta );
		}
		/**  */
		//change completed from -1 to 0
		$this->maybe_change_recipe_log_to_zero( $recipe_id, $user_id, $recipe_log_id, true );
		// Move on if the user didn't trigger the trigger enough times
		if ( $user_num_times < $num_times ) {
			return [
				'result' => false,
				'error'  => 'Number of times condition is not completed.',
			];
		}
		// If the trigger was hit the enough times then complete the trigger
		if ( $user_num_times >= $num_times ) {
			return array(
				'result'     => true,
				'error'      => 'Number of times condition met.',
				'run_number' => $args['run_number'],
			);
		}
		return array(
			'result' => false,
			'error'  => 'Default return. Something is wrong.',
		);
	}
	/**
	 * Insert the trigger for the user
	 *
	 * @param $args
	 *
	 * @return null
	 * @deprecated 3.0
	 */
	public function insert_trigger_meta( $args ) {
		if ( defined( 'AUTOMATOR_DEBUG_MODE' ) && true === AUTOMATOR_DEBUG_MODE ) {
			_doing_it_wrong( 'Automator()->process->user->insert_trigger_meta( $args )', 'Use Automator()->db->trigger->insert_meta( $trigger_id, $trigger_log_id, $run_number, $args ) instead.', '3.0' );
		}
		$trigger_id     = absint( $args['trigger_id'] );
		$trigger_log_id = absint( $args['trigger_log_id'] );
		$run_number     = absint( $args['run_number'] );
		return Automator()->db->trigger->add_meta( $trigger_id, $trigger_log_id, $run_number, $args );
	}
	/**
	 * @param      $recipe_id
	 * @param      $user_id
	 * @param      $recipe_log_id
	 * @param bool $change_to_zero
	 */
	public function maybe_change_recipe_log_to_zero( $recipe_id, $user_id, $recipe_log_id, $change_to_zero = false ) {
		$if_exists = Automator()->db->recipe->log_run_pre_exists( $recipe_id, $user_id );
		if ( ! empty( $if_exists ) && (int) $if_exists === (int) $recipe_log_id && true === $change_to_zero ) {
			Automator()->db->recipe->mark_incomplete( $recipe_id, $recipe_log_id );
		}
	}
	/**
	 * Validate if the number of times of a trigger condition met
	 *
	 * @param      $option_meta
	 * @param null $save_for_option
	 *
	 * @return array
	 */
	public function maybe_trigger_add_any_option_meta( $option_meta, $save_for_option = null ) {
		if ( is_null( $save_for_option ) ) {
			return array(
				'result' => false,
				'error'  => __( 'Option meta not defined.', 'uncanny-automator' ),
			);
		}
		$trigger_id     = key_exists( 'trigger_id', $option_meta ) ? absint( $option_meta['trigger_id'] ) : null;
		$user_id        = key_exists( 'user_id', $option_meta ) ? absint( $option_meta['user_id'] ) : null;
		$trigger_log_id = key_exists( 'trigger_log_id', $option_meta ) ? absint( $option_meta['trigger_log_id'] ) : null;
		$post_id        = key_exists( 'post_id', $option_meta ) ? $option_meta['post_id'] : null;
		$is_signed_in   = Automator()->is_user_signed_in( $option_meta );
		$run_number     = Automator()->get->next_run_number( $option_meta['recipe_id'], $user_id, true );
		$trigger        = key_exists( 'trigger', $option_meta ) ? $option_meta['trigger'] : null;
		$trigger_meta   = ! empty( $save_for_option ) ? $save_for_option : null;
		if ( null === $trigger_id || null === $trigger || null === $user_id ) {
			return array(
				'result' => false,
				'error'  => 'One of the required field is missing.',
			);
		}
		$args = array(
			'user_id'        => $user_id,
			'trigger_id'     => $trigger_id,
			'meta_key'       => $trigger_meta,
			'meta_value'     => $post_id,
			'run_number'     => $run_number,
			'trigger_log_id' => $trigger_log_id,
		);
		$meta_already_saved = Automator()->get->maybe_get_meta_id_from_trigger_log( $run_number, $trigger_id, $trigger_log_id, $trigger_meta, $user_id );
		if ( ! $meta_already_saved ) {
			return array(
				'result' => Automator()->db->trigger->add_meta( $trigger_id, $trigger_log_id, $run_number, $args ),
				'error'  => __( 'Meta entry added.', 'uncanny-automator' ),
			);
		}
		if ( is_numeric( $meta_already_saved ) ) {
			$args['trigger_log_meta_id'] = $meta_already_saved;
			return array(
				'result' => $this->update_trigger_meta( $user_id, $trigger_id, $trigger_meta, $post_id, $trigger_log_id ),
				'error'  => __( 'Meta entry updated.', 'uncanny-automator' ),
			);
		}
		return array(
			'result' => false,
			'error'  => __( 'No action happened.', 'uncanny-automator' ),
		);
	}
	/**
	 * Update the trigger for the user
	 *
	 * @param $user_id       null
	 * @param $trigger_id    null
	 * @param $meta_key      null
	 * @param $meta_value    string
	 * @param $trigger_log_id
	 *
	 * @return null
	 * @deprecated 3.0
	 */
	public function update_trigger_meta( $user_id = null, $trigger_id = null, $meta_key = null, $meta_value = '', $trigger_log_id = null ) {
		// Set user ID
		if ( null === $user_id ) {
			$user_id = get_current_user_id();
		}
		// No user id is aviable.
		if ( 0 === $user_id ) {
			Automator()->error->add_error( 'update_trigger_meta', 'ERROR: You are trying to update trigger meta when a there is no logged in user.', $this );
			return null;
		}
		if ( null === $trigger_id || ! is_numeric( $trigger_id ) ) {
			Automator()->error->add_error( 'update_trigger_meta', 'ERROR: You are trying to update trigger meta without providing a trigger_id', $this );
			return null;
		}
		if ( null === $meta_key || ! is_string( $meta_key ) ) {
			Automator()->error->add_error( 'update_trigger_meta', 'ERROR: You are trying to update trigger meta without providing a meta_key', $this );
			return null;
		}
		$update = array(
			'meta_value' => $meta_value,
			'run_time'   => current_time( 'mysql' ),
		);
		$where = array(
			'user_id'              => $user_id,
			'automator_trigger_id' => $trigger_id,
			'meta_key'             => $meta_key,
		);
		if ( ! empty( $trigger_log_id ) && is_numeric( $trigger_log_id ) ) {
			$where['automator_trigger_log_id'] = (int) $trigger_log_id;
		}
		$update_format = array(
			'%d',
			'%s',
		);
		$where_format = array(
			'%d',
			'%d',
			'%s',
		);
		if ( ! empty( $trigger_log_id ) && is_numeric( $trigger_log_id ) ) {
			$where_format[] = '%d';
		}
		return Automator()->db->trigger->update(
			$update,
			$where,
			$update_format,
			$where_format
		);
	}
	/**
	 *
	 * Complete a trigger once all validation & trigger entry added
	 * and number of times met, complete the trigger
	 *
	 * @param $args
	 *
	 * @return bool
	 */
	public function maybe_trigger_complete( $args ) {
		$is_signed_in = Automator()->is_user_signed_in( $args );
		if ( empty( $args ) && false === $is_signed_in ) {
			return false;
		}
		Automator()->complete->trigger( $args );
	}
	/**
	 * Get the trigger for the user
	 *
	 * @param $user_id       null
	 * @param $trigger_id    null
	 * @param $meta_key      null
	 * @param $recipe_log_id null
	 *
	 * @return null|int
	 */
	public function trigger_meta_id( $user_id = null, $trigger_id = null, $meta_key = null, $recipe_log_id = null ) {
		// Set user ID
		if ( null === $user_id ) {
			$user_id = get_current_user_id();
		}
		// No user id is aviable.
		if ( 0 === $user_id ) {
			Automator()->error->add_error( 'get_trigger_meta_id', 'ERROR: You are trying to get trigger meta ID when a there is no logged in user.', $this );
			return null;
		}
		if ( null === $trigger_id || ! is_numeric( $trigger_id ) ) {
			Automator()->error->add_error( 'get_trigger_meta_id', 'ERROR: You are trying to get trigger meta ID without providing a trigger_id', $this );
			return null;
		}
		if ( null === $meta_key || ! is_string( $meta_key ) ) {
			Automator()->error->add_error( 'get_trigger_meta_id', 'ERROR: You are trying to get trigger meta ID without providing a meta_key', $this );
			return null;
		}
		global $wpdb;
		$table_name = $wpdb->prefix . 'uap_trigger_log_meta';
		$results    = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $table_name WHERE user_id = %d AND meta_key LIKE %s AND automator_trigger_id = %d", $user_id, $meta_key, $trigger_id ) );
		if ( null !== $results ) {
			return (int) $results;
		}
		return $results;
	}

	/**
	 * Update the trigger for the user
	 *
	 * @param $user_id    null
	 * @param $trigger_id null
	 * @param $recipe_id  null
	 * @param $ID         null
	 *
	 * @return null
	 */
	public function update_trigger( $user_id = null, $trigger_id = null, $recipe_id = null, $ID = null ) {
		// Set user ID
		if ( null === $user_id ) {
			$user_id = get_current_user_id();
		}
		// No user id is aviable.
		if ( 0 === $user_id ) {
			Automator()->error->add_error( 'update_trigger', 'ERROR: You are trying to update a trigger when a there is no logged in user.', $this );
			return null;
		}
		if ( null === $trigger_id || ! is_numeric( $trigger_id ) ) {
			Automator()->error->add_error( 'update_trigger', 'ERROR: You are trying to update a trigge without providing a trigger_id', $this );
			return null;
		}
		if ( null === $recipe_id || ! is_numeric( $recipe_id ) ) {
			Automator()->error->add_error( 'update_trigger', 'ERROR: You are trying to update a trigge without providing a recipe_id', $this );
			return null;
		}
	}
}

Methods Methods