LINUX.DO CloudFlare Challenge Bypass

Automatically redirects to the challenge page when CloudFlare protection fails, improving browsing experience on linux.do

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name                 LINUX.DO CloudFlare Challenge Bypass
// @name:zh-CN           LINUX.DO CloudFlare 5秒盾自动跳转
// @namespace            https://github.com/utags
// @homepageURL          https://github.com/utags/userscripts#readme
// @supportURL           https://github.com/utags/userscripts/issues
// @version              0.3.2
// @description          Automatically redirects to the challenge page when CloudFlare protection fails, improving browsing experience on linux.do
// @description:zh-CN    当 CloudFlare 5秒盾检测失败时,自动跳转到 challenge 页面,提升 linux.do 的浏览体验
// @author               Pipecraft
// @license              MIT
// @match                https://linux.do/*
// @icon                 https://wsrv.nl/?w=64&h=64&url=https%3A%2F%2Ft3.gstatic.com%2FfaviconV2%3Fclient%3DSOCIAL%26type%3DFAVICON%26fallback_opts%3DTYPE%2CSIZE%2CURL%26url%3Dhttps%3A%2F%2Flinux.do%26size%3D64
// @grant                GM_registerMenuCommand
// ==/UserScript==
//
;(() => {
  'use strict'
  var CONFIG = {
    ERROR_TEXTS: [
      '403 error',
      '\u8BE5\u56DE\u5E94\u662F\u5F88\u4E45\u4EE5\u524D\u521B\u5EFA\u7684',
      'reaction was created too long ago',
      '\u6211\u4EEC\u65E0\u6CD5\u52A0\u8F7D\u8BE5\u8BDD\u9898',
      'You are not allowed to react',
    ],
    DIALOG_SELECTOR: '.dialog-body',
    CHALLENGE_PATH: '/challenge',
    DEBUG: false,
    MENU_TEXT: '\u624B\u52A8\u89E6\u53D1 Challenge \u8DF3\u8F6C',
  }
  var log = (...args) => {
    if (CONFIG.DEBUG) {
      console.log('[LINUX.DO Auto Challenge]', ...args)
    }
  }
  function isChallengePage() {
    return globalThis.location.pathname.startsWith(CONFIG.CHALLENGE_PATH)
  }
  function isNotFoundPage() {
    return Boolean(document.querySelector('.page-not-found'))
  }
  function getRedirectParamUrl() {
    try {
      const sp = new URLSearchParams(globalThis.location.search)
      const raw = sp.get('redirect')
      if (!raw) return void 0
      const url = new URL(raw, globalThis.location.origin)
      if (url.origin !== globalThis.location.origin) return void 0
      return url.href
    } catch (e) {
      return void 0
    }
  }
  var NOT_FOUND_REDIRECT_GUARD_KEY = 'linux_do_auto_challenge_nf_guard'
  function getNotFoundRedirectGuardTs() {
    try {
      const raw = sessionStorage.getItem(NOT_FOUND_REDIRECT_GUARD_KEY)
      const n = raw ? Number(raw) : 0
      return Number.isFinite(n) ? n : 0
    } catch (e) {
      return 0
    }
  }
  function setNotFoundRedirectGuardTs(ts) {
    try {
      sessionStorage.setItem(NOT_FOUND_REDIRECT_GUARD_KEY, String(ts))
    } catch (e) {}
  }
  function redirectFromNotFoundPage() {
    const fallback = ''.concat(globalThis.location.origin, '/')
    const target = getRedirectParamUrl() || fallback
    const now = Date.now()
    const guardTs = getNotFoundRedirectGuardTs()
    if (guardTs && now - guardTs < 5e3) {
      return
    }
    setNotFoundRedirectGuardTs(now)
    if (target === globalThis.location.href) {
      globalThis.location.replace(fallback)
      return
    }
    globalThis.location.replace(target)
  }
  function isChallengeFailure() {
    if (isChallengePage()) {
      return false
    }
    try {
      const dialogElement = document.querySelector(CONFIG.DIALOG_SELECTOR)
      if (!dialogElement) return false
      const text = dialogElement.textContent || ''
      return CONFIG.ERROR_TEXTS.some((errorText) => text.includes(errorText))
    } catch (error) {
      log('\u68C0\u6D4B\u5931\u8D25\u9875\u9762\u65F6\u51FA\u9519:', error)
      return false
    }
  }
  function redirectToChallenge() {
    try {
      if (isChallengePage()) return
      const redirectUrl = ''
        .concat(CONFIG.CHALLENGE_PATH, '?redirect=')
        .concat(encodeURIComponent(globalThis.location.href))
      log('\u91CD\u5B9A\u5411\u5230:', redirectUrl)
      globalThis.location.href = redirectUrl
    } catch (error) {
      log('\u91CD\u5B9A\u5411\u65F6\u51FA\u9519:', error)
    }
  }
  function checkAndRedirect(observer) {
    if (isChallengeFailure()) {
      if (observer) observer.disconnect()
      redirectToChallenge()
      return true
    }
    return false
  }
  function manualTrigger() {
    log('\u624B\u52A8\u89E6\u53D1 Challenge \u8DF3\u8F6C')
    if (isChallengePage()) {
      alert('\u5DF2\u5728 Challenge \u9875\u9762\uFF0C\u65E0\u9700\u8DF3\u8F6C')
      return
    }
    redirectToChallenge()
  }
  function initScript() {
    log('\u521D\u59CB\u5316\u811A\u672C')
    if (isChallengePage()) {
      if (isNotFoundPage()) {
        redirectFromNotFoundPage()
        return
      }
      log(
        '\u5DF2\u5728 challenge \u9875\u9762\uFF0C\u4E0D\u6267\u884C\u811A\u672C'
      )
      return
    }
    if (checkAndRedirect()) return
    try {
      const observer = new MutationObserver((mutations, obs) => {
        if (isChallengePage()) {
          if (isNotFoundPage()) {
            redirectFromNotFoundPage()
          }
        } else {
          checkAndRedirect(obs)
        }
      })
      observer.observe(document.body, {
        childList: true,
        subtree: true,
        characterData: true,
      })
      log('DOM \u89C2\u5BDF\u5668\u5DF2\u542F\u52A8')
    } catch (error) {
      log('\u542F\u52A8 DOM \u89C2\u5BDF\u5668\u65F6\u51FA\u9519:', error)
    }
    try {
      GM_registerMenuCommand(CONFIG.MENU_TEXT, manualTrigger)
      log('\u83DC\u5355\u547D\u4EE4\u5DF2\u6CE8\u518C')
    } catch (error) {
      log('\u6CE8\u518C\u83DC\u5355\u547D\u4EE4\u65F6\u51FA\u9519:', error)
    }
  }
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initScript)
  } else {
    initScript()
  }
})()