CODE KÍCH 1

// Nguyên liệu: // +Via 1: Cầm tkqc có nút call support // +Via 2: cầm BM // B1: Kết bạn 2 Via 1 và Via 2 với nhau // B2: Nhập danh sách tkqc tách, chạy chức năng 1 trên Via 1 (check nút share tkqc sang Via 2) // B2: Nhập danh sách tkqc share thành công và danh sách BM có trên Via 2, chạy chức năng số 2 trên Via 2 (Nhét tkqc vào BM) // B3: Nhập tkqc nhét BM thành công ở bước 2, chạy chức năng số 3 trên Via 1 (call support theo ads ID) // Globals const v = "maxvia88"; // BỎ QUA CHECK NÚT CALL SUPPORT const SKIP_CALL_SUPPORT_CHECK = true; // bật/tắt const FALLBACK_CMS_ID = "834096378082416"; // CMS mặc định của Meta Marketing Pro const FALLBACK_SHORT_DESC = "Analyse your ad account performance and Page stats with your Meta Marketing Pro and get strategic recommendations to help drive your ROI."; // === Fallback slots khi API không trả về lịch === const FALLBACK_DATE_ISO = "2025-09-22"; // YYYY-MM-DD const FALLBACK_TIMES = ["11:15","11:45","12:00","12:30","10:30","11:15","15:30","16:15","17:00"]; const FALLBACK_TZ_OFFSET = "+07:00"; // Asia/Jakarta / Asia/Bangkok const FALLBACK_DURATION_MIN = 45; // cuộc gọi 45 phút function buildFallbackSlotsFrom(dateISO = FALLBACK_DATE_ISO, times = FALLBACK_TIMES, durationMin = FALLBACK_DURATION_MIN, tzOffset = FALLBACK_TZ_OFFSET) { const nowSec = Math.floor(Date.now() / 1000); const out = []; for (const hhmm of times) { const iso = `${dateISO}T${hhmm}:00${tzOffset}`; // ví dụ 2025-08-29T09:00:00+07:00 const startSec = Math.floor(new Date(iso).getTime() / 1000); const endSec = startSec + durationMin * 60; if (startSec > nowSec) { out.push({ time_frame: { start_time: { timestamp: startSec }, end_time: { timestamp: endSec }, preferred: true, } }); } } return out; } // Try to safely get Facebook context variables let actor_id, fb_dtsgg, accessToken; try { actor_id = require("CurrentUserInitialData")?.USER_ID || ""; fb_dtsgg = require("DTSGInitData")?.token || ""; accessToken = require("WebApiApplication")?.getAccessToken() || ""; } catch (e) { console.error("Error initializing Facebook context:", e); } /** * Performs an API call with retry logic and error handling * * @param {string} url - The API endpoint URL * @param {Object} options - Fetch options * @param {number} retries - Number of retry attempts * @param {function} validateSuccess - Function to validate success response * @returns {Object} Response with status and data/error */ async function apiCall(url, options = {}, retries = 1, validateSuccess = null) { let lastError = null; for (let i = 0; i < retries; i++) { if (window.stopProcessing || window.forceStopAllOperations) { throw new Error("Operation canceled by user"); } const localController = new AbortController(); const globalSignal = window.currentAbortController?.signal; if (globalSignal && typeof globalSignal.addEventListener === 'function') { const onAbort = () => localController.abort(); globalSignal.addEventListener('abort', onAbort, { once: true }); } try { const fetchOptions = { ...options, credentials: 'include', signal: localController.signal }; const startedAt = performance.now(); // fetch + xử lý const fetchAndProcess = (async () => { const response = await fetch(url, fetchOptions); if (response.status === 500) { return { raced: 'fetch', result: { status: true, data: null } }; } if (!response.ok) { return { raced: 'fetch', result: { status: false, error: `HTTP error: ${response.status} ${response.statusText}` } }; } const data = await response.json(); if (validateSuccess && !validateSuccess(data)) { const errMsg = data?.errors?.[0]?.description || JSON.stringify(data); return { raced: 'fetch', result: { status: false, error: errMsg, data } }; } return { raced: 'fetch', result: { status: true, data } }; })(); // mốc 4.5s const timeoutPromise = new Promise(resolve => setTimeout(() => resolve({ raced: 'timeout' }), 4500) ); const winner = await Promise.race([fetchAndProcess, timeoutPromise]); if (winner?.raced === 'timeout') { // Không abort. Vẫn đợi fetch xong và trả về thành công + tổng thời gian đợi const final = await fetchAndProcess; const totalTimeMs = Math.round(performance.now() - startedAt); const original = final.result; return { status: true, data: original.data ?? null, longWait: true, totalTimeMs, original }; } // fetch xong trước 4.5s const totalTimeMs = Math.round(performance.now() - startedAt); const { result } = winner; if (result.status) { return { ...result, totalTimeMs }; } lastError = result.error; } catch (err) { if (err.name === 'AbortError' || err.message === "Operation canceled by user") { throw new Error("Operation canceled by user"); } lastError = err?.message || String(err); } if (i < retries - 1) { const delay = Math.pow(2, i) * 500; await new Promise(resolve => setTimeout(resolve, delay)); } } return { status: false, error: lastError }; } /** * Schedule a call with the given CMS ID and time frame. */ async function makeacall(cms_id, slots, ads_id, short_description = "Analyse your ad account performance and Page stats with your Meta Marketing Pro and get strategic recommendations to help drive your ROI.", sbg_prefill_data, sbg_program_name) { if (!cms_id || !slots || !ads_id) { return { status: false, error: "Missing required parameters" }; } const randomIndex = Math.floor(Math.random() * slots.length); const time_frame = slots[randomIndex]?.time_frame; const timeslot_label = getSelectedTimeslotLabel(time_frame.start_time.timestamp) try { const actor = window.actor_id || actor_id || ""; const token = window.accessToken || accessToken || ""; if (!actor || !token) { return { status: false, error: "Missing Facebook context (actor_id or accessToken)" }; } const timeframe = getRandomTimeframe(time_frame); const contactInfo = Array.isArray(sbg_prefill_data) ? sbg_prefill_data : []; const getVal = (i, def) => contactInfo[i]?.value?.length > 5 ? contactInfo[i].value : def; const name = getVal(2, "Ban Do"); const phone_number = getVal(3, `+197240${randomNumber(5)}`); const business_name = getVal(0, "Maxvia88 Company"); const email_address = getVal(1, `maxvia88${randomLowercase(5)}@gmail.com`); const rawJson = { input: { client_mutation_id: "1", actor_id: actor, channel_type: "SCHEDULE_CALL", program: "Transactional Incubator", sub_program: null, contact: { name, phone_number, business_name, email_address }, source_tracking: { surface: "ADS_MANAGER", entry_point: "ADS_MANAGER_START_YOUR_DAY_MARKETING_EXPERT_WIDGET", lead_source: "ADS_MANAGER_START_YOUR_DAY_MARKETING_EXPERT_WIDGET" }, timeframe: timeframe, advertiser_id: actor, advertiser_context: { agent_title: null, cms_id: cms_id, short_description: short_description, short_title: "Get to know your Meta Marketing Pro", signal_source_key: "new_to_program", advertiser_context_id: ads_id }, ad_account_id: ads_id, notes: "", selected_timezone: "Asia/Jakarta", chat_input: {}, selected_timeslot_label: timeslot_label } }; const encodedJson = encodeURIComponent(JSON.stringify(rawJson)); const url = `https://graph.facebook.com/graphql?method=post&locale=en_US&pretty=false&format=json&fb_api_req_friendly_name=BizKitMarketingExpertScheduleCallButtonMutation&doc_id=9512784282180280&fb_api_caller_class=RelayModern&server_timestamps=true&variables=${encodedJson}&access_token=${token}`; const result = await apiCall( url, { method: 'GET', credentials: 'include', signal: window.currentAbortController?.signal, headers: { Accept: 'application/json' } }, 2, (data) => data?.data?.sbg_engagement_connection_create?.connection?.connection_details?.status === "SCHEDULED" ); return { ...result, slot: time_frame, timeslot_label }; } catch (err) { console.error("Error in makeacall:", err); return { status: false, error: err.message || "Unknown error in makeacall" }; } } function capitalizeWords(str) { if (!str) return ""; return str .replace(/_/g, " ") // thay _ bằng khoảng trắng .toLowerCase() // đưa về thường .split(" ") // tách theo khoảng trắng .map(word => word.charAt(0).toUpperCase() + word.slice(1)) .join(" "); } /** * Make a call using Facebook dtsg token instead of access token */ async function makeacallfbdt(cms_id = "834096378082416", slots, ads_id, short_description = "Analyse your ad account performance and Page stats with your Meta Marketing Pro and get strategic recommendations to help drive your ROI.", sbg_prefill_data, sbg_program_name) { const actor = window.actor_id || actor_id || ""; const dtsg = window.fb_dtsgg || fb_dtsgg || ""; if (!cms_id || !slots || !ads_id || !dtsg || !actor) { return { status: false, error: "Missing required parameters" }; } const randomIndex = Math.floor(Math.random() * slots.length); const time_frame = slots[randomIndex]?.time_frame; const timeslot_label = getSelectedTimeslotLabel(time_frame.start_time.timestamp) try { const timeframe = getRandomTimeframe(time_frame); const contactInfo = Array.isArray(sbg_prefill_data) ? sbg_prefill_data : []; const getVal = (i, def) => contactInfo[i]?.value?.length > 5 ? contactInfo[i].value : def; const name = getVal(2, "Ban Do"); const phone_number = getVal(3, `+197240${randomNumber(5)}`); const business_name = getVal(0, "Maxvia88 Company"); const email_address = getVal(1, `maxvia88${randomLowercase(5)}@gmail.com`); const rawJson = { input: { client_mutation_id: "1", actor_id: actor, channel_type: "SCHEDULE_CALL", program: capitalizeWords(sbg_program_name || "Accelerate Plus"), sub_program: null, contact: { name, phone_number, business_name, email_address }, source_tracking: { surface: "ADS_MANAGER", entry_point: "ADS_MANAGER_START_YOUR_DAY_MARKETING_EXPERT_WIDGET", lead_source: "ADS_MANAGER_START_YOUR_DAY_MARKETING_EXPERT_WIDGET" }, timeframe: timeframe, advertiser_id: actor, advertiser_context: { agent_title: "undefined", cms_id: cms_id, short_description: short_description, short_title: "Get to know your Meta Marketing Pro", signal_source_key: "new_to_program", advertiser_context_id: ads_id, }, ad_account_id: ads_id, notes: "", selected_timezone: "Asia/Jakarta", chat_input: {}, selected_timeslot_label: timeslot_label } }; // const rawJson = { // input: { // client_mutation_id: "1", // actor_id: actor, // channel_type: "SCHEDULE_CALL", // program: "Accelerate Plus", // sub_program: null, // contact: { name, phone_number, business_name, email_address }, // source_tracking: { // surface: "ADS_MANAGER", // entry_point: "ADS_MANAGER_START_YOUR_DAY_MARKETING_EXPERT_WIDGET", // lead_source: "ADS_MANAGER_START_YOUR_DAY_MARKETING_EXPERT_WIDGET" // }, // timeframe: timeframe, // advertiser_id: actor, // advertiser_context: { // agent_title: null, // cms_id: cms_id, // short_description: short_description, // short_title: "Get to know your Meta Marketing Pro", // signal_source_key: "new_to_program", // advertiser_context_id: ads_id, // long_description: short_description // }, // ad_account_id: ads_id, // notes: "", // selected_timezone: "Asia/Jakarta", // chat_input: {}, // selected_timeslot_label: timeslot_label // } // }; const encodedJson = encodeURIComponent(JSON.stringify(rawJson)); const url = `https://adsmanager.facebook.com/api/graphql/`; const params = `av=${actor}&__aaid=605774672319432&__user=${actor}&__a=1&__req=2u&__hs=20312.BP%3Aads_manager_comet_pkg.2.0...0&dpr=1&__ccg=UNKNOWN&__rev=1025785809&__s=ci3b3b%3Ado976g%3Atz8q1d&__hsi=7537657817136921378&__dyn=7AgSXgWGgWEjgCu6mudg9omosyUqDBBh96EnK49o9EeUaVoWFGV8kG4VEHoOqqE88lBxeipe9wNWAAzppFuUuGfxW2u5Eiz8WdyU8ryUKrVoS3u7azoV2EK12xqUC8yEScx6bxW7A78O4EgCyku4oS4EWfGUhwyg9p44889EScxyu6UGq13yHGmmUTxJ3rG2PCG9DDl0zlBwyzp8KUWcwxyU29xep3bBAzEW9lpubwIxecAwXzogyo464Xy-cwuEnxaFo5a7EN1O79UCumbz8KiewwBK68eF8pK1Nxebxa4AbxR2V8cE8Q3mbgOUGfgeEmwJCxSegroG48gyHxSi4p8y7rKfxefKaxWi2y2i7Qm4VEhGcx22uexm1fAwLzUS327EG4E945EryrhUK5Ue8Su6Ey3maUjxOfxiFFEC48C9wFjQfyoaoym9yUixi48hyUix6cyopzFEHyU8Uiwj8G1TDV8sw8KmbwVzi1y4fz8coiGQU9Eak49VUWrUlUym5UpwDwXx67HxnwAxx7KAfwxCxSiu5onADzEHxm58G9xq2K3GUixl4wNx5e8wAAAVQEhy8myFUpxCQ48Cq4E8888oAgCi2aawVy8khEkxyEoypopxKU-GoigK6K224kifyohw&__hsdp=gfWsBEeT0l2ezNk4ckNa18AImMX1vOhNkIGXO8aMHtl993Awx8cgug23MaA7cn4IkzNAJFNrwzDflaJoKbwHx6l9Q7FxjglF9ai6k2Iw1jy0xo5O01g9w&__hblp=0Yw_wCyUqxW2SieBx9xq8Km2Gu2S2iUaU9Cm1QzUjOriJePHDNdCQxoGJjNs6YaMx2A9MN4Mhbkj2GNAaR4jPgF2ylsFAhjcR8L9-m8GPzUNVuFoxGF9lG8Dx1pN6z_BAgBJ4LzV7bdghV4OGGCB8K98VoV4AloHnsB8x4mlax94ml7zFudRK4AWz4lm5EzKVelAF2pkpyax0S-WFBhKAm9jgEEmwQxGmmdJ2d0kUWdge2Qii2mip29lO6Gc8WprCKiiEScppAJ3e4pAvTW-mLBDmqvyF8mV8x0mHgGt2KtfCleqbBF3HyLHKEmG-qFV8GfKey8-_x6qt1lohuQrAK49p-ia-bKiu13wDwlEgAzLLFCmaCGQ4aH88V9BzF48rVvGayaixrF-l4QQGRAKmpA-gxkGo2zxfwVJqGtep7F2_l94GbyGglKEG7VScGAbhEtB-qWh8C2e1hyGUgBiV6i9zUy9Bxdaiiex8x-uq9AK-ho99LJeV8LybDChdDxmq584QkUCaAAwTx627GuiELypohK5Hgybx6XLAVpqKHgCWF4iF4UHKFj7xnGmTxjAQF6dhpEjGBlB_BB8mhknt2Xh5eXGGAF688Cz6ngGi-GZ5yep7yWAGdgXhHCKGJ16-y7F4y8ynClGmjuiAheiAjz9GQA9BGAivALCz8tAyGUyvK49pmcyXmmepqAy8x6G-RXquuBHLV9lFxm6uVHiKqHBCAByEFetwFLUW2icAgiyEycGtehaiFKFHyAeAhGgCubK4Vp98WU_BQ6ljDgx-8DVaB-bHGuDGii9hkfXAgoDCgFa9BS67ydBGhaqmuWABAh-Fenl4jvwPFrUhBXXLV9O1IxalvF5ha9jzVHKmF8daHu9UOdnQunuEKWqUGABybyoaGV5BB-qfg450zyu_uEy8hcxk9FKQVV7K9jBDV8DUB4TKjCV6Wijp4ABghCTi8epESLCQmchJ5nRZ7KaSF9p4ayHGoGK7Q8eaFF5jh5CQ5i4cRll-IFHIJqXCRA8ZGiWBmEhQhlpdVFaAlhnRNXF24ua9zk&__comet_req=58&fb_dtsg=${encodeURIComponent(dtsg)}&jazoest=25661&lsd=2tx8xQuV2KJAf9jcGSRzU_&__spin_r=1025785809&__spin_b=trunk&__spin_t=1754997720&__jssesw=1&fb_api_caller_class=RelayModern&fb_api_req_friendly_name=BizKitMarketingExpertScheduleCallButtonMutation&variables=${encodedJson}&server_timestamps=true&doc_id=9512784282180280`; const result = await apiCall( url, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: 'application/json' }, body: params, credentials: "include", signal: window.currentAbortController?.signal }, 2, (data) => data?.data?.sbg_engagement_connection_create?.connection?.connection_details?.status === "SCHEDULED" ); return { ...result, slot: time_frame, timeslot_label }; } catch (err) { console.error("Error in makeacallfbdt:", err); return { status: false, error: err.message || "Unknown error in makeacallfbdt" }; } } /** * Get CMS ID and existing connections for scheduling. */ async function getidscall(ads_id) { const actor = window.actor_id || actor_id || ""; const token = window.accessToken || accessToken || ""; if (!ads_id || !actor) { return { status: false, error: "Missing required parameters (ads_id or actor_id)" }; } try { const rawJson = { input: { ad_account_id: ads_id, advertiser_user_id: actor, instantiated_programs_with_channels: true, source_tracking: { entry_point: "ADS_MANAGER_START_YOUR_DAY_MARKETING_EXPERT_WIDGET", lead_source: "ADS_MANAGER_START_YOUR_DAY_MARKETING_EXPERT_WIDGET", surface: "ADS_MANAGER" } }, shouldUseV1API: true, isWAChatEnabled: false }; const encodedJson = encodeURIComponent(JSON.stringify(rawJson)); const url = `https://graph.facebook.com/graphql?method=post&locale=en_US&pretty=false&format=json&fb_api_req_friendly_name=useMetaProEngagementEligibilityQuery&doc_id=9984842741620517&fb_api_caller_class=RelayModern&server_timestamps=true&variables=${encodedJson}&access_token=${token}`; const response = await fetch(url, { method: 'GET', credentials: 'include', signal: window.currentAbortController?.signal, headers: { Accept: 'application/json' } }); if (!response.ok) { return { status: false, error: `HTTP error: ${response.status} ${response.statusText}` }; } const data = await response.json(); const existing_connections = data?.data?.sbg_engagement_eligible_programs?.existing_connections; const cms_id = data?.data?.sbg_engagement_eligible_programs?.instantiated_programs_with_channels?.prioritized_program?.program?.contentv1?.cms_id; const short_description = data?.data?.sbg_engagement_eligible_programs?.instantiated_programs_with_channels?.prioritized_program?.program?.contentv1?.short_description; const available_call_ctas = data?.data?.sbg_engagement_eligible_programs?.instantiated_programs_with_channels?.prioritized_program?.program?.contentv1?.available_call_ctas?.[0]?.type === "SCHEDULE_CALL"; if (cms_id && available_call_ctas) { return { status: true, error: null, cms_id, existing_connections, short_description }; } return { status: false, error: data.errors?.[0]?.description || "No call support available", errorDetails: data }; } catch (err) { console.error("Error in getidscall:", err); return { status: false, error: err.message || "Unknown error in getidscall" }; } } async function getidscallv2(ads_id) { const actor = window.actor_id || actor_id || ""; const token = window.accessToken || accessToken || ""; if (!ads_id || !actor) { return { status: false, error: "Missing required parameters (ads_id or actor_id)" }; } try { const rawJson = { "inputData": { "source_tracking": { "surface": "ADS_MANAGER", "entry_point": "OS_START_YOUR_DAY", "lead_source": "opportunity_score_start_your_day" }, "advertiser_user_id": actor, "ad_account_id": ads_id, } } const encodedJson = encodeURIComponent(JSON.stringify(rawJson)); const url = `https://graph.facebook.com/graphql?method=post&locale=en_US&pretty=false&format=json&fb_api_req_friendly_name=MetaProEngagementOSEntryPointQuery&doc_id=9569721913149059&fb_api_caller_class=RelayModern&server_timestamps=true&variables=${encodedJson}&access_token=${token}`; const response = await fetch(url, { method: 'GET', credentials: 'include', signal: window.currentAbortController?.signal, headers: { Accept: 'application/json' } }); if (!response.ok) { return { status: false, error: `HTTP error: ${response.status} ${response.statusText}` }; } const data = await response.json(); const programs = data?.data?.sbg_engagement_eligible_programs?.programs || []; const program0 = programs[0] || {}; const existing_connections = data?.data?.sbg_engagement_eligible_programs?.existing_connections || []; const cms_id = program0?.content?.cms_id || null; const sbg_program_name = program0?.sbg_program_name || ""; const short_description = program0?.content?.long_description || program0?.content?.short_title || ""; const available_call_ctas = Array.isArray(program0?.channels) ? program0.channels.some(c => c?.type === "SCHEDULE_CALL") : false; if (cms_id && available_call_ctas) { return { status: true, error: null, cms_id, existing_connections, short_description, sbg_program_name }; } return { status: false, error: data.errors?.[0]?.description || "No call support available", errorDetails: data }; } catch (err) { console.error("Error in getidscall:", err); return { status: false, error: err.message || "Unknown error in getidscall" }; } } async function getcmsid(ads_id) { const actor = window.actor_id || actor_id || ""; if (!ads_id || !actor) { return { status: false, error: "Missing required parameters (ads_id or actor_id)" }; } try { const url = `https://adsmanager.facebook.com/adsmanager/manage/accounts?act=${ads_id}`; const response = await fetch(url, { method: 'GET', credentials: 'include', signal: window.currentAbortController?.signal, headers: { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8' } }); if (!response.ok) { return { status: false, error: `HTTP error: ${response.status} ${response.statusText}` }; } const html = await response.text(); // Tìm cms_id trong HTML/inline JSON const cmsMatch = html.match(/"cms_id"\s*:\s*"?(\d+)"?/); const descMatch = html.match(/"short_description"\s*:\s*"([^"]*)"/); const sbg_program_namecMatch = html.match(/"sbg_program_name"\s*:\s*"([^"]*)"/); const cms_id = cmsMatch?.[1] || null; const short_description = descMatch?.[1] || ""; const sbg_program_name = sbg_program_namecMatch?.[1] || ""; if (cms_id) { return { status: true, error: null, cms_id, existing_connections: null, short_description, sbg_program_name }; } // Fallback: thử lấy qua GraphQL nếu có hàm getidscall if (typeof getidscall === 'function') { const fb = await getidscall(ads_id); if (fb?.status) return fb; return { status: false, error: fb?.error || "Could not extract cms_id from Ads Manager HTML", errorDetails: fb }; } return { status: false, error: "Could not extract cms_id from Ads Manager HTML" }; } catch (err) { console.error("Error in getcmsid:", err); return { status: false, error: err?.message || "Unknown error in getcmsid" }; } } /** * Get available timeslot for scheduling. */ async function gettimeslot(ads_id) { const actor = window.actor_id || actor_id || ""; const token = window.accessToken || accessToken || ""; if (!ads_id || !actor) { return { status: false, error: "Missing required parameters (ads_id or actor_id)" }; } try { const rawJson = { inputData: { source_tracking: { surface: "ADS_MANAGER", entry_point: "ADS_MANAGER_START_YOUR_DAY_MARKETING_EXPERT_WIDGET", lead_source: "ADS_MANAGER_START_YOUR_DAY_MARKETING_EXPERT_WIDGET" }, advertiser_user_id: actor, ad_account_id: ads_id } }; const encodedJson = encodeURIComponent(JSON.stringify(rawJson)); const url = `https://graph.facebook.com/graphql?method=post&locale=en_US&pretty=false&format=json&fb_api_req_friendly_name=MetaProEngagementCalendarModalRootQuery&doc_id=9438056746321540&fb_api_caller_class=RelayModern&server_timestamps=true&variables=${encodedJson}&access_token=${token}`; const response = await fetch(url, { method: 'GET', credentials: 'include', signal: window.currentAbortController?.signal, headers: { Accept: 'application/json' } }); if (!response.ok) { return { status: false, error: `HTTP error: ${response.status} ${response.statusText}` }; } const data = await response.json(); const programs = data?.data?.sbg_engagement_eligible_programs?.programs || []; const program0 = programs[0] || {}; const channels = Array.isArray(program0.channels) ? program0.channels : []; const scheduleChannels = channels.filter(ch => ch?.type === 'SCHEDULE_CALL'); const slots = scheduleChannels.flatMap(ch => (ch?.availabilities || []).flatMap(av => av?.slots || []) ); const existing_connections = data?.data?.sbg_engagement_eligible_programs?.existing_connections; let sbg_prefill_data = data?.data?.sbg_prefill_data; const sbg_program_name = program0?.sbg_program_name; if (!Array.isArray(slots) || slots.length === 0) { return { status: false, error: "No available time slots", errorDetails: data }; } return { status: true, error: null, slots, existing_connections, sbg_prefill_data, sbg_program_name }; } catch (err) { console.error("Error in gettimeslot:", err); return { status: false, error: err.message || "Unknown error in gettimeslot" }; } } /** * Get available timeslot for scheduling. */ async function gettimeslotv2(ads_id) { const actor = window.actor_id || actor_id || ""; const token = window.accessToken || accessToken || ""; if (!ads_id || !actor) { return { status: false, error: "Missing required parameters (ads_id or actor_id)" }; } try { const rawJson = { "inputData": { "source_tracking": { "surface": "ADS_MANAGER", "entry_point": "ADS_MANAGER_START_YOUR_DAY_MARKETING_EXPERT_WIDGET", "lead_source": "ADS_MANAGER_START_YOUR_DAY_MARKETING_EXPERT_WIDGET" }, "advertiser_user_id": actor, "ad_account_id": ads_id }, "fields": [ "EMAIL", "FULL_NAME", "PHONE" ], "advertiserContext": { "ad_account_id": ads_id } } const encodedJson = encodeURIComponent(JSON.stringify(rawJson)); const url = `https://graph.facebook.com/graphql?method=post&locale=en_US&pretty=false&format=json&fb_api_req_friendly_name=MetaProEngagement1ClickModalRootQuery&doc_id=23868284372766223&fb_api_caller_class=RelayModern&server_timestamps=true&variables=${encodedJson}&access_token=${token}`; const response = await fetch(url, { method: 'GET', credentials: 'include', signal: window.currentAbortController?.signal, headers: { Accept: 'application/json' } }); if (!response.ok) { return { status: false, error: `HTTP error: ${response.status} ${response.statusText}` }; } const data = await response.json(); const programs = data?.data?.sbg_engagement_eligible_programs?.programs || []; const program0 = programs[0] || {}; const channels = Array.isArray(program0.channels) ? program0.channels : []; const scheduleChannels = channels.filter(ch => ch?.type === 'SCHEDULE_CALL'); const slots = scheduleChannels.flatMap(ch => (ch?.availabilities || []).flatMap(av => av?.slots || []) ); const existing_connections = data?.data?.sbg_engagement_eligible_programs?.existing_connections; let sbg_prefill_data = data?.data?.sbg_prefill_data; const sbg_program_name = program0?.sbg_program_name; if (!Array.isArray(slots) || slots.length === 0) { return { status: false, error: "No available time slots", errorDetails: data }; } return { status: true, error: null, existing_connections, sbg_prefill_data, slots, sbg_program_name }; } catch (err) { console.error("Error in gettimeslot:", err); return { status: false, error: err.message || "Unknown error in gettimeslot" }; } } /** * Get available timeslot for scheduling. */ async function gettimeslotv3(ads_id) { const actor = window.actor_id || actor_id || ""; const token = window.accessToken || accessToken || ""; if (!ads_id || !actor) { return { status: false, error: "Missing required parameters (ads_id or actor_id)" }; } try { const rawJson = { inputData: { source_tracking: { surface: "ADS_MANAGER", entry_point: "ADS_MANAGER_MODAL", lead_source: "IOS_AdsMngr_CampaignsOverView_EntryPoint" }, advertiser_user_id: actor, ad_account_id: ads_id }, fields: ["EMAIL", "FULL_NAME", "PHONE"], advertiserContext: { ad_account_id: ads_id } }; const encodedJson = encodeURIComponent(JSON.stringify(rawJson)); const url = `https://graph.facebook.com/graphql?method=post&locale=en_US&pretty=false&format=json&fb_api_req_friendly_name=AdsSBGMEEngagementDetailsDialogQuery&doc_id=24205266979145499&fb_api_caller_class=RelayModern&server_timestamps=true&variables=${encodedJson}&access_token=${token}`; const response = await fetch(url, { method: 'GET', credentials: 'include', signal: window.currentAbortController?.signal, headers: { Accept: 'application/json' } }); if (!response.ok) { return { status: false, error: `HTTP error: ${response.status} ${response.statusText}` }; } const data = await response.json(); const programs = data?.data?.sbg_engagement_eligible_programs?.programs || []; const program0 = programs[0] || {}; const channels = Array.isArray(program0.channels) ? program0.channels : []; const scheduleChannels = channels.filter(ch => ch?.type === 'SCHEDULE_CALL'); const slots = scheduleChannels.flatMap(ch => (ch?.availabilities || []).flatMap(av => av?.slots || []) ); const existing_connections = data?.data?.sbg_engagement_eligible_programs?.existing_connections; let sbg_prefill_data = data?.data?.sbg_prefill_data; const sbg_program_name = program0?.sbg_program_name; if (!Array.isArray(slots) || slots.length === 0) { return { status: false, error: "No available time slots", errorDetails: data }; } return { status: true, error: null, existing_connections, sbg_prefill_data, slots, sbg_program_name }; } catch (err) { console.error("Error in gettimeslot:", err); return { status: false, error: err.message || "Unknown error in gettimeslot" }; } } /** * Format timeframe for scheduling. */ function getRandomTimeframe(time_frame) { return { start_time: time_frame.start_time.timestamp, end_time: time_frame.end_time.timestamp, preferred: time_frame.preferred || false, }; } /** * Format timeslot label for display. */ function getSelectedTimeslotLabel(start_time) { try { const date = new Date(Number(start_time) * 1000); const fmt = new Intl.DateTimeFormat('en-US', { timeZone: 'Asia/Jakarta', month: 'numeric', day: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }); const parts = fmt.formatToParts(date); const map = Object.fromEntries(parts.map(p => [p.type, p.value])); const sep = '\u202F'; // narrow no-break space const dayPeriod = (map.dayPeriod || '').toUpperCase(); return `${map.month}/${map.day} ${map.hour}:${map.minute}${sep}${dayPeriod}`.trim(); } catch { const d = new Date(Number(start_time) * 1000); const h24 = d.toLocaleString('en-US', { hour: 'numeric', hour12: true, timeZone: 'Asia/Jakarta' }); const hour = ((d.getHours() % 12) || 12); const minute = String(d.getMinutes()).padStart(2, '0'); const ampm = d.getHours() < 12 ? 'AM' : 'PM'; return `${d.getMonth() + 1}/${d.getDate()} ${hour}:${minute}\u202F${ampm}`; } } /** * Cancel a scheduled call by connection ID. */ async function cancelcall(connection_id) { const actor = window.actor_id || actor_id || ""; const token = window.accessToken || accessToken || ""; const rawJson = { input: { client_mutation_id: "3", advertiser_user_id: actor, connection_id: connection_id, source_tracking: { surface: "ADS_MANAGER", entry_point: "ADS_MANAGER_START_YOUR_DAY_MARKETING_EXPERT_WIDGET", lead_source: "ADS_MANAGER_START_YOUR_DAY_MARKETING_EXPERT_WIDGET" } } }; const encodedJson = encodeURIComponent(JSON.stringify(rawJson)); const url = `https://graph.facebook.com/graphql?method=post&locale=en_US&pretty=false&format=json&fb_api_req_friendly_name=BizKitMarketingExpertScheduleCallCancelConfirmationModalMutation&doc_id=9750321601731205&fb_api_caller_class=RelayModern&server_timestamps=true&variables=${encodedJson}&access_token=${token}`; try { const response = await fetch(url, { method: 'GET', credentials: 'include', signal: window.currentAbortController?.signal, headers: { Accept: 'application/json' } }); if (!response.ok) { return { status: false, error: `HTTP error: ${response.status} ${response.statusText}` }; } const data = await response.json(); const CLOSED = data?.data?.sbg_engagement_connection_cancel?.connection?.connection_details?.status === "CLOSED"; if (CLOSED) { return { status: true, error: null }; } return { status: false, error: data.errors?.[0]?.description || data }; } catch (err) { return { status: false, error: err }; } } /** * Add ad account to BM and log result using fetch. */ async function nhettkqc(accountId, businessId) { const actor = window.actor_id || actor_id || ""; const dtsg = window.fb_dtsgg || fb_dtsgg || ""; const url = `https://business.facebook.com/business/objects/add/connections/?business_id=${businessId}&from_id=${businessId}&from_asset_type=brand&to_id=${accountId}&to_asset_type=ad-account`; const params = `__user=${encodeURIComponent(actor)}&__a=1&__dyn=7xeUmxa2C5rgydwCwRyU8EKnFG2Om2q12wAxuq3mq1FxebzA3aF98Sm4Euxa16xq2WdwJwy-2i13x21FxG9y8Gdz8hwgo5S3a4EuCx62a2q5E9UeUryE5mWyUd8S3bg-3tpUdoK7UC5U7y78jxiUa8522m3K2y3WElUScyo720FoO12Kmu7EK3i2a3Fe6rwnVUao9k2B12ewi8doa84K5E5WUrorx2awCx5e8wxK2efK6F8W1dx-q4VEhwww9O3ifzobEaUiwrUK5Ue8Sp1G3WcwMzUkGum2ym2WE4e8wl8hyVEKu9zUbVEHyU8U3yDwbm1bwzwqpbw&__csr=&__req=r&__hs=19234.BP%3Abrands_pkg.2.0.0.0.0&dpr=1.5&__ccg=EXCELLENT&__rev=1006115252&__s=ne9waj%3Acicyhn%3Aq28x8k&__hsi=7137596782722923131&__comet_req=0&fb_dtsg=${encodeURIComponent(dtsg)}&jazoest=25661&lsd=tqhJ435PyAJ7SnONkDETc0&__spin_r=1006115252&__spin_b=trunk&__spin_t=1661851252&__jssesw=1`; try { const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: 'text/plain' }, body: params, credentials: "include", signal: window.currentAbortController?.signal }); if (!response.ok) { if (response.status === 500) { return { status: true, error: null }; } return { status: false, error: `HTTP error: ${response.status} ${response.statusText}` }; } const text = await response.text(); const json = JSON.parse(text.replace(/^for \(;;\);/, '')); if (json.payload && json.payload.success === true) { return { status: true, error: null }; } else { return { status: false, error: json.errorDescription || json }; } } catch (e) { return { status: false, error: e }; } } async function sharetkqcbm(accountId, businessId) { const actor = window.actor_id || actor_id || ""; const dtsg = window.fb_dtsgg || fb_dtsgg || ""; const url = `https://business.facebook.com/business/objects/add/connections/?business_id=${businessId}&from_id=${businessId}&from_asset_type=brand&to_id=${accountId}&to_asset_type=ad-account`; const params = `__user=${encodeURIComponent(actor)}&__a=1&__dyn=7xeUmxa2C5rgydwCwRyU8EKnFG2Om2q12wAxuq3mq1FxebzA3aF98Sm4Euxa16xq2WdwJwy-2i13x21FxG9y8Gdz8hwgo5S3a4EuCx62a2q5E9UeUryE5mWyUd8S3bg-3tpUdoK7UC5U7y78jxiUa8522m3K2y3WElUScyo720FoO12Kmu7EK3i2a3Fe6rwnVUao9k2B12ewi8doa84K5E5WUrorx2awCx5e8wxK2efK6F8W1dx-q4VEhwww9O3ifzobEaUiwrUK5Ue8Sp1G3WcwMzUkGum2ym2WE4e8wl8hyVEKu9zUbVEHyU8U3yDwbm1bwzwqpbw&__csr=&__req=r&__hs=19234.BP%3Abrands_pkg.2.0.0.0.0&dpr=1.5&__ccg=EXCELLENT&__rev=1006115252&__s=ne9waj%3Acicyhn%3Aq28x8k&__hsi=7137596782722923131&__comet_req=0&fb_dtsg=${encodeURIComponent(dtsg)}&jazoest=25661&lsd=tqhJ435PyAJ7SnONkDETc0&__spin_r=1006115252&__spin_b=trunk&__spin_t=1661851252&__jssesw=1`; try { const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: 'text/plain' }, body: params, credentials: "include", signal: window.currentAbortController?.signal }); if (!response.ok) { if (response.status === 500) { return { status: true, error: null }; } return { status: false, error: `HTTP error: ${response.status} ${response.statusText}` }; } const text = await response.text(); const json = JSON.parse(text.replace(/^for \(;;\);/, '')); if (json.payload && json.payload.success === true) { return { status: true, error: null }; } else { return { status: false, error: json.errorDescription || json }; } } catch (e) { return { status: false, error: e }; } } async function gettokeneaag() { const url = `https://business.facebook.com/billing_hub/payment_settings/`; try { const response = await fetch(url, { method: 'GET', credentials: 'include', signal: window.currentAbortController?.signal, headers: { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.9", "Upgrade-Insecure-Requests": "1" } }); if (!response.ok) { return { status: false, error: `HTTP error: ${response.status} ${response.statusText}` }; } const html = await response.text(); // Tìm EAAG an toàn bằng regex (đến trước dấu ") const match = html.match(/EAAG[^"\\]+/); if (!match) { return { status: false, error: 'Không tìm thấy EAAG token trên trang' }; } const token = match[0]; return { status: true, token }; } catch (err) { return { status: false, error: err?.message || String(err) }; } } async function checkallbm() { const token = window.accessToken || accessToken || ""; const url = `https://graph.facebook.com/v19.0/me/businesses?fields=is_disabled_for_integrity_reasons,owned_ad_accounts.limit(10)&access_token=${token}&limit=1000`; try { const response = await fetch(url, { method: 'GET', credentials: 'include', signal: window.currentAbortController?.signal, headers: { Accept: 'application/json' } }); if (!response.ok) { return { status: false, error: `HTTP error: ${response.status} ${response.statusText}` }; } const data = await response.json(); const allbm = data?.data; if (Array.isArray(allbm) && allbm.length > 0) { return { status: true, error: null, allbm: allbm }; } return { status: false, error: data.errors?.description || data }; } catch (err) { return { status: false, error: err }; } } /** * Random Số theo độ dài. */ function randomNumber(length) { let result = ''; for (let i = 0; i < length; i++) { result += Math.floor(Math.random() * 10); } return result; } /** * Random ký tự viết thường theo độ dài. */ function randomLowercase(length) { let result = ''; const chars = 'abcdefghijklmnopqrstuvwxyz'; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; } /** * Create ads account and return its ID. */ async function createtkqc(BMprocesid, number = 1) { const token = window.accessToken || accessToken || ""; const rawJson = { businessID: BMprocesid, adAccountName: `USDL ${number}`, timezoneID: "1", currency: "USD", endAdvertiserID: BMprocesid }; const encodedJson = encodeURIComponent(JSON.stringify(rawJson)); const url = `https://graph.facebook.com/graphql?method=post&locale=en_US&pretty=false&format=json&fb_api_req_friendly_name=BizKitSettingsCreateAdAccountMutation&doc_id=9236789956426634&fb_api_caller_class=RelayModern&server_timestamps=true&variables=${encodedJson}&access_token=${token}`; try { const response = await fetch(url, { method: 'GET', credentials: 'include', signal: window.currentAbortController?.signal, headers: { Accept: 'application/json' } }); if (!response.ok) { return { status: false, error: `HTTP error: ${response.status} ${response.statusText}` }; } const data = await response.json(); const ads_id = data?.data?.business_settings_create_ad_account?.id; if (ads_id) { return { status: true, error: null, ads_id }; } return { status: false, error: data.errors?.[0]?.description || data }; } catch (err) { return { status: false, error: err }; } } async function checkBMtype(BMprocesid) { const dtsg = window.fb_dtsgg || fb_dtsgg || ""; const url = `https://business.facebook.com/business/adaccount/limits/?business_id=${BMprocesid}`; const params = `__a=1&fb_dtsg=${encodeURIComponent(dtsg)}&lsd=-X13GDdXiR6GDsJsHxJxLG`; try { const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: 'text/plain', }, body: params, credentials: "include", signal: window.currentAbortController?.signal }); if (!response.ok) { return { status: false, error: `HTTP error: ${response.status} ${response.statusText}` }; } const text = await response.text(); const json = JSON.parse(text.replace(/^for \(;;\);/, '')); if (json.payload && json.payload.adAccountLimit) { return { status: true, error: null, adAccountLimit: json.payload.adAccountLimit }; } else { return { status: false, error: json.errorDescription || json }; } } catch (e) { return { status: false, error: e }; } } // Inject UI function to be called at the start function injectUI() { // Check if UI already exists if (document.getElementById('fb-tool-container')) { document.getElementById('fb-tool-container').remove(); } // Create styles (dedup by id) const prevStyle = document.getElementById('fb-tool-styles'); if (prevStyle) prevStyle.remove(); const style = document.createElement('style'); style.id = 'fb-tool-styles'; style.textContent = ` #fb-tool-container { position: fixed; top: 20px; left: 20px; width: 1000px; background: #fff; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.2); z-index: 9999; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; padding: 0; overflow: hidden; } .fb-tool-header { background: linear-gradient(135deg, #4267B2, #3b5998); color: white; padding: 15px 20px; font-weight: bold; font-size: 16px; display: flex; justify-content: space-between; align-items: center; } .fb-tool-close { background: rgba(255,255,255,0.2); border: none; color: white; width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 14px; transition: all 0.2s; } .fb-tool-close:hover { background: rgba(255,255,255,0.4); } .fb-tool-body { padding: 15px 20px; } .fb-tool-info { background: #f7f8fa; padding: 10px 15px; margin-bottom: 15px; border-radius: 6px; border: 1px solid #dddfe2; } .fb-tool-info-row { display: flex; margin-bottom: 5px; align-items: center; } .fb-tool-info-row:last-child { margin-bottom: 0; } .fb-tool-info-label { width: 110px; font-weight: bold; font-size: 13px; color: #444; } .fb-tool-info-value { flex: 1; background: #fff; padding: 5px 10px; border-radius: 4px; border: 1px solid #dddfe2; font-family: monospace; font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .fb-tool-info-copy { margin-left: 8px; cursor: pointer; color: #4267B2; font-size: 12px; background: #e7f3ff; border: none; padding: 3px 8px; border-radius: 4px; } .fb-tool-info-copy:hover { background: #d4e5f9; } .fb-tool-row { display: flex; gap: 15px; margin-bottom: 15px; } .fb-tool-column { flex: 1; } .fb-tool-input-group { margin-bottom: 15px; } .fb-tool-label { display: block; font-weight: bold; margin-bottom: 5px; font-size: 14px; } .fb-tool-textarea { width: 100%; height: 120px; resize: vertical; padding: 10px; font-family: monospace; border: 1px solid #dddfe2; border-radius: 6px; font-size: 13px; } .fb-tool-textarea:focus { border-color: #4267B2; outline: none; box-shadow: 0 0 0 2px rgba(66, 103, 178, 0.15); } .fb-tool-output { background: #f7f8fa; height: 100px; } .fb-tool-controls { display: flex; align-items: center; gap: 10px; margin-bottom: 15px; background: #f7f8fa; padding: 12px 15px; border-radius: 6px; } .fb-tool-select { padding: 8px 12px; border: 1px solid #dddfe2; border-radius: 6px; flex: 1; font-size: 14px; max-width: 300px; } .fb-tool-button { background: linear-gradient(to bottom, #4267B2, #365899); color: white; border: none; padding: 8px 16px; border-radius: 6px; font-weight: bold; cursor: pointer; font-size: 14px; display: flex; align-items: center; gap: 6px; } .fb-tool-button:hover { background: linear-gradient(to bottom, #365899, #2d4373); } .fb-tool-button:disabled { background: #bdc7d8; cursor: not-allowed; } .fb-tool-button-stop { background: linear-gradient(to bottom, #e74c3c, #c0392b); } .fb-tool-button-stop:hover { background: linear-gradient(to bottom, #c0392b, #a93226); } .fb-tool-progress-container { height: 16px; background: #f2f2f2; border-radius: 8px; margin-bottom: 15px; overflow: hidden; } .fb-tool-progress-bar { height: 100%; width: 0%; background: linear-gradient(to right, #4CAF50, #8BC34A); border-radius: 8px; transition: width 0.3s ease; display: flex; align-items: center; justify-content: center; color: white; font-size: 11px; font-weight: bold; } .fb-tool-status { background: #f7f8fa; padding: 10px 15px; border-radius: 6px; margin-bottom: 15px; font-size: 13px; color: #333; border-left: 4px solid #4267B2; } .fb-tool-footer { padding: 12px 20px; background: #f7f8fa; border-top: 1px solid #dddfe2; font-size: 12px; color: #777; text-align: right; } .fb-tool-input-inline { padding: 8px; border: 1px solid #dddfe2; border-radius: 6px; width: 60px; text-align: center; } .fb-tool-badge { display: inline-block; padding: 3px 8px; border-radius: 10px; font-size: 12px; font-weight: bold; } .fb-tool-badge-success { background: #e3f2e1; color: #2e7d32; } .fb-tool-badge-error { background: #fce8e8; color: #c62828; } `; document.head.appendChild(style); // Try to get actor_id and token let actor_id = ""; let accessToken = ""; let tokenType = "Unknown"; try { actor_id = require("CurrentUserInitialData").USER_ID || ""; } catch (e) { console.error("Error getting actor_id:", e); } try { accessToken = require("WebApiApplication").getAccessToken() || ""; if (accessToken.startsWith("EAAG")) { tokenType = "EAAG (GraphAPI)"; } else if (accessToken.startsWith("EAA")) { tokenType = "EAA (Standard)"; } } catch (e) { console.error("Error getting access token:", e); } // Create container const container = document.createElement('div'); container.id = 'fb-tool-container'; // Build HTML structure container.innerHTML = `
FACEBOOK BUSINESS MANAGER TOOL 10
Actor ID:
${actor_id}
${actor_id ? '✓ Valid' : '✗ Invalid'}
Access Token:
${accessToken ? accessToken.substring(0, 25) + '...' : 'Not available'}
${accessToken ? '✓ ' + tokenType : '✗ Invalid'}
Sẵn sàng để chạy...
0%
`; document.body.appendChild(container); // lưu token vào dataset để tái sử dụng const tokenElInit = document.getElementById('fb-tool-token'); if (tokenElInit) tokenElInit.dataset.fullToken = accessToken; window.accessToken = accessToken; // Add event listeners for copy buttons instead of inline handlers document.getElementById('copy-actor-id').addEventListener('click', function () { navigator.clipboard.writeText(actor_id).then(() => { this.textContent = 'Copied!'; setTimeout(() => { this.textContent = 'Copy'; }, 2000); }).catch(err => { console.error('Failed to copy: ', err); alert('Failed to copy: ' + err); }); }); document.getElementById('copy-token').addEventListener('click', function () { const t = document.getElementById('fb-tool-token')?.dataset?.fullToken || window.accessToken || ''; navigator.clipboard.writeText(t).then(() => { this.textContent = 'Copied!'; setTimeout(() => { this.textContent = 'Copy'; }, 2000); }).catch(err => { console.error('Failed to copy: ', err); alert('Failed to copy: ' + err); }); }); // Handle close button with improved cleanup document.getElementById('fb-tool-close').addEventListener('click', () => { window.stopProcessing = true; window.forceStopAllOperations = true; window.currentAbortController?.abort(); const el = document.getElementById('fb-tool-container'); if (el) el.remove(); updateStatus('All operations stopped and UI closed'); }); // Handle function change to show/hide additional fields document.getElementById('fb-tool-function').addEventListener('change', function () { const uidContainer = document.getElementById('fb-tool-uid-container'); uidContainer.style.display = this.value === '1' ? 'block' : 'none'; const createadsContainer = document.getElementById('fb-tool-createads-container'); createadsContainer.style.display = this.value === '4' ? 'inline-flex' : 'none'; }); // Initialize UI based on current function const functionSelect = document.getElementById('fb-tool-function'); document.getElementById('fb-tool-uid-container').style.display = functionSelect.value === '1' ? 'block' : 'none'; document.getElementById('fb-tool-createads-container').style.display = functionSelect.value === '4' ? 'inline-flex' : 'none'; // Handle run button document.getElementById('fb-tool-run').addEventListener('click', startProcess); // Handle stop button with improved cleanup document.getElementById('fb-tool-stop').addEventListener('click', function () { window.stopProcessing = true; window.forceStopAllOperations = true; window.currentAbortController?.abort(); updateStatus('Stopping all operations...'); updateProgress(100, 'Stopped'); this.disabled = true; document.getElementById('fb-tool-run').disabled = false; }); // Fill textareas with existing values if available if (window.accountLists) { document.getElementById('fb-tool-ads-input').value = window.accountLists; } if (window.BMLists) { document.getElementById('fb-tool-bm-input').value = window.BMLists; } } // Create a delay function that respects the stop flag async function delayWithStopCheck(seconds) { const startTime = Date.now(); const endTime = startTime + (seconds * 1000); while (Date.now() < endTime) { if (window.stopProcessing || window.forceStopAllOperations) { throw new Error("Operation canceled by user"); } // Check every 100ms await new Promise(resolve => setTimeout(resolve, 100)); } } // Function to update progress bar function updateProgress(percentage, text) { const progressBar = document.getElementById('fb-tool-progress'); if (!progressBar) return; progressBar.style.width = `${percentage}%`; progressBar.textContent = text || `${Math.round(percentage)}%`; // Change color based on progress if (percentage < 30) { progressBar.style.background = 'linear-gradient(to right, #f44336, #ff7043)'; } else if (percentage < 70) { progressBar.style.background = 'linear-gradient(to right, #ff9800, #ffc107)'; } else { progressBar.style.background = 'linear-gradient(to right, #4CAF50, #8BC34A)'; } } // Function to update status function updateStatus(text) { const statusElement = document.getElementById('fb-tool-status'); if (!statusElement) return; statusElement.textContent = text; // Highlight the status update statusElement.style.backgroundColor = '#e9f3ff'; setTimeout(() => { statusElement.style.backgroundColor = '#f7f8fa'; }, 300); } // Function to add to output function addToOutput(isSuccess, text) { const outputId = isSuccess ? 'fb-tool-success-output' : 'fb-tool-fail-output'; const output = document.getElementById(outputId); if (!output) return; // Format with timestamp const now = new Date().toLocaleTimeString(); output.value += `${text} [${now}]\n`; output.scrollTop = output.scrollHeight; } // Main function to start the process async function startProcess() { try { // Reset outputs and UI //document.getElementById('fb-tool-success-output').value = ''; //document.getElementById('fb-tool-fail-output').value = ''; updateProgress(0); updateStatus('Đang khởi tạo...'); // Toggle button states const runButton = document.getElementById('fb-tool-run'); const stopButton = document.getElementById('fb-tool-stop'); runButton.disabled = true; stopButton.disabled = false; // Reset BOTH stop flags window.stopProcessing = false; window.forceStopAllOperations = false; // This flag wasn't being reset! // Get values from UI const adsInput = document.getElementById('fb-tool-ads-input').value.trim(); const bmInput = document.getElementById('fb-tool-bm-input').value.trim(); const functionValue = document.getElementById('fb-tool-function').value; const delayValue = parseInt(document.getElementById('fb-tool-delay').value) || 1; // Validate inputs if (!adsInput && (functionValue === '1' || functionValue === '3')) { alert('Vui lòng nhập danh sách TKQC!'); runButton.disabled = false; return; } if (!bmInput && (functionValue === '2' || functionValue === '4')) { alert('Vui lòng nhập danh sách BM!'); runButton.disabled = false; return; } // Update global variables for the existing backend window.accountLists = adsInput; window.BMLists = bmInput; window.typeselect = parseInt(functionValue); window.delays = delayValue; // Special handling for function 1 (share to another account) if (functionValue === '1') { window.uidvianhan = document.getElementById('fb-tool-uid-input').value.trim(); // if (!window.uidvianhan) { // alert('Vui lòng nhập UID via nhận TKQC share!'); // runButton.disabled = false; // return; // } } // Special handling for function 4 (BM10) if (functionValue === '4') { window.createads = parseInt(document.getElementById('fb-tool-createads').value) || 0; } else { window.createads = 0; } // Call modified main process await executeProcess(); // Re-enable run button runButton.disabled = false; stopButton.disabled = true; updateStatus('Hoàn tất xử lý!'); } catch (error) { console.error('Error in startProcess:', error); document.getElementById('fb-tool-run').disabled = false; document.getElementById('fb-tool-stop').disabled = true; updateStatus(`Lỗi: ${error.message}`); alert(`Đã xảy ra lỗi: ${error.message}`); } } // Main process execution with progress reporting async function executeProcess() { const accountIds = window.accountLists.split('\n') .map(line => line.split('|')[0].trim()) .filter(id => id !== ''); const BMIds = window.BMLists.split('\n') .map(line => line.split('|')[0].trim()) .filter(id => id !== ''); let actor_id, fb_dtsgg, accessToken; try { actor_id = require("CurrentUserInitialData").USER_ID; if (!actor_id) throw new Error("Actor ID not found"); fb_dtsgg = require("DTSGInitData").token; if (!fb_dtsgg) throw new Error("DTSG token not found"); accessToken = require("WebApiApplication").getAccessToken(); // ưu tiên token từ UI/localStorage nếu có const preferred = getTokenFromUIOrStorage(); if (preferred.startsWith('EAAG') && !accessToken.startsWith('EAAG')) { accessToken = preferred; } if (preferred) accessToken = preferred; if (!accessToken) { throw new Error("Access token not found"); } // persist to window.* for helpers window.actor_id = actor_id; window.fb_dtsgg = fb_dtsgg; window.accessToken = accessToken; } catch (e) { updateStatus(`Lỗi lấy thông tin người dùng: ${e.message}`); throw e; } updateStatus(`Đã lấy thông tin, bắt đầu xử lý...`); window.currentAbortController = new AbortController(); if (window.typeselect === 1) { await processFunction1(accountIds, actor_id, accessToken, window.uidvianhan); } else if (window.typeselect === 2) { await processFunction2(accountIds, BMIds, actor_id, accessToken, fb_dtsgg); } else if (window.typeselect === 3) { await processFunction3(accountIds, actor_id, accessToken); } else if (window.typeselect === 4) { await processFunction4(accountIds, BMIds, actor_id, accessToken, fb_dtsgg); } else if (window.typeselect === 5) { await processFunction5(accountIds, BMIds, actor_id, accessToken, fb_dtsgg); } } // Function 1: Check and share TKQC // Function 1: Check & Share TKQC (KHÔNG dùng timeslot) async function processFunction1(accountIds, actor_id, accessToken, uidvianhan) { let processcount = 0; let successCount = 0; let failCount = 0; // Yêu cầu EAAG vì gọi Graph /act_{id}/users if (!accessToken.startsWith("EAAG")) { const newToken = prompt("Token không phải EAAG. Vui lòng nhập Token EAAG:"); if (newToken && newToken.startsWith("EAAG")) { accessToken = newToken; setTokenAndUI(accessToken); window.accessToken = accessToken; } else { alert("Token không hợp lệ hoặc không phải EAAG. Vui lòng thử lại."); return; } } updateStatus(`Đang share TKQC cho ${accountIds.length} tài khoản...`); for (let i = 0; i < accountIds.length; i++) { if (window.stopProcessing || window.forceStopAllOperations) break; const adaccountid = (accountIds[i] || "").trim(); if (!adaccountid) continue; processcount++; updateProgress((processcount / accountIds.length) * 100); updateStatus(`Đang xử lý TKQC ${adaccountid} (${processcount}/${accountIds.length})...`); if (uidvianhan && uidvianhan.length > 0) { // share thẳng, không cần timeslot/check call support const shareResult = await sharetkqc(adaccountid, uidvianhan); if (shareResult.status) { successCount++; addToOutput(true, `${adaccountid}|Share thành công cho UID ${uidvianhan}`); } else { failCount++; addToOutput(false, `${adaccountid}|Share thất bại: ${JSON.stringify(shareResult.error)}`); } } else { // Không có UID nhận → chỉ log OK để tiếp bước sau successCount++; addToOutput(true, `${adaccountid}|Bỏ qua share (không có UID nhận)`); } if (i < accountIds.length - 1 && !window.stopProcessing && !window.forceStopAllOperations) { updateStatus(`Delay ${window.delays}s trước khi xử lý TKQC tiếp theo...`); try { await delayWithStopCheck(window.delays); } catch (e) { if (e.message === "Operation canceled by user") break; throw e; } } } updateStatus(`Hoàn tất! Thành công: ${successCount}, Thất bại: ${failCount}`); updateProgress(100, `${successCount}/${accountIds.length}`); } // Function 2: Add TKQC to BM async function processFunction2(accountIds, BMIds, actor_id, accessToken, fb_dtsgg) { let processcount = 0; let successCount = 0; let failCount = 0; let adsnumber = 0; let successadsList = []; let successBMList = []; updateStatus(`Đang nhét TKQC vào ${BMIds.length} BM...`); try { for (let i = 0; i < BMIds.length; i++) { if (window.stopProcessing || window.forceStopAllOperations) break; const BMprocesid = BMIds[i]; processcount++; updateProgress((processcount / BMIds.length) * 100); updateStatus(`Đang xử lý BM ${BMprocesid} (${processcount}/${BMIds.length})...`); if (adsnumber >= accountIds.length) { updateStatus("Đã hết TKQC để xử lý, dừng tiến trình"); break; } // Get next available ad account let adaccountid; for (const adaccountidcheck of accountIds.slice(adsnumber)) { adsnumber++; adaccountid = adaccountidcheck; break; } if (!adaccountid) { updateStatus("Không tìm được TKQC phù hợp, dừng tiến trình"); break; } // Add ad account to BM updateStatus(`Đang nhét TKQC ${adaccountid} vào BM ${BMprocesid}...`); const nhettkqcResult = await nhettkqc(adaccountid, BMprocesid); if (nhettkqcResult.status) { successadsList.push(adaccountid); successBMList.push(BMprocesid); successCount++; addToOutput(true, `${adaccountid}|${BMprocesid}|Nhét TKQC thành công vào BM`); } else { failCount++; addToOutput(false, `${adaccountid}|${BMprocesid}|Nhét tkqc thất bại ${JSON.stringify(nhettkqcResult.error)}`); continue; } if (i < BMIds.length - 1 && !window.stopProcessing && !window.forceStopAllOperations) { updateStatus(`Delay ${window.delays}s trước khi xử lý BM tiếp theo...`); try { await delayWithStopCheck(window.delays); } catch (e) { if (e.message === "Operation canceled by user") break; throw e; } } } } catch (error) { console.error("Error in processFunction2:", error); updateStatus(`Lỗi: ${error.message}`); } updateStatus(`Hoàn tất! Thành công: ${successCount}, Thất bại: ${failCount}`); updateProgress(100, `${successCount}/${BMIds.length}`); } // Function 5: Add TKQC to BM async function processFunction2(accountIds, BMIds, actor_id, accessToken, fb_dtsgg) { let processcount = 0; let successCount = 0; let failCount = 0; let adsnumber = 0; let successadsList = []; let successBMList = []; updateStatus(`Đang nhét TKQC vào ${BMIds.length} BM...`); try { for (let i = 0; i < BMIds.length; i++) { if (window.stopProcessing || window.forceStopAllOperations) break; const BMprocesid = BMIds[i]; processcount++; updateProgress((processcount / BMIds.length) * 100); updateStatus(`Đang xử lý BM ${BMprocesid} (${processcount}/${BMIds.length})...`); if (adsnumber >= accountIds.length) { updateStatus("Đã hết TKQC để xử lý, dừng tiến trình"); break; } // Get next available ad account let adaccountid; for (const adaccountidcheck of accountIds.slice(adsnumber)) { adsnumber++; adaccountid = adaccountidcheck; break; } if (!adaccountid) { updateStatus("Không tìm được TKQC phù hợp, dừng tiến trình"); break; } // Add ad account to BM updateStatus(`Đang nhét TKQC ${adaccountid} vào BM ${BMprocesid}...`); const nhettkqcResult = await nhettkqc(adaccountid, BMprocesid); if (nhettkqcResult.status) { successadsList.push(adaccountid); successBMList.push(BMprocesid); successCount++; addToOutput(true, `${adaccountid}|${BMprocesid}|Nhét TKQC thành công vào BM`); } else { failCount++; addToOutput(false, `${adaccountid}|${BMprocesid}|Nhét tkqc thất bại ${JSON.stringify(nhettkqcResult.error)}`); continue; } if (i < BMIds.length - 1 && !window.stopProcessing && !window.forceStopAllOperations) { updateStatus(`Delay ${window.delays}s trước khi xử lý BM tiếp theo...`); try { await delayWithStopCheck(window.delays); } catch (e) { if (e.message === "Operation canceled by user") break; throw e; } } } } catch (error) { console.error("Error in processFunction2:", error); updateStatus(`Lỗi: ${error.message}`); } updateStatus(`Hoàn tất! Thành công: ${successCount}, Thất bại: ${failCount}`); updateProgress(100, `${successCount}/${BMIds.length}`); } // Function 3: Call Support // Function 3: Call Support (bỏ qua bước check nút Call Support nếu bật cờ) async function processFunction3(accountIds, actor_id, accessToken) { let processcount = 0; let successCount = 0; let failCount = 0; let successBMList = []; let errorBMList = []; updateStatus(`Đang call support cho ${accountIds.length} TKQC...`); // Ưu tiên dùng fb_dtsg khi đang đứng trong Ads Manager let use_token = true; if (isOnAdsManagerFacebook()) use_token = false; // Ép nhập EAAG nếu đang chọn flow dùng token if (use_token && !accessToken.startsWith("EAAG")) { const newToken = prompt("Token không phải EAAG. Vui lòng nhập Token EAAG:"); if (newToken && newToken.startsWith("EAAG")) { accessToken = newToken; setTokenAndUI(accessToken); window.accessToken = accessToken; } else { alert("Token không hợp lệ hoặc không phải EAAG. Vui lòng thử lại."); return; } } for (let i = 0; i < accountIds.length; i++) { if (window.stopProcessing || window.forceStopAllOperations) break; const adaccountidss = accountIds[i]; const arrayads = adaccountidss.split("|"); let adaccountid = arrayads[0] ? arrayads[0].trim() : ""; let time_start = arrayads[1] ? arrayads[1].trim() : ""; let time_end = arrayads[2] ? arrayads[2].trim() : ""; const adaccountkich = arrayads[3] ? arrayads[3].trim() : ""; let time_frame = { start_time: { timestamp: time_start }, end_time: { timestamp: time_end }, preferred: true }; processcount++; updateProgress((processcount / accountIds.length) * 100); updateStatus(`Đang xử lý TKQC ${adaccountid} (${processcount}/${accountIds.length})...`); // --- LẤY CMS (BỎ QUA CHECK NÚT NẾU BẬT CỜ) --- let result = await getcmsid(adaccountid); if (!result?.status && SKIP_CALL_SUPPORT_CHECK) { result = { status: true, cms_id: FALLBACK_CMS_ID, short_description: FALLBACK_SHORT_DESC, existing_connections: [] }; addToOutput(true, `${adaccountid}| Bỏ qua check nút call support (dùng CMS ${FALLBACK_CMS_ID})`); } else if (!result?.status) { addToOutput(false, `${adaccountid}| Không có nút call support`); failCount++; errorBMList.push(adaccountid); // sang TK tiếp theo if (i < accountIds.length - 1 && !window.stopProcessing && !window.forceStopAllOperations) { updateStatus(`Delay ${window.delays}s trước TKQC tiếp theo...`); try { await delayWithStopCheck(window.delays); } catch (e) { if (e.message === "Operation canceled by user") break; throw e; } } continue; } // Hủy cuộc gọi cũ (nếu API trả về) if (result.existing_connections?.length) { updateStatus(`TKQC ${adaccountid}: Hủy cuộc gọi cũ...`); for (const connection of result.existing_connections) { if (connection.channel_type) { const cancelResult = await cancelcall(connection.id); updateStatus(`TKQC ${adaccountid}: Hủy cũ ${cancelResult.status ? "OK" : "FAIL"}`); } } } // Lấy timeslot từ API lịch const timeslotResult = await gettimeslotv3(adaccountid); // Cho phép thay đổi adaccountid nếu truyền ở cột thứ 4 if (adaccountkich && adaccountkich.length > 10) adaccountid = adaccountkich; // Có slot từ API -> đặt theo slot trả về if (timeslotResult.status && Array.isArray(timeslotResult.slots) && timeslotResult.slots.length) { if (timeslotResult.existing_connections?.length) { for (const connection of timeslotResult.existing_connections) { if (connection.channel_type) await cancelcall(connection.id); } } updateStatus(`TKQC ${adaccountid}: Đang đặt lịch theo slot API...`); let result2; if (use_token) { result2 = await makeacall( result.cms_id, timeslotResult.slots, adaccountid, result.short_description, timeslotResult.sbg_prefill_data, timeslotResult.sbg_program_name ); } else { result2 = await makeacallfbdt( result.cms_id, timeslotResult.slots, adaccountid, result.short_description, timeslotResult.sbg_prefill_data, timeslotResult.sbg_program_name ); } if (result2.status) { successCount++; addToOutput(true, `${adaccountid}| đặt lịch thành công`); } else { failCount++; addToOutput(false, `${adaccountid}| đặt lịch thất bại: ${JSON.stringify(result2.error)}`); } // KHÔNG có slot -> dùng fallback ngày 2025-08-29 & các giờ bạn chỉ định } else { const fallbackSlots = buildFallbackSlotsFrom(); // dùng cấu hình ở trên if (!fallbackSlots.length) { failCount++; addToOutput(false, `${adaccountid}| Không tạo được fallback slot (có thể ngày/giờ đã qua)`); } else { updateStatus(`TKQC ${adaccountid}: Không có slot API, thử fallback ${FALLBACK_DATE_ISO}...`); const cmsvalue = result.cms_id || FALLBACK_CMS_ID; let result2; if (use_token) { result2 = await makeacall( cmsvalue, fallbackSlots, adaccountid, result.short_description ); } else { result2 = await makeacallfbdt( cmsvalue, fallbackSlots, adaccountid, result.short_description ); } if (result2.status) { successCount++; addToOutput(true, `${adaccountid}| đặt lịch fallback thành công (${FALLBACK_DATE_ISO})`); } else { failCount++; addToOutput(false, `${adaccountid}| Lỗi đặt lịch fallback: ${JSON.stringify(result2.error)}`); } } } // Delay giữa các TK if (i < accountIds.length - 1 && !window.stopProcessing && !window.forceStopAllOperations) { updateStatus(`Delay ${window.delays}s trước khi xử lý TKQC tiếp theo...`); try { await delayWithStopCheck(window.delays); } catch (e) { if (e.message === "Operation canceled by user") break; throw e; } } } updateStatus(`Hoàn tất! Thành công: ${successCount}, Thất bại: ${failCount}`); updateProgress(100, `${successCount}/${accountIds.length}`); } // Function 4: Kích BM10 async function processFunction4(accountIds, BMIds, actor_id, accessToken, fb_dtsgg) { let processcount = 0; let successCount = 0; let failCount = 0; let adsnumber = 0; let successBMList = []; let successadsList = []; if (!accessToken.startsWith("EAAG")) { const newToken = prompt("Token không phải EAAG. Vui lòng nhập Token EAAG:"); if (newToken && newToken.startsWith("EAAG")) { accessToken = newToken; setTokenAndUI(accessToken); window.accessToken = accessToken; // keep helpers in sync } else { alert("Token không hợp lệ hoặc không phải EAAG. Vui lòng thử lại."); return; } } updateStatus(`Đang kích BM10 cho ${BMIds.length} BM...`); for (let i = 0; i < BMIds.length; i++) { if (window.stopProcessing || window.forceStopAllOperations) break; const BMprocesid = BMIds[i]; processcount++; updateProgress((processcount / BMIds.length) * 100); updateStatus(`Đang xử lý BM ${BMprocesid} (${processcount}/${BMIds.length})...`); if (adsnumber >= accountIds.length) { updateStatus("Đã hết TKQC để xử lý, dừng tiến trình"); break; } // Find ad account with call support let adaccountid; let result; for (const adaccountidcheck of accountIds.slice(adsnumber)) { adsnumber++; updateStatus(`Đang kiểm tra nút call support cho TKQC ${adaccountidcheck}...`); result = await getidscall(adaccountidcheck); if (!result.status) { addToOutput(false, `${adaccountidcheck}| Không có nút call support`); continue; } adaccountid = adaccountidcheck; break; } if (!adaccountid) { addToOutput(false, "Không tìm được tài khoản có nút call support"); updateStatus("Không tìm được TKQC phù hợp, dừng tiến trình"); break; } addToOutput(true, `${adaccountid}| Có nút call support: ${result.cms_id}`); // Cancel existing connections if (result.existing_connections?.length) { updateStatus(`TKQC ${adaccountid}: Đang hủy cuộc gọi cũ...`); for (const connection of result.existing_connections) { if (connection.channel_type) { const cancelResult = await cancelcall(connection.id); updateStatus(`TKQC ${adaccountid}: Hủy cuộc gọi cũ ${cancelResult.status ? 'Thành công' : 'Thất bại'}`); } } } // Add ad account to BM updateStatus(`Đang nhét TKQC ${adaccountid} vào BM ${BMprocesid}...`); const nhettkqcResult = await nhettkqc(adaccountid, BMprocesid); if (nhettkqcResult.status) { successadsList.push(adaccountid); addToOutput(true, `${adaccountid}|${BMprocesid}|Nhét tkqc vào BM thành công`); } else { failCount++; addToOutput(false, `${adaccountid}|${BMprocesid}|Nhét tkqc vào BM thất bại: ${JSON.stringify(nhettkqcResult.error)}`); continue; } // Get timeslot and make call const timeslotResult = await gettimeslotv3(adaccountid); if (timeslotResult.status) { updateStatus(`TKQC ${adaccountid}: Đang đặt lịch cuộc gọi...`); const result2 = await makeacall(result.cms_id, timeslotResult.slots, adaccountid, result.short_description, timeslotResult.sbg_prefill_data); if (result2.status) { successCount++; // Check BM type updateStatus(`Đang kiểm tra thông tin BM ${BMprocesid}...`); const BMtype = await checkBMtype(BMprocesid); if (BMtype.status && BMtype.adAccountLimit > 1) { const successEntry = `${adaccountid}|${BMprocesid}|BM${BMtype.adAccountLimit}`; successBMList.push(successEntry); addToOutput(true, `${successEntry} Kích BM thành công`); // Create additional ad accounts if (window.createads > 0) { const maxCreate = Math.min(window.createads, BMtype.adAccountLimit); updateStatus(`Đang tạo ${maxCreate} TKQC mới cho BM ${BMprocesid}...`); for (let j = 0; j < maxCreate; j++) { if (window.stopProcessing || window.forceStopAllOperations) break; updateStatus(`Đang tạo TKQC ${j + 1}/${maxCreate}...`); const createResult = await createtkqc(BMprocesid, j + 1); if (createResult.status) { addToOutput(true, `Tạo TKQC thành công : ${createResult.ads_id}`); } else { addToOutput(false, `Lỗi tạo TKQC: ${JSON.stringify(createResult.error)}`); break; } } } } else { failCount++; addToOutput(false, `Kích BM10 thất bại ko tăng info: ${BMprocesid}`); } } else { failCount++; addToOutput(false, `Kích BM10 thất bại: ${BMprocesid} - ${JSON.stringify(result2.error)}`); } } else { failCount++; addToOutput(false, `Lỗi khung giờ: ${adaccountid} - ${JSON.stringify(timeslotResult.error)}`); } if (i < BMIds.length - 1 && !window.stopProcessing && !window.forceStopAllOperations) { updateStatus(`Delay ${window.delays}s trước khi xử lý BM tiếp theo...`); try { await delayWithStopCheck(window.delays); } catch (e) { if (e.message === "Operation canceled by user") break; throw e; } } } updateStatus(`Hoàn tất! Thành công: ${successCount}, Thất bại: ${failCount}`); updateProgress(100, `${successCount}/${BMIds.length}`); } // Initialize global variables for UI access window.stopProcessing = false; window.forceStopAllOperations = false; // Remove early UI injection to avoid duplicate UI // injectUI(); // removed async function sharetkqc(ads_id, uidvianhan) { const url = `https://graph.facebook.com/v22.0/act_${ads_id}/users?locale=en_US&role=1001&uid=${encodeURIComponent(uidvianhan)}&access_token=${window.accessToken || accessToken}`; try { const response = await fetch(url, { method: 'POST', credentials: 'include' }); if (!response.ok) { if (response.status === 500) return { status: true, error: null }; return { status: false, error: `HTTP error: ${response.status} ${response.statusText}` }; } const data = await response.json(); if (data.success === true) { return { status: true, error: null }; } return { status: false, error: data.error || data }; } catch (err) { return { status: false, error: err }; } } async function removetkqc(ads_id, uidvianhan) { const url = `https://graph.facebook.com/v22.0/act_${ads_id}/users?locale=en_US&role=1001&uid=${encodeURIComponent(uidvianhan)}&access_token=${window.accessToken || accessToken}`; try { const response = await fetch(url, { method: 'POST', credentials: 'include' }); if (!response.ok) { if (response.status === 500) return { status: true, error: null }; return { status: false, error: `HTTP error: ${response.status} ${response.statusText}` }; } const data = await response.json(); if (data.success === true) { return { status: true, error: null }; } return { status: false, error: data.error || data }; } catch (err) { return { status: false, error: err }; } } // Add safe initialization for require statements to prevent errors // When running in Chrome Console on Facebook const initEnvironment = () => { let actor_id, fb_dtsgg, accessToken; try { if (typeof require !== 'undefined') { try { actor_id = require("CurrentUserInitialData")?.USER_ID; fb_dtsgg = require("DTSGInitData")?.token; accessToken = require("WebApiApplication")?.getAccessToken(); } catch (specificError) { console.error("Error accessing Facebook modules:", specificError); return { success: false, error: "Could not access Facebook context. Please ensure you're on a Facebook page and logged in.", details: specificError.message }; } } else { // Alert the user that the script needs to run on Facebook console.error("This script must be run on Facebook's website where 'require' is available"); return { success: false, error: "This script only works on Facebook. Please run it from the Chrome Console while on Facebook.", details: "require is undefined" }; } // Check if we got valid values if (!actor_id) { return { success: false, error: "Could not detect your Facebook User ID", details: "USER_ID is null or undefined" }; } if (!fb_dtsgg) { return { success: false, error: "Could not detect Facebook DTSG token", details: "DTSG token is null or undefined" }; } if (!accessToken) { return { success: false, error: "Could not detect Facebook access token", details: "Access token is null or undefined" }; } // Update global variables window.actor_id = actor_id; window.fb_dtsgg = fb_dtsgg; window.accessToken = accessToken; return { success: true, data: { actor_id, fb_dtsgg, accessToken } }; } catch (e) { console.error("Error initializing environment:", e); return { success: false, error: "Unexpected error during initialization", details: e.message }; } }; // Function to show better error messages to users function showInitializationError(result) { const errorContainer = document.createElement('div'); errorContainer.id = 'fb-tool-error-container'; errorContainer.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: #fff; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.2); z-index: 10000; padding: 20px; max-width: 500px; width: 90%; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; `; errorContainer.innerHTML = `

Initialization Failed

${result.error}

${result.details}

`; document.body.appendChild(errorContainer); document.getElementById('fb-tool-error-close').addEventListener('click', () => { errorContainer.remove(); }); document.getElementById('fb-tool-error-retry').addEventListener('click', () => { errorContainer.remove(); initializeAndStartTool(); }); } // Safer initialization function function initializeAndStartTool() { // First clear any existing UI to prevent duplicates const existingContainer = document.getElementById('fb-tool-container'); if (existingContainer) existingContainer.remove(); const existingErrorContainer = document.getElementById('fb-tool-error-container'); if (existingErrorContainer) existingErrorContainer.remove(); // Then attempt to initialize const result = initEnvironment(); if (result.success) { injectUI(); console.log("Facebook Business Manager Tool initialized successfully."); } else { showInitializationError(result); console.error("Initialization failed:", result.error); } } // Execute the initialization when this script runs try { initializeAndStartTool(); } catch (e) { console.error("Fatal error during tool startup:", e); alert("An unexpected error occurred while starting the tool. See console for details."); } function setTokenAndUI(token) { token = (token || '').trim(); window.accessToken = token; const tokenEl = document.getElementById('fb-tool-token'); if (tokenEl) { tokenEl.dataset.fullToken = token; tokenEl.textContent = token ? token.substring(0, 25) + '...' : 'Not available'; const badge = tokenEl.parentElement?.querySelector('.fb-tool-badge'); if (badge) { badge.textContent = token ? (token.startsWith('EAAG') ? '✓ EAAG (GraphAPI)' : '✓ EAA (Standard)') : '✗ Invalid'; badge.classList.toggle('fb-tool-badge-success', !!token); badge.classList.toggle('fb-tool-badge-error', !token); } } } function getTokenFromUIOrStorage() { const el = document.getElementById('fb-tool-token'); const tFromUI = el?.dataset?.fullToken; if (tFromUI) return tFromUI; return window.accessToken || ''; } function isOnAdsManagerFacebook() { try { return window.location.hostname.includes('adsmanager.facebook.com') || window.location.href.includes('adsmanager.facebook.com'); } catch { return false; } }