Source: wpt-auto-repost.php

<?php
/**
 * Schedule automatically sent Tweets in XPoster Pro
 *
 * @category Scheduling
 * @package  XPoster Pro
 * @author   Joe Dolson
 * @license  GPLv2 or later
 * @link     https://www.xposterpro.com
 */

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

register_deactivation_hook( __FILE__, 'wpt_deactivate_cron' );
/**
 * Clear schedule hook on deactivation.
 */
function wpt_deactivate_cron() {
	wp_clear_scheduled_hook( 'wptcron' );
}

add_action( 'wptcron', 'wpt_auto_schedule' );
/**
 * On cron schedule, set up a new post to Tweet and insert into scheduler.
 */
function wpt_auto_schedule() {
	$notify_user = get_option( 'wpt_autopost_notification' );
	$template    = ( '' !== get_option( 'wpt_schedule_template', '' ) ) ? get_option( 'wpt_schedule_template' ) : '#title# #url#';
	$post_ID     = wpt_select_post();

	if ( ! $post_ID ) {
		if ( $notify_user && is_email( $notify_user ) ) {
			wp_mail( $notify_user, __( 'Failed to select any post for Tweeting', 'wp-tweets-pro' ), __( 'XPoster Pro did not find a valid post to repost. This may mean that there are no posts between the selected minimum and maximum ages.', 'wp-tweets-pro' ) );
		}
		die;
	}

	// If a custom template is set on this post & the option is enabled, use custom template instead of settings.
	$custom_template = get_post_meta( $post_ID, '_jd_twitter', true );
	$post_info       = wpt_post_info( $post_ID );
	$template        = ( 'true' === get_option( 'wpt_schedule_custom' ) && '' !== $custom_template ) ? $custom_template : $template;
	$sentence        = jd_truncate_tweet( $template, $post_info, $post_ID );
	$auth            = apply_filters( 'wpt_auto_repost_authors', false, array( $sentence, $post_ID ) );
	update_option( 'wpt_last_autopost_tweet', $sentence );

	// For new updates, send current job, then select a new post & continue.
	$wpt_cron = get_option( 'wpt_cron_system_updated' );
	if ( 'true' !== $wpt_cron ) {
		$media = wpt_post_with_media( $post_ID, $post_info );
		$tweet = wpt_post_to_service( $sentence, $auth, $post_ID, $media );
		update_option( 'wpt_cron_system_updated', 'true' );
		$post_ID         = wpt_select_post();
		$custom_template = get_post_meta( $post_ID, '_jd_twitter', true );
		$post_info       = wpt_post_info( $post_ID );
		$template        = ( 'true' === get_option( 'wpt_schedule_custom' ) && '' !== $custom_template ) ? $custom_template : $template;
		$sentence        = jd_truncate_tweet( $template, $post_info, $post_ID );
		$auth            = apply_filters( 'wpt_auto_repost_authors', false, array( $sentence, $post_ID ) );
		do_action( 'wpt_autopost', $tweet, $post_ID );
	}

	// Get the next schedule for this job, then randomize it over a +/- 5 minute range.
	$timestamp = wp_next_scheduled( 'wptcron' );
	// `wp_rand()` only returns positive integers, so this needs to stay.
	$variable = rand( -300, 300 );
	wp_schedule_single_event(
		$timestamp + $variable,
		'wpt_schedule_tweet_action',
		array(
			'id'       => $auth,
			'sentence' => $sentence,
			'rt'       => 0,
			'post_id'  => $post_ID,
		)
	);

	// Notify admin about scheduled post.
	if ( $notify_user && is_email( $notify_user ) ) {
			$post = get_post( $post_ID );
			$url  = admin_url( 'admin.php?page=wp-to-twitter-schedule' );
			// Translators: Tweet title.
			wp_mail( $notify_user, sprintf( __( 'Post auto scheduled for Tweeting: %s', 'wp-tweets-pro' ), $post->post_title ), __( 'This Tweet can be viewed, edited, and re-scheduled at XPoster Pro > Scheduled Tweets', 'wp-tweets-pro' ) . "\n\n" . $url . "\n\n" . "&ldquo;$sentence&rdquo;" );
	}

	$id = md5( $timestamp . $auth . 0 . $post_ID . $sentence );
	set_transient( 'wpt' . $id, true, $timestamp );
}

