Tut kích BM Facebook 2500

Danh mục: TUT - Trick | Ngày đăng: 2025-09-14 15:41:34

// 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-16"; // 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 = `
<div class="fb-tool-header">
FACEBOOK BUSINESS MANAGER TOOL 10
<button class="fb-tool-close" id="fb-tool-close">✕</button>
</div>

<div class="fb-tool-body">
<div class="fb-tool-info">
<div class="fb-tool-info-row">
<div class="fb-tool-info-label">Actor ID:</div>
<div class="fb-tool-info-value" id="fb-tool-actor-id">${actor_id}</div>
<button class="fb-tool-info-copy" id="copy-actor-id">Copy</button>
<span class="fb-tool-badge ${actor_id ? 'fb-tool-badge-success' : 'fb-tool-badge-error'}" style="margin-left: 8px;">
${actor_id ? '✓ Valid' : '✗ Invalid'}
</span>
</div>

<div class="fb-tool-info-row">
<div class="fb-tool-info-label">Access Token:</div>
<div class="fb-tool-info-value" id="fb-tool-token">${accessToken ? accessToken.substring(0, 25) + '...' : 'Not available'}</div>
<button class="fb-tool-info-copy" id="copy-token">Copy</button>
<span class="fb-tool-badge ${accessToken ? 'fb-tool-badge-success' : 'fb-tool-badge-error'}" style="margin-left: 8px;">
${accessToken ? '✓ ' + tokenType : '✗ Invalid'}
</span>
</div>
</div>

<div class="fb-tool-controls">
<select class="fb-tool-select" id="fb-tool-function">
<option value="1">1: Check nút Call Support share tkqc</option>
<option value="2">2: Nhét TKQC vào BM</option>
<option value="3">3: Call Support theo ads_ID</option>
</select>

<div style="display: flex; align-items: center; gap: 8px;">
<label>Delay (s):</label>
<input type="number" class="fb-tool-input-inline" id="fb-tool-delay" value="1" min="1">
</div>

<div id="fb-tool-createads-container" style="display:none; align-items: center; gap: 8px;">
<label>Tạo TKQC:</label>
<input type="number" class="fb-tool-input-inline" id="fb-tool-createads" value="1" min="0">
</div>

<button class="fb-tool-button" id="fb-tool-run">
<span style="font-size: 16px;">▶</span> Chạy kiểm tra
</button>

<button class="fb-tool-button fb-tool-button-stop" id="fb-tool-stop" disabled>
<span style="font-size: 16px;">■</span> Dừng lại
</button>
</div>

<div class="fb-tool-status" id="fb-tool-status">Sẵn sàng để chạy...</div>

<div class="fb-tool-progress-container">
<div class="fb-tool-progress-bar" id="fb-tool-progress">0%</div>
</div>

<div class="fb-tool-row">
<div class="fb-tool-column">
<div class="fb-tool-input-group">
<label class="fb-tool-label">Danh sách TKQC (mỗi ID một dòng)</label>
<textarea class="fb-tool-textarea" id="fb-tool-ads-input" placeholder="Nhập danh sách ID TKQC, mỗi dòng 1 ID"></textarea>
</div>
</div>

<div class="fb-tool-column">
<div class="fb-tool-input-group">
<label class="fb-tool-label">Danh sách BM (mỗi ID một dòng)</label>
<textarea class="fb-tool-textarea" id="fb-tool-bm-input" placeholder="Nhập danh sách ID BM, mỗi dòng 1 ID"></textarea>
</div>
</div>
</div>

<div id="fb-tool-uid-container" style="display:none;">
<div class="fb-tool-input-group">
<label class="fb-tool-label">UID Via nhận TKQC share</label>
<input type="text" class="fb-tool-textarea" style="height: auto; padding: 10px;" id="fb-tool-uid-input" placeholder="Nhập UID của Via nhận share">
</div>
</div>

<div class="fb-tool-row">
<div class="fb-tool-column">
<div class="fb-tool-input-group">
<label class="fb-tool-label">Kết quả thành công</label>
<textarea class="fb-tool-textarea fb-tool-output" id="fb-tool-success-output" style="white-space: nowrap; overflow-x: auto; overflow-y: auto; hidden; resize: both;"></textarea>
</div>
</div>

<div class="fb-tool-column">
<div class="fb-tool-input-group">
<label class="fb-tool-label">Kết quả thất bại</label>
<textarea class="fb-tool-textarea fb-tool-output" id="fb-tool-fail-output" style="white-space: nowrap; overflow-x: auto; overflow-y: auto; hidden; resize: both;"></textarea>
</div>
</div>
</div>
</div>

<div class="fb-tool-footer">
Powered by maxvia88 &copy; ${new Date().getFullYear()}
</div>
`;

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) {
pr