= $max_per_hour) { $reset_time = (int) (get_transient($exp_key) ?: time()); $remaining = max(0, $reset_time - time()); $minutes = (int) ceil($remaining / 60); return sprintf(__('You have reached the hourly comment limit. Try again in %d minutes.', 'aksrt'), $minutes); } if ($hourly_count === false) { set_transient($hour_key, 1, HOUR_IN_SECONDS); set_transient($exp_key, time() + HOUR_IN_SECONDS, HOUR_IN_SECONDS); } else { set_transient($hour_key, ((int) $hourly_count) + 1, HOUR_IN_SECONDS); } set_transient($last_key, time(), $min_interval); return true; } // ============================================================ // 5. GeoIP // ============================================================ private function check_geoip(array $config, string $ip) { if (empty($config['geoip_enabled'])) { return true; } $block_countries = $config['geoip_block_countries'] ?? []; if (!is_array($block_countries) || empty($block_countries)) { return true; } $country_code = $this->lookup_geoip($ip, $config); if ($country_code === null) { return true; } $country_code = strtoupper($country_code); $block_upper = array_map('strtoupper', $block_countries); if (in_array($country_code, $block_upper, true)) { return sprintf(__('Comments from your region (%s) are currently blocked.', 'aksrt'), esc_html($country_code)); } return true; } private function lookup_geoip(string $ip, array $config): ?string { // Dedicated ISO country-code path for block lists (CN/US/…). // IPGeo returns localized names ("中国") which are not ISO codes. $provider = sanitize_key((string) ($config['geoip_provider'] ?? 'ipapi')); $api_key = (string) ($config['geoip_api_key'] ?? ''); if ($provider === 'ipinfo' || $provider === 'ipinfo.io') { $code = $this->geoip_ipinfo($ip, $api_key); if ($code !== null) { return $code; } } $code = $this->geoip_ipapi($ip); if ($code !== null) { return $code; } // Last resort: map IPGeo country label → ISO when possible. if (class_exists('\AKSRT\Extensions\IPGeo')) { $result = \AKSRT\Extensions\IPGeo::get_instance()->lookup($ip); $label = trim((string) ($result['country'] ?? '')); if ($label !== '') { return $this->country_label_to_iso($label); } } return null; } /** * Map common country labels / ISO aliases to ISO-3166 alpha-2. */ private function country_label_to_iso(string $label): ?string { $raw = trim($label); if ($raw === '') { return null; } if (preg_match('/^[A-Za-z]{2}$/', $raw)) { return strtoupper($raw); } $map = [ '中国' => 'CN', '中华人民共和国' => 'CN', 'china' => 'CN', 'cn' => 'CN', '美国' => 'US', '美利坚合众国' => 'US', 'united states' => 'US', 'usa' => 'US', 'us' => 'US', '日本' => 'JP', 'japan' => 'JP', 'jp' => 'JP', '韩国' => 'KR', '大韩民国' => 'KR', 'south korea' => 'KR', 'korea' => 'KR', 'kr' => 'KR', '香港' => 'HK', '中国香港' => 'HK', 'hong kong' => 'HK', 'hk' => 'HK', '澳门' => 'MO', '中国澳门' => 'MO', 'macao' => 'MO', 'macau' => 'MO', 'mo' => 'MO', '台湾' => 'TW', '中国台湾' => 'TW', 'taiwan' => 'TW', 'tw' => 'TW', '新加坡' => 'SG', 'singapore' => 'SG', 'sg' => 'SG', '英国' => 'GB', 'united kingdom' => 'GB', 'uk' => 'GB', 'gb' => 'GB', '德国' => 'DE', 'germany' => 'DE', 'de' => 'DE', '法国' => 'FR', 'france' => 'FR', 'fr' => 'FR', '俄罗斯' => 'RU', 'russia' => 'RU', 'ru' => 'RU', '澳大利亚' => 'AU', 'australia' => 'AU', 'au' => 'AU', '加拿大' => 'CA', 'canada' => 'CA', 'ca' => 'CA', '印度' => 'IN', 'india' => 'IN', 'in' => 'IN', '越南' => 'VN', 'vietnam' => 'VN', 'vn' => 'VN', '泰国' => 'TH', 'thailand' => 'TH', 'th' => 'TH', '马来西亚' => 'MY', 'malaysia' => 'MY', 'my' => 'MY', '菲律宾' => 'PH', 'philippines' => 'PH', 'ph' => 'PH', '印度尼西亚' => 'ID', 'indonesia' => 'ID', 'id' => 'ID', ]; $key = function_exists('mb_strtolower') ? mb_strtolower($raw, 'UTF-8') : strtolower($raw); if (isset($map[$raw])) { return $map[$raw]; } if (isset($map[$key])) { return $map[$key]; } return null; } /** * Free ip-api.com endpoint. */ private function geoip_ipapi(string $ip): ?string { $url = 'http://ip-api.com/json/' . urlencode($ip) . '?fields=status,countryCode,message'; $response = wp_remote_get($url, [ 'timeout' => 5, 'headers' => ['Accept' => 'application/json'], ]); if (is_wp_error($response)) { return null; } $body = json_decode((string) wp_remote_retrieve_body($response), true); if (($body['status'] ?? '') === 'success') { return $body['countryCode'] ?? null; } return null; } private function geoip_ipinfo(string $ip, string $api_key): ?string { $url = 'https://ipinfo.io/' . urlencode($ip) . '/json?token=' . urlencode($api_key ?: ''); $response = wp_remote_get($url, [ 'timeout' => 5, 'headers' => ['Accept' => 'application/json'], ]); if (is_wp_error($response)) { return null; } $body = json_decode((string) wp_remote_retrieve_body($response), true); return $body['country'] ?? null; } // ============================================================ // Utilities // ============================================================ private function get_client_ip(): string { if (function_exists('aksrt_get_client_ip')) { return (string) aksrt_get_client_ip(); } $keys = [ 'HTTP_CF_CONNECTING_IP', 'HTTP_TRUE_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'HTTP_CLIENT_IP', ]; foreach ($keys as $key) { if (!empty($_SERVER[$key])) { $parts = explode(',', (string) $_SERVER[$key]); $ip = trim($parts[0]); if (filter_var($ip, FILTER_VALIDATE_IP)) { return $ip; } } } return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'; } }