Source: wpt-rate-limiting.php

<?php
/**
 * Rate limiting in XPoster
 *
 * @category Core
 * @package  XPoster
 * @author   Joe Dolson
 * @license  GPLv2 or later
 * @link     https://www.joedolson.com/wp-to-twitter/
 */

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

add_action( 'wptratelimits', 'wpt_clear_rate_limits' );
/**
 * Hourly cron job to reset rate limits
 */
function wpt_clear_rate_limits() {
	delete_option( 'wpt_rate_limits' );
}

/**
 * Logs successful status updates for rate limiting.
 *
 * @param int    $auth Author.
 * @param string $ts Timestamp.
 * @param int    $post_ID Post ID.
 */
function wpt_log_success( $auth, $ts, $post_ID ) {
	if ( ! $post_ID ) {
		return;
	}

	// get record of recent updates.
	$rate_limit = get_option( 'wpt_rate_limits' );
	if ( ! is_array( $rate_limit ) ) {
		$rate_limit = array();
	}
	$post              = get_post( $post_ID );
	$post_type         = $post->post_type;
	$object_taxonomies = get_object_taxonomies( $post_type );
	$terms             = wp_get_object_terms( $post_ID, $object_taxonomies, array( 'fields' => 'all' ) );

	foreach ( $terms as $term ) {
		$term_id = $term->term_id;
		$tax     = $term->taxonomy;

		$rate_limit[ $auth ][ $term_id . '+' . $tax ][] = $post_ID;
	}

	update_option( 'wpt_rate_limits', $rate_limit );
}

/**
 * Test updates against rate limiting rules.
 *
 * @param int $post_ID Post ID.
 * @param int $auth Author ID.
 *
 * @return boolean True if OK to post update.
 */
function wpt_test_rate_limit( $post_ID, $auth ) {
	// record of recent updates.
	$rate_limit = get_option( 'wpt_rate_limits' );
	$return     = true;
	if ( ! $rate_limit ) {
		return true;
	} else {
		$post = get_post( $post_ID );
		if ( is_object( $post ) ) {
			$post_type         = $post->post_type;
			$object_taxonomies = get_object_taxonomies( $post_type );
			$terms             = wp_get_object_terms( $post_ID, $object_taxonomies, array( 'fields' => 'all' ) );

			foreach ( $terms as $term ) {
				$term_id = $term->term_id;
				$limit   = wpt_get_rate_limit( $term_id );
				$tax     = $term->taxonomy;
				$count   = ( isset( $rate_limit[ $auth ][ $term_id . '+' . $tax ] ) ) ? count( $rate_limit[ $auth ][ $term_id . '+' . $tax ] ) : false;
				if ( $count && $count >= $limit ) {
					$return = false;
				}
			}
		} else {
			// don't rate limit if no post.
			$return = true;
		}
	}

	return $return;
}

/**
 * Default rate limiting value. Limit can't be 0.
 *
 * @param int $term Term ID.
 *
 * @return integer Default rate limit
 */
function wpt_default_rate_limit( $term = false ) {
	$limit = ( '' !== get_option( 'wpt_default_rate_limit' ) ) ? get_option( 'wpt_default_rate_limit' ) : 10;
	$limit = ( '0' === (string) $limit ) ? 1 : $limit;

	/**
	 * Filter the default rate limit.
	 *
	 * @hook wpt_default_rate_limit
	 *
	 * @param {int} $limit Integer number of posts allowed.
	 * @param {int|bool} $term  Term ID or false for default value.
	 *
	 * @return {int}
	 */
	return apply_filters( 'wpt_default_rate_limit', $limit, $term );
}

/**
 * Get the current rate limit for a given term ID.
 *
 * @param string $term Term ID.
 * @uses filter wpt_default_rate_limit
 *
 * @return integer Number of status updates allowed per hour in this category.
 */
function wpt_get_rate_limit( $term ) {
	$limits = get_option( 'wpt_rate_limit' );
	$limit  = isset( $limits[ $term ] ) ? $limits[ $term ] : wpt_default_rate_limit( $term );
	if ( ! is_int( $limit ) ) {
		$limit = wpt_default_rate_limit( $term );
	}

	return $limit;
}

add_action( 'init', 'wpt_term_rate_limits' );
/**
 * Get term-based rate limits.
 */
function wpt_term_rate_limits() {
	$args       = apply_filters( 'wpt_rate_limit_taxonomies', array() );
	$taxonomies = get_taxonomies( $args );
	if ( ! is_array( $taxonomies ) ) {
		$taxonomies = array();
	}
	foreach ( $taxonomies as $value ) {
		add_action( $value . '_add_form_fields', 'wpt_add_term_rate_limit', 10, 1 );
		add_action( $value . '_edit_form_fields', 'wpt_edit_term_rate_limit', 10, 2 );
		add_action( 'edit_' . $value, 'wpt_save_term_rate_limit', 10, 2 );
		add_action( 'created_' . $value, 'wpt_save_term_rate_limit', 10, 2 );
	}
}

/**
 * Save rate limit for a term.
 *
 * @param int $term_id Term ID.
 * @param int $tax_id Taxonomy ID.
 */
function wpt_save_term_rate_limit( $term_id, $tax_id ) {
	$nonce  = isset( $_REQUEST['_wpnonce_add-tag'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce_add-tag'] ) ) : false;
	$verify = wp_verify_nonce( $nonce, 'add-tag' );
	if ( ! $verify ) {
		wp_die( esc_html__( 'XPoster: Nonce verification failed', 'wp-to-twitter' ) );
	}
	$limits     = get_option( 'wpt_rate_limit' );
	$option_set = isset( $_POST['wpt_rate_limit'] ) ? sanitize_text_field( wp_unslash( $_POST['wpt_rate_limit'] ) ) : wpt_default_rate_limit( $term_id );
	if ( isset( $_POST['taxonomy'] ) ) {
		if ( isset( $_POST['wpt_rate_limit'] ) ) {
			$limits[ $term_id ] = $option_set;
			update_option( 'wpt_rate_limit', $limits );
		}
	}
}

/**
 * Edit term rate limits.
 *
 * @param object $term Term object.
 * @param object $taxonomy Taxonomy object.
 */
function wpt_edit_term_rate_limit( $term, $taxonomy ) {
	$t_id       = $term->term_id;
	$limits     = get_option( 'wpt_rate_limit' );
	$option_set = isset( $limits[ $t_id ] ) ? $limits[ $t_id ] : wpt_default_rate_limit( $t_id );
	?>
	<tr class="form-field">
		<th valign="top" scope="row">
			<label for="wpt_rate_limit"><?php esc_html_e( 'Max updates per hour on this term', 'wp-to-twitter' ); ?></label>
		</th>
		<td>
			<input type='number' size='4' value='<?php echo esc_attr( $option_set ); ?>' name='wpt_rate_limit' id='wpt_rate_limit' />
		</td>
	</tr>
	<?php
}

/**
 * Add a rate limit for a given term.
 *
 * @param object $term Term Object.
 */
function wpt_add_term_rate_limit( $term ) {
	$default = wpt_default_rate_limit();
	?>
	<div class="form-field">
		<label for="wpt_rate_limit"><?php esc_html_e( 'Max updates per hour on this term', 'wp-to-twitter' ); ?></label> <input type='number' value='<?php echo esc_attr( $default ); ?>' id='wpt_rate_limit' name='wpt_rate_limit' />
	</div>
	<?php
}

/**
 * View rate limit status.
 */
function wpt_view_rate_limits() {
	$limits = get_option( 'wpt_rate_limits' );
	if ( ! wp_next_scheduled( 'wptratelimits' ) ) {
		wp_schedule_event( time() + 3600, 'hourly', 'wptratelimits' );
	}
	$next_scheduled = human_time_diff( wp_next_scheduled( 'wptratelimits' ), time() );
	if ( is_array( $limits ) ) {
		?>
		<ul>
		<?php
		foreach ( $limits as $auth => $term ) {
			$author = ( 0 === (int) $auth ) ? get_option( 'wtt_twitter_username' ) : get_user_meta( $auth, 'wtt_twitter_username', true );
			?>
			<li><h4><a href='https://x.com/$author'>@<?php echo esc_html( $author ); ?></a>:</h4><ul>
				<?php
				foreach ( $term as $id => $value ) {
					$count         = count( $value );
					$term_array    = explode( '+', $id );
					$t             = $term_array[0];
					$x             = $term_array[1];
					$limit         = wpt_get_rate_limit( $t );
					$term_object   = get_term( $t, $x );
					$term_label    = $term_object->name;
					$rate_limiting = ( $count >= $limit ) ? 'rate-limited' : 'active';
					$dashicon      = ( $count >= $limit ) ? "<span class='dashicons dashicons-no' aria-hidden='true'></span>" : "<span class='dashicons dashicons-yes' aria-hidden='true'></span>";
					?>
					<li class='<?php echo esc_attr( $rate_limiting ); ?>'>
						<?php wp_kses_post( $dashicon . $term_label ); ?>:
					<?php
					// Translators: Number of tweets sent, number allowed.
					echo wp_kses_post( sprintf( _n( '%1$s update sent, %2$s allowed.', '%1$s updates sent, %2$s allowed.', $count, 'wp-to-twitter' ), "<strong>$count</strong>", "<strong>$limit</strong>" ) );
					?>
					</li>
					<?php
				}
				?>
			</ul>
			<?php
		}
		?>
		</ul>
		<?php
	} else {
		?>
		<p><?php esc_html_e( 'No updates have been sent this hour.', 'wp-to-twitter' ); ?></p>
		<?php
	}
	// Translators: Time until next scheduled rate limiting reset.
	$next = sprintf( __( 'Next reset in %s.', 'wp-to-twitter' ), $next_scheduled );
	?>
	<p><?php echo esc_html( $next ); ?></p>
	<?php
}