Download background.js from fritreefw/facebook: direct link, hf CLI and curl.
- Browser
- Download file 69.5 kB
-
https://huggingface.co/spaces/fritreefw/facebook/resolve/main/background.js
- Command line
-
hf download hf://spaces/fritreefw/facebook/background.js
-
curl -L -o background.js https://huggingface.co/spaces/fritreefw/facebook/resolve/main/background.js
69.5 kB
| // ============================================================================ | |
| // File: background.js | |
| // ============================================================================ | |
| ; | |
| // Import cryptographic storage routers | |
| importScripts('crypto.js', 'storage.js'); | |
| const DEFAULT_APP_URL = chrome.runtime.getURL('index.html'); | |
| const APP_ORIGIN = chrome.runtime.getURL('').slice(0, -1); | |
| // Facebook session parameters and secure Graph ID container | |
| const USER_CONFIG = { | |
| 'userId': null, | |
| 'doc_id_groups': '3884641628300421', | |
| 'doc_id_post_text': '3559434960802556', | |
| 'doc_id_post_photo': '4669579913112843', | |
| 'fb_dtsg': null, | |
| 'lsd': null, | |
| 'dyn': null, | |
| 'jazoest': '25415', | |
| 'upload_id': 'jsc_c_9', | |
| 'targetGroupId': null | |
| }; | |
| let publishCancelled = false; | |
| let isPublishingNow = false; | |
| let appTabId = null; | |
| let currentPublishTabId = null; | |
| let resolveStorageReady; | |
| let waPublishCancelled = false; | |
| let isWaPublishingNow = false; | |
| let currentWaCampaign = null; | |
| const STATE_INTEGRITY_SALT = "FritreeRotationMatrixDynamicIntegrityVerificationSalt_2026"; | |
| const SHIELD_INTEGRITY_SALT = "FritreeFacebookStealthShieldSymmetricSignatureSalt_SHA256_2026_EnterpriseSecureForce"; | |
| const storageReady = new Promise(resolve => { | |
| resolveStorageReady = resolve; | |
| }); | |
| // ============================================================================ | |
| // Persistent Port Keep-Alive Listener (Prevents service worker sleep) | |
| // ============================================================================ | |
| const activePorts = new Set(); | |
| chrome.runtime.onConnect.addListener((port) => { | |
| if (port.name === "fritree-keep-alive") { | |
| activePorts.add(port); | |
| console.log("[Background Engine] Keep-alive channel connected from workspace."); | |
| port.onDisconnect.addListener(() => { | |
| activePorts.delete(port); | |
| console.log("[Background Engine] Keep-alive channel disconnected."); | |
| }); | |
| port.onMessage.addListener((msg) => { | |
| if (msg.ping) { | |
| // Heartbeat echo to maintain worker lifespan | |
| port.postMessage({ pong: true }); | |
| chrome.runtime.getPlatformInfo(() => {}); // Lightweight API call to assert activity | |
| } | |
| }); | |
| } | |
| }); | |
| // Boot verification sequence to audit database parameters with auto-healing capability | |
| (async () => { | |
| await runIntrusionDetectionCheck(); | |
| const fb_dtsg = await FritreeStorage.get('fb_dtsg', null); | |
| const lsd = await FritreeStorage.get('lsd', null); | |
| const dyn = await FritreeStorage.get('dyn', null); | |
| const userId = await FritreeStorage.get('userId', null); | |
| const targetGroupId = await FritreeStorage.get('targetGroupId', null); | |
| const cancelledState = await FritreeStorage.get('publishCancelled', false); | |
| if (fb_dtsg) USER_CONFIG.fb_dtsg = fb_dtsg; | |
| if (lsd) USER_CONFIG.lsd = lsd; | |
| if (dyn) USER_CONFIG.dyn = dyn; | |
| if (userId) USER_CONFIG.userId = userId; | |
| if (targetGroupId) USER_CONFIG.targetGroupId = targetGroupId; | |
| if (cancelledState) { | |
| publishCancelled = cancelledState; | |
| await FritreeStorage.set('publishCancelled', false); | |
| } | |
| console.log('[Background Engine] System verification completed. Workspace environment online.'); | |
| resolveStorageReady(); | |
| })(); | |
| /** | |
| * Double-Anchor Signature Audit Loop to auto-heal and sync external changes | |
| */ | |
| async function runIntrusionDetectionCheck() { | |
| try { | |
| // 1. Audit rotation matrix templates signatures | |
| const rawPosts = await FritreeStorage.get('local_previous_posts', []); | |
| if (rawPosts.length > 0) { | |
| const savedSignature = await FritreeStorage.get('local_rotation_matrix_integrity_sig', ''); | |
| if (savedSignature) { | |
| const structuralConcat = rawPosts.map(p => `${p.id}:${p.status}`).sort().join('||'); | |
| let computedSignature = "lite_hash_" + structuralConcat.length; | |
| if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.signReceipt === 'function') { | |
| computedSignature = await FritreeCrypto.signReceipt("ROT_MATRIX", structuralConcat.length, "verify", structuralConcat, STATE_INTEGRITY_SALT); | |
| } | |
| if (savedSignature !== computedSignature) { | |
| console.log("[Fritree Storage] Aligned rotation matrix integrity signatures."); | |
| await FritreeStorage.set('local_rotation_matrix_integrity_sig', computedSignature); | |
| } | |
| } | |
| } | |
| // 2. Audit Stealth behavioral parameters signatures | |
| const savedShieldSig = await FritreeStorage.get('local_shield_config_matrix_sig_256', ''); | |
| const loadedShieldConfig = await FritreeStorage.get('local_shield_config_matrix', null); | |
| if (loadedShieldConfig && savedShieldSig) { | |
| const serialized = JSON.stringify(loadedShieldConfig); | |
| const concatString = `${serialized}:${SHIELD_INTEGRITY_SALT}`; | |
| let computedShieldSig = "fallback_shield_sig_256_" + concatString.length; | |
| if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.signReceipt === 'function') { | |
| computedShieldSig = await FritreeCrypto.signReceipt( | |
| "FB_STEALTH_SHIELD_V3", | |
| concatString.length, | |
| "config_signature_256", | |
| concatString, | |
| SHIELD_INTEGRITY_SALT | |
| ); | |
| } | |
| if (savedShieldSig !== computedShieldSig) { | |
| console.log("[Fritree Crypto] Synchronized Stealth Shield signatures."); | |
| await FritreeStorage.set('local_shield_config_matrix_sig_256', computedShieldSig); | |
| } | |
| } | |
| return true; | |
| } catch (e) { | |
| return true; | |
| } | |
| } | |
| /** | |
| * Spintax markup syntax compiler to bypass content duplicate detection filters | |
| */ | |
| function parseSpintax(text) { | |
| if (!text) return text; | |
| let matches; | |
| const regEx = new RegExp(/{([^{}]+?)}/); | |
| let executionBoundary = 500; | |
| while (((matches = regEx.exec(text)) !== null) && executionBoundary > 0) { | |
| const options = matches[1].split('|'); | |
| const random = Math.floor(Math.random() * options.length); | |
| text = text.replace(matches[0], options[random]); | |
| executionBoundary--; | |
| } | |
| return text; | |
| } | |
| /** | |
| * Variables compiler to inject random string patterns | |
| */ | |
| function parseVariables(text) { | |
| if (!text) return text; | |
| text = text.replace(/\{\{RAN_T\((\d+)\)\}\}/g, (match, p1) => { | |
| const len = Math.min(parseInt(p1), 100); | |
| const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; | |
| let res = ''; | |
| for(let i = 0; i < len; i++) res += chars.charAt(Math.floor(Math.random() * chars.length)); | |
| return res; | |
| }); | |
| text = text.replace(/\{\{RAN_D\((\d+)\)\}\}/g, (match, p1) => { | |
| const len = Math.min(parseInt(p1), 100); | |
| const nums = '0123456789'; | |
| let res = ''; | |
| for(let i = 0; i < len; i++) res += nums.charAt(Math.floor(Math.random() * nums.length)); | |
| return res; | |
| }); | |
| return text; | |
| } | |
| function waitTabToComplete(tabId) { | |
| return new Promise(resolve => { | |
| chrome.tabs.get(tabId, tab => { | |
| if (tab && tab.status === 'complete') { resolve(); return; } | |
| const listener = (tid, changeInfo) => { | |
| if (tid === tabId && changeInfo.status === 'complete') { | |
| chrome.tabs.onUpdated.removeListener(listener); | |
| resolve(); | |
| } | |
| }; | |
| chrome.tabs.onUpdated.addListener(listener); | |
| setTimeout(() => { chrome.tabs.onUpdated.removeListener(listener); resolve(); }, 15000); | |
| }); | |
| }); | |
| } | |
| function getFacebookUserId() { | |
| return new Promise(resolve => { | |
| chrome.cookies.get({ 'url': 'https://www.facebook.com', 'name': 'c_user' }, cookie => { | |
| if (cookie && cookie.value) resolve(cookie.value); | |
| else resolve(null); | |
| }); | |
| }); | |
| } | |
| function getFacebookPageId() { | |
| return new Promise(resolve => { | |
| chrome.cookies.get({ 'url': 'https://www.facebook.com', 'name': 'i_user' }, cookie => { | |
| if (cookie && cookie.value) resolve(cookie.value); | |
| else resolve(null); | |
| }); | |
| }); | |
| } | |
| async function extractFbDtsg(tabId) { | |
| return new Promise(resolve => { | |
| chrome.scripting.executeScript({ | |
| target: { tabId: tabId }, | |
| func: () => { | |
| let dtsg = null, lsd = null; | |
| const inputDtsg = document.querySelector('input[name="fb_dtsg"]'); | |
| if (inputDtsg && inputDtsg.value) dtsg = inputDtsg.value; | |
| const inputLsd = document.querySelector('input[name="lsd"]'); | |
| if (inputLsd && inputLsd.value) lsd = inputLsd.value; | |
| const html = document.documentElement.innerHTML; | |
| if (!dtsg) { | |
| const dtsgMatch = html.match(/["']DTSGInitialData["']\s*,\s*\[\s*\]\s*,\s*\{\s*["']token["']\s*:\s*["']([^"']+)["']/); | |
| if (dtsgMatch) dtsg = dtsgMatch[1]; | |
| } | |
| if (!dtsg) { | |
| const dtsgMatch2 = html.match(/["']token["']\s*:\s*["'](AQ[^"']+)["']/); | |
| if (dtsgMatch2) dtsg = dtsgMatch2[1]; | |
| } | |
| if (!dtsg) { | |
| const dtsgMatch3 = html.match(/async_get_token:"([^"]+)"/); | |
| if (dtsgMatch3) dtsg = dtsgMatch3[1]; | |
| } | |
| if (!lsd) { | |
| const lsdMatch = html.match(/["']LSD["']\s*,\s*\[\s*\]\s*,\s*\{\s*["']token["']\s*:\s*["']([^"']+)["']/); | |
| if (lsdMatch) lsd = lsdMatch[1]; | |
| } | |
| return { 'fb_dtsg': dtsg, 'lsd': lsd }; | |
| } | |
| }, results => { | |
| if (chrome.runtime.lastError) resolve({ 'fb_dtsg': null, 'lsd': null }); | |
| else if (results && results[0] && results[0].result) resolve(results[0].result); | |
| else resolve({ 'fb_dtsg': null, 'lsd': null }); | |
| }); | |
| }); | |
| } | |
| function getPlatformBrowserUserAgents() { | |
| return [ | |
| "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", | |
| "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36" | |
| ]; | |
| } | |
| async function extractD_Dyn(tabId) { | |
| return new Promise(resolve => { | |
| chrome.scripting.executeScript({ | |
| target: { tabId: tabId }, | |
| func: () => { | |
| const dyn = document.querySelector('input[name="__dyn"]')?.value; | |
| if (dyn && dyn.length > 20) return dyn; | |
| const html = document.documentElement.innerHTML; | |
| const m = html.match(/__dyn\s*:\s*"([^"]+)"/); | |
| return m ? m[1] : null; | |
| } | |
| }, results => { | |
| if (chrome.runtime.lastError) resolve(null); | |
| else resolve(results?.[0]?.result || null); | |
| }); | |
| }); | |
| } | |
| async function refreshSession(tabId) { | |
| try { | |
| await chrome.scripting.executeScript({ | |
| target: { tabId: tabId }, | |
| func: () => fetch('https://www.facebook.com/help/463972400461409/?_rdr', { 'credentials': 'include', 'headers': { 'Accept': 'text/html' } }) | |
| }); | |
| await new Promise(r => setTimeout(r, 2000)); | |
| } catch (e) { console.error('Error refreshing session:', e); } | |
| } | |
| async function ensureFacebookAndCaptureKeys() { | |
| return new Promise(async resolve => { | |
| const userId = await getFacebookUserId(); | |
| const pageId = await getFacebookPageId(); | |
| const activeId = pageId || userId; | |
| if (!activeId) { | |
| resolve({ | |
| 'success': false, | |
| 'error': 'not_logged_in', | |
| 'message': 'Active Facebook session not found. Please log in to Facebook first to initialize sync.' | |
| }); | |
| return; | |
| } | |
| USER_CONFIG.userId = activeId; | |
| const tabs = await chrome.tabs.query({ 'url': ['*://*.facebook.com/*', '*://*.web.facebook.com/*'] }); | |
| let targetTabId = null; | |
| if (tabs.length === 0) { | |
| const newTab = await chrome.tabs.create({ 'url': 'https://www.facebook.com', 'active': false }); | |
| targetTabId = newTab.id; | |
| await new Promise(r => setTimeout(r, 6000)); | |
| } else { | |
| targetTabId = tabs[0].id; | |
| } | |
| await refreshSession(targetTabId); | |
| let keys = await extractFbDtsg(targetTabId); | |
| if (!keys.fb_dtsg) { | |
| await chrome.tabs.reload(targetTabId); | |
| await new Promise(r => setTimeout(r, 6000)); | |
| keys = await extractFbDtsg(targetTabId); | |
| if (!keys.fb_dtsg) { | |
| resolve({ 'success': false, 'error': 'no_logged_in', 'message': 'Session Bridge Failure: Failed to capture secure session handshake keys. Try logging into Facebook again.' }); | |
| return; | |
| } | |
| } | |
| USER_CONFIG.fb_dtsg = keys.fb_dtsg; | |
| USER_CONFIG.lsd = keys.lsd; | |
| USER_CONFIG.dyn = await extractD_Dyn(targetTabId); | |
| await FritreeStorage.set('fb_dtsg', keys.fb_dtsg); | |
| await FritreeStorage.set('lsd', keys.lsd); | |
| await FritreeStorage.set('dyn', USER_CONFIG.dyn); | |
| await FritreeStorage.set('userId', activeId); | |
| resolve({ 'success': true, 'userId': activeId, 'tabId': targetTabId }); | |
| }); | |
| } | |
| /** | |
| * Direct Service-Worker Media Upload | |
| */ | |
| async function uploadImage(dataUrl, fileName) { | |
| return new Promise(resolve => { | |
| try { | |
| const parts = dataUrl.split(','); | |
| if (parts.length < 2) { | |
| resolve({ 'success': false, 'error': 'Upload Failure: Invalid image payload encoding format.' }); | |
| return; | |
| } | |
| const binary = atob(parts[1]); | |
| const mimeType = parts[0].split(':')[1].split(';')[0]; | |
| const arrayBuffer = new ArrayBuffer(binary.length); | |
| const uint8Array = new Uint8Array(arrayBuffer); | |
| for (let i = 0; i < binary.length; i++) { | |
| uint8Array[i] = binary.charCodeAt(i); | |
| } | |
| const blob = new Blob([arrayBuffer], { type: mimeType }); | |
| const fd = new FormData(); | |
| fd.append('fb_dtsg', USER_CONFIG.fb_dtsg); | |
| fd.append('profile_id', USER_CONFIG.userId); | |
| fd.append('source', '8'); | |
| fd.append('waterfallxapp', 'comet'); | |
| fd.append('farr', blob, fileName); | |
| const dynamicUploadId = Math.floor(Math.random() * 1000000000000000).toString(); | |
| fd.append('upload_id', dynamicUploadId); | |
| fd.append('jazoest', (25000 + Math.floor(Math.random() * 100)).toString()); | |
| const uploadUrl = 'https://upload.facebook.com/ajax/react_composer/attachments/photo/upload?av=' + USER_CONFIG.userId + '&__user=' + USER_CONFIG.userId + '&__a=1&fb_dtsg=' + USER_CONFIG.fb_dtsg; | |
| fetch(uploadUrl, { | |
| 'method': 'POST', | |
| 'credentials': 'include', | |
| 'body': fd, | |
| 'headers': { 'Accept': '*/*' } | |
| }) | |
| .then(async r => { | |
| const responseText = await r.text(); | |
| let photoId = null; | |
| try { | |
| const cleanText = responseText.replace("for (;;);", "").trim(); | |
| const parsedData = JSON.parse(cleanText); | |
| if (parsedData && parsedData.payload && parsedData.payload.photoID) { | |
| photoId = parsedData.payload.photoID; | |
| } | |
| } catch (jsonErr) { | |
| const m1 = responseText.match(/"photoID"\s*:\s*"?(\d+)"?/) || responseText.match(/"fbid"\s*:\s*"?(\d+)"?/); | |
| if (m1) photoId = m1[1]; | |
| } | |
| if (photoId) { | |
| resolve({ 'success': true, 'photoId': photoId }); | |
| } else { | |
| resolve({ 'success': false, 'error': 'Upload Failure: Server returned incomplete payload.' }); | |
| } | |
| }) | |
| .catch(err => resolve({ 'success': false, 'error': 'Network Failure: Media upload request failed: ' + err.message })); | |
| } catch (err) { resolve({ 'success': false, 'error': 'Critical Exception: Failed to encode and upload image asset: ' + err.message }); } | |
| }); | |
| } | |
| async function publishToGroup(tabId, text, photoIds, targetGroupId) { | |
| return new Promise(resolve => { | |
| if (!targetGroupId) { resolve({ 'success': false, 'error': 'Dispatch Denied: Target group destination ID not specified.' }); return; } | |
| const hasPhotos = photoIds && photoIds.length > 0; | |
| const docId = hasPhotos ? USER_CONFIG.doc_id_post_photo : USER_CONFIG.doc_id_post_text; | |
| let hasResolved = false; | |
| const timeout = setTimeout(() => { | |
| if (!hasResolved) { | |
| hasResolved = true; | |
| resolve({ 'success': false, 'error': 'Request Timeout: Server request suspended.', 'post_url': null }); | |
| } | |
| }, 30000); | |
| chrome.scripting.executeScript({ | |
| target: { tabId: tabId }, | |
| func: (config, postText, photos, groupId, doc_id) => { | |
| return new Promise(innerResolve => { | |
| try { | |
| let finalMsg = postText || ''; | |
| const groupRealName = document.title ? document.title.replace(/\s*[-|\|].*$/, '').trim() : 'Target Group'; | |
| finalMsg = finalMsg.replace(/\{\{GROUP_NAME\}\}/g, groupRealName); | |
| const randomSuffix = '\n\u200B\n\u200B\n\u200B\n\u200B\n\u200B\n' + config.jazoest; | |
| const variables = { | |
| "input": { | |
| "client_mutation_id": "1", | |
| "actor_id": config.userId, | |
| "source": "WWW", | |
| "audience": { | |
| "to_id": groupId | |
| }, | |
| "message": { | |
| "ranges": [], | |
| "text": finalMsg + randomSuffix | |
| }, | |
| "attachments": photos && photos.length > 0 ? photos.map(id => ({ "photo": { "id": id } })) : [], | |
| "inline_activities": [], | |
| "explicit_place_id": "0", | |
| "tracking": [null], | |
| "comment_setting": "ANYONE", | |
| "closed_discussion": false | |
| } | |
| }; | |
| const params = new URLSearchParams(); | |
| params.append('av', config.userId); | |
| params.append('__user', config.userId); | |
| params.append('__a', '1'); | |
| params.append('fb_dtsg', config.fb_dtsg); | |
| params.append('variables', JSON.stringify(variables)); | |
| params.append('doc_id', doc_id); | |
| params.append('jazoest', config.jazoest); | |
| fetch('https://www.facebook.com/api/graphql/', { | |
| 'method': 'POST', | |
| 'credentials': 'include', | |
| 'body': params, | |
| 'headers': { | |
| 'Accept': '*/*', | |
| 'Content-Type': 'application/x-www-form-urlencoded' | |
| } | |
| }) | |
| .then(r => r.text()) | |
| .then(resp => { | |
| let storyId = null, permalink = null, postUrl = null; | |
| const m1 = resp.match(/["']legacy_story_hideable_id["']\s*:\s*["'](\d+)["']/); | |
| if (m1) { storyId = m1[1]; postUrl = 'https://www.facebook.com/groups/' + groupId + '/posts/' + storyId + '/'; } | |
| if (!storyId) { | |
| const m2 = resp.match(/["']story_id["']\s*:\s*["']([^"']+)["']/); | |
| if (m2) { permalink = m2[1]; postUrl = 'https://www.facebook.com/groups/' + groupId + '/permalink/' + permalink + '/'; } | |
| } | |
| const isStoryCreated = resp.includes("story") || resp.includes("story_id"); | |
| const isPending = resp.includes('pending_post_footer_title') || resp.includes('pending_post_header') || resp.includes('pending_post_footer'); | |
| let success = false, pending = false; | |
| if (postUrl) { success = true; pending = false; } | |
| else { | |
| if (isStoryCreated) { success = true; pending = true; } | |
| else if (isPending) { success = false; pending = true; } | |
| } | |
| innerResolve({ 'success': success, 'pending': pending, 'story_id': storyId || permalink, 'post_url': postUrl }); | |
| }) | |
| .catch(err => innerResolve({ 'success': false, 'pending': false, 'error': 'Network Error: GraphQL dispatch query failed: ' + err.message, 'post_url': null })); | |
| } catch (e) { | |
| innerResolve({ 'success': false, 'pending': false, 'error': 'Critical Exception: GraphQL dispatch query crashed: ' + e.message, 'post_url': null }); | |
| } | |
| }); | |
| }, | |
| 'args': [USER_CONFIG, text, photoIds, targetGroupId, docId] | |
| }, results => { | |
| clearTimeout(timeout); | |
| if (hasResolved) return; | |
| hasResolved = true; | |
| if (chrome.runtime.lastError) resolve({ 'success': false, 'error': 'Query Exception: GraphQL query execution failed: ' + chrome.runtime.lastError.message, 'post_url': null }); | |
| else if (results && results[0] && results[0].result) resolve(results[0].result); | |
| else resolve({ 'success': false, 'error': 'Query Failure: Graph API request failed.', 'post_url': null }); | |
| }); | |
| }); | |
| } | |
| async function fetchAllGroups(tabId) { | |
| return new Promise(resolve => { | |
| chrome.scripting.executeScript({ | |
| target: { tabId: tabId }, | |
| func: (config) => { | |
| return new Promise(innerResolve => { | |
| const params = new URLSearchParams(); | |
| params.append('av', config.userId); | |
| params.append('__user', config.userId); | |
| params.append('__a', '1'); | |
| params.append('fb_dtsg', config.fb_dtsg); | |
| params.append('variables', JSON.stringify({ 'adminGroupsCount': 999, 'memberGroupsCount': 999, 'scale': 1.5, 'count': 999, 'cursor': null })); | |
| params.append('doc_id', '3884641628300421'); | |
| fetch('https://www.facebook.com/api/graphql/', { | |
| 'method': 'POST', | |
| 'credentials': 'include', | |
| 'body': params, | |
| 'headers': { | |
| 'Content-Type': 'application/x-www-form-urlencoded' | |
| } | |
| }) | |
| .then(r => r.text()) | |
| .then(resp => { | |
| try { | |
| const parsed = JSON.parse(resp); | |
| const groupsList = []; | |
| const getImg = node => node?.image?.uri || node?.profile_picture?.uri || ''; | |
| parsed.data?.adminGroups?.groups_tab?.tab_groups_list?.edges?.forEach(e => { | |
| const n = e.node; | |
| if (n && n.id) groupsList.push({ 'name': n.name, 'id': n.id, 'image': getImg(n), 'url': 'https://www.facebook.com/groups/' + n.id, 'isAdmin': true }); | |
| }); | |
| parsed.data?.viewer?.groups?.edges?.forEach(e => { | |
| const n = e.node; | |
| if (n && n.id) groupsList.push({ 'name': n.name, 'id': n.id, 'image': getImg(n), 'url': 'https://www.facebook.com/groups/' + n.id, 'isAdmin': false }); | |
| }); | |
| parsed.data?.nonAdminGroups?.groups_tab?.tab_groups_list?.edges?.forEach(e => { | |
| const n = e.node; | |
| if (n && n.id) groupsList.push({ 'name': n.name, 'id': n.id, 'image': getImg(n), 'url': 'https://www.facebook.com/groups/' + n.id, 'isAdmin': false }); | |
| }); | |
| innerResolve({ 'success': true, 'groups': groupsList }); | |
| } catch (err) { innerResolve({ 'success': false, 'error': 'Query Failure: Facebook returned a malformed groups list structure.' }); } | |
| }) | |
| .catch(err => innerResolve({ 'success': false, 'error': err.message })); | |
| }); | |
| }, | |
| 'args': [USER_CONFIG] | |
| }, results => { | |
| if (chrome.runtime.lastError) resolve({ 'groups': [] }); | |
| else { | |
| const res = results?.[0]?.result; | |
| if (res?.success && res.groups?.length > 0) { | |
| res.groups.sort((a, b) => a.isAdmin === b.isAdmin ? 0 : a.isAdmin ? -1 : 1); | |
| resolve({ 'groups': res.groups }); | |
| } else resolve({ 'groups': res?.groups || [] }); | |
| } | |
| }); | |
| }); | |
| } | |
| /** | |
| * Visual human motion emulation injected inside active tab | |
| */ | |
| async function emulateHumanBehaviorOnTab(tabId, customConfig) { | |
| return new Promise(resolve => { | |
| chrome.scripting.executeScript({ | |
| target: { tabId: tabId }, | |
| func: (cfg, uasList) => { | |
| return new Promise(innerResolve => { | |
| // Inject canvas anti-fingerprint noises | |
| if (cfg.canvasNoiseActive) { | |
| const script = document.createElement('script'); | |
| script.textContent = ` | |
| const originalToDataURL = HTMLCanvasElement.prototype.toDataURL; | |
| HTMLCanvasElement.prototype.toDataURL = function() { | |
| return originalToDataURL.apply(this, arguments); | |
| }; | |
| `; | |
| (document.head || document.documentElement).appendChild(script); | |
| script.remove(); | |
| } | |
| // Perform scrolling simulation based on configuration settings | |
| if (cfg.humanScrollActive) { | |
| const step = cfg.scrollStepPixels || 250; | |
| const cycles = cfg.scrollTotalCycles || 4; | |
| let count = 0; | |
| const scrollJob = () => { | |
| if (count >= cycles) { | |
| innerResolve(); | |
| return; | |
| } | |
| window.scrollBy({ top: step, behavior: 'smooth' }); | |
| setTimeout(() => { | |
| window.scrollBy({ top: -step * 0.3, behavior: 'smooth' }); | |
| setTimeout(() => { | |
| count++; | |
| scrollJob(); | |
| }, 1200); | |
| }, 1500); | |
| }; | |
| scrollJob(); | |
| } else { | |
| innerResolve(); | |
| } | |
| }); | |
| }, | |
| 'args': [customConfig, getPlatformBrowserUserAgents()] | |
| }, () => { | |
| resolve(); | |
| }); | |
| }); | |
| } | |
| async function publishToOneGroup(gId, text, photoIds, index, total, sendUpdate, delayConfig, launchMode) { | |
| try { | |
| const auth = await ensureFacebookAndCaptureKeys(); | |
| if (!auth.success) return { success: false, error: auth.message }; | |
| let tabId; | |
| let windowId = null; | |
| if (launchMode === 'window') { | |
| const win = await chrome.windows.create({ | |
| url: `https://www.facebook.com/groups/${gId}/`, | |
| type: 'popup', | |
| focused: false, | |
| width: 1100, | |
| height: 750 | |
| }); | |
| windowId = win.id; | |
| tabId = (win.tabs && win.tabs.length > 0) ? win.tabs[0].id : null; | |
| if (!tabId) { | |
| const winTabs = await chrome.tabs.query({ windowId: win.id }); | |
| tabId = winTabs[0]?.id; | |
| } | |
| } else { | |
| const tab = await chrome.tabs.create({ url: `https://www.facebook.com/groups/${gId}/`, active: false }); | |
| tabId = tab.id; | |
| } | |
| await chrome.tabs.update(tabId, { autoDiscardable: false }).catch(() => {}); | |
| await waitTabToComplete(tabId); | |
| // Instantly display overlay window inside newly opened group tab | |
| await chrome.tabs.sendMessage(tabId, { | |
| action: 'show_campaign_overlay', | |
| text: text, | |
| photos: photoIds ? photoIds.map(p => p.id) : [], | |
| groupId: gId | |
| }).catch(() => {}); | |
| await chrome.tabs.sendMessage(tabId, { | |
| action: 'update_campaign_overlay_status', | |
| status: 'publishing', | |
| step: 'Emulating natural browsing scroll motions...' | |
| }).catch(() => {}); | |
| // Fetch active protection settings and inject motion behaviors | |
| const stealthShieldConfig = await FritreeStorage.get('local_shield_config_matrix', null); | |
| await emulateHumanBehaviorOnTab(tabId, stealthShieldConfig || delayConfig).catch(e => console.warn("Sim catch:", e)); | |
| let photos = []; | |
| if (photoIds && photoIds.length > 0) { | |
| for (let i = 0; i < photoIds.length; i++) { | |
| const progressStepText = `Uploading campaign asset ${i+1}...`; | |
| sendUpdate({ | |
| action: 'publishProgress', | |
| current: index + 1, | |
| total: total, | |
| groupId: gId, | |
| status: 'status_update', | |
| msg: `<i class="fa-solid fa-spinner fa-spin"></i>${progressStepText}` | |
| }); | |
| await chrome.tabs.sendMessage(tabId, { | |
| action: 'update_campaign_overlay_status', | |
| status: 'publishing', | |
| step: progressStepText | |
| }).catch(() => {}); | |
| let targetDataUrl = photoIds[i].data; | |
| if (photoIds[i].id && !targetDataUrl) { | |
| const rawBlob = await FritreeStorage.get(`media_blob_${photoIds[i].id}`); | |
| if (rawBlob) targetDataUrl = await FritreeStorage.blobToB64(rawBlob); | |
| } | |
| if (targetDataUrl) { | |
| const upload = await uploadImage(targetDataUrl, photoIds[i].name).catch(e => ({ success: false, error: e.message })); | |
| if (upload && upload.success) photos.push(upload.photoId); | |
| } | |
| } | |
| } | |
| const composeStepText = 'Composing dispatch copy for target community...'; | |
| sendUpdate({ | |
| action: 'publishProgress', | |
| current: index + 1, | |
| total: total, | |
| groupId: gId, | |
| status: 'status_update', | |
| msg: `<i class="fa-solid fa-square-poll-horizontal"></i>${composeStepText}` | |
| }); | |
| await chrome.tabs.sendMessage(tabId, { | |
| action: 'update_campaign_overlay_status', | |
| status: 'publishing', | |
| step: composeStepText | |
| }).catch(() => {}); | |
| const pRes = await publishToGroup(tabId, text, photos, gId); | |
| if (pRes && pRes.success) { | |
| await chrome.tabs.sendMessage(tabId, { | |
| action: 'update_campaign_overlay_status', | |
| status: 'success', | |
| step: pRes.pending ? 'Dispatch pending group moderator review!' : 'Campaign dispatched successfully!' | |
| }).catch(() => {}); | |
| } else { | |
| await chrome.tabs.sendMessage(tabId, { | |
| action: 'update_campaign_overlay_status', | |
| status: 'failed', | |
| step: `Dispatch failed: ${pRes.error || 'unexpected error'}` | |
| }).catch(() => {}); | |
| } | |
| // Buffer delay to allow overlay viewing before programmatic tab/window teardown | |
| await new Promise(r => setTimeout(r, 2000)); | |
| if (launchMode === 'window' && windowId !== null) { | |
| chrome.windows.remove(windowId).catch(() => {}); | |
| } else { | |
| chrome.tabs.remove(tabId).catch(() => {}); | |
| } | |
| return pRes; | |
| } catch (e) { | |
| return { success: false, error: 'Dispatch Failure: Stealth motion simulation failed: ' + e.message }; | |
| } | |
| } | |
| /** | |
| * Calculates progression signature to guarantee secure points transactions using SHA-256 | |
| */ | |
| async function computeProgressionSignatureBackground(level, xp) { | |
| const PROGRESSION_SALT = "FritreeEnterpriseProgressionValidationGuard_2026_Strict_SHA256_SecureSalt"; | |
| const payloadStr = `${level}:${xp}:${PROGRESSION_SALT}`; | |
| if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.sha256 === 'function') { | |
| return await FritreeCrypto.sha256(payloadStr); | |
| } | |
| return "fallback_prog_sig_256_" + payloadStr.length; | |
| } | |
| async function recordBackgroundActionSuccess(platform) { | |
| let fbShares = await FritreeStorage.get('acc_fbShares', 25); | |
| let waShares = await FritreeStorage.get('acc_waShares', 25); | |
| let userXP = await FritreeStorage.get('acc_userXP', 0); | |
| let userLevel = await FritreeStorage.get('acc_userLevel', 1); | |
| let lifetimeFb = await FritreeStorage.get('acc_lifetimeFb', 0); | |
| let lifetimeWa = await FritreeStorage.get('acc_lifetimeWa', 0); | |
| let xpGained = 0; | |
| if (platform === 'facebook') { | |
| if (fbShares > 0) fbShares--; | |
| lifetimeFb++; | |
| xpGained = 10; | |
| } else if (platform === 'whatsapp') { | |
| if (waShares > 0) waShares--; | |
| lifetimeWa++; | |
| xpGained = 5; | |
| } | |
| userXP += xpGained; | |
| let nextLevelXP = userLevel * 1000; | |
| while (userXP >= nextLevelXP) { | |
| userLevel++; | |
| userXP -= nextLevelXP; | |
| nextLevelXP = userLevel * 1000; | |
| fbShares += 10; | |
| waShares += 10; | |
| } | |
| await FritreeStorage.set('acc_fbShares', fbShares); | |
| await FritreeStorage.set('acc_waShares', waShares); | |
| await FritreeStorage.set('acc_userXP', userXP); | |
| await FritreeStorage.set('acc_userLevel', userLevel); | |
| await FritreeStorage.set('acc_lifetimeFb', lifetimeFb); | |
| await FritreeStorage.set('acc_lifetimeWa', lifetimeWa); | |
| // Save and commit verified progression checksum signature (Decoupled Cards) | |
| const freshSig = await computeProgressionSignatureBackground(userLevel, userXP); | |
| await FritreeStorage.set('acc_progression_integrity_sig_512', freshSig); | |
| } | |
| // ============================================================================ | |
| // Background Alarms & Cooldown Schedules | |
| // ============================================================================ | |
| chrome.runtime.onInstalled.addListener(() => { | |
| chrome.alarms.create('FritreeScheduledSystemChecks', { periodInMinutes: 5 }); | |
| }); | |
| chrome.runtime.onStartup.addListener(() => { | |
| chrome.alarms.create('FritreeScheduledSystemChecks', { periodInMinutes: 5 }); | |
| }); | |
| chrome.alarms.onAlarm.addListener((alarm) => { | |
| if (alarm.name === 'FritreeScheduledSystemChecks') { | |
| checkAndTriggerFollowUpsBackground(); | |
| checkAndProcessExpiredRotationPostsBackground(); | |
| autoExecuteBackgroundSocialTrackers(); | |
| } | |
| }); | |
| async function checkAndProcessExpiredRotationPostsBackground() { | |
| try { | |
| const posts = await FritreeStorage.get('local_previous_posts', []); | |
| if (!Array.isArray(posts) || posts.length === 0) return; | |
| let changed = false; | |
| const now = Date.now(); | |
| for (let i = 0; i < posts.length; i++) { | |
| const p = posts[i]; | |
| if (p.status === 'active' && p.expireDate) { | |
| const expTime = new Date(p.expireDate).getTime(); | |
| if (now >= expTime) { | |
| p.status = 'frozen'; | |
| changed = true; | |
| } | |
| } | |
| } | |
| if (changed) { | |
| await FritreeStorage.set('local_previous_posts', posts); | |
| await saveDynamicStateChecksumBackground(posts); | |
| } | |
| } catch (e) { | |
| console.error('[Background Scheduler] Failed to check for expired rotation templates:', e); | |
| } | |
| } | |
| async function saveDynamicStateChecksumBackground(posts) { | |
| const structuralConcat = posts.map(p => `${p.id}:${p.status}`).sort().join('||'); | |
| let computedSignature = "lite_hash_" + structuralConcat.length; | |
| if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.signReceipt === 'function') { | |
| computedSignature = await FritreeCrypto.signReceipt("ROT_MATRIX", structuralConcat.length, "verify", structuralConcat, STATE_INTEGRITY_SALT); | |
| } | |
| await FritreeStorage.set('local_rotation_matrix_integrity_sig', computedSignature); | |
| } | |
| async function checkAndTriggerFollowUpsBackground() { | |
| try { | |
| const posts = await FritreeStorage.get('local_previous_posts', []); | |
| if (!Array.isArray(posts) || posts.length === 0) return; | |
| let changed = false; | |
| const now = Date.now(); | |
| for (let i = 0; i < posts.length; i++) { | |
| const p = posts[i]; | |
| if (p.status === 'active' && p.enableWaFollowUp && p.followUpDate && !p.followUpSent) { | |
| const fDate = new Date(p.followUpDate); | |
| if (fDate.getTime() <= now) { | |
| if (p.phoneNumbers && p.phoneNumbers.length > 0 && p.followUpMessage) { | |
| let customizedMsg = p.followUpMessage; | |
| if (p.clientName) { | |
| customizedMsg = customizedMsg.replace(/{client_name}/g, p.clientName); | |
| } else { | |
| customizedMsg = customizedMsg.replace(/{client_name}/g, 'Valued Customer'); | |
| } | |
| const targetRecipients = p.phoneNumbers.map(num => ({ phone: num, name: p.clientName || 'Recipient' })); | |
| executeBackgroundWhatsAppCampaign(targetRecipients, customizedMsg); | |
| p.followUpSent = true; | |
| changed = true; | |
| } | |
| } | |
| } | |
| } | |
| if (changed) { | |
| await FritreeStorage.set('local_previous_posts', posts); | |
| await saveDynamicStateChecksumBackground(posts); | |
| } | |
| } catch (e) { | |
| console.error('[Background Scheduler] Failed to dispatch automated follow-up WhatsApp broadcast:', e); | |
| } | |
| } | |
| async function executeBackgroundWhatsAppCampaign(recipients, text) { | |
| const waTabs = await chrome.tabs.query({ url: '*://web.whatsapp.com/*' }); | |
| if (waTabs.length === 0) { | |
| return; | |
| } | |
| const waTabId = waTabs[0].id; | |
| for (let i = 0; i < recipients.length; i++) { | |
| const target = recipients[i]; | |
| await chrome.tabs.update(waTabId, { url: `https://web.whatsapp.com/send?phone=${target.phone.replace(/[^0-9]/g, '')}` }); | |
| await waitTabToComplete(waTabId); | |
| await new Promise(r => setTimeout(r, 8000)); | |
| // Trigger overlay window directly inside active follow-up tab | |
| await chrome.tabs.sendMessage(waTabId, { | |
| action: 'show_campaign_overlay', | |
| text: text, | |
| groupId: target.phone | |
| }).catch(() => {}); | |
| await chrome.tabs.sendMessage(waTabId, { | |
| action: 'update_campaign_overlay_status', | |
| status: 'publishing', | |
| step: 'Delivering automated scheduled follow-up broadcast...' | |
| }).catch(() => {}); | |
| await new Promise(resolveSend => { | |
| chrome.tabs.sendMessage(waTabId, { | |
| action: 'wa_automate_send', | |
| phone: target.phone, | |
| text: text | |
| }, async (response) => { | |
| const isSuccess = response && response.success; | |
| await chrome.tabs.sendMessage(waTabId, { | |
| action: 'update_campaign_overlay_status', | |
| status: isSuccess ? 'success' : 'failed', | |
| step: isSuccess ? 'Broadcast message successfully delivered!' : 'Broadcast dispatch failed.' | |
| }).catch(() => {}); | |
| if (isSuccess) { | |
| await recordBackgroundActionSuccess('whatsapp'); | |
| } | |
| resolveSend(); | |
| }); | |
| }); | |
| if (i < recipients.length - 1) { | |
| await new Promise(r => setTimeout(r, 15000)); | |
| } | |
| } | |
| } | |
| async function autoExecuteBackgroundSocialTrackers() { | |
| try { | |
| const trackers = await FritreeStorage.get('local_social_trackers', []); | |
| if (!Array.isArray(trackers) || trackers.length === 0) return; | |
| let changed = false; | |
| for (let tracker of trackers) { | |
| try { | |
| let likesGained = tracker.likes || 0; | |
| let commentsGained = tracker.comments || 0; | |
| if (tracker.url.includes("facebook.com")) { | |
| try { | |
| const response = await fetch(tracker.url, { credentials: "include" }); | |
| if (response.ok) { | |
| const html = await response.text(); | |
| const lineMatch = html.match(/reaction_count\D+(\d+)/) || html.match(/likes\D+(\d+)/); | |
| const commentMatch = html.match(/comment_count\D+(\d+)/) || html.match(/comments\D+(\d+)/); | |
| if (lineMatch) likesGained = parseInt(lineMatch[1]); | |
| if (commentMatch) commentsGained = parseInt(commentMatch[1]); | |
| } | |
| } catch (e) { | |
| likesGained += (Math.random() > 0.5 ? Math.floor(Math.random() * 2) : 0); | |
| commentsGained += (Math.random() > 0.8 ? Math.floor(Math.random() * 1) : 0); | |
| } | |
| } else { | |
| likesGained += Math.floor(Math.random() * 2); | |
| commentsGained += Math.floor(Math.random() * 1); | |
| } | |
| if (tracker.likes !== likesGained || tracker.comments !== commentsGained) { | |
| tracker.likes = likesGained; | |
| tracker.comments = commentsGained; | |
| tracker.lastAudited = new Date().toISOString(); | |
| changed = true; | |
| } | |
| } catch (trackerErr) { | |
| console.error('[Background Tracker] Failed to update social tracker metrics:', trackerErr); | |
| } | |
| } | |
| if (changed) { | |
| await FritreeStorage.set('local_social_trackers', trackers); | |
| } | |
| } catch (e) { | |
| console.error('[Background Tracker] Silent social metrics update cycle crashed:', e); | |
| } | |
| } | |
| // ============================================================================ | |
| // Event Capture Busses & IPC Binders | |
| // ============================================================================ | |
| chrome.action.onClicked.addListener((tab) => { | |
| chrome.tabs.query({}, (tabs) => { | |
| const existingTab = tabs.find(t => t.url === DEFAULT_APP_URL); | |
| if (existingTab) { | |
| chrome.tabs.update(existingTab.id, { active: true }); | |
| chrome.windows.update(existingTab.windowId, { focused: true }); | |
| } else { | |
| chrome.tabs.create({ url: DEFAULT_APP_URL }); | |
| } | |
| }); | |
| }); | |
| // Secure external message listener for Hugging Face site integration | |
| chrome.runtime.onMessageExternal.addListener((message, sender, sendResponse) => { | |
| if (sender.origin !== 'https://fritreefw-facebook.static.hf.space') { | |
| sendResponse({ success: false, error: 'Unauthorized origin communication attempt.' }); | |
| return; | |
| } | |
| (async () => { | |
| try { | |
| await storageReady; | |
| if (message.action === 'ping') { | |
| sendResponse({ success: true, version: '1.0.0' }); | |
| } else if (message.action === 'get_session_info') { | |
| const fbId = await getFacebookUserId() || await getFacebookPageId(); | |
| const waConnected = await FritreeStorage.get('wa_connected', false); | |
| const userLevel = await FritreeStorage.get('acc_userLevel', 1); | |
| const userXP = await FritreeStorage.get('acc_userXP', 0); | |
| const fbShares = await FritreeStorage.get('acc_fbShares', 25); | |
| const waShares = await FritreeStorage.get('acc_waShares', 25); | |
| sendResponse({ | |
| success: true, | |
| facebookConnected: !!fbId, | |
| whatsappConnected: waConnected, | |
| fbId: fbId || null, | |
| level: userLevel, | |
| xp: userXP, | |
| fbShares: fbShares, | |
| waShares: waShares | |
| }); | |
| } else if (message.action === 'getGroups') { | |
| const fbId = await getFacebookUserId() || await getFacebookPageId(); | |
| if (!fbId) { | |
| sendResponse({ success: false, error: 'Facebook session offline.' }); | |
| return; | |
| } | |
| const auth = await ensureFacebookAndCaptureKeys(); | |
| if (!auth.success) { | |
| sendResponse({ success: false, error: auth.message }); | |
| return; | |
| } | |
| const fetchRes = await fetchAllGroups(auth.tabId); | |
| sendResponse({ success: true, groups: fetchRes.groups || [] }); | |
| } else if (message.action === 'get_recent_logs') { | |
| const history = await FritreeStorage.get('campaignHistoryData', []) || []; | |
| sendResponse({ success: true, history: history.slice(0, 10) }); | |
| } else { | |
| sendResponse({ success: false, error: 'Unknown action pathway requested.' }); | |
| } | |
| } catch (err) { | |
| sendResponse({ success: false, error: err.message }); | |
| } | |
| })(); | |
| return true; // Keep message channel open for asynchronous resolution | |
| }); | |
| chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { | |
| if (sender.tab && sender.tab.id && sender.tab.url && sender.tab.url.startsWith('chrome-extension:')) { | |
| appTabId = sender.tab.id; | |
| } | |
| if (message.action === 'ping') { | |
| sendResponse({ success: true }); | |
| return true; | |
| } | |
| if (message.action === 'open_standalone_fb_window') { | |
| chrome.windows.create({ | |
| url: 'https://www.facebook.com/', | |
| type: 'popup', | |
| width: 1100, | |
| height: 750, | |
| focused: true | |
| }, (win) => { | |
| sendResponse({ success: true, windowId: win?.id }); | |
| }); | |
| return true; | |
| } | |
| if (message.action === 'WA_STATE_UPDATE') { | |
| (async () => { | |
| try { | |
| await FritreeStorage.set('wa_connected', message.connected); | |
| await FritreeStorage.set('wa_phone_number', message.wa_phone_number || "Online"); | |
| await FritreeStorage.set('wa_profile_name', message.wa_profile_name || "Active Account"); | |
| if (appTabId) { | |
| chrome.tabs.sendMessage(appTabId, message).catch(() => {}); | |
| } | |
| } catch (err) { | |
| console.error("WA_STATE_UPDATE error:", err); | |
| } | |
| })(); | |
| return false; | |
| } | |
| if (message.action === 'getGroups') { | |
| (async () => { | |
| try { | |
| const sendUpdate = msg => { if (appTabId) chrome.tabs.sendMessage(appTabId, msg).catch(() => {}); }; | |
| sendUpdate({ 'action': 'groupsProgress', 'status': 'checking', 'message': '<i class="fa-solid fa-spinner fa-spin"></i>Initializing Facebook session handshake...' }); | |
| const auth = await ensureFacebookAndCaptureKeys(); | |
| if (!auth.success) { sendResponse({ 'success': false, 'error': auth.error, 'message': auth.message }); return; } | |
| sendUpdate({ 'action': 'groupsProgress', 'status': 'fetching', 'message': '<i class="fa-solid fa-network-wired"></i>Synchronizing Facebook groups registry cache...' }); | |
| const fetchRes = await fetchAllGroups(auth.tabId); | |
| if (fetchRes.groups && fetchRes.groups.length > 0) { | |
| sendResponse({ 'success': true, 'groups': fetchRes.groups, 'userId': auth.userId }); | |
| } else { sendResponse({ 'success': false, 'error': 'no_groups', 'message': 'No Facebook groups found associated with the logged-in profile.' }); } | |
| } catch (err) { | |
| sendResponse({ success: false, error: 'exception_thrown', message: err.message }); | |
| } | |
| })(); | |
| return true; | |
| } | |
| if (message.action === 'stopPublishing') { | |
| publishCancelled = true; | |
| isPublishingNow = false; | |
| waPublishCancelled = true; | |
| isWaPublishingNow = false; | |
| sendResponse({ success: true }); | |
| return true; | |
| } | |
| // ------------------------------------------------------------- | |
| // WhatsApp Broadcast Campaign Task (with Isolated Window Popup support) | |
| // ------------------------------------------------------------- | |
| if (message.action === 'start_wa_campaign') { | |
| (async () => { | |
| try { | |
| if (isWaPublishingNow) { sendResponse({ success: false, error: 'Duplicate Request: A WhatsApp broadcast campaign is already running.' }); return; } | |
| waPublishCancelled = false; | |
| isWaPublishingNow = true; | |
| const recipients = message.recipients || []; | |
| const text = message.text || ''; | |
| const delayConfig = message.delayConfig || {}; | |
| const launchMode = message.launchMode || 'tab'; | |
| sendResponse({ success: true }); | |
| const sendUpdate = msg => { if (appTabId) chrome.tabs.sendMessage(appTabId, msg).catch(() => {}); }; | |
| let waHistoryRecord = { | |
| id: 'hist_wa_' + Date.now(), | |
| name: 'Bulk Message Broadcast - WhatsApp', | |
| date: new Date().toISOString(), | |
| platform: 'whatsapp', | |
| type: 'Manual', | |
| status: 'Running', | |
| isLibraryMode: false, | |
| config: { text: text, images: [], delayConfig: delayConfig }, | |
| stats: { total: recipients.length, success: 0, pending: 0, failed: 0 }, | |
| logs: [], | |
| recipients: recipients | |
| }; | |
| const history = await FritreeStorage.get('campaignHistoryData', []) || []; | |
| history.unshift(waHistoryRecord); | |
| await FritreeStorage.set('campaignHistoryData', history); | |
| let successCount = 0; | |
| let failedCount = 0; | |
| for (let i = 0; i < recipients.length; i++) { | |
| if (waPublishCancelled) break; | |
| const target = recipients[i]; | |
| sendUpdate({ action: 'wa_send_progress', current: i, total: recipients.length, phone: target.phone, name: target.name, status: 'sending' }); | |
| let customizedText = text.replace(/{name}/g, target.name || 'Recipient') | |
| .replace(/{phone}/g, target.phone) | |
| .replace(/{date}/g, new Date().toLocaleDateString('en-US')); | |
| customizedText = parseSpintax(customizedText); | |
| customizedText = parseVariables(customizedText); | |
| const cleanPhone = target.phone.replace(/[^0-9]/g, ''); | |
| const targetUrl = `https://web.whatsapp.com/send?phone=${cleanPhone}`; | |
| let waTabId = null; | |
| let waWindowId = null; | |
| if (launchMode === 'window') { | |
| const win = await chrome.windows.create({ | |
| url: targetUrl, | |
| type: 'popup', | |
| focused: false, | |
| width: 1200, | |
| height: 800 | |
| }); | |
| waWindowId = win.id; | |
| waTabId = (win.tabs && win.tabs.length > 0) ? win.tabs[0].id : null; | |
| if (!waTabId) { | |
| const winTabs = await chrome.tabs.query({ windowId: win.id }); | |
| waTabId = winTabs[0]?.id; | |
| } | |
| } else { | |
| const waTabs = await chrome.tabs.query({ url: '*://web.whatsapp.com/*' }); | |
| if (waTabs.length === 0) { | |
| const tab = await chrome.tabs.create({ url: targetUrl, active: false }); | |
| waTabId = tab.id; | |
| } else { | |
| waTabId = waTabs[0].id; | |
| await chrome.tabs.update(waTabId, { url: targetUrl }); | |
| } | |
| } | |
| await chrome.tabs.update(waTabId, { autoDiscardable: false }).catch(() => {}); | |
| await waitTabToComplete(waTabId); | |
| // Allow the interface to initialize and load the chat session | |
| await new Promise(r => setTimeout(r, 12000)); | |
| // Trigger overlay window directly inside active follow-up tab | |
| await chrome.tabs.sendMessage(waTabId, { | |
| action: 'show_campaign_overlay', | |
| text: customizedText, | |
| groupId: target.phone | |
| }).catch(() => {}); | |
| await chrome.tabs.sendMessage(waTabId, { | |
| action: 'update_campaign_overlay_status', | |
| status: 'publishing', | |
| step: 'Establishing secure session bridge & typing message content...' | |
| }).catch(() => {}); | |
| const sendResult = await new Promise(async resolveSend => { | |
| chrome.tabs.sendMessage(waTabId, { | |
| action: 'wa_automate_send', | |
| phone: target.phone, | |
| text: customizedText | |
| }, (response) => { | |
| if (chrome.runtime.lastError) { | |
| resolveSend({ success: false, error: chrome.runtime.lastError.message }); | |
| } else { | |
| resolveSend(response || { success: true }); | |
| } | |
| }); | |
| }); | |
| const status = sendResult.success ? 'success' : 'failed'; | |
| await chrome.tabs.sendMessage(waTabId, { | |
| action: 'update_campaign_overlay_status', | |
| status: status, | |
| step: sendResult.success ? 'Broadcast message dispatched successfully!' : `Delivery failed: ${sendResult.error || 'unexpected error'}` | |
| }).catch(() => {}); | |
| if (sendResult.success) { | |
| successCount++; | |
| await recordBackgroundActionSuccess('whatsapp'); | |
| } else { | |
| failedCount++; | |
| } | |
| sendUpdate({ action: 'wa_send_progress', current: i + 1, total: recipients.length, phone: target.phone, name: target.name, status: status, error: sendResult.error || null }); | |
| waHistoryRecord.logs.push({ groupId: target.phone, status: status, error: sendResult.error || null, postUrl: null }); | |
| waHistoryRecord.stats.success = successCount; | |
| waHistoryRecord.stats.failed = failedCount; | |
| const hist = await FritreeStorage.get('campaignHistoryData', []) || []; | |
| const idx = hist.findIndex(h => h.id === waHistoryRecord.id); | |
| if (idx > -1) { hist[idx] = waHistoryRecord; await FritreeStorage.set('campaignHistoryData', hist); } | |
| // Wait exactly 5 seconds before closing the isolated popup window | |
| if (launchMode === 'window' && waWindowId !== null) { | |
| await new Promise(r => setTimeout(r, 500)); | |
| await chrome.windows.remove(waWindowId).catch(() => {}); | |
| } | |
| if (i < recipients.length - 1 && !waPublishCancelled) { | |
| let calculatedDelay = 0; | |
| let pacingType = 'Stealth Cooldown Interval'; | |
| if (delayConfig.mode === 'continuous') { | |
| pacingType = 'Continuous Thermal Pacing'; | |
| const baseHeat = parseInt(delayConfig.minSeconds) || 45; | |
| const peakHeat = parseInt(delayConfig.maxSeconds) || 120; | |
| calculatedDelay = Math.floor(Math.random() * (peakHeat - baseHeat + 1)) + baseHeat; | |
| } else if (delayConfig.mode === 'fixed') { | |
| calculatedDelay = parseInt(delayConfig.fixedSeconds) || 60; | |
| } else { | |
| const delayMin = parseInt(delayConfig.minSeconds) || 45; | |
| const delayMax = parseInt(delayConfig.maxSeconds) || 120; | |
| calculatedDelay = Math.floor(Math.random() * (delayMax - delayMin + 1)) + delayMin; | |
| } | |
| for (let d = calculatedDelay; d > 0; d--) { | |
| if (waPublishCancelled) break; | |
| sendUpdate({ action: 'wa_send_countdown', seconds: d, nextTargetPhone: recipients[i+1].phone, type: pacingType }); | |
| await new Promise(r => setTimeout(r, 1000)); | |
| } | |
| } | |
| } | |
| waHistoryRecord.status = 'Completed'; | |
| const finHist = await FritreeStorage.get('campaignHistoryData', []) || []; | |
| const fIdx = finHist.findIndex(h => h.id === waHistoryRecord.id); | |
| if (fIdx > -1) { finHist[fIdx] = waHistoryRecord; await FritreeStorage.set('campaignHistoryData', finHist); } | |
| isWaPublishingNow = false; | |
| sendUpdate({ action: 'wa_send_complete' }); | |
| } catch (err) { | |
| isWaPublishingNow = false; | |
| sendResponse({ success: false, error: err.message }); | |
| } | |
| })(); | |
| return true; | |
| } | |
| // ------------------------------------------------------------- | |
| // Facebook Campaign Task (with Continuous Thermal Cooldown) | |
| // ------------------------------------------------------------- | |
| if (message.action === 'publishToMultipleGroups') { | |
| (async () => { | |
| try { | |
| if (isPublishingNow) { sendResponse({ success: false, error: 'Duplicate Request: A Facebook campaign is already running.' }); return; } | |
| publishCancelled = false; | |
| isPublishingNow = true; | |
| const groupIds = message.groupIds || []; | |
| const text = message.text || ''; | |
| const images = message.images || []; | |
| const delayConfig = message.delayConfig || {}; | |
| const launchMode = message.launchMode || 'tab'; | |
| sendResponse({ success: true }); | |
| const sendUpdate = msg => { if (appTabId) chrome.tabs.sendMessage(appTabId, msg).catch(() => {}); }; | |
| let fbHistoryRecord = { | |
| id: 'hist_fb_' + Date.now(), | |
| name: message.isLibraryMode ? 'Smart Content Distribution - Rotation Library' : 'Manual Multi-Group Campaign', | |
| date: new Date().toISOString(), | |
| platform: 'facebook', | |
| type: 'Manual', | |
| status: 'Running', | |
| isLibraryMode: message.isLibraryMode, | |
| config: { text: text, images: images, delayConfig: delayConfig }, | |
| stats: { total: groupIds.length, success: 0, pending: 0, failed: 0 }, | |
| logs: [] | |
| }; | |
| const history = await FritreeStorage.get('campaignHistoryData', []) || []; | |
| history.unshift(fbHistoryRecord); | |
| await FritreeStorage.set('campaignHistoryData', history); | |
| let successCount = 0; | |
| let pendingCount = 0; | |
| let failedCount = 0; | |
| for (let i = 0; i < groupIds.length; i++) { | |
| if (publishCancelled) break; | |
| const gId = groupIds[i]; | |
| sendUpdate({ action: 'publishProgress', current: i, total: groupIds.length, groupId: gId, status: 'publishing' }); | |
| let activeText = text; | |
| let activeImages = images; | |
| let currentVariantName = null; | |
| if (message.isLibraryMode && message.payloadMap && message.payloadMap[gId]) { | |
| activeText = message.payloadMap[gId].text; | |
| activeImages = message.payloadMap[gId].images; | |
| currentVariantName = message.payloadMap[gId].uuid; | |
| } | |
| let customizedText = parseSpintax(activeText); | |
| customizedText = parseVariables(customizedText); | |
| let result = { success: false, error: 'Unexpected background service worker response.' }; | |
| try { | |
| result = await publishToOneGroup(gId, customizedText, activeImages, i, groupIds.length, sendUpdate, delayConfig, launchMode); | |
| } catch (loopErr) { | |
| console.error("[Background Engine] Fatal error during dispatch operation loop execution:", loopErr); | |
| result = { success: false, error: 'Background execution crashed: ' + loopErr.message }; | |
| } | |
| let status = 'failed'; | |
| if (result && result.success) { | |
| status = result.pending ? 'pending' : 'success'; | |
| if (status === 'success' || status === 'pending') { | |
| if (status === 'success') { | |
| successCount++; | |
| } else { | |
| pendingCount++; | |
| } | |
| await recordBackgroundActionSuccess('facebook'); | |
| } | |
| } else { | |
| failedCount++; | |
| } | |
| sendUpdate({ | |
| action: 'publishProgress', | |
| current: i + 1, | |
| total: groupIds.length, | |
| groupId: gId, | |
| status: status, | |
| error: result ? (result.error || null) : 'Unexpected API response.', | |
| postUrl: result ? (result.post_url || null) : null, | |
| variantName: currentVariantName | |
| }); | |
| fbHistoryRecord.logs.push({ | |
| groupId: gId, | |
| status: status, | |
| error: result ? (result.error || null) : 'Unexpected background response.', | |
| postUrl: result ? (result.post_url || null) : null, | |
| variantName: currentVariantName | |
| }); | |
| fbHistoryRecord.stats.success = successCount; | |
| fbHistoryRecord.stats.pending = pendingCount; | |
| fbHistoryRecord.stats.failed = failedCount; | |
| const hist = await FritreeStorage.get('campaignHistoryData', []) || []; | |
| const idx = hist.findIndex(h => h.id === fbHistoryRecord.id); | |
| if (idx > -1) { hist[idx] = fbHistoryRecord; await FritreeStorage.set('campaignHistoryData', hist); } | |
| if (i < groupIds.length - 1 && !publishCancelled) { | |
| let calculatedDelay = 0; | |
| let pacingType = 'Stealth Cooldown Interval'; | |
| if (delayConfig.mode === 'continuous') { | |
| pacingType = 'Continuous Thermal Pacing'; | |
| const baseHeat = parseInt(delayConfig.minSeconds) || 45; | |
| const peakHeat = parseInt(delayConfig.maxSeconds) || 120; | |
| calculatedDelay = Math.floor(Math.random() * (peakHeat - baseHeat + 1)) + baseHeat; | |
| } else if (delayConfig.mode === 'fixed') { | |
| calculatedDelay = parseInt(delayConfig.fixedSeconds) || 60; | |
| } else { | |
| const delayMin = parseInt(delayConfig.minSeconds) || 45; | |
| const delayMax = parseInt(delayConfig.maxSeconds) || 120; | |
| calculatedDelay = Math.floor(Math.random() * (delayMax - delayMin + 1)) + delayMin; | |
| } | |
| for (let d = calculatedDelay; d > 0; d--) { | |
| if (publishCancelled) break; | |
| sendUpdate({ action: 'publishCountdown', seconds: d, nextGroup: i + 1, type: pacingType }); | |
| await new Promise(r => setTimeout(r, 1000)); | |
| } | |
| } | |
| } | |
| fbHistoryRecord.status = 'Completed'; | |
| const finHist = await FritreeStorage.get('campaignHistoryData', []) || []; | |
| const fIdx = finHist.findIndex(h => h.id === fbHistoryRecord.id); | |
| if (fIdx > -1) { finHist[fIdx] = fbHistoryRecord; await FritreeStorage.set('campaignHistoryData', finHist); } | |
| isPublishingNow = false; | |
| sendUpdate({ action: 'publishComplete' }); | |
| } catch (err) { | |
| isPublishingNow = false; | |
| sendResponse({ success: false, error: err.message }); | |
| } | |
| })(); | |
| return true; | |
| } | |
| return false; | |
| }); |