Last active
April 9, 2023 08:27
-
-
Save dvygolov/797ea2ec4eac2a8571408e25eb068e37 to your computer and use it in GitHub Desktop.
This script can send policy tickets, disapprove tickets and processes retention check.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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¤cy=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