-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathsorry.js
More file actions
22 lines (22 loc) · 9.29 KB
/
sorry.js
File metadata and controls
22 lines (22 loc) · 9.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function getTimestamp(){return`[${(new Date).toISOString()}]`}const REDIRECT_STATUS_CODES=new Set([300,301,302,303,307,308]),DEFAULT_MAX_REDIRECTS=10,EXTENSION_ORIGIN=chrome.runtime.getURL("").replace(/\/$/,""),GOOGLE_WEBREQUEST_FILTER={urls:["*://*.google.com/*"]};function isSorryUrl(a){if(!a)return!1;try{return(new URL(a,"https://www.google.com")).pathname.includes("/sorry")}catch{return a.includes("/sorry")}}
function resolveRedirectUrl(a,c){if(!a)return null;try{return(new URL(a,c)).toString()}catch(f){return console.warn(getTimestamp(),`[SORRY] Unable to resolve redirect URL "${a}" from base "${c}": ${f}`),null}}
function createHeaderStore(a={}){const c=new Map,f=(e,b)=>{e&&void 0!==b&&null!==b&&c.set(e.toLowerCase(),{name:e,value:b})};Object.entries(a||{}).forEach(([e,b])=>f(e,b));return{ensureHeader:(e,b)=>{if(b||0===b){var d=e.toLowerCase();c.has(d)||f(e,b)}},hasHeaders:()=>0<c.size,buildHeaders:e=>{const b=new Headers;c.forEach(({name:d,value:h})=>{const k=d.toLowerCase();h=e&&"referer"===k?e:h;try{b.set(d,h)}catch(g){console.warn(getTimestamp(),`[SORRY] Unable to set header ${d}: ${g.message}`)}});return b}}}
let fetchContextCounter=0;const redirectAwaitMap=new Map,requestIdToContext=new Map;function createFetchContext(a,c){return{id:`fetch-${Date.now()}-${(++fetchContextCounter).toString(16)}`,controller:c,redirectChain:[a],requestIds:new Set,sorryDetected:!1,sorryUrl:null,lastRequestUrl:a,pendingRedirects:[]}}
function cleanupFetchContext(a){for(const [c,f]of redirectAwaitMap.entries()){const e=f.indexOf(a);-1!==e&&(f.splice(e,1),0===f.length&&redirectAwaitMap.delete(c))}for(const c of a.requestIds)requestIdToContext.delete(c);a.requestIds.clear()}function queueContextForUrl(a,c){if(a&&c){var f=redirectAwaitMap.get(a);f||(f=[],redirectAwaitMap.set(a,f));f.push(c)}}
function consumeContextForUrl(a){const c=redirectAwaitMap.get(a);if(c&&0<c.length){const f=c.shift();0===c.length&&redirectAwaitMap.delete(a);return f}return null}function trackRequestForContext(a,c,f){a.requestIds.add(c);a.lastRequestUrl=f;requestIdToContext.set(c,a)}function releaseRequestTracking(a){const c=requestIdToContext.get(a);c&&(requestIdToContext.delete(a),c.requestIds.delete(a))}
function isExtensionServiceWorkerRequest(a){return-1===a.tabId&&a.initiator&&a.initiator.startsWith(EXTENSION_ORIGIN)}chrome.webRequest.onBeforeRequest.addListener(a=>{if(isExtensionServiceWorkerRequest(a)){var c=consumeContextForUrl(a.url);c&&(trackRequestForContext(c,a.requestId,a.url),console.log(getTimestamp(),`[SORRY] Tracking request ${a.requestId} for ${c.id}: ${a.url}`))}},GOOGLE_WEBREQUEST_FILTER);
chrome.webRequest.onHeadersReceived.addListener(a=>{const c=requestIdToContext.get(a.requestId);if(c&&REDIRECT_STATUS_CODES.has(a.statusCode)){var f=a.responseHeaders?.find(e=>e.name&&"location"===e.name.toLowerCase());if(a=resolveRedirectUrl(f?.value,a.url))if(c.redirectChain[c.redirectChain.length-1]!==a&&c.redirectChain.push(a),isSorryUrl(a)){c.sorryDetected=!0;c.sorryUrl=a;console.warn(getTimestamp(),`[SORRY] Anti-bot redirect detected for ${c.id}: ${a}`);try{c.controller?.abort()}catch(e){console.warn(getTimestamp(),
"[SORRY] Failed to abort fetch after /sorry detection:",e)}}else c.pendingRedirects.push(a)}},GOOGLE_WEBREQUEST_FILTER,["responseHeaders","extraHeaders"]);chrome.webRequest.onCompleted.addListener(a=>{releaseRequestTracking(a.requestId)},GOOGLE_WEBREQUEST_FILTER);chrome.webRequest.onErrorOccurred.addListener(a=>{releaseRequestTracking(a.requestId)},GOOGLE_WEBREQUEST_FILTER);
async function fetchWithSorryDetection(a,c=3E4,f={}){const e=new AbortController;c=setTimeout(()=>e.abort(),c);const b=createFetchContext(a,e);try{console.log(getTimestamp(),`[SORRY] Starting fetch for: ${a}`);var d=createHeaderStore(f.headers||{});d.ensureHeader("User-Agent",f.userAgent);d.ensureHeader("Referer",f.referer);d.ensureHeader("Accept-Language",f.language?`${f.language},en;q=0.9`:"en-US,en;q=0.9");d.ensureHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8");
d.ensureHeader("Cache-Control","max-age=0");d.ensureHeader("Pragma","no-cache");d.ensureHeader("Upgrade-Insecure-Requests","1");d.ensureHeader("Sec-Fetch-Dest","document");d.ensureHeader("Sec-Fetch-Mode","navigate");d.ensureHeader("Sec-Fetch-Site","none");d.ensureHeader("Sec-Fetch-User","?1");const h={signal:e.signal,redirect:"manual",credentials:"include",method:"GET"};d.hasHeaders()&&(h.headers=d.buildHeaders());const k=f.maxRedirects||DEFAULT_MAX_REDIRECTS;for(f=0;;){queueContextForUrl(a,b);d=
{...h};console.log(getTimestamp(),`[SORRY] Fetching URL: ${a}`);const g=await fetch(a,d);if(b.sorryDetected&&b.sorryUrl)return console.warn(getTimestamp(),`[SORRY] Verification needed (redirect): ${b.sorryUrl}`),{success:!1,needsVerification:!0,verificationUrl:b.sorryUrl,redirectChain:b.redirectChain,error:"Google anti-bot verification required"};if("opaqueredirect"===g.type||REDIRECT_STATUS_CODES.has(g.status)){if(f>=k)return console.warn(getTimestamp(),`[SORRY] Exceeded redirect limit (${k})`),
{success:!1,error:`Too many redirects (>${k})`,redirectChain:b.redirectChain};d=null;if(REDIRECT_STATUS_CODES.has(g.status)){const m=g.headers.get("Location");d=resolveRedirectUrl(m,a)}!d&&0<b.pendingRedirects.length&&(d=b.pendingRedirects.shift());if(!d)return console.warn(getTimestamp(),"[SORRY] Redirect response missing Location header or pending redirect URL"),{success:!1,error:"Redirect response missing Location header",redirectChain:b.redirectChain};b.redirectChain[b.redirectChain.length-1]!==
d&&b.redirectChain.push(d);if(isSorryUrl(d))return console.warn(getTimestamp(),`[SORRY] Anti-bot redirect detected: ${d}`),{success:!1,needsVerification:!0,verificationUrl:d,redirectChain:b.redirectChain,error:"Google anti-bot verification required"};f+=1;a=d;continue}console.log(getTimestamp(),`[SORRY] Final response URL: ${g.url}`);if(isSorryUrl(g.url))return b.redirectChain[b.redirectChain.length-1]!==g.url&&b.redirectChain.push(g.url),console.warn(getTimestamp(),`[SORRY] Anti-bot page detected at URL: ${g.url}`),
{success:!1,needsVerification:!0,verificationUrl:g.url,redirectChain:b.redirectChain,error:"Google anti-bot verification required"};if(!g.ok)return console.warn(getTimestamp(),`[SORRY] Response not ok, status: ${g.status}`),{success:!1,error:`Fetch failed with status: ${g.status}`,redirectChain:b.redirectChain};const l=await g.text();console.log(getTimestamp(),`[SORRY] Successfully fetched ${l.length} bytes`);g.url&&b.redirectChain[b.redirectChain.length-1]!==g.url&&b.redirectChain.push(g.url);return{success:!0,
data:l,redirectChain:b.redirectChain}}}catch(h){if(b.sorryDetected&&b.sorryUrl)return console.warn(getTimestamp(),`[SORRY] Verification needed after aborted fetch: ${b.sorryUrl}`),{success:!1,needsVerification:!0,verificationUrl:b.sorryUrl,redirectChain:b.redirectChain,error:"Google anti-bot verification required"};console.error(getTimestamp(),"[SORRY] Fetch error:",h);return{success:!1,error:"AbortError"===h.name?"Request timeout":h.message}}finally{cleanupFetchContext(b),clearTimeout(c)}}
async function monitorVerificationTab(a){return new Promise(c=>{let f=0;console.log(getTimestamp(),`[SORRY] Starting to monitor tab ${a} for verification completion`);const e=setInterval(async()=>{f++;try{const b=await chrome.tabs.get(a);if(b)if(console.log(getTimestamp(),`[SORRY] Check ${f}/${30}: Tab URL = ${b.url}`),b.url&&!b.url.includes("/sorry")){console.log(getTimestamp(),`[SORRY] \u2713 Verification complete! URL changed to: ${b.url}`);try{await chrome.tabs.remove(a),console.log(getTimestamp(),
"[SORRY] \u2713 Verification tab closed")}catch(d){console.warn(getTimestamp(),"[SORRY] Could not close tab:",d)}clearInterval(e);c({success:!0,verified:!0})}else{if(30<=f){console.warn(getTimestamp(),"[SORRY] Timeout waiting for verification (60s). Auto-closing tab and continuing...");try{await chrome.tabs.remove(a),console.log(getTimestamp(),"[SORRY] Verification tab auto-closed due to timeout")}catch(d){console.warn(getTimestamp(),"[SORRY] Could not close tab:",d)}clearInterval(e);c({success:!1,
timeout:!0})}}else console.log(getTimestamp(),`[SORRY] Tab ${a} was closed by user`),clearInterval(e),c({success:!1,userClosed:!0})}catch(b){console.log(getTimestamp(),`[SORRY] Tab ${a} no longer accessible:`,b.message),clearInterval(e),c({success:!1,userClosed:!0})}},2E3)})}
chrome.runtime.onMessage.addListener((a,c,f)=>"fetchWithSorryDetection"===a.action?(console.log(getTimestamp(),`[SORRY] Received fetch request for CID: ${a.cid}`),(async()=>{const e=await fetchWithSorryDetection(a.url,a.timeout||3E4,a.browserContext||{});if(e.needsVerification&&e.verificationUrl){console.log(getTimestamp(),`[SORRY] Opening verification page: ${e.verificationUrl}`);try{const b=await chrome.tabs.create({url:e.verificationUrl,active:!0});console.log(getTimestamp(),`[SORRY] Verification tab opened with ID: ${b.id}`);
const d=await monitorVerificationTab(b.id);d.verified?(console.log(getTimestamp(),"[SORRY] \u2713 User successfully completed verification"),e.verificationComplete=!0):d.userClosed?(console.log(getTimestamp(),"[SORRY] User closed verification tab manually"),e.verificationComplete=!1,e.userCanceled=!0):d.timeout&&(console.log(getTimestamp(),"[SORRY] Verification timeout"),e.verificationComplete=!1,e.timeout=!0)}catch(b){console.error(getTimestamp(),"[SORRY] Failed to open verification tab:",b)}}f(e)})(),
!0):!1);console.log(getTimestamp(),"[SORRY] Module loaded successfully");