phpwordpresscrongoogle-search-console

WordPress Cron Jobs with Google Site Kit - Missing Required Scopes in Scheduled Event for Google Search Console Data Fetching


I'm working on a WordPress plugin where I fetch keyword data like Total Clicks, Total Impressions, Average CTR, and Average Positions from the Google Search Console using the Google Site Kit plugin. To automate this, I’ve implemented cron jobs using wp_next_scheduled() and wp_schedule_single_event() to schedule the fetching of keyword data and send an email with the results.

The issue arises when I try to schedule these functions. I get the following error in my email:

broken backlinks semrush: Array ( [errors] => Array ( [missing_required_scopes] => Array ( [0] => Site Kit can’t access the relevant data from Search Console because you haven’t granted all permissions requested during setup. ) ) [error_data] => Array ( [missing_required_scopes] => Array ( [status] => 403 [scopes] => Array ( [0] => https://www.googleapis.com/auth/webmasters ) ) ) )

However, when I remove the scheduling code and directly fetch the data without using cron jobs, everything works fine, and I successfully receive the following results:

Keyword: broken backlinks semrush, Clicks: 0, Impressions: 2, Position: 19.5

What I've Tried:

My Code:

`<?php if ( ! class_exists( 'GSC_Data_Fetching_Cron' ) ) {

class GSC_Data_Fetching_Cron {

    /**
     * Schedule an email event if not already scheduled.
     *
     * @param int $interval The interval in seconds for scheduling the email.
     */
    public function gsc_data_schedule_email( $interval ) {
        if ( ! wp_next_scheduled( 'gsc_data_send_email_event_' . $interval ) ) {
            wp_schedule_single_event( time() + $interval, 'gsc_data_send_email_event', array( $interval ) );
        }
    }

    /**
     * Send email with Google Search Console data.
     *
     * @param int $interval The interval for which the email event is triggered.
     */
    public function gsc_data_send_email_function( $interval ) {
        global $wpdb;

        if ( ! class_exists( 'Google\Site_Kit\Plugin' ) ) {
            return; // Exit if Google Site Kit is not active.
        }

        // Fetch keyword data from Google Search Console.
        $site_kit       = \Google\Site_Kit\Plugin::instance();
        $context        = $site_kit->context();
        $modules        = new \Google\Site_Kit\Core\Modules\Modules( $context );
        $search_console = $modules->get_module( 'search-console' );

        if ( ! $search_console ) {
            return; // Ensure the Search Console module is available.
        }

        $table_name = $wpdb->prefix . 'gsc_data_settings_table';
        $settings   = $wpdb->get_results( "SELECT * FROM $table_name" );

        if ( ! $settings ) {
            return; // Exit if no threshold settings found.
        }

        $gsc_data = array();

        foreach ( $settings as $setting ) {
            $selected_keywords = maybe_unserialize( $setting->selected_keywords );
            $start_date        = date( 'Y-m-d', strtotime( '-7 days' ) );
            $end_date          = date( 'Y-m-d' );

            // Fetch Queries data from GSC.
            $queries_data = $search_console->get_data(
                'searchanalytics',
                array(
                    'dimensions' => array( 'query' ),
                    'startDate'  => $start_date,
                    'endDate'    => $end_date,
                    'rowLimit'   => 100,
                    'filters'    => array(
                        array(
                            'dimension'  => 'query',
                            'operator'   => 'in',
                            'expression' => $selected_keywords,
                        ),
                    ),
                )
            );

            // Prepare GSC data to save.
            $gsc_data[] = array(
                'query_name' => $setting->query_name,
                'gsc_data'   => json_encode( $queries_data ),
                'created_at' => current_time( 'mysql' ),
            );
        }

        // Insert data into gsc_data table.
        $wpdb->insert(
            $wpdb->prefix . 'gsc_data',
            array(
                'gsc_data' => json_encode( $gsc_data ),
            )
        );

        // Prepare email content.
        $message = '<h2>' . esc_html__( 'Google Search Console Fetched Data', 'text-domain' ) . '</h2>';
        foreach ( $gsc_data as $data ) {
            $message .= '<p>' . esc_html( $data['query_name'] ) . ': ' . print_r( json_decode( $data['gsc_data'], true ), true ) . '</p>';
        }

        // Send email.
        $to      = 'waqas@gmail.com';
        $subject = esc_html__( 'Google Search Console Fetched Data', 'text-domain' );
        $headers = array( 'Content-Type: text/html; charset=UTF-8' );
        wp_mail( $to, $subject, $message, $headers );

        // Clear the scheduled hook after the email is sent.
        wp_clear_scheduled_hook( 'gsc_data_send_email_event_' . $interval );
    }

    /**
     * Clear the cron event for GSC data email.
     */
    public function gsc_data_clear_cron_event() {
        wp_clear_scheduled_hook( 'gsc_data_send_email_event' );
    }
}

}`


Solution

  • After thoroughly studying the Google Site Kit mechanism for API requests, I found a workaround that ensures successful retrieval of Search Console data via Cron jobs.

    Solution Overview: Before sending the request, it's essential to perform a user access check to verify that the user has the necessary permissions for both Google Site Kit and the WordPress admin dashboard. This layer ensures that the system only retrieves Search Console data when the correct user permissions are in place.

    By setting up this pre-check, you can reliably pull the data and send it to an email via Cron.

    Here's the code snippet that works in the theme’s functions.php file:

        <?php
    // Add a custom interval of 5 minutes for cron jobs
    add_filter('cron_schedules', 'add_five_minute_cron_schedule');
    function add_five_minute_cron_schedule($schedules) {
        $schedules['five_minutes'] = array(
            'interval' => 300, // 300 seconds = 5 minutes
            'display' => __('Every 5 Minutes')
        );
        return $schedules;
    }
    
    // Schedule the event on theme/plugin activation
    add_action('wp', 'schedule_gsc_data_cron_if_not_scheduled');
    function schedule_gsc_data_cron_if_not_scheduled() {
        if (!wp_next_scheduled('fetch_gsc_data_cron_job')) {
            wp_schedule_event(time(), 'five_minutes', 'fetch_gsc_data_cron_job');
        }
    }
    
    // Define the function to get scheduled GSC event details
    function get_gsc_scheduled_event() {
        $event = wp_get_scheduled_event('fetch_gsc_data_cron_job');
    
        if ($event) {
            // Log details for debugging
            error_log('GSC data fetch cron job is scheduled.');
            error_log('Scheduled time: ' . date('Y-m-d H:i:s', $event->timestamp));
            error_log('Recurrence: ' . $event->schedule); // Recurrence pattern, e.g., 'five_minutes'
    
            // Return event data if needed elsewhere
            return $event;
        } else {
            error_log('No GSC data fetch cron job is scheduled.');
            return false;
        }
    }
    
    // Display event info in the admin dashboard
    add_action('admin_notices', 'display_gsc_scheduled_event_notice');
    function display_gsc_scheduled_event_notice() {
        $event = get_gsc_scheduled_event(); // Call the function here
    
        if ($event) {
            echo '<div class="notice notice-success"><p>Next GSC data fetch is scheduled for: ' . date('Y-m-d H:i:s', $event->timestamp) . '</p></div>';
        } else {
            echo '<div class="notice notice-error"><p>No GSC data fetch cron job is scheduled.</p></div>';
        }
    }
    
    // Clear the cron job on plugin/theme deactivation
    register_deactivation_hook(__FILE__, 'clear_gsc_data_cron');
    function clear_gsc_data_cron() {
        $timestamp = wp_next_scheduled('fetch_gsc_data_cron_job');
        if ($timestamp) {
            wp_unschedule_event($timestamp, 'fetch_gsc_data_cron_job');
        }
    }
    
    // Hook the function to the cron event
    add_action('fetch_gsc_data_cron_job', 'fetch_gsc_data_scheduled');
    
    // Fetch GSC data and send email every 5 minutes
    function fetch_gsc_data_scheduled() {
        // Switch to a user with sufficient permissions
        $admin_user = get_user_by('email', 'example@domain.com'); // Use your admin's email
        if ($admin_user) {
            wp_set_current_user($admin_user->ID);
        }
    
        $selected_keywords = ['wordpress maintenance services', 'wordpress support', 'wordpress seo', 'seo kit', 'seo repair kit'];
        
        if (!class_exists('Google\Site_Kit\Plugin')) {
            error_log('Google Site Kit is not available.');
            return;
        }
    
        // Get the Google Site Kit plugin instance
        $site_kit = \Google\Site_Kit\Plugin::instance();
        $context = $site_kit->context();
        $modules = new \Google\Site_Kit\Core\Modules\Modules($context);
        $search_console = $modules->get_module('search-console');
    
        // Check if Search Console is connected
        if (!$search_console->is_connected()) {
            error_log('Search Console is not connected.');
            return;
        }
    
        // Set the date range for fetching the data
        $end_date = date('Y-m-d');
        $start_date = date('Y-m-d', strtotime('-7 days')); // Fetch data for the last 7 days
    
        // Fetch search analytics data from Search Console
        $queries_data = $search_console->get_data('searchanalytics', array(
            'dimensions' => array('query'),
            'startDate' => $start_date,
            'endDate' => $end_date,
            'rowLimit' => 100,
        ));
    
        // Process the fetched data and send the email
        // Check if data is fetched
        if (empty($queries_data)) {
            
            error_log('No data fetched from GSC.');
            return;
        } else {
            
            error_log('Data fetched from GSC.');
            
            $email_subject = "GSC Data Results for Selected Keywords";
            $email_body = "Here are the fetched results for the specified keywords:\n\n";
    
            // Loop through the fetched data and filter by selected keywords
            foreach ($queries_data as $row) {
                $keyword = esc_html($row['keys'][0]); // Get the keyword from the data
                if (in_array($keyword, $selected_keywords)) {
                    $clicks = esc_html($row['clicks']);
                    $impressions = esc_html($row['impressions']);
                    // Append keyword data to the email body
                    $email_body .= "Keyword: $keyword, Clicks: $clicks, Impressions: $impressions\n";
                }
            }
    
            // Send the email with the results
            wp_mail('example@domain.com', $email_subject, $email_body);
            error_log('Email sent.');
        }
        wp_reset_current_user();
    }
    
    error_log('Google Site Kit Plugin class exists: ' . class_exists('Google\Site_Kit\Plugin'));
    
    // Schedule the cron job on plugin/theme activation
    register_activation_hook(__FILE__, 'schedule_gsc_data_cron_activation');
    function schedule_gsc_data_cron_activation() {
        if (!wp_next_scheduled('fetch_gsc_data_cron_job')) {
            wp_schedule_event(time(), 'five_minutes', 'fetch_gsc_data_cron_job');
        }
    }
    

    Feel free to adjust it according to your specific use case!