chore: initial snapshot of 2meet-data-optimizer v0.1.0

Baseline before backporting wp-data-optimizer v3.0.1-v3.4.6.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
This commit is contained in:
2026-07-31 05:06:36 +08:00
commit d36bb954d1
206 changed files with 66538 additions and 0 deletions
@@ -0,0 +1,249 @@
<?php // phpcs:ignore Squiz.Classes.ClassFileName.NoMatch -- Abstract class; filename intentionally uses 'abstract-class-' prefix.
/**
* TMDO_Abstract_Notifier — Base for all health-alert notifiers (v2.5.0 M15).
*
* Subclasses (Email / Slack / Telegram / Discord) inherit:
* - wpdo/health_alert_critical action subscription
* - opt-in toggle (`wpdo_{channel}_enabled`)
* - 24h-default throttle keyed by md5 fingerprint of summary
* - shared subject / body builders (overridable)
* - severity subscription (critical_only / critical_and_recommended)
*
* Subclass只 implements `actually_send( $subject, $body, $context ): bool`
* and `channel_id(): string` (used as option-key prefix).
*
* @package WP_Data_Optimizer
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Abstract base — concrete subclasses below.
*/
abstract class TMDO_Abstract_Notifier {
/** Default throttle window. */
public const DEFAULT_THROTTLE_HOURS = 24;
/**
* Channel ID — used as option key prefix (e.g. 'email', 'slack').
*
* @return string
*/
abstract public static function channel_id(): string;
/**
* Channel-specific delivery. Implementations should return false on
* transient failure so caller can re-throttle attempts.
*
* @param string $subject Subject / title (Slack uses as text-prefix).
* @param string $body Body text (channel-specific markdown).
* @param array $context Optional context (recipient/webhook URL/etc.).
* @return bool
*/
abstract public static function actually_send( string $subject, string $body, array $context ): bool;
/**
* Hook subclass into `wpdo/health_alert_critical`.
*
* @return void
*/
public static function register(): void {
add_action( 'wpdo/health_alert_critical', array( static::class, 'maybe_send' ), 10, 1 );
}
/**
* Decide whether to send + throttle + dispatch.
*
* @param array $summary Summary array from Health_Cron::run().
* @return bool
*/
public static function maybe_send( array $summary ): bool {
if ( ! static::is_enabled() ) {
return false;
}
// Severity subscription check.
$severity_filter = static::severity_filter();
if ( 'critical_only' === $severity_filter && (int) ( $summary['critical_count'] ?? 0 ) === 0 ) {
return false;
}
// Channel-specific recipient/config sanity check.
$context = static::build_context();
if ( null === $context ) {
return false;
}
// Throttle.
$fingerprint = static::fingerprint( $summary );
if ( static::is_throttled( $fingerprint ) ) {
return false;
}
// Build + send.
$subject = static::build_subject( $summary );
$body = static::build_body( $summary );
$sent = static::actually_send( $subject, $body, $context );
if ( $sent ) {
static::mark_sent( $fingerprint );
if ( class_exists( 'TMDO_Logger' ) ) {
TMDO_Logger::info(
sprintf( '%s_alert_sent', static::channel_id() ),
array(
'fingerprint' => $fingerprint,
'critical' => (int) ( $summary['critical_count'] ?? 0 ),
)
);
}
}
return $sent;
}
// ─── settings accessorsoptional override)────────────────────────
/**
* Whether this notification channel is enabled.
*
* @return bool
*/
public static function is_enabled(): bool {
return '1' === (string) get_option( static::option_key( 'enabled' ), '0' );
}
/**
* Throttle window in hours (clamped 1..168).
*
* @return int
*/
public static function throttle_hours(): int {
$v = (int) get_option( static::option_key( 'throttle_hours' ), self::DEFAULT_THROTTLE_HOURS );
return max( 1, min( 168, $v ) );
}
/**
* Severity subscription filter.
*
* @return string 'critical_only' or 'critical_and_recommended'
*/
public static function severity_filter(): string {
$v = (string) get_option( static::option_key( 'severity' ), 'critical_only' );
return in_array( $v, array( 'critical_only', 'critical_and_recommended' ), true ) ? $v : 'critical_only';
}
/**
* Build channel-specific context (e.g. webhook URL or recipient address).
* Return null to skip send (e.g. invalid email or empty webhook).
*
* @return array|null
*/
public static function build_context(): ?array {
return array();
}
// ─── shared subject / body builders ───────────────────────────────
/**
* Build alert subject line.
*
* @param array $summary Summary array from Health_Cron::run().
* @return string
*/
public static function build_subject( array $summary ): string {
$site = (string) get_option( 'blogname', 'WordPress' );
$crit = (int) ( $summary['critical_count'] ?? 0 );
$first = '';
foreach ( (array) ( $summary['tests'] ?? array() ) as $slug => $t ) {
if ( 'critical' === ( $t['status'] ?? '' ) ) {
$first = $slug;
break;
}
}
return sprintf( '[%s] WPDO 警告:%d 項 critical (%s)', $site, $crit, $first );
}
/**
* Generic plain-text body. Subclasses can override to add channel-specific
* formatting (Slack mrkdwn, Discord markdown, Telegram MarkdownV2, etc.).
*
* @param array $summary Summary.
* @return string
*/
public static function build_body( array $summary ): string {
$lines = array();
$lines[] = sprintf( '站台:%s', home_url( '/' ) );
$lines[] = sprintf( '檢查時間:%s UTC', (string) ( $summary['ran_at'] ?? '?' ) );
$lines[] = sprintf(
'結果:%d critical / %d recommended',
(int) ( $summary['critical_count'] ?? 0 ),
(int) ( $summary['recommended_count'] ?? 0 )
);
$lines[] = '';
$lines[] = '失敗的檢查:';
foreach ( (array) ( $summary['tests'] ?? array() ) as $slug => $t ) {
if ( in_array( ( $t['status'] ?? '' ), array( 'critical', 'recommended' ), true ) ) {
$lines[] = sprintf(
' [%s] %s — %s',
strtoupper( (string) ( $t['status'] ?? '' ) ),
$slug,
(string) ( $t['description'] ?? '' )
);
}
}
$lines[] = '';
$lines[] = '查看詳情:' . admin_url( 'tools.php?page=wp-data-optimizer&tab=doctor' );
return implode( "\n", $lines );
}
// ─── private helpers ─────────────────────────────────────────────
/**
* Per-channel option key (e.g. wpdo_email_enabled, wpdo_slack_webhook).
*
* @param string $field 'enabled' / 'throttle_hours' / 'severity' / etc.
* @return string
*/
protected static function option_key( string $field ): string {
return sprintf( 'wpdo_%s_%s', static::channel_id(), $field );
}
/**
* Stable fingerprint from the alert content for deduplication.
*
* @param array $summary Summary array from Health_Cron::run().
* @return string md5 hash.
*/
protected static function fingerprint( array $summary ): string {
$relevant = array(
'critical_count' => (int) ( $summary['critical_count'] ?? 0 ),
'first_critical' => null,
);
foreach ( (array) ( $summary['tests'] ?? array() ) as $slug => $t ) {
if ( 'critical' === ( $t['status'] ?? '' ) ) {
$relevant['first_critical'] = $slug;
break;
}
}
return md5( wp_json_encode( $relevant ) );
}
/**
* Check whether an alert with this fingerprint was recently sent.
*
* @param string $fingerprint Alert fingerprint (md5).
* @return bool
*/
protected static function is_throttled( string $fingerprint ): bool {
$key = sprintf( 'wpdo_%s_sent_%s', static::channel_id(), $fingerprint );
return false !== get_transient( $key );
}
/**
* Record that an alert was sent (sets throttle transient).
*
* @param string $fingerprint Alert fingerprint (md5).
* @return void
*/
protected static function mark_sent( string $fingerprint ): void {
$key = sprintf( 'wpdo_%s_sent_%s', static::channel_id(), $fingerprint );
set_transient( $key, time(), static::throttle_hours() * HOUR_IN_SECONDS );
}
}