container = $container; } /** * Schedule the weekly digest cron event. * * @return void */ public function schedule(): void { if ( ! wp_next_scheduled( self::CRON_HOOK ) ) { wp_schedule_event( time(), 'weekly', self::CRON_HOOK ); } } /** * Unschedule the weekly digest. * * @return void */ public function unschedule(): void { $timestamp = wp_next_scheduled( self::CRON_HOOK ); if ( $timestamp ) { wp_unschedule_event( $timestamp, self::CRON_HOOK ); } } /** * Send the weekly digest email. * * @return void */ public function send(): void { if ( ! aicommerce_is_pro() ) { return; } $recipients = array( get_option( 'admin_email' ) ); $recipients = array_filter( array_unique( $recipients ) ); if ( empty( $recipients ) ) { return; } $plugin_name = WhiteLabel::get_plugin_name(); $site_name = get_bloginfo( 'name' ); $subject = sprintf( /* translators: 1: Plugin name 2: Site name */ __( '%1$s Weekly AI Visibility Report — %2$s', 'ai-discoverability-kit' ), $plugin_name, $site_name ); $body = $this->build_email_body( $plugin_name, $site_name ); // Set HTML content type. add_filter( 'wp_mail_content_type', array( $this, 'set_html_content_type' ) ); wp_mail( $recipients, $subject, $body ); // Remove HTML content type filter. remove_filter( 'wp_mail_content_type', array( $this, 'set_html_content_type' ) ); } /** * Set HTML content type for wp_mail. * * @return string */ public function set_html_content_type(): string { return 'text/html'; } /** * Build the HTML email body. * * @param string $plugin_name The plugin display name. * @param string $site_name The site name. * * @return string HTML email content. */ private function build_email_body( string $plugin_name, string $site_name ): string { $score_data = $this->get_score_data(); $issues = $this->get_top_issues( 3 ); $feeds = $this->get_feed_status(); $bot_visits = $this->get_bot_visits_7d(); $revenue = $this->get_revenue_7d(); $soa_data = $this->get_soa_data(); $agent_count = $this->get_agent_requests_7d(); $logo_url = WhiteLabel::get_logo_url(); $primary = get_option( 'aicommerce_wl_primary_color', '#00e58a' ); $dashboard_url = admin_url( 'admin.php?page=tyralix-aicommerce' ); $overall_score = $score_data['overall'] ?? 0; $trend = $score_data['trend'] ?? null; $trend_text = ''; if ( $trend && isset( $trend['delta'] ) ) { $delta = (int) $trend['delta']; $direction = $delta >= 0 ? '+' : ''; $trend_text = " ({$direction}{$delta} vs last week)"; } $html = ''; // Header. if ( $logo_url ) { $html .= '
' . esc_attr( $plugin_name ) . '
'; } $html .= '

' . esc_html( $plugin_name ) . '

'; $html .= '

Weekly AI Visibility Report for ' . esc_html( $site_name ) . '

'; $html .= '
'; // Overall score. $html .= '

Overall AI Readiness Score

'; $html .= '

' . (int) $overall_score . '/100' . esc_html( $trend_text ) . '

'; // Top issues. if ( ! empty( $issues ) ) { $html .= '

Top Issues to Fix

'; $html .= ''; } // Revenue (Business only). if ( null !== $revenue ) { $html .= '

AI-Attributed Revenue (7 days)

'; $html .= '

$' . number_format( $revenue['total_revenue'] ?? 0, 2 ) . '

'; $html .= '

' . (int) ( $revenue['order_count'] ?? 0 ) . ' orders

'; } // Share of Answer. if ( ! empty( $soa_data ) && ! empty( $soa_data['by_platform'] ) ) { $html .= '

Share of Answer This Week

'; $html .= ''; $html .= ''; foreach ( $soa_data['by_platform'] as $platform => $data ) { $change_color = ( $data['change'] ?? 0 ) >= 0 ? '#00a32a' : '#d63638'; $change_text = ( $data['change'] ?? 0 ) >= 0 ? '+' . $data['change'] : (string) $data['change']; $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; } $html .= '
PlatformSOA%Change
' . esc_html( ucfirst( $platform ) ) . '' . esc_html( $data['current'] . '%' ) . '' . esc_html( $change_text . '%' ) . '
'; } // MCP Agent activity (Business only). if ( $agent_count > 0 ) { $html .= '

MCP Agent Activity (7 days)

'; $html .= '

' . number_format( $agent_count ) . ' requests

'; } // Feed status. if ( ! empty( $feeds ) ) { $html .= '

Feed Status

'; $html .= ''; foreach ( $feeds as $name => $status ) { $html .= ''; $html .= ''; $html .= ''; $html .= ''; } $html .= '
' . esc_html( ucfirst( $name ) ) . '' . esc_html( $status ) . '
'; } // Bot visits. if ( $bot_visits > 0 ) { $html .= '

AI Bot Visits (7 days)

'; $html .= '

' . number_format( $bot_visits ) . '

'; } // CTA. $html .= '
'; $html .= 'View Full Dashboard'; $html .= '
'; // Footer. $html .= '
'; $html .= ''; return $html; } /** * Get current score data. * * @return array Score data array. */ private function get_score_data(): array { if ( ! $this->container->has( ReadinessScore::class ) ) { return array( 'overall' => 0 ); } /** @var ReadinessScore $score */ $score = $this->container->get( ReadinessScore::class ); return $score->calculate_store_score(); } /** * Get top N issues. * * @param int $count Number of issues to return. * * @return array Top issues. */ private function get_top_issues( int $count ): array { if ( ! $this->container->has( ReadinessScore::class ) ) { return array(); } /** @var ReadinessScore $score */ $score = $this->container->get( ReadinessScore::class ); $issues = $score->get_issues(); return array_slice( $issues, 0, $count ); } /** * Get feed generation status. * * @return array Feed name => last generated timestamp or 'Never'. */ private function get_feed_status(): array { $feeds = get_option( 'aicommerce_feed_types', array() ); $status = array(); if ( is_array( $feeds ) ) { foreach ( $feeds as $feed ) { $name = $feed['type'] ?? $feed; $last = get_option( "aicommerce_feed_{$name}_last_generated", '' ); $status[ $name ] = $last ?: __( 'Never', 'ai-discoverability-kit' ); } } return $status; } /** * Get bot visit count for the last 7 days. * * @return int Total visits. */ private function get_bot_visits_7d(): int { global $wpdb; $table = "{$wpdb->prefix}aicommerce_crawler_log"; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE request_time >= %s", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared gmdate( 'Y-m-d H:i:s', strtotime( '-7 days' ) ) ) ); return (int) $count; } /** * Get AI-attributed revenue for the last 7 days (Business tier only). * * @return array|null Revenue data or null if not Business tier. */ private function get_revenue_7d(): ?array { if ( ! function_exists( 'aicommerce_is_business' ) || ! aicommerce_is_business() ) { return null; } if ( ! $this->container->has( AttributionTracker::class ) ) { return null; } /** @var AttributionTracker $tracker */ $tracker = $this->container->get( AttributionTracker::class ); return $tracker->get_summary( 7 ); } /** * Get SOA data for the last 7 days. * * @return array SOA summary or empty array. */ private function get_soa_data(): array { if ( ! $this->container->has( SOATracker::class ) ) { return array(); } /** @var SOATracker $soa */ $soa = $this->container->get( SOATracker::class ); return $soa->get_soa_summary( 7 ); } /** * Get MCP agent request count for the last 7 days. * * @return int Request count. */ private function get_agent_requests_7d(): int { global $wpdb; $table = "{$wpdb->prefix}aicommerce_agent_log"; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE agent_platform = %s AND created_at >= %s", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 'mcp', gmdate( 'Y-m-d H:i:s', strtotime( '-7 days' ) ) ) ); return (int) $count; } }