From 33553729f8c8053556ab811c84ac1f2d7430fd9b Mon Sep 17 00:00:00 2001 From: goon-foss Date: Wed, 22 Jul 2026 11:25:11 +0200 Subject: [PATCH] feat(diag): sxyprn resolve reason in bug-report auto-context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'sxyprn ładuje reklamy' jest niereprodukowalny z naszych IP (strona zwraca data-vnfo → resolver OK) → podejrzenie region/IP-gate u usera. resolveSxyprnPage zapamiętuje powód ostatniego resolve (http_ / challenge_ / no_vnfo_ / post_not_found / vnfo_parse_err / ok_N) i BugReportFAB dokleja go jako sxyprn= do auto-contextu. Przy kolejnym zgłoszeniu zobaczymy CO sxyprn realnie zwrócił temu userowi, bez pytania. Non-behavioral (nie rusza resolve). --- mobile/src/components/BugReportFAB.tsx | 5 +++ mobile/src/lib/sxyprnResolver.ts | 43 ++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/mobile/src/components/BugReportFAB.tsx b/mobile/src/components/BugReportFAB.tsx index dd3874f..a09e437 100644 --- a/mobile/src/components/BugReportFAB.tsx +++ b/mobile/src/components/BugReportFAB.tsx @@ -35,6 +35,7 @@ import { captureScreen } from 'react-native-view-shot'; import { GoonClient } from '../api'; import { theme } from '../theme'; import { formatLastPlayback } from '../lib/lastPlayback'; +import { getSxyprnDiag } from '../lib/sxyprnResolver'; interface Props { client: GoonClient | null; @@ -200,6 +201,10 @@ export function BugReportFAB({ client, appVersion, navRef }: Props) { // się rozjeżdża", bo mówi KTÓRE źródło i gdzie, niezależnie od ekranu zgłoszenia. const play = formatLastPlayback(); if (play) extraContext.push(`play=${play}`); + // Diagnostyka sxyprn "ładuje reklamy" (niereprodukowalny z naszych IP → region-gate?). + // Powód ostatniego phone-resolve sxyprn (http status / challenge / brak vnfo / ok_N). + const sxy = getSxyprnDiag(); + if (sxy) extraContext.push(`sxyprn=${sxy}`); const sceneId = rawSceneId && /^[0-9a-f-]{36}$/.test(rawSceneId) ? rawSceneId : null; const finalMessage = extraContext.length > 0 diff --git a/mobile/src/lib/sxyprnResolver.ts b/mobile/src/lib/sxyprnResolver.ts index 2f0404a..d8b9200 100644 --- a/mobile/src/lib/sxyprnResolver.ts +++ b/mobile/src/lib/sxyprnResolver.ts @@ -24,6 +24,22 @@ export function isSxyprnUrl(url: string | null | undefined): boolean { return !!url && /(?:^|\/\/|\.)sxyprn\.com\//i.test(url); } +// Diagnostyka: bug-report "sxyprn ładuje reklamy zamiast wideo" jest NIEreprodukowalny +// z naszych IP (strona zwraca data-vnfo → resolver działa), więc podejrzenie na +// region/IP-gate po stronie usera. Zapamiętujemy powód OSTATNIEGO resolve i doklejamy go +// do auto-contextu zgłoszenia (BugReportFAB) — przy kolejnym raporcie zobaczymy CO sxyprn +// realnie zwrócił temu userowi (http status / challenge / brak vnfo / ok_N), bez pytania. +let _lastDiag: { at: number; reason: string } | null = null; +function _diag(reason: string): void { + _lastDiag = { at: Date.now(), reason }; +} +/** Powód ostatniego resolve sxyprn + wiek w sekundach (null gdy nic jeszcze). */ +export function getSxyprnDiag(): string | null { + if (!_lastDiag) return null; + const ageS = Math.round((Date.now() - _lastDiag.at) / 1000); + return `${_lastDiag.reason} ${ageS}s ago`; +} + /** Suma cyfr w stringu (ssut51 z main2.js). */ function ssut51(s: string): number { let sum = 0; @@ -66,18 +82,38 @@ export async function resolveSxyprnPage(pageUrl: string): Promise let html: string; try { const r = await fetch(pageUrl, { headers: { 'User-Agent': UA, Accept: 'text/html' } }); - if (!r.ok) return []; + if (!r.ok) { + _diag(`http_${r.status}`); + return []; + } html = await r.text(); } catch { + _diag('fetch_error'); + return []; + } + // Challenge / ad-gate: gdy user dostaje CF/turnstile albo interstitial zamiast strony + // z playerem, resolve pada i coś w fallbacku pokazuje reklamę. To najbardziej + // podejrzana ścieżka dla "ładuje reklamy" (region/IP-specific). + if (/cf-challenge|challenge-platform|turnstile|Just a moment|Attention Required|Checking your browser/i.test(html)) { + _diag(`challenge_${html.length}b`); + return []; + } + if (html.includes('Post Not Found')) { + _diag('post_not_found'); // usunięty post return []; } - if (html.includes('Post Not Found')) return []; // usunięty post const m = _VNFO_RE.exec(html); - if (!m) return []; + if (!m) { + // Brak data-vnfo: albo zmiana struktury, albo user dostał inną stronę (ad-gate). + // Długość HTML rozróżnia "pełna strona bez vnfo" od "krótki interstitial". + _diag(`no_vnfo_${html.length}b`); + return []; + } let vnfo: Record; try { vnfo = JSON.parse(m[1]); } catch { + _diag('vnfo_parse_err'); return []; } const links: StreamLink[] = []; @@ -100,5 +136,6 @@ export async function resolveSxyprnPage(pageUrl: string): Promise type: 'mp4', }); } + _diag(links.length > 0 ? `ok_${links.length}` : 'vnfo_empty'); return links; }