/**
 * Select a post for auto posting.
 *
 * @param string $return_type type of data to return.
 *
 * @return object|array Single post object or array of data about available posts.
 */
function wpt_select_post( $return_type = 'post' ) {
	global $wpdb;
	$value       = false;
	$minimum_age = get_option( 'wpt_minimum_age' );
	$maximum_age = get_option( 'wpt_maximum_age' );
	if ( ! $minimum_age ) {
		$minimum_age = 0;
	}
	if ( ! $maximum_age ) {
		$maximum_age = 60 * 60 * 24 * 365 * 5;
	}
	if ( $maximum_age < $minimum_age ) {
		$maximum_age = 60 * 60 * 24 * 365 * 5;
	}
	$post_types = get_option( 'wpt_autoretweet_post_types' );

	$after  = apply_filters( 'wpt_custom_cron_maximum', current_time( 'timestamp' ) - $maximum_age ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
	$before = apply_filters( 'wpt_custom_cron_minimum', current_time( 'timestamp' ) - $minimum_age ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
	$after  = array(
		'year'  => date( 'Y', $after ), // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
		'month' => date( 'n', $after ), // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
		'day'   => date( 'j', $after ), // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
	);
	$before = array(
		'year'  => date( 'Y', $before ), // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
		'month' => date( 'n', $before ), // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
		'day'   => date( 'j', $before ), // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
	);
	$args   = array(
		'post_type'      => $post_types,
		'date_query'     => array(
			'after'     => $after,
			'before'    => $before,
			'inclusive' => true,
		),
		'fields'         => 'ids',
		'post_status'    => 'publish',
		'posts_per_page' => '-1',
	);

	if ( 'true' === get_option( 'wpt_only_custom' ) ) {
		$args['meta_query'] = array(
			array(
				'key'     => '_jd_twitter',
				'compare' => 'EXISTS',
			),
			array(
				'key'     => '_jd_twitter',
				'compare' => '!=',
				'value'   => '',
			),
			'relation' => 'AND',
		);
	}

	/**
	 * Filter the arguments to select posts eligible for Tweets.
	 *
	 * @hook wpt_autopost_args
	 *
	 * @param {array} $args Original query arguments.
	 *
	 * @return {array}
	 */
	$args        = apply_filters( 'wpt_autopost_args', $args );
	$posts       = new WP_Query( $args );
	$posted_args = array(
		'post_type'      => $post_types,
		'post_status'    => 'publish',
		'meta_query'     => array(
			array(
				'key'     => '_wpt_autoposted',
				'value'   => '1',
				'compare' => '=',
			),
			array(
				'key'     => '_wpt_noautopost',
				'value'   => '1',
				'compare' => '=',
			),
			'relation' => 'OR',
		),
		'fields'         => 'ids',
		'posts_per_page' => '-1',
	);

	$posted = new WP_Query( $posted_args );
	$posts  = $posts->posts;
	$posted = $posted->posts;

	// get all posts from result that aren't already posted.
	$diff = array_diff( $posts, $posted );

	if ( empty( $diff ) && ! empty( $posted ) ) {
		// If the diff leaves no remaining posts, then all eligible posts have already been Tweeted.
		$wpdb->query( 'DELETE from ' . $wpdb->prefix . "postmeta WHERE meta_key = '_wpt_autoposted'" );
	} else {
		$posts = $diff;
	}
	if ( ! empty( $posts ) ) {
		// array_values ensures that the keys are in numeric order after the diff.
		$posts = array_values( $posts );
		$max   = count( $posts );
		$rand  = wp_rand( 0, $max - 1 );
		$post  = $posts[ $rand ];
		$value = $post;
	}
	if ( 'post' === $return_type ) {
		return $value;
	} else {
		return array(
			'post'   => $value,
			'posts'  => $posts,
			'posted' => $posted,
		);
	}
}

/**
 * Set up a schedule to Tweet. Generate first Tweet directly and set up schedule.
 *
 * @param string               $schedule Chosen schedule.
 * @param mixed boolean/string $start_time Defined start time for schedules.
 */
function wpt_setup_schedules( $schedule, $start_time = false ) {
	update_option( 'wpt_schedule', $schedule );
	if ( $start_time ) {
		$timestamp = strtotime( get_gmt_from_date( date( 'Y-m-d H:i:s', $start_time ) ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
		if ( $timestamp < time() ) {
			$timestamp = $timestamp + DAY_IN_SECONDS;
		}
	} else {
		$timestamp = time() + 600; // first auto schedule in 10 minutes.
	}

	$notify_user = get_option( 'wpt_autopost_notification' );
	$template    = ( '' !== (string) get_option( 'wpt_schedule_template' ) ) ? get_option( 'wpt_schedule_template' ) : '#title# #url#';
	$post_ID     = wpt_select_post();
	if ( ! $post_ID ) {
		if ( $notify_user && is_email( $notify_user ) ) {
			wp_mail( $notify_user, __( 'Failed to select any post for Tweeting', 'wp-tweets-pro' ), __( 'XPoster Pro did not find a valid post to repost. This may mean that there are no posts between the selected minimum and maximum ages.', 'wp-tweets-pro' ) );
		}
		die;
	}
	/**
	 * If a custom template is set on this post & the option to use them is enabled, use the custom template instead of the setting.
	 */
	$custom_template = get_post_meta( $post_ID, '_jd_twitter', true );
	$post_info       = wpt_post_info( $post_ID );
	$template        = ( 'true' === get_option( 'wpt_schedule_custom' ) && '' !== (string) $custom_template ) ? $custom_template : $template;
	$sentence        = jd_truncate_tweet( $template, $post_info, $post_ID );
	$auth            = apply_filters( 'wpt_auto_repost_authors', false, array( $sentence, $post_ID ) );
	wp_schedule_single_event(
		$timestamp,
		'wpt_schedule_tweet_action',
		array(
			'id'       => $auth,
			'sentence' => $sentence,
			'rt'       => 0,
			'post_id'  => $post_ID,
		)
	);

	wp_schedule_event( $timestamp, $schedule, 'wptcron' );

	$id = md5( $timestamp . $auth . 0 . $post_ID . $sentence );
	set_transient( 'wpt' . $id, true, $timestamp );
}

/**
 * Add a variety of custom schedules for increased flexibility.
 *
 * @param array $schedules Array of defined cron scheduls.
 *
 * @return array
 */
function wpt_custom_schedules( $schedules ) {
	$schedules = apply_filters( 'wpt_cron_schedules', $schedules );

	$schedules['four-hours'] = array(
		'interval' => 14400,
		'display'  => __( 'Every 4 hours', 'wp-tweets-pro' ),
	);

	$schedules['eight-hours'] = array(
		'interval' => 28800,
		'display'  => __( 'Every 8 hours', 'wp-tweets-pro' ),
	);

	$schedules['sixteen-hours'] = array(
		'interval' => 57600,
		'display'  => __( 'Every 16 hours', 'wp-tweets-pro' ),
	);

	$schedules['fortyeight-hours'] = array(
		'interval' => 172800,
		'display'  => __( 'Every other day', 'wp-tweets-pro' ),
	);

	$schedules['ninetysix-hours'] = array(
		'interval' => 345600,
		'display'  => __( 'Every four days', 'wp-tweets-pro' ),
	);

	$schedules['weekly'] = array(
		'interval' => 604800,
		'display'  => __( 'Once Weekly', 'wp-tweets-pro' ),
	);

	$schedules['monthly'] = array(
		'interval' => 2635200,
		'display'  => __( 'Once Monthly', 'wp-tweets-pro' ),
	);

	return $schedules;
}
add_filter( 'cron_schedules', 'wpt_custom_schedules' );

add_shortcode( 'wpt_test_autoschedule_arrays', 'wpt_test_shortcode' );
/**
 * This exists for testing. You can use this if you want to examine what is currently in the Tweetable arrays
 *
 * @return string
 */
function wpt_test_shortcode() {
	$object = wpt_select_post( 'test' );
	$post   = $object['post'];
	$posted = $object['posted'];
	$posts  = $object['posts'];

	return '<pre>Post: ' . print_r( $post, 1 ) . ' / Posted: ' . print_r( $posted, 1 ) . ' / Posts: ' . print_r( $posts, 1 ) . '</pre>';
}