-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbackground.js
More file actions
1 lines (1 loc) · 7.12 KB
/
background.js
File metadata and controls
1 lines (1 loc) · 7.12 KB
1
const y=new Map,k="https://unfluff.standardagents.ai";let s=null,m=!1,w=null,h=null,g=0;const p=new Set,B=6e4,R=30,u=[];function _(t){const e=Date.now();for(;u.length>0&&u[0]<e-B;)u.shift();if(u.length+t>R)return!1;for(let n=0;n<t;n++)u.push(e);return!0}const f=new Map,S=new Map;async function L(){const t=await chrome.storage.local.get("installId");if(t.installId)return t.installId;const e=crypto.randomUUID();return await chrome.storage.local.set({installId:e}),e}async function C(){return(await chrome.storage.local.get("apiUrl")).apiUrl||k}async function W(){if(s&&s.readyState===WebSocket.OPEN||s&&s.readyState===WebSocket.CONNECTING)return;const t=await L(),e=await C(),n=e.startsWith("https")?"wss":"ws",r=`${e.replace(/^https?/,n)}/api/unfluff/ws?installId=${encodeURIComponent(t)}`;console.log(`[Unfluff SW] Connecting WebSocket to ${r}`);try{s=new WebSocket(r)}catch(a){console.error("[Unfluff SW] WebSocket constructor failed:",a),T();return}s.onopen=()=>{console.log("[Unfluff SW] WebSocket connected"),m=!0,g=0,v()},s.onmessage=a=>{if(typeof a.data=="string"&&a.data!=="pong")try{const l=JSON.parse(a.data);N(l)}catch(l){console.error("[Unfluff SW] Failed to parse WS message:",l)}},s.onclose=()=>{console.log("[Unfluff SW] WebSocket closed"),m=!1,s=null,A(),T()},s.onerror=a=>{console.error("[Unfluff SW] WebSocket error:",a)}}function T(){if(w)return;const t=Math.min(1e3*Math.pow(2,g),3e4);g++,console.log(`[Unfluff SW] Reconnecting in ${t}ms (attempt ${g})`),w=setTimeout(()=>{w=null,W()},t)}function v(){A(),h=setInterval(()=>{s&&s.readyState===WebSocket.OPEN&&s.send("ping")},25e3)}function A(){h&&(clearInterval(h),h=null)}function N(t){if(t.type==="RESULT"){const e=t.result,n=S.get(e.id);if(!n){y.set(e.id,e);return}const o=f.get(n);if(!o)return;y.set(e.id,e),o.results.push(e),o.postIds.delete(e.id),S.delete(e.id),p.delete(e.id),o.postIds.size===0&&I(n)}else if(t.type==="BATCH_DONE")for(const[e,n]of f)n.postIds.size===0&&I(e);else if(t.type==="BATCH_ERROR"){console.error("[Unfluff SW] Server batch error:",t.error);for(const[e,n]of f)clearTimeout(n.timeout),n.resolve({type:"BATCH_ERROR",error:t.error||"Server error"}),b(e)}}function I(t){const e=f.get(t);if(!e)return;clearTimeout(e.timeout);const n=[...e.cachedResults,...e.results];console.log(`[Unfluff SW] Batch ${t} complete: ${n.length} results`),e.resolve({type:"BATCH_RESULT",results:n}),b(t)}function b(t){const e=f.get(t);if(e)for(const n of e.postIds)S.delete(n),p.delete(n);f.delete(t)}chrome.runtime.onMessage.addListener((t,e,n)=>t.type!=="ANALYZE_BATCH"?!1:(console.log(`[Unfluff SW] Received batch of ${t.posts.length} posts`),O(t).then(o=>{console.log(`[Unfluff SW] Sending response: ${o.type}`,o.type==="BATCH_RESULT"?`${o.results.length} results`:""),n(o)}).catch(o=>{console.error("[Unfluff SW] Error:",o),n({type:"BATCH_ERROR",error:o instanceof Error?o.message:"Unknown error"})}),!0));async function O(t){const{posts:e}=t,n=[],r=e.filter(c=>{const i=y.get(c.id);return i?(n.push(i),!1):!0}).filter(c=>!p.has(c.id));if(r.length===0)return console.log("[Unfluff SW] All posts cached or in-flight"),{type:"BATCH_RESULT",results:n};if(!_(r.length))return console.warn(`[Unfluff SW] Rate limited — ${r.length} posts rejected (${u.length}/${R} in window)`),{type:"BATCH_ERROR",error:"Rate limited — please wait a moment"};for(const c of r)p.add(c.id);const a=await M(r);if(await W(),await P(5e3),!s||s.readyState!==WebSocket.OPEN)throw new Error("WebSocket not connected");const l=crypto.randomUUID(),d=new Set(a.map(c=>c.id));for(const c of d)S.set(c,l);return new Promise(c=>{const i=setTimeout(()=>{console.error(`[Unfluff SW] Batch ${l} timed out after 60s`),c({type:"BATCH_ERROR",error:"Analysis timed out"}),b(l)},6e4);f.set(l,{postIds:d,results:[],resolve:c,cachedResults:n,timeout:i});const U=JSON.stringify({type:"ANALYZE_BATCH",posts:a});s.send(U),console.log(`[Unfluff SW] Sent batch ${l} (${a.length} posts) via WebSocket`)})}function P(t){return m&&(s==null?void 0:s.readyState)===WebSocket.OPEN?Promise.resolve():new Promise((e,n)=>{const o=Date.now(),r=()=>{m&&(s==null?void 0:s.readyState)===WebSocket.OPEN?e():Date.now()-o>t?n(new Error("WebSocket connection timeout")):setTimeout(r,100)};r()})}const H=new Set(["image/jpeg","image/png","image/gif","image/webp"]);async function M(t){return Promise.all(t.map(async e=>{if(!e.imageUrls||e.imageUrls.length===0)return e;console.log(`[Unfluff SW] Fetching ${e.imageUrls.length} images for post ${e.id}`);const n=[];for(const a of e.imageUrls)try{const l=await fetch(a);if(!l.ok){console.warn(`[Unfluff SW] Image fetch failed: ${l.status} ${l.statusText} for ${a.slice(0,100)}`);continue}const d=await l.blob(),c=d.type||"image/jpeg";if(!H.has(c)){console.warn(`[Unfluff SW] Skipping non-raster type ${c} from ${a.slice(0,80)}`);continue}const i=await d.arrayBuffer(),U=D(i);console.log(`[Unfluff SW] Image fetched OK: ${(i.byteLength/1024).toFixed(0)}KB ${c} from ${a.slice(0,80)}`),n.push({base64:U,mimeType:c})}catch(l){console.error(`[Unfluff SW] Image fetch error for ${a.slice(0,100)}:`,l)}n.length>0?console.log(`[Unfluff SW] Post ${e.id}: ${n.length}/${e.imageUrls.length} images fetched successfully`):e.imageUrls.length>0&&console.warn(`[Unfluff SW] Post ${e.id}: ALL ${e.imageUrls.length} image fetches failed! URLs: ${e.imageUrls.map(a=>a.slice(0,60)).join(", ")}`);const{imageUrls:o,...r}=e;return{...r,images:n.length>0?n:void 0}}))}function D(t){const e=new Uint8Array(t);let n="";for(let o=0;o<e.length;o++)n+=String.fromCharCode(e[o]);return btoa(n)}chrome.runtime.onInstalled.addListener(()=>{console.log("[Unfluff] Extension installed")});const x=["www.linkedin.com"];chrome.tabs.onUpdated.addListener((t,e,n)=>{if(!(!e.url||!n.url)){try{const o=new URL(n.url).hostname;if(!x.some(r=>o===r||o.endsWith("."+r)))return}catch{return}console.log(`[Unfluff SW] Tab URL changed → ${n.url}, re-injecting content script into all frames`),chrome.scripting.executeScript({target:{tabId:t,allFrames:!0},files:["content.js"]}).catch(o=>{console.log("[Unfluff SW] executeScript error (expected for some frames):",o.message)})}});W();const $="unfluff-update-check",j="https://api.github.com/repos/standardagents/unfluff/releases/latest";function F(t,e){const n=t.split(".").map(Number),o=e.split(".").map(Number);for(let r=0;r<3;r++){if((n[r]??0)>(o[r]??0))return 1;if((n[r]??0)<(o[r]??0))return-1}return 0}async function E(){try{const t=await fetch(j,{headers:{Accept:"application/vnd.github.v3+json"}});if(!t.ok)return;const e=await t.json(),n=e.tag_name??"",o=n.replace(/^v/,""),r=chrome.runtime.getManifest().version;if(o&&F(o,r)>0){const a=e.html_url??`https://github.com/standardagents/unfluff/releases/tag/${n}`;await chrome.storage.local.set({updateAvailable:{latestVersion:o,releaseUrl:a,checkedAt:Date.now()}}),chrome.action.setBadgeText({text:"!"}),chrome.action.setBadgeBackgroundColor({color:"#0a66c2"}),console.log(`[Unfluff SW] Update available: v${o} (current: v${r})`)}else await chrome.storage.local.remove("updateAvailable"),chrome.action.setBadgeText({text:""})}catch(t){console.error("[Unfluff SW] Update check failed:",t)}}chrome.alarms.create($,{periodInMinutes:360});chrome.alarms.onAlarm.addListener(t=>{t.name===$&&E()});E();console.log("[Unfluff SW] Service worker started (WebSocket mode)");