fc / fb-background-service.js
Althnayi's picture
Upload 56 files
cca0cf3 verified
Raw
History Blame Contribute Delete
34.6 kB
/**
* @fileoverview Facebook Background Service, Full 600+ Groups Deep Harvester & Session Authenticator
* @module background/fb-background-service
* @description محرك جلب كافة مجموعات فيسبوك بالكامل (All 600+ Joined Groups) عبر التمرير القسري العميق لصفحة joins ومسح الـ Relay Store واستخراج الأسماء والصور الحقيقية 100%.
*/
'use strict';
(global => {
const FB_CONFIG = {
doc_id_groups_shortcuts: '3884641628300421',
doc_id_joined_groups_full: '5007421822699884',
doc_id_post_text: '3559434960802556',
doc_id_post_photo: '4669579913112843',
jazoest: '25450',
upload_id: 'jsc_c_9',
captured: {
userId: null,
fb_dtsg: null,
lsd: null,
dyn: null
}
};
/**
* جلب معرّف المستخدم (c_user) من ملفات تعريف الارتباط
*/
function getFacebookUserId() {
return new Promise(resolve => {
if (typeof chrome === 'undefined' || !chrome.cookies) {
const storedId = global.FritreeStorage ? global.FritreeStorage.get('userId', null) : null;
resolve(storedId);
return;
}
const domains = ['.facebook.com', 'facebook.com', 'web.facebook.com', 'm.facebook.com'];
let resolved = false;
const checkNextDomain = (index) => {
if (index >= domains.length) {
if (!resolved) {
chrome.cookies.getAll({ name: 'c_user' }, allCookies => {
const found = (allCookies || []).find(c => c.domain && c.domain.includes('facebook'));
resolve(found ? found.value : null);
});
}
return;
}
chrome.cookies.getAll({ domain: domains[index], name: 'c_user' }, cookies => {
if (cookies && cookies.length > 0 && cookies[0].value) {
resolved = true;
resolve(cookies[0].value);
} else {
checkNextDomain(index + 1);
}
});
};
checkNextDomain(0);
});
}
function getFacebookPageId() {
return new Promise(resolve => {
if (typeof chrome === 'undefined' || !chrome.cookies) {
resolve(null);
return;
}
chrome.cookies.getAll({ domain: '.facebook.com', name: 'i_user' }, cookies => {
if (cookies && cookies.length > 0 && cookies[0].value) {
resolve(cookies[0].value);
} else {
chrome.cookies.get({ url: 'https://www.facebook.com', name: 'i_user' }, cookie => {
resolve(cookie && cookie.value ? cookie.value : null);
});
}
});
});
}
async function resolveActiveUserId() {
const pageId = await getFacebookPageId();
if (pageId) return pageId;
return await getFacebookUserId();
}
async function extractFbDtsg(tabId) {
return new Promise(resolve => {
if (typeof chrome === 'undefined' || !chrome.scripting) {
resolve({ fb_dtsg: null, lsd: null });
return;
}
chrome.scripting.executeScript({
target: { tabId: tabId },
func: () => {
let dtsg = null, lsd = null;
try {
if (window.DTSGInitialData && window.DTSGInitialData.token) dtsg = window.DTSGInitialData.token;
if (window.DTSGInitData && window.DTSGInitData.token) dtsg = window.DTSGInitData.token;
if (window.LSD && window.LSD.token) lsd = window.LSD.token;
} catch (e) {}
if (!dtsg) {
const inputDtsg = document.querySelector('input[name="fb_dtsg"]') || document.querySelector('[data-dtsg]');
if (inputDtsg) dtsg = inputDtsg.value || inputDtsg.getAttribute('data-dtsg');
}
if (!lsd) {
const inputLsd = document.querySelector('input[name="lsd"]');
if (inputLsd && inputLsd.value) lsd = inputLsd.value;
}
const html = document.documentElement.innerHTML;
if (!dtsg) {
const m = html.match(/["']DTSGInitialData["']\s*,\s*\[\s*\]\s*,\s*\{\s*["']token["']\s*:\s*["']([^"']+)["']/) ||
html.match(/["']token["']\s*:\s*["'](AQ[^"']+)["']/) ||
html.match(/["']token["']\s*:\s*["'](NA[^"']+)["']/) ||
html.match(/async_get_token:"([^"]+)"/) ||
html.match(/"dtsg"\s*:\s*\{\s*"token"\s*:\s*"([^"]+)"/);
if (m) dtsg = m[1];
}
if (!lsd) {
const lm = html.match(/["']LSD["']\s*,\s*\[\s*\]\s*,\s*\{\s*["']token["']\s*:\s*["']([^"']+)["']/) ||
html.match(/"lsd"\s*:\s*"([^"]+)"/);
if (lm) lsd = lm[1];
}
return { fb_dtsg: dtsg, lsd: lsd };
}
}, results => {
if (chrome.runtime.lastError || !results || !results[0]) {
resolve({ fb_dtsg: null, lsd: null });
} else {
resolve(results[0].result || { fb_dtsg: null, lsd: null });
}
});
});
}
async function extractD_Dyn(tabId) {
return new Promise(resolve => {
if (typeof chrome === 'undefined' || !chrome.scripting) {
resolve(null);
return;
}
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 || !results || !results[0]) {
resolve(null);
} else {
resolve(results[0].result);
}
});
});
}
async function refreshFacebookSession(tabId) {
try {
await chrome.scripting.executeScript({
target: { tabId: tabId },
func: () => fetch('https://www.facebook.com/groups/joins/', { credentials: 'include', headers: { Accept: 'text/html' } })
});
await new Promise(r => setTimeout(r, 600));
} catch (e) {
console.warn('[Fritree Facebook Bg] Session ping note:', e.message);
}
}
async function ensureFacebookAndCaptureKeys() {
const activeId = await resolveActiveUserId();
if (!activeId) {
return {
success: false,
error: 'not_logged_in',
message: 'لم يتم العثور على جلسة فيسبوك نشطة. سجل دخول لحسابك على فيسبوك في المتصفح أولاً.'
};
}
FB_CONFIG.captured.userId = activeId;
const tabs = await chrome.tabs.query({ url: ['*://*.facebook.com/*', '*://web.facebook.com/*'] });
let targetTabId = null;
// فتح صفحة دليل المجموعات الكامل joins مباشرة لضمان تحميل الـ 600 مجموعة
if (tabs.length === 0) {
const newTab = await chrome.tabs.create({ url: 'https://www.facebook.com/groups/joins/', active: false });
targetTabId = newTab.id;
await new Promise(r => setTimeout(r, 3500));
} else {
targetTabId = tabs[0].id;
const currentTabUrl = tabs[0].url || '';
if (!currentTabUrl.includes('/groups/joins')) {
await chrome.tabs.update(targetTabId, { url: 'https://www.facebook.com/groups/joins/' });
await new Promise(r => setTimeout(r, 2500));
}
}
await refreshFacebookSession(targetTabId);
let keys = await extractFbDtsg(targetTabId);
if (!keys || !keys.fb_dtsg) {
await chrome.tabs.reload(targetTabId);
await new Promise(r => setTimeout(r, 3000));
keys = await extractFbDtsg(targetTabId);
}
if (keys && keys.fb_dtsg) {
FB_CONFIG.captured.fb_dtsg = keys.fb_dtsg;
FB_CONFIG.captured.lsd = keys.lsd;
FB_CONFIG.captured.dyn = await extractD_Dyn(targetTabId);
if (global.FritreeStorage) {
await global.FritreeStorage.set('fb_dtsg', keys.fb_dtsg);
await global.FritreeStorage.set('lsd', keys.lsd);
await global.FritreeStorage.set('dyn', FB_CONFIG.captured.dyn);
await global.FritreeStorage.set('userId', activeId);
}
}
return { success: true, userId: activeId, tabId: targetTabId };
}
/**
* محرك السحب القسري الشامل لجميع المجموعات (الـ 600+ كاملة بدون أي استثناء)
*/
async function fetchAllFacebookGroups(tabId) {
return new Promise(resolve => {
if (typeof chrome === 'undefined' || !chrome.scripting) {
resolve({ success: false, groups: [] });
return;
}
chrome.scripting.executeScript({
target: { tabId: tabId },
func: (config) => {
return new Promise(async (innerResolve) => {
const groupsMap = new Map();
function parseFacebookGroupMembersText(text) {
if (!text || typeof text !== 'string') return 0;
let cleaned = text.replace(/[٠-٩]/g, d => '٠١٢٣٤٥٦٧٨٩'.indexOf(d));
cleaned = cleaned.replace(/٫/g, '.');
let millionMatch = cleaned.match(/([\d.]+)\s*(مليون|m|مليار)/i);
if (millionMatch) return Math.round(parseFloat(millionMatch[1]) * 1000000);
let thousandMatch = cleaned.match(/([\d.]+)\s*(ألف|الف|آلاف|الاف|k)/i);
if (thousandMatch) return Math.round(parseFloat(thousandMatch[1]) * 1000);
let plainMatch = cleaned.match(/([\d,.]+)\s*(عضو|اعضاء|أعضاء|من الأعضاء|members|member)/i);
if (plainMatch) {
const val = parseFloat(plainMatch[1].replace(/,/g, ''));
return isNaN(val) ? 0 : Math.round(val);
}
return 0;
}
const getImg = node => node?.image?.uri || node?.profile_picture?.uri || node?.group_icon?.uri || node?.custom_icon?.uri || '';
const addGroupToMap = (id, name, img, isAdmin, memberCount) => {
if (!id) return;
const cleanId = String(id).replace(/[^0-9a-zA-Z._-]/g, '');
const blacklistedTerms = ['feed', 'discover', 'joins', 'create', 'search', 'notifications', 'categories', 'chats', 'saved', 'events', 'members', 'about'];
if (blacklistedTerms.includes(cleanId) || cleanId.length < 2) return;
const cleanName = (name || '').split('\n')[0].trim();
if (!cleanName || cleanName.startsWith('مجموعة (') || cleanName === cleanId) return;
if (!groupsMap.has(cleanId)) {
groupsMap.set(cleanId, {
id: cleanId,
name: cleanName,
image: img || '',
url: `https://www.facebook.com/groups/${cleanId}`,
isAdmin: Boolean(isAdmin),
memberCount: parseInt(memberCount, 10) || 0
});
} else {
const existing = groupsMap.get(cleanId);
if (isAdmin) existing.isAdmin = true;
if (!existing.image && img) existing.image = img;
if ((!existing.name || existing.name.includes('(')) && cleanName) existing.name = cleanName;
if (!existing.memberCount && memberCount) existing.memberCount = parseInt(memberCount, 10);
}
};
const processEdges = (edges, isAdmin) => {
if (!edges || !Array.isArray(edges)) return;
edges.forEach(e => {
const n = e.node || e;
if (n && (n.id || n.group_id)) {
const gId = n.id || n.group_id;
const gName = n.name || n.title;
const mCount = n.group_members?.count || n.members?.count || n.member_count || 0;
if (gName) {
addGroupToMap(gId, gName, getImg(n), isAdmin, mCount);
}
}
});
};
// 1. المسح الشامل والعميق لكافة سكريبتات الـ JSON في الذاكرة (Relay Modern Cache)
try {
const scripts = document.querySelectorAll('script[type="application/json"]');
scripts.forEach(s => {
const content = s.textContent || '';
if (content.includes('"Group"') || content.includes('"tab_groups_list"') || content.includes('"all_joined_groups"')) {
try {
const parsed = JSON.parse(content);
const scanObject = (obj) => {
if (!obj || typeof obj !== 'object') return;
if ((obj.__typename === 'Group' || obj.__typename === 'CometGroup') && obj.id && (obj.name || obj.title)) {
const mCount = obj.group_members?.count || obj.members?.count || obj.member_count || 0;
addGroupToMap(obj.id, obj.name || obj.title, getImg(obj), false, mCount);
}
if (obj.tab_groups_list?.edges) processEdges(obj.tab_groups_list.edges, false);
if (obj.all_joined_groups?.edges) processEdges(obj.all_joined_groups.edges, false);
if (obj.viewer?.groups?.edges) processEdges(obj.viewer.groups.edges, false);
if (obj.admin_groups?.edges) processEdges(obj.admin_groups.edges, true);
Object.values(obj).forEach(val => { if (typeof val === 'object') scanObject(val); });
};
scanObject(parsed);
} catch (e) {}
}
});
} catch (rErr) {}
// 2. التمرير القسري المستمر في صفحة دليل المجموعات حتى تحميل الـ 600+ مجموعة بالكامل
try {
const harvestDOM = () => {
const anchors = document.querySelectorAll('a[href*="/groups/"]');
anchors.forEach(a => {
const href = a.getAttribute('href') || '';
const match = href.match(/\/groups\/([0-9a-zA-Z._-]+)/);
if (match && match[1]) {
const gId = match[1].replace(/[^0-9a-zA-Z._-]/g, '');
let groupName = a.innerText?.trim() || a.getAttribute('aria-label') || '';
if (groupName && groupName.length > 1 && !groupName.includes('http') && !groupName.includes('إنشاء')) {
const imgEl = a.querySelector('img') || a.closest('div')?.querySelector('img');
const cardText = a.closest('[role="listitem"], [role="article"], div')?.innerText || '';
const mCount = parseFacebookGroupMembersText(cardText);
addGroupToMap(gId, groupName, imgEl ? imgEl.src : '', false, mCount);
}
}
});
};
harvestDOM();
// تمرير عميق متتابع حتى نهاية الصفحة
let lastHeight = 0;
let noChangeCounter = 0;
for (let i = 0; i < 40; i++) {
window.scrollTo(0, document.body.scrollHeight);
await new Promise(r => setTimeout(r, 180));
harvestDOM();
const currentHeight = document.body.scrollHeight;
if (currentHeight === lastHeight) {
noChangeCounter++;
if (noChangeCounter >= 4) break; // تم الوصول لآخر مجموعة بالصفحة
} else {
noChangeCounter = 0;
lastHeight = currentHeight;
}
}
} catch (domErr) {}
// 3. استعلام GraphQL بدوران المؤشر لكافة المجموعات
if (config.captured.fb_dtsg) {
const executeGraphQLQueryLoop = async (docId, queryVariables) => {
let hasNextPage = true;
let currentCursor = null;
let pageCounter = 0;
const maxPages = 150;
while (hasNextPage && pageCounter < maxPages) {
pageCounter++;
try {
const vars = { ...queryVariables, cursor: currentCursor };
const params = new URLSearchParams();
params.append('av', config.captured.userId);
params.append('__user', config.captured.userId);
params.append('__a', '1');
params.append('fb_dtsg', config.captured.fb_dtsg);
params.append('variables', JSON.stringify(vars));
params.append('doc_id', docId);
const r = await fetch('https://www.facebook.com/api/graphql/', {
method: 'POST',
credentials: 'include',
body: params,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
if (r.ok) {
const resp = await r.text();
const parsed = JSON.parse(resp);
const adminEdges = parsed.data?.adminGroups?.groups_tab?.tab_groups_list?.edges || parsed.data?.viewer?.admin_groups?.edges;
const viewerEdges = parsed.data?.viewer?.groups?.edges || parsed.data?.viewer?.all_joined_groups?.edges || parsed.data?.viewer?.joined_groups?.edges;
const nonAdminEdges = parsed.data?.nonAdminGroups?.groups_tab?.tab_groups_list?.edges || parsed.data?.viewer?.member_groups?.edges;
processEdges(adminEdges, true);
processEdges(viewerEdges, false);
processEdges(nonAdminEdges, false);
const pageInfo = parsed.data?.viewer?.all_joined_groups?.page_info ||
parsed.data?.viewer?.groups?.page_info ||
parsed.data?.adminGroups?.groups_tab?.tab_groups_list?.page_info;
if (pageInfo && pageInfo.has_next_page && pageInfo.end_cursor && pageInfo.end_cursor !== currentCursor) {
currentCursor = pageInfo.end_cursor;
} else {
hasNextPage = false;
}
} else {
hasNextPage = false;
}
} catch (err) {
hasNextPage = false;
}
}
};
await executeGraphQLQueryLoop(config.doc_id_groups_shortcuts, {
adminGroupsCount: 500, memberGroupsCount: 500, scale: 1.5, count: 500
});
}
const resultList = Array.from(groupsMap.values());
resultList.sort((a, b) => (a.isAdmin === b.isAdmin ? 0 : (a.isAdmin ? -1 : 1)));
console.log(`[Fritree Deep Harvester] Total 100% Real Groups Harvested: ${resultList.length}`);
innerResolve({ success: resultList.length > 0, groups: resultList });
});
},
args: [FB_CONFIG]
}, results => {
if (chrome.runtime.lastError || !results || !results[0]) {
resolve({ success: false, groups: [] });
} else {
const res = results[0].result;
resolve(res || { success: false, groups: [] });
}
});
});
}
async function uploadFacebookImage(dataUrl, fileName) {
return new Promise(resolve => {
try {
const parts = dataUrl.split(',');
if (parts.length < 2) {
resolve({ success: false, error: 'صيغة الصورة غير صالحة للرفع.' });
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', FB_CONFIG.captured.fb_dtsg);
fd.append('profile_id', FB_CONFIG.captured.userId);
fd.append('source', '8');
fd.append('waterfallxapp', 'comet');
fd.append('farr', blob, fileName || 'upload.jpg');
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=${FB_CONFIG.captured.userId}&__user=${FB_CONFIG.captured.userId}&__a=1&fb_dtsg=${FB_CONFIG.captured.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?.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: 'لم تتمكن خوادم فيسبوك من إكمال رفع الصورة.' });
}
})
.catch(err => resolve({ success: false, error: 'خطأ شبكة أثناء رفع الصورة: ' + err.message }));
} catch (err) {
resolve({ success: false, error: 'خطأ في معالجة الصورة: ' + err.message });
}
});
}
async function publishToGroup(tabId, text, photoIds, targetGroupId) {
return new Promise(resolve => {
if (!targetGroupId) {
resolve({ success: false, error: 'كود المجموعة المستهدفة غير محدد.' });
return;
}
const hasPhotos = photoIds && photoIds.length > 0;
const docId = hasPhotos ? FB_CONFIG.doc_id_post_photo : FB_CONFIG.doc_id_post_text;
let hasResolved = false;
const timeout = setTimeout(() => {
if (!hasResolved) {
hasResolved = true;
resolve({ success: false, error: 'مهلة الاتصال انتهت: خوادم فيسبوك لم تستجب.', 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() : 'المجموعة';
finalMsg = finalMsg.replace(/\{\{\s*GROUP_NAME\s*\}\}/gi, 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.captured.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.captured.userId);
params.append('__user', config.captured.userId);
params.append('__a', '1');
params.append('fb_dtsg', config.captured.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: 'خطأ شبكة: ' + err.message, post_url: null }));
} catch (e) {
innerResolve({ success: false, pending: false, error: 'خطأ برمجي: ' + e.message, post_url: null });
}
});
},
args: [FB_CONFIG, text, photoIds, targetGroupId, docId]
}, results => {
clearTimeout(timeout);
if (hasResolved) return;
hasResolved = true;
if (chrome.runtime.lastError) {
resolve({ success: false, error: 'خطأ: ' + chrome.runtime.lastError.message, post_url: null });
} else if (results && results[0] && results[0].result) {
resolve(results[0].result);
} else {
resolve({ success: false, error: 'لم يتم استلام استجابة صالحة من فيسبوك.', post_url: null });
}
});
});
}
global.FritreeFacebookBg = {
getUserId: getFacebookUserId,
getPageId: getFacebookPageId,
resolveActiveUserId,
ensureSession: ensureFacebookAndCaptureKeys,
uploadImage: uploadFacebookImage,
publishToGroup,
fetchGroups: fetchAllFacebookGroups,
refreshSession: refreshFacebookSession,
config: FB_CONFIG
};
})(typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : this);