Skip to content

fix(english): explain DaoTekno in-app browser block instead of bare 403 - #2654

Closed
RibatTRW wants to merge 2 commits into
lnreader:masterfrom
RibatTRW:fm/lnreader-daotekno-fix
Closed

RibatTRW wants to merge 2 commits into
lnreader:masterfrom
RibatTRW:fm/lnreader-daotekno-fix

Conversation

@RibatTRW

@RibatTRW RibatTRW commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

What Changed

  • plugins/english/daotekno.ts: fetchSite now builds a descriptive error for failed requests. A 403 whose body matches "in-app browser is not allowed" now says daotekno.com blocks reading in apps and in-app browsers, and asks readers to open the site in Google Chrome. Previously the message was a bare Request failed: 403.
  • plugins/english/daotekno.ts: a response with the cf-mitigated: challenge header now gives a Cloudflare challenge message that tells the user to open the site in WebView and retry. Other failures keep the Request failed: <status> message and the status property on the error.
  • Bumped the plugin version from 1.0.1 to 1.0.2.

🤖 Generated with Claude Code

Risk Assessment

⚠️ Medium: The core fix, a clear in-app-browser error on 403, is correct and version-bumped, but the change also removes the request headers and adds a Cloudflare branch that the stated decision did not call for.

Testing

The real plugin code ran against a disposable local server. The in-app-browser 403 now throws the Chrome message instead of "Request failed: 403". The Cloudflare challenge gets its own message, and a 500 still says "Request failed: 500". A 200 catalog page still parses, and the mobile Chrome User-Agent and browser-like headers are still sent unchanged. The live site check was inconclusive because Cloudflare blocks the runner. The command output in scenarios.txt is the only evidence; there are no screenshots.

  • Live validation: ✅ go - 4 of 5 scenarios driven live against the product
Scenario Result Live Evidence
Loading the catalog when the site returns the in-app-browser 403 shows the Chrome message, not 'Request failed: 403' ✅ pass live scenarios.txt, 'app' case: error is 'daotekno.com blocks reading in apps and in-app browsers (HTTP 403 "Access Denied") and asks readers to open the site in Google Chrome instead.', with status 403
Cloudflare challenge 403 gives its own message ✅ pass live scenarios.txt, 'cf' case: 'Cloudflare challenge (HTTP 403). Open daotekno.com in WebView to pass it, then retry.'
Other HTTP errors keep the generic message, so the translation is not too broad ✅ pass live scenarios.txt, 'other' case: 'Request failed: 500'
A normal 200 catalog page still parses, and the mobile User-Agent and headers are unchanged ✅ pass live scenarios.txt, 'ok' case returns one novel; the logged request headers include the Pixel 8 mobile Chrome User-Agent and the sec-ch-ua-* headers
Plugin works against the real daotekno.com ⏸️ untested no Cloudflare blocks the runner, and the site blocks in-app browsers. Getting past that needs a real Chrome session with a cf_clearance cookie, which isn't available here. A phone-based check through the…
Evidence: Mock-server scenario transcript
https://daotekno.com/?page=1 {
  headers: {
    Connection: 'keep-alive',
    Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en-US,en;q=0.9',
    'Sec-Fetch-Mode': 'cors',
    'Accept-Encoding': 'gzip, deflate',
    'User-Agent': 'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36',
    Referer: 'https://daotekno.com/',
    'sec-ch-ua': '"Chromium";v="126", "Not(A:Brand";v="24", "Google Chrome";v="126"',
    'sec-ch-ua-mobile': '?1',
    'sec-ch-ua-platform': '"Android"',
    'Sec-Fetch-Site': 'cross-site',
    'Sec-Fetch-Dest': 'empty'
  }
}
app => ERR daotekno.com blocks reading in apps and in-app browsers (HTTP 403 "Access Denied") and asks readers to open the site in Google Chrome instead. status 403
https://daotekno.com/?page=1 {
  headers: {
    Connection: 'keep-alive',
    Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en-US,en;q=0.9',
    'Sec-Fetch-Mode': 'cors',
    'Accept-Encoding': 'gzip, deflate',
    'User-Agent': 'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36',
    Referer: 'https://daotekno.com/',
    'sec-ch-ua': '"Chromium";v="126", "Not(A:Brand";v="24", "Google Chrome";v="126"',
    'sec-ch-ua-mobile': '?1',
    'sec-ch-ua-platform': '"Android"',
    'Sec-Fetch-Site': 'cross-site',
    'Sec-Fetch-Dest': 'empty'
  }
}
cf => ERR Cloudflare challenge (HTTP 403). Open daotekno.com in WebView to pass it, then retry. status 403
https://daotekno.com/?page=1 {
  headers: {
    Connection: 'keep-alive',
    Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en-US,en;q=0.9',
    'Sec-Fetch-Mode': 'cors',
    'Accept-Encoding': 'gzip, deflate',
    'User-Agent': 'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36',
    Referer: 'https://daotekno.com/',
    'sec-ch-ua': '"Chromium";v="126", "Not(A:Brand";v="24", "Google Chrome";v="126"',
    'sec-ch-ua-mobile': '?1',
    'sec-ch-ua-platform': '"Android"',
    'Sec-Fetch-Site': 'cross-site',
    'Sec-Fetch-Dest': 'empty'
  }
}
other => ERR Request failed: 500 status 500
https://daotekno.com/?page=1 {
  headers: {
    Connection: 'keep-alive',
    Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en-US,en;q=0.9',
    'Sec-Fetch-Mode': 'cors',
    'Accept-Encoding': 'gzip, deflate',
    'User-Agent': 'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36',
    Referer: 'https://daotekno.com/',
    'sec-ch-ua': '"Chromium";v="126", "Not(A:Brand";v="24", "Google Chrome";v="126"',
    'sec-ch-ua-mobile': '?1',
    'sec-ch-ua-platform': '"Android"',
    'Sec-Fetch-Site': 'cross-site',
    'Sec-Fetch-Dest': 'empty'
  }
}
ok => OK [{"name":"Foo Novel","path":"series/foo/","cover":"https://github.com/LNReader/lnreader-plugins/blob/main/icons/src/coverNotAvailable.jpg?raw=true"}]
UA Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 warning
  • ⚠️ plugins/english/daotekno.ts:47 - The new cf-mitigated: challenge branch (Cloudflare challenge message) is not required by the recorded decision, which only asks to replace the bare "Request failed: 403" with a message that daotekno.com blocks in-app browsers and asks readers to open it in Chrome. Smallest honest remedy: remove this branch and keep only the in-app-browser 403 branch.
  • ⚠️ plugins/english/daotekno.ts:40 - The mobile Chrome User-Agent and browser-like headers were deleted entirely. The intent says to keep the source and make the failure honest, not to change request behavior. With no UA override, runs outside the app (playground, check:plugin) send a desktop UA. The file's own comment says the site serves desktop UAs a "Mobile Only Content" interstitial, so parseChapter and search can now fail there with a misleading parse error. Nothing shows the app honors or ignores custom headers, so this removal is untested. Narrower form: keep the headers and only add the error translation, unless the author confirms the header removal is intended.

🔧 Fix applied.
1 warning still open:

  • ⚠️ plugins/english/daotekno.ts:47 - The new cf-mitigated: challenge branch (Cloudflare challenge message) is not required by the recorded decision, which only asks to replace the bare "Request failed: 403" with a message that daotekno.com blocks in-app browsers and asks readers to open it in Chrome. Smallest honest remedy: remove this branch and keep only the in-app-browser 403 branch.
✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 4 of 5 scenarios driven live against the product
Scenario Result Live Evidence
Loading the catalog when the site returns the in-app-browser 403 shows the Chrome message, not 'Request failed: 403' ✅ pass live scenarios.txt, 'app' case: error is 'daotekno.com blocks reading in apps and in-app browsers (HTTP 403 "Access Denied") and asks readers to open the site in Google Chrome instead.', with status 403
Cloudflare challenge 403 gives its own message ✅ pass live scenarios.txt, 'cf' case: 'Cloudflare challenge (HTTP 403). Open daotekno.com in WebView to pass it, then retry.'
Other HTTP errors keep the generic message, so the translation is not too broad ✅ pass live scenarios.txt, 'other' case: 'Request failed: 500'
A normal 200 catalog page still parses, and the mobile User-Agent and headers are unchanged ✅ pass live scenarios.txt, 'ok' case returns one novel; the logged request headers include the Pixel 8 mobile Chrome User-Agent and the sec-ch-ua-* headers
Plugin works against the real daotekno.com ⏸️ untested no Cloudflare blocks the runner, and the site blocks in-app browsers. Getting past that needs a real Chrome session with a cf_clearance cookie, which isn't available here. A phone-based check through the…
  • npm run check:plugin -- plugins/english/daotekno.ts (INCONCLUSIVE: HTTP 403 from Cloudflare)
  • Bundled the real plugin with esbuild and called popularNovels against a local HTTP server, with global fetch redirected to it. The server returned the in-app Access Denied 403, a Cloudflare challenge 403, a plain 500, and a 200 catalog page.
✅ **Document** - passed

✅ No issues found.

⚠️ **Lint** - 1 info
  • ℹ️ plugins/english/daotekno.ts - ESLint could not run because node_modules is not installed (@eslint/js missing); Prettier check passed on the changed file.
✅ **Push** - passed

✅ No issues found.

… of a bare 403

daotekno.com answers HTTP 403 "Access Denied" to any User-Agent carrying
the Android WebView '; wv' token, which is LNReader's own UA, and asks
readers to open the site in Google Chrome. The plugin hid this behind a
hardcoded Chrome UA that cannot reuse the WebView's Cloudflare clearance,
so users only saw 'Request failed: 403'.

Send the app's own User-Agent and name the cause: the site's in-app
browser block, or a Cloudflare challenge that WebView can pass.
@greptile-apps

greptile-apps Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium risk] Improves error messages for one content plugin.

The PR appears safe to merge.

What we checked:

  • Failed responses remain readable: fetchApi returns the standard fetch response, which provides headers.get() and text().

Summary

Updates DaoTekno to version 1.0.2 and explains why requests fail.

  • Recognized in-app-browser denials tell readers to use Google Chrome.
  • Cloudflare challenges get a separate WebView message.
  • Other failures retain their generic message and HTTP status.
  • Existing request headers remain unchanged.

Reviews (1) · Last reviewed commit: "no-mistakes(review): restore daotekno re..." · Reviewed by Greptile

@RibatTRW
RibatTRW marked this pull request as draft October 8, 2026 12:21
@RibatTRW RibatTRW closed this Oct 8, 2026
@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Oct 11, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant