|null */ public static function build(string $ticketType, string $pageUrl): ?array { $enabledTypes = self::getEnabledTypes(); if (!in_array($ticketType, $enabledTypes, true)) { return null; } $sessionHost = self::resolveSessionHost($ticketType, $pageUrl); if ($sessionHost === '') { return null; } $sessionKey = $ticketType . ':' . $sessionHost; $ttlMinutes = self::getSessionTtlMinutes(); return [ 'enabled' => true, 'profile' => 'real', 'turnstile' => self::defaultTurnstile($ticketType), 'solverFallback' => self::defaultSolverFallback($ticketType), 'cfClearanceRequired'=> self::defaultCfClearanceRequired($ticketType), 'sessionKey' => $sessionKey, 'challengeTimeoutMs' => 60000, 'sessionTtlMinutes' => $ttlMinutes, 'businessHostHint' => self::defaultBusinessHostHint($ticketType, $sessionHost), ] + self::postCfReadyExtras($ticketType); } /** * 会话域:A2C 短链(yyk.ink)仍用业务域 user.a2c.chat 复用 Real Browser 会话 */ public static function resolveSessionHost(string $ticketType, string $pageUrl): string { $host = strtolower(trim((string) parse_url($pageUrl, PHP_URL_HOST))); if ($host === '') { return ''; } if ($ticketType === 'a2c') { if (strpos($host, 'a2c.chat') !== false) { return $host; } return 'user.a2c.chat'; } return $host; } /** * 解析 sessionKey(供 cron 分组调度使用) */ public static function resolveSessionKey(string $ticketType, string $pageUrl): string { $host = self::resolveSessionHost($ticketType, $pageUrl); return $ticketType . ':' . ($host !== '' ? $host : 'unknown'); } /** * 是否对该工单类型启用 antiBot */ public static function isEnabledForType(string $ticketType): bool { return in_array($ticketType, self::getEnabledTypes(), true); } /** * @return list */ public static function getEnabledTypes(): array { $raw = (string) Config::get('site.split_scrm_antibot_types'); if ($raw === '') { return ['whatshub', 'chatknow']; } $parts = array_map('trim', explode(',', $raw)); return array_values(array_filter($parts, static function (string $v): bool { return $v !== ''; })); } public static function getSessionTtlMinutes(): int { $minutes = (int) Config::get('site.split_scrm_session_ttl_minutes'); return $minutes > 0 ? $minutes : 120; } /** * 按工单类型附加 CF 过盾后的业务页就绪等待 * * @return array */ private static function postCfReadyExtras(string $ticketType): array { if ($ticketType === 'whatshub') { return [ 'postCfReadyUrlContains' => 'work-order-sharing', 'postCfReadySelector' => '.el-input__inner', 'postCfReadyTimeoutMs' => 30000, 'postCfSettleMs' => 500, 'postCfSpaWaitMs' => 4000, ]; } if ($ticketType === 'a2c') { return [ 'postCfReadyUrlContains' => '/visitors/counter/share', 'postCfReadySelector' => '.btn-next', 'postCfReadyTimeoutMs' => 30000, 'postCfSettleMs' => 500, 'postCfSpaWaitMs' => 4000, ]; } return []; } private static function defaultTurnstile(string $ticketType): bool { return true; } private static function defaultSolverFallback(string $ticketType): bool { if ($ticketType === 'a2c') { return false; } return true; } private static function defaultCfClearanceRequired(string $ticketType): bool { if ($ticketType === 'a2c') { return false; } return true; } private static function defaultBusinessHostHint(string $ticketType, string $sessionHost): string { if ($ticketType === 'a2c') { return $sessionHost; } return ''; } }