Skip to content

Instantly share code, notes, and snippets.

@dvygolov
Last active April 9, 2023 08:27
Show Gist options
  • Save dvygolov/797ea2ec4eac2a8571408e25eb068e37 to your computer and use it in GitHub Desktop.
Save dvygolov/797ea2ec4eac2a8571408e25eb068e37 to your computer and use it in GitHub Desktop.
This script can send policy tickets, disapprove tickets and processes retention check.
//TODO:
//Check non-ecommerce tickets
//Publish unpublished Fan Pages
//Log that account is FREE or Has Running Ads
//Log total spend and that account has big billing
//Cure Billing bug using low budget + prebill
//Delete phone
var debugMessages = false;
var maxFpsCount = 4;
var rejectedAdsAction = "delete"; //or replace
console.log(
"%c💉FB Account Doctor💉 v3.2a by Yellow Web (https://yellowweb.top)",
"font-size:25px;color:yellow;font-weight:bold"
);
let saq = await getSocialAccountQualityInfo();
let socHasZRD = await processZRD(
saq,
require("CurrentUserInitialData").USER_ID
);
console.log("%cProcessing Fan Pages", "font-size:20px;font-weight:bold");
var fps = await getAllFanPages();
debug(fps);
var bannedFps = [];
var okFps = [];
for (let i = 0; i < fps.length; i++) {
const fp = fps[i];
console.log(
`%cProcessing fan page ${fp.name}...`,
"font-size:15px;font-weight:bold;"
);
var fpaq = await getFanPageQualityInfo(fp.id);
debug(fpaq);
var fpFeedback = await getFanPageFeedback(fp.id);
debug(fpFeedback);
if (fpaq.advertising_restriction_info.is_restricted) {
bannedFps.push(fp);
await processFanPageUnban(
fp.id,
fpaq.advertising_restriction_info.ids_issue_ent_id
);
} else {
okFps.push(fp);
if (fpaq.advertising_restriction_info.status == "APPEAL_ACCEPTED")
console.log(
`%cPZRD FAN PAGE!`,
"font-size:13px;font-weight:bold;color:green;"
);
if (fpFeedback.isNotEcommerce) {
console.log(
`%cNon-eCommerce FAN PAGE!`,
"font-size:13px;font-weight:bold;color:green;"
);
} else {
await sendNonEcommerceTicket(fp.id);
if (fpFeedback.satisfactionScore != null) {
//TODO: various color for this number
console.log(
`Fan Page satisfaciton score: ${payload.satisfactionScore}`
);
}
}
if (fpFeedback.isYoungPage)
console.log(
`%cYoung FAN PAGE!`,
"font-size:13px;font-weight:bold;color:cyan;"
);
else
console.log(
`%cOLD FAN PAGE!`,
"font-size:13px;font-weight:bold;color:green;"
);
}
}
if (okFps.length < maxFpsCount) {
for (let i = okFps.length; i < maxFpsCount; i++) {
let newFpId = await createFanPage();
okFps.push(newFpId);
}
}
console.log(
"%cProcessing Personal Accounts",
"font-size:20px;font-weight:bold"
);
var profResp = await privateApiRequest(
{ category_id: "2347428775505624", surface: null },
5578508822226155
);
if (profResp?.data?.profile_plus_mutation ?? "" === "SUCCESS")
console.log(
"%cProfile switched to PROFESSIONAL MODE!",
"color:cyan;font-size:20px;font-weight:bold;"
);
var aq = await getAdAccountsQualityInfo();
debug(aq);
var accounts = aq.data.assetOwnerData.ad_accounts.edges;
await processAdAccounts(accounts, socHasZRD);
console.log(
"%cProcessing Business Accounts",
"font-size:20px;font-weight:bold"
);
var bms = await getAllBms();
for (let i = 0; i < bms.data.length; i++) {
var bm = bms.data[i];
console.log(
`%cProcessing BM: ${bm.name}`,
"font-size:18px;font-weight:bold;"
);
var bmAccLimit = await getBMAdAccountCreationLimit(bm.id);
var bmaq = await getBMAccountQualityInfo(bm.id);
var bmHasZRD = await processZRD(bmaq, bms.data[i].id, true);
var bmAccsAq = await getBMAdAccountQualityInfo(bm.id);
var bmAccs = bmAccsAq.data.assetOwnerData.business_ad_accounts.edges;
var bannedBmAccs = bmAccs.filter(
(acc) => acc.node.advertising_restriction_info.is_restricted == true
);
if (bmAccs.length < bmAccLimit && bannedBmAccs.length == 0)
await createMaxBmAccounts(bm.id, bm.name, bmAccs.length, bmAccLimit);
await processAdAccounts(bmAccs, bmHasZRD);
}
console.log("%cALL DONE!", "font-size:25px;color:yellow;font-weight:bold");
async function processAdAccounts(accounts, zrdStatus) {
for (let i = 0; i < accounts.length; i++) {
const acc = accounts[i];
console.log(
`%cProcessing ${acc.node.name}...`,
"font-size:15px;font-weight:bold;"
);
var rInfo = acc.node.advertising_restriction_info;
var accRestricted = rInfo.is_restricted;
if (accRestricted) {
switch (rInfo.restriction_type) {
case "PREAUTH":
if (await processRetention(acc)) accRestricted = false;
break;
case "SDC":
console.log("%cYou should get a 1$ payment code!", "color:yellow");
if (await processOneDollarCheck(acc)) accRestricted = false;
break;
case "RISK_PAYMENT":
console.log("%cAccount has RISK_PAYMENT restriction!", "color:red");
break;
case "TRUST_TIER":
console.log("%cAccount is restricted in spend!", "color:yellow");
accRestricted = false;
break;
case "UNSETTLED":
console.log(
"%cAccount is UNSETTLED! Processing payment...",
"color:yellow"
);
if (await payUnsettled(acc)) accRestricted = false;
break;
case "GENERIC":
if (
rInfo.additional_parameters?.disable_reason ??
"" === "RISK_PAYMENT"
) {
if (!(await processRetention(acc))) {
console.log(
"%cAccount has RISK_PAYMENT restriction!",
"color:red"
);
}
} else await processPolicyBan(acc.node.id);
break;
default:
await processPolicyBan(acc.node.id);
}
}
if (!accRestricted) {
await changeFpIfNeeded(acc, bannedFps, okFps);
var rejCount = acc.node.rejected_ad_count;
if (rejCount > 0) {
console.log(`%cAccount has ${rejCount} rejected ads!`, "color:yellow");
await sendDisapproveTickets(acc.node.id, zrdStatus);
}
await processWithIssuesAds(acc);
if (await accountHasBillingBug(acc)) {
console.log("%cAccount HAS BILLING BUG!", "color:red");
if (!(await processRetention2(acc))) {
// let postInfo = await boostFanPagePost(acc.node.id, okFps[0]);
// console.log(`Boosted FanPage post ${postInfo[0]}`);
// if (!(await accountHasBillingBug(acc)))
// console.log( "%cBILLING BUG FIXED!", "color:green;font-weight:bold;");
// else
// console.log( "%cCouldn't fix BILLING BUG((", "color:red;font-weight:bold;");
// let delRes = await publicApiRequest(postInfo[0], "method=delete", postInfo[1]);
} else
console.log("%cBILLING BUG FIXED!", "color:green;font-weight:bold;");
}
}
}
}
async function getSocialAccountQualityInfo() {
let uid = require("CurrentUserInitialData").USER_ID;
let variables = { assetOwnerId: uid };
return await privateApiRequest(variables, 5734580019936225);
}
async function getAllBms() {
return await publicApiRequest(
"me/businesses",
"fields=id,name,is_disabled_for_integrity_reasons"
);
}
async function getBMAccountQualityInfo(bmId) {
let variables = { assetOwnerId: bmId };
return await privateApiRequest(variables, 5816699831746699);
}
async function getBMAdAccountQualityInfo(bmId) {
let variables = {
assetOwnerId: bmId,
startTime: 1670630400,
count: 20,
cursor: null,
};
return await privateApiRequest(variables, 5688466787864440);
}
async function getBMAdAccountCreationLimit(bmId) {
let subDomain = getSubDomain();
let js = await privateApiRequest2(
null,
null,
`https://${subDomain}.facebook.com/business/adaccount/limits/?business_id=${bmId}`
);
return js.payload.adAccountLimit;
}
async function createMaxBmAccounts(bmId, bmName, curCount, limit) {
let users = await getBMUsers(bmId);
console.log("Creating BM accounts...");
while (curCount < limit) {
let name = `${bmName} ${curCount + 1}`;
let js = await publicApiRequest(
`${bmId}/adaccount`,
`method=post&end_advertiser=NONE&media_agency=NONE&partner=NONE&currency=USD&timezone_id=116&name=${name}`
);
await assignUsersToAccount(js.id, users);
console.log(`Created Ad Account ${name}!`);
curCount++;
}
}
async function getBMUsers(bmId) {
let js = await publicApiRequest(`${bmId}/business_users`, "");
return js.data;
}
async function assignUsersToAccount(accId, users) {
for (let i = 0; i < users.length; i++) {
const user = users[i];
let js = await publicApiRequest(
`${accId}/assigned_users`,
`method=post&user=${user.id}&tasks=['ANALYZE','ADVERTISE','MANAGE']`
);
}
}
async function getAdAccountsQualityInfo() {
let variables = { count: 20, cursor: null, startTime: null };
return await privateApiRequest(variables, 5353108281440501);
}
async function getAllFanPages() {
let js = await publicApiRequest(
"me",
"fields=accounts.limit(100){id,additional_profile_id,name,access_token,verification_status,is_published,ad_campaign,is_promotable,is_restricted,parent_page,promotion_eligible,promotion_ineligible_reason,fan_count,has_transitioned_to_new_page_experience,ads_posts.limit(100),picture}"
);
return js.accounts.data;
}
async function getFanPageQualityInfo(pageId) {
let uid = require("CurrentUserInitialData").USER_ID;
let variables = { assetOwnerId: uid, assetId: pageId };
let fp = await privateApiRequest(variables, 8605172106222051);
return fp.data.pageData;
}
async function getFanPageFeedback(pageId) {
let subDomain = getSubDomain();
let js = await privateApiRequest2(
null,
null,
`https://${subDomain}.facebook.com/ads/async/advertiser_dashboard/page_feedback/?page_id=${pageId}`
);
return js.payload;
}
async function sendNonEcommerceTicket(pageId) {
let uid = require("CurrentUserInitialData").USER_ID;
let lsd = require("LSD").token;
let dtsg = require("DTSGInitialData").token;
let subDomain = getSubDomain();
let url = `https://${subDomain}.facebook.com/ads/async/advertiser_dashboard/appeal_information/`;
let params = {
reason_for_appeal: 1,
page_id: pageId,
additional_info: null,
is_update: false,
__user: uid,
__a: 1,
fb_dtsg: dtsg,
lsd: lsd,
};
let js = await privateApiRequest2(
null,
null,
`${url}?${new URLSearchParams(params).toString()}`
);
if (
js.payload.ACEAdvertiserDashboardAppealRejectReason[0] == "NO_ACTIVE_ADS"
) {
console.log("Can't send non-eCommerce ticket: no active ads!");
return false;
}
return true;
}
async function processFanPageUnban(fpId, issueId) {
let uid = require("CurrentUserInitialData").USER_ID;
let variables = { assetOwnerId: uid, assetId: fpId };
let js = await privateApiRequest(variables, 5703537586402775);
let fpStatus = js.data.pageData.advertising_restriction_info.status;
switch (fpStatus) {
case "APPEAL_REJECTED_NO_RETRY":
console.log(
"%cThis fan page is banned FOREVER!",
"color:red;font-weight:bold;"
);
return false;
break;
case "APPEAL_PENDING":
console.log("%cThis fan page is in review!", "color:yellow;");
return true;
break;
}
variables = {
input: {
client_mutation_id: "1",
actor_id: uid,
entity_id: fpId,
ids_issue_ent_id: issueId,
appeal_comment: "I'm not sure which policy was violated.",
callsite: "ACCOUNT_QUALITY",
},
};
js = await privateApiRequest(variables, 5329872773703511);
let pSent = js.data.ale_banhammer_appeal_create.success;
if (pSent) console.log("%cUnban ticket sent!", "color:green");
else console.log("%cCan't send unban ticket!", "color:red");
return pSent;
}
async function createFanPage() {
let names = [
"Apple",
"Green",
"Clean",
"Eating",
"Diet",
"Complex",
"Carbo",
"Meso",
"Paleo",
"Ecology",
"Detox",
"Freegan",
"Vegan",
"Healthy",
"Fruit",
"Food",
"Security",
"Empty",
"Calory",
"Fasting",
"Lean",
"Body",
"Nutrition",
"Fatty",
"Lower",
"Plant",
"Slim",
"Sugarfree",
"Balanced",
"Life",
"Keto",
"Well",
"Sport",
"Ketogenic",
"Nutra",
"Laktovo",
"Regimen",
"Plan",
"Reduction",
"Boiled",
"Steamed",
"Cooking",
"Snack",
"Meals",
"Pounds",
"Kilograms",
"Teeth",
"Cutout",
"Grilled",
"Cabbage",
"Soup",
"Cambridge",
"Atkins",
"Burner",
"Vinegar",
"Pills",
"Tips",
"Tricks",
"Meal",
"Belly",
"Gain",
];
const shuffled = names.sort(() => 0.5 - Math.random());
let selected = shuffled.slice(0, 2);
let name = `${selected[0]} ${selected[1]}`;
console.log(`Creating new FanPage with name: ${name}`);
let uid = require("CurrentUserInitialData").USER_ID;
let variables = {
input: {
bio: "",
categories: ["164886566892249"],
creation_source: "comet",
name: name,
page_referrer: "launch_point",
actor_id: uid,
client_mutation_id: "1",
},
};
let js = await privateApiRequest(variables, 4722866874428654);
debug(js);
let fpProfileId =
js.data.additional_profile_plus_create.additional_profile.id;
let fpId = await setFanPageAvatar(fpProfileId);
return fpId;
}
async function setFanPageAvatar(fpProfileId) {
console.log(`Setting FanPage avatar...`);
let fps = await getAllFanPages();
let fp = fps.filter((d) => d.additional_profile_id == fpProfileId)[0];
debug(fp);
try {
let pUrl = encodeURIComponent("https://picsum.photos/200");
js = await publicApiRequest(
`${fp.id}/photos`,
`method=post&url=${pUrl}`,
fp.access_token
);
debug(js);
js = await publicApiRequest(
`${fp.id}/picture`,
`method=post&photo=${js.id}`,
fp.access_token
);
debug(js);
} catch {
console.log("Couldn't set avatar!((");
}
return fp.id;
}
async function changeFpIfNeeded(acc, bannedFps, okFps) {
var selectedOkFp = okFps[Math.floor(Math.random() * okFps.length)];
var accId = acc.node.payment_account.id;
let js = await publicApiRequest(
`act_${accId}/ads`,
"fields=creative{name,object_story_spec,url_tags}"
);
for (let i = 0; i < js.data.length; i++) {
let ad = js.data[i];
let adPageId = ad.creative.object_story_spec?.page_id ?? null;
if (adPageId == null) {
console.log("Ad doesn't have a valid page!");
continue;
}
let bannedFp = bannedFps.filter((p) => p.id == adPageId);
let okFp = okFps.filter((p) => p.id == adPageId);
if (bannedFp.length > 0 || (bannedFp.length == 0 && okFp.length == 0)) {
console.log(
`%cAd ${ad.id} uses a banned FP! Changing it to ${selectedOkFp.name}...`,
"color:yellow"
);
await changeAdFanPage(accId, ad, selectedOkFp.id);
}
}
}
async function getInstagramId(fpId) {
let tokenJs = await publicApiRequest(fpId, "fields=access_token");
let fpToken = tokenJs["access_token"];
let instJs = await publicApiRequest(
`${fpId}/page_backed_instagram_accounts`,
"fields=id",
fpToken
);
if (instJs.data.length == 0) {
debug("Creating Instagram backed account...");
instJs = await publicApiPostRequest(
`${fpId}/page_backed_instagram_accounts`,
"",
fpToken
);
}
let instId = instJs.data[0].id;
debug("Got Instagram id: " + instId);
return instId;
}
async function changeAdFanPage(accId, ad, fpId) {
let oss = ad.creative.object_story_spec;
oss["page_id"] = fpId;
debug(oss);
//oss.instagram_actor_id = await getInstagramId(fpId); //Instagram doesn't work somehow
delete oss["instagram_actor_id"];
let body = `method=post&locale=en_US&name=ReplaceCreative&object_story_spec=${JSON.stringify(
oss
)}`;
let urlParams = ad.creative.url_tags;
if (urlParams?.length ?? 0 > 0) {
if (urlParams[0] == "?") urlParams = urlParams.substring[1];
body += `&url_tags=${escape(urlParams)}`;
}
let res = await publicApiRequest(`act_${accId}/adcreatives`, body);
if (res.id != null) {
let creative = { creative_id: res.id };
res = await publicApiRequest(
ad.id,
`method=post&locale=en_US&creative=${JSON.stringify(creative)}`
);
if (res.success) console.log("Updated ad's adcreative!");
else console.log("Couldn't update ad's creative:" + JSON.stringify(res));
return res.success;
}
return false;
}
async function processZRD(aqInfo, entityId, isBm = false) {
let actor = isBm ? "BM" : "Account";
let rInfo = aqInfo.data.assetOwnerData.advertising_restriction_info;
switch (rInfo.status) {
case "APPEAL_PENDING":
console.log(
`%c${actor} has ZRD, appeal PENDING!`,
"font-size:15px;color:yellow;font-weight:bold"
);
return true;
break;
case "APPEAL_REJECTED_NO_RETRY":
console.log(
`%c${actor} has LIFETIME ZRD! GG WP`,
"font-size:15px;color:red;font-weight:bold"
);
return true;
break;
case "APPEAL_ACCEPTED":
console.log(
`%c${actor} IS PZRD!`,
"font-size:15px;color:green;font-weight:bold"
);
return false;
break;
}
if (!rInfo.is_restricted) return false;
console.log(
`%c${actor} has ZRD!`,
"font-size:15px;color:red;font-weight:bold"
);
let issueId =
aqInfo.data.assetOwnerData.advertising_restriction_info.ids_issue_ent_id;
let variables = {
input: {
client_mutation_id: "1",
actor_id: require("CurrentUserInitialData").USER_ID,
entity_id: entityId,
ids_issue_ent_id: issueId,
appeal_comment: "I'm not sure which policy was violated.",
callsite: "ACCOUNT_QUALITY",
},
};
let js = await privateApiRequest(variables, 5329872773703511);
let zrdSent = js.data.ale_banhammer_appeal_create.success;
if (zrdSent) console.log("%cZRD ticket sent!", "color:green");
else console.log("%cCan't send ZRD ticket!", "color:red");
return true;
}
async function processPolicyBan(accountId) {
let uid = require("CurrentUserInitialData").USER_ID;
let variables = { assetOwnerId: uid, assetId: accountId };
let js = await privateApiRequest(variables, 5703537586402775);
let accStatus = js.data.adAccountData.advertising_restriction_info.status;
switch (accStatus) {
case "APPEAL_REJECTED_NO_RETRY":
console.log(
"%cThis account is banned FOREVER!",
"color:red;font-weight:bold;"
);
return false;
break;
case "APPEAL_PENDING":
console.log("%cThis account is in review!", "color:yellow;");
return true;
break;
}
variables = {
input: {
client_mutation_id: "1",
actor_id: uid,
ad_account_id: accountId,
ids_issue_ent_id: "880164863403788",
appeal_comment: "I'm not sure which policy was violated.",
callsite: "ACCOUNT_QUALITY",
},
};
js = await privateApiRequest(variables, 5197966936890203);
let pSent = js.data.xfb_alr_ad_account_appeal_create.success === true;
if (pSent) console.log("%cPolicy ticket sent!", "color:green");
else console.log("%cCan't send policy ticket!", "color:red");
return pSent;
}
async function sendDisapproveTickets(accountId, zrdStatus) {
let lsd = require("LSD").token;
let dtsg = require("DTSGInitialData").token;
let uid = require("CurrentUserInitialData").USER_ID;
let allDisapproved = [];
let notSentDisapproved = [];
let js = await getAccountAds(accountId);
for (let i = 0; i < js.data.length; i++) {
let ad = js.data[i];
if (ad.effective_status != "DISAPPROVED") continue;
allDisapproved.push(ad.id);
let disStatus = await getDisapproveStatus(ad.id);
switch (disStatus) {
case "Unchanged":
console.log(
`%cAd ${ad.id} didn't pass the review and will remain Unchanged!`,
"color:red"
);
if (!zrdStatus) {
console.log(`%cDeleting ad...`, "color:red;");
await publicApiRequest(ad.id, "method=delete");
}
break;
case "NotCreated":
notSentDisapproved.push(ad.id);
break;
default:
console.log(`%cAd ${ad.id} status is ${disStatus}!`, "color:cyan;");
break;
}
}
let reasons = await publicApiRequest(
"",
`fields=ad_review_feedback&ids=${allDisapproved.join(",")}`
);
for (var adReasonId in reasons) {
if (isNaN(adReasonId)) continue;
console.log(
`${adReasonId} - ${
Object.keys(reasons[adReasonId].ad_review_feedback.global)[0]
}`
);
}
if (notSentDisapproved.length == 0) return true;
var body = {
ad_account_id: accountId,
callsite: "ACCOUNT_QUALITY",
__user: uid,
__a: 1,
fb_dtsg: dtsg,
lsd: lsd,
};
for (let i = 0; i < notSentDisapproved.length; i++) {
let key = `adgroup_ids[${i}]`;
body[key] = notSentDisapproved[i];
}
let subDomain = getSubDomain();
let graphUrl = `https://${subDomain}.facebook.com/ads/integrity/appeals_case/creation/ajax/`;
f = await fetch(graphUrl, {
headers: {
accept: "*/*",
"accept-language": "ca-ES,ca;q=0.9,en-US;q=0.8,en;q=0.7",
"content-type": "application/x-www-form-urlencoded",
"sec-ch-ua":
'"Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"x-fb-lsd": lsd,
},
body: new URLSearchParams(body).toString(),
method: "POST",
mode: "cors",
credentials: "include",
});
let t = await f.text();
js = JSON.parse(t.substring(9));
let results = js.payload.adgroupIDToSuccess.__map;
for (let i = 0; i < results.length; i++) {
console.log(results[i]);
}
}
async function getDisapproveStatus(adId) {
let dtsg_ag = require("DTSGInitData").async_get_token;
let subDomain = getSubDomain();
let graphUrl = `https://${subDomain}.facebook.com/ads/integrity/appeals/status/ajax/`;
graphUrl += `?adgroup_id=${adId}&fb_dtsg_ag=${dtsg_ag}&__a=1&locale=en_US`;
let f = await fetch(graphUrl, {
headers: {
accept: "*/*",
"accept-language": "ca-ES,ca;q=0.9,en-US;q=0.8,en;q=0.7",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
},
method: "GET",
mode: "cors",
credentials: "include",
});
let t = await f.text();
let js = JSON.parse(t.substring(9)).payload;
if (js?.appeal_created ?? false) return js.status;
else return "NotCreated";
}
async function payUnsettled(acc) {
let uid = require("CurrentUserInitialData").USER_ID;
let credId = await getCardId(acc);
let variables = {
input: {
billable_account_payment_legacy_account_id: acc.node.payment_account.id,
credential_id: credId,
logging_data: { logging_counter: 28, logging_id: "965054858" },
transaction_initiation_source: "CUSTOMER",
actor_id: uid,
client_mutation_id: "2",
},
};
js = await privateApiRequest(
variables,
5410734278994659,
"https://business.secure.facebook.com/ajax/payment/token_proxy.php?tpe=%2Fapi%2Fgraphql%2F"
);
debug(js);
return js.errors == null;
}
async function accountHasBillingBug(acc) {
let variables = { paymentAccountID: acc.node.payment_account.id };
var js = await privateApiRequest(variables, 5423026971129377);
let hasBug =
js.data?.billable_account_by_payment_account?.is_reauth_restricted ?? false;
return hasBug;
}
async function processRetention2(acc) {
let uid = require("CurrentUserInitialData").USER_ID;
let variables = {
input: {
billable_account_payment_legacy_account_id: acc.node.payment_account.id,
actor_id: uid,
client_mutation_id: "4",
},
};
var js = await privateApiRequest(variables, 4823893574396261);
let hasBug =
js.data?.billable_account_by_payment_account?.is_reauth_restricted ?? false;
return hasBug;
}
async function processRetention(acc) {
let uid = require("CurrentUserInitialData").USER_ID;
let variables = {
input: {
billable_account_payment_legacy_account_id: acc.node.payment_account.id,
entry_point: "BILLING_2_0",
actor_id: uid,
client_mutation_id: "1",
},
};
var json = await privateApiRequest(variables, 4552428401528620);
let success =
(json.data?.billing_prisk_restriction_preauth_permit?.success ?? false) ===
true;
if (success) console.log("%cPreauth processed successfully!", "color:green;");
else console.log("%cCouldn't process preauth!", "color:red;");
return success;
}
async function processOneDollarCheck(acc) {
let uid = require("CurrentUserInitialData").USER_ID;
let credId = await getCardId(acc);
let variables = {
input: {
billable_account_payment_legacy_account_id: acc.node.payment_account.id,
credential_id: credId,
actor_id: uid,
client_mutation_id: 1,
},
};
let js = await privateApiRequest(variables, 5032761333411971);
debug(js);
if (!js.data?.send_dynamic_descriptor_auth.sent ?? false) {
console.log("%cCouldn't send verification request!", "color:red;");
return false;
}
let code = prompt("Please write verification code:");
variables = {
input: {
billable_account_payment_legacy_account_id: acc.node.payment_account.id,
credential_id: credId,
verification_code: code,
actor_id: uid,
client_mutation_id: 1,
},
};
js = await privateApiRequest(variables, 4982114225214837);
let verified = js.data.verify_dynamic_descriptor_auth.verified;
if (verified) console.log("%cCard verified!", "color:green;");
else console.log("%cError verifying the card!", "color:red;");
return verified;
}
async function getCardId(acc) {
let variables = { paymentAccountID: acc.node.payment_account.id };
let js = await privateApiRequest(variables, 5584576741653814);
return (
js.data.billable_account_by_payment_account.billing_payment_account
.billing_payment_methods[0]?.credential?.credential_id ?? -1
);
}
async function processWithIssuesAds(acc) {
var accId = acc.node.payment_account.id;
let token = __accessToken;
let withIssues = [];
let js = await getAccountAds(accId);
for (let i = 0; i < js.data.length; i++) {
let ad = js.data[i];
if (ad.effective_status != "WITH_ISSUES") continue;
console.log("Processing Ad WITH_ISSUES...");
let t = await publicApiRequest(ad.id, "method=post&status=ACTIVE");
debug(t);
let errorCode = t?.error?.error_subcode ?? 0;
if (errorCode === 1487194) {
let adCreo = await publicApiRequest(
ad.id,
"fields=creative{name,object_story_spec,url_tags}"
);
await changeAdFanPage(
accId,
adCreo,
adCreo.creative.object_story_spec.page_id
);
}
}
}
async function getAccountAds(accountId) {
return await publicApiRequest(
`act_${accountId}/ads`,
"fields=effective_status"
);
}
async function boostFanPagePost(accId, fpId) {
let js = await publicApiRequest("me/accounts", "fields=name,access_token");
let fp = js.data[0];
let postInfo = await publicApiRequest(
`${fp.id}/feed`,
"method=post&message=Do%20you%20love%20Jesus",
fp.access_token
);
let fpPostId = postInfo.id.split("_")[1];
let uid = require("CurrentUserInitialData").USER_ID;
let variables = {
input: {
boost_id: null,
client_spec_override: null,
page_id: fp.id,
product: "BOOSTED_POST",
target_id: fpPostId,
ad_account_id: accId,
objective: "POST_ENGAGEMENT",
},
cta_type: "NO_BUTTON",
};
js = await privateApiRequest(variables, 3830262120432240);
return [postInfo.id, fp.access_token];
}
async function publicApiRequest(path, qs, token = null) {
token = token ?? __accessToken;
let f = await fetch(
`https://graph.facebook.com/v15.0/${path}?${qs}&access_token=${token}`,
{
headers: {
accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"accept-language": "ca-ES,ca;q=0.9,en-US;q=0.8,en;q=0.7",
"cache-control": "max-age=0",
"sec-ch-ua":
'"Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site",
},
referrerPolicy: "strict-origin-when-cross-origin",
body: null,
method: "GET",
mode: "cors",
credentials: "include",
referrer: "https://business.facebook.com/",
referrerPolicy: "origin-when-cross-origin",
}
);
let js = await f.json();
return js;
}
async function publicApiPostRequest(path, body, token = null) {
token = token ?? __accessToken;
body = `access_token=${token}&${body}`;
let f = await fetch(`https://graph.facebook.com/v14.0/${path}`, {
headers: {
accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"accept-language": "ca-ES,ca;q=0.9,en-US;q=0.8,en;q=0.7",
"cache-control": "max-age=0",
"sec-ch-ua":
'"Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "none",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1",
},
referrerPolicy: "strict-origin-when-cross-origin",
body: body,
method: "POST",
});
let js = await f.json();
return js;
}
async function privateApiRequest2(body = null, headers = null, url = null) {
let graphUrl = "";
if (url != null) graphUrl = url;
else {
let subDomain = getSubDomain();
graphUrl = `https://${subDomain}.facebook.com/api/graphql/`;
}
if (body == null) {
let uid = require("CurrentUserInitialData").USER_ID;
let dtsg = require("DTSGInitialData").token;
let lsd = require("LSD").token;
body = `__user=${uid}&__a=1&fb_dtsg=${dtsg}&lsd=${lsd}`;
}
headers = headers ?? {
accept: "*/*",
"accept-language": "ca-ES,ca;q=0.9,en-US;q=0.8,en;q=0.7",
"content-type": "application/x-www-form-urlencoded",
"sec-ch-ua":
'"Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"x-fb-lsd": require("LSD").token,
};
let f = await fetch(graphUrl, {
headers: headers,
body: body,
method: "POST",
mode: "cors",
credentials: "include",
});
let t = await f.text();
if (t.startsWith("for (;;);")) t = t.substring(9);
return JSON.parse(t);
}
async function privateApiRequest(variables, docId, url = null) {
let graphUrl = "";
if (url != null) graphUrl = url;
else {
let subDomain = getSubDomain();
graphUrl = `https://${subDomain}.facebook.com/api/graphql/`;
}
let lsd = require("LSD").token;
let dtsg = require("DTSGInitialData").token;
let uid = require("CurrentUserInitialData").USER_ID;
let body = {
av: uid,
__user: uid,
__a: 1,
fb_dtsg: dtsg,
lsd: lsd,
variables: JSON.stringify(variables),
server_timestamps: true,
doc_id: docId,
};
let headers = {
accept: "*/*",
"accept-language": "ca-ES,ca;q=0.9,en-US;q=0.8,en;q=0.7",
"content-type": "application/x-www-form-urlencoded",
"sec-ch-ua":
'"Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"x-fb-lsd": lsd,
};
return await privateApiRequest2(
new URLSearchParams(body).toString(),
headers,
graphUrl
);
}
function getSubDomain(){
let curUrl = window.location.href;
let subDomain = curUrl.includes("/business.") ? "business" : (curUrl.includes("/adsmanager.") ?"adsmanager" : "www");
return subDomain;
}
function debug(msg) {
if (!debugMessages) return;
console.log(msg);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment