Skip to content

Instantly share code, notes, and snippets.

@dvygolov
Last active February 16, 2024 15:54
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dvygolov/c2077f391bd15ba2f75d7496afb47a67 to your computer and use it in GitHub Desktop.
Save dvygolov/c2077f391bd15ba2f75d7496afb47a67 to your computer and use it in GitHub Desktop.
This script adds 4 (+2) Facebook Autorules, read comment below
let api = 'https://graph.facebook.com/v14.0';
let headers = {
"accept": "*/*",
"accept-language": "en-US,en;q=0.9",
"content-type": "application/x-www-form-urlencoded",
"sec-ch-ua": "\"Google Chrome\";v=\"107\", \"Chromium\";v=\"107\", \"Not=A?Brand\";v=\"24\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site"
};
var cbo = await campUsesCBO();
await main(cbo);
async function campUsesCBO(){
let accountId = require("BusinessUnifiedNavigationContext").adAccountID;
let f = await fetch(`${api}/act_${accountId}/campaigns?fields=bid_strategy&access_token=${__accessToken}`, {
"headers": headers,
"referrer": "https://business.facebook.com/",
"referrerPolicy": "origin-when-cross-origin",
"body": null,
"method": "GET",
"mode": "cors",
"credentials": "include"
});
let camps = await f.json();
if (camps.data.length==0) return false;
if (camps.data[0]?.bid_strategy??null!=null)
return true;
return false;
}
async function main(cbo){
if (cbo)
alert("Your campaign uses CBO so we'll create auto-rules for campaigns!");
else
alert("Your campaign does NOT use CBO so we'll create rules for adsets!");
let accountId = require("BusinessUnifiedNavigationContext").adAccountID;
await clearRules(accountId);
let maxCPA = prompt('Please write your maximum CPA:');
if (maxCPA == null) {
alert('Max CPA not set, exiting...');
return;
}
let hour = parseInt(prompt(`At which hour should we switch ON ${cbo?"campaign":"adsets"}?`));
if (hour == null) {
alert('Switch ON hour not set, exiting...');
return;
}
let name = `TurnOff ${cbo?'Campaign':'Adset'} Withoud Leads`;
let evalSpec = {
"evaluation_type":"SCHEDULE",
"filters":[
{"field":"spent","operator":"GREATER_THAN","value":(maxCPA*100).toString()},
{"field":"offsite_conversion.fb_pixel_lead","operator":"LESS_THAN","value":"1"},
{"field":"entity_type","value":`${cbo?'CAMPAIGN':'ADSET'}`,"operator":"EQUAL"},
{"field":"time_preset","value":"TODAY","operator":"EQUAL"}
]};
let execSpec = {
"execution_type":"PAUSE",
"execution_options":[{"field":"alert_preferences","value":{"instant":{"trigger":"CHANGE"}},"operator":"EQUAL"}]
};
let schedSpec = {"schedule_type":"SEMI_HOURLY"};
await addRule(accountId, name, evalSpec, execSpec, schedSpec);
name = `TurnOff ${cbo?'Campaign':'Adset'} With Expensive Leads`;
evalSpec = {
"evaluation_type":"SCHEDULE",
"filters":[
{"field":"cost_per_lead_fb","operator":"GREATER_THAN","value":(maxCPA*100).toString()},
{"field":"entity_type","value":`${cbo?'CAMPAIGN':'ADSET'}`,"operator":"EQUAL"},
{"field":"time_preset","value":"TODAY","operator":"EQUAL"}
]
};
execSpec = {
"execution_type":"PAUSE",
"execution_options":[
{"field":"alert_preferences","value":{"instant":{"trigger":"CHANGE"}},"operator":"EQUAL"}
]};
await addRule(accountId, name, evalSpec, execSpec, schedSpec);
name = `TurnOn ${cbo?'Campaign':'Adset'} With Cheap Leads`;
evalSpec = {
"evaluation_type":"SCHEDULE",
"filters":[
{"field":"cost_per_lead_fb","operator":"LESS_THAN","value":(maxCPA*100).toString()},
{"field":"offsite_conversion.fb_pixel_lead","operator":"GREATER_THAN","value":"0"},
{"field":"entity_type","value":`${cbo?'CAMPAIGN':'ADSET'}`,"operator":"EQUAL"},
{"field":"time_preset","value":"TODAY","operator":"EQUAL"}
]
};
execSpec = {
"execution_type":"UNPAUSE",
"execution_options":[
{"field":"alert_preferences","value":{"instant":{"trigger":"CHANGE"}},"operator":"EQUAL"}
]};
await addRule(accountId, name, evalSpec, execSpec, schedSpec);
name = `TurnOn All ${cbo?'Campaigns':'Adsets'} at ${hour}`;
evalSpec = {
"evaluation_type":"SCHEDULE",
"filters":[{"field":"entity_type","value":`${cbo?'CAMPAIGN':'ADSET'}`,"operator":"EQUAL"},{"field":"time_preset","value":"MAXIMUM","operator":"EQUAL"}]
};
execSpec = {
"execution_type":"UNPAUSE",
"execution_options":[
{"field":"alert_preferences","value":{"instant":{"trigger":"CHANGE"}},"operator":"EQUAL"}
]};
schedSpec = {
"schedule_type":"CUSTOM",
"schedule":[
{"days":[0],"start_minute":hour*60,"end_minute":hour*60},
{"days":[1],"start_minute":hour*60,"end_minute":hour*60},
{"days":[2],"start_minute":hour*60,"end_minute":hour*60},
{"days":[3],"start_minute":hour*60,"end_minute":hour*60},
{"days":[4],"start_minute":hour*60,"end_minute":hour*60},
{"days":[5],"start_minute":hour*60,"end_minute":hour*60},
{"days":[6],"start_minute":hour*60,"end_minute":hour*60}
]
};
await addRule(accountId, name, evalSpec, execSpec, schedSpec);
let createCpcRules = confirm('Do you want to create rules based on Max CPC?');
if (createCpcRules){
let maxCPC = prompt("Enter your maximum CPC (for example 0.13):");
let maxClicks = prompt(`Enter clicks limit after which the ${cbo?'campaign':'adset'} will be shut down:`);
name = `TurnOff ${cbo?'Campaign':'Adset'} With Expensive CPC`;
evalSpec = {
"evaluation_type":"SCHEDULE",
"filters":[
{"field":"clicks","operator":"GREATER_THAN","value":(maxClicks).toString()},
{"field":"cpc","operator":"GREATER_THAN","value":(maxCPC*100).toString()},
{"field":"entity_type","value":`${cbo?'CAMPAIGN':'ADSET'}`,"operator":"EQUAL"},
{"field":"time_preset","value":"TODAY","operator":"EQUAL"}
]};
execSpec = {
"execution_type":"PAUSE",
"execution_options":[{"field":"alert_preferences","value":{"instant":{"trigger":"CHANGE"}},"operator":"EQUAL"}]
};
schedSpec = {"schedule_type":"SEMI_HOURLY"};
await addRule(accountId, name, evalSpec, execSpec, schedSpec);
}
let createCpmRules = confirm('Do you want to create rules based on Max CPM?');
if (createCpmRules){
let maxCPM = prompt("Enter your maximum CPM:");
let maxImpressions = prompt(`Enter your maximum impressions after which the ${cbo?'campaign':'adset'} will be shut down:`);
name = `TurnOff ${cbo?'Campaign':'Adset'} With Expensive CPM`;
evalSpec = {
"evaluation_type":"SCHEDULE",
"filters":[
{"field":"impressions","operator":"GREATER_THAN","value":maxImpressions.toString()},
{"field":"cpm","operator":"GREATER_THAN","value":(maxCPM*100).toString()},
{"field":"entity_type","value":`${cbo?'CAMPAIGN':'ADSET'}`,"operator":"EQUAL"},
{"field":"time_preset","value":"TODAY","operator":"EQUAL"}
]};
execSpec = {
"execution_type":"PAUSE",
"execution_options":[{"field":"alert_preferences","value":{"instant":{"trigger":"CHANGE"}},"operator":"EQUAL"}]
};
schedSpec = {"schedule_type":"SEMI_HOURLY"};
await addRule(accountId, name, evalSpec, execSpec, schedSpec);
}
alert('All Done!');
}
async function getAllRules(accountId){
let f = await fetch(`${api}/act_${accountId}/adrules_library?access_token=${__accessToken}`, {
"headers": headers,
"referrer": "https://business.facebook.com/",
"referrerPolicy": "origin-when-cross-origin",
"body": null,
"method": "GET",
"mode": "cors",
"credentials": "include"
});
let rules = await f.json();
return rules;
}
async function deleteRule(ruleId){
let f = await fetch(`${api}/${ruleId}?access_token=${__accessToken}&method=delete`, {
"headers": headers,
"referrer": "https://business.facebook.com/",
"referrerPolicy": "origin-when-cross-origin",
"body": "method=delete",
"method": "POST",
"mode": "cors",
"credentials": "include"
});
let js = await f.json();
console.log(js);
}
async function clearRules(accountId){
let rules = await getAllRules(accountId);
let rulesCount = rules.data.length;
if (rulesCount==0) return;
let clear = confirm(`Do you want to clear all existing autorules (${rulesCount} found)?`);
if (!clear) return;
console.log(`Deleting ${rulesCount} rules.`);
for (const rule of rules.data) {
console.log(`Deleting rule ${JSON.stringify(rule)}...`);
await deleteRule(rule['id']);
};
}
async function addRule(accountId, name, evalSpec, execSpec, schedSpec){
let body = {
"locale":"en_US",
"evaluation_spec":JSON.stringify(evalSpec),
"execution_spec":JSON.stringify(execSpec),
"name":name,
"schedule_spec":JSON.stringify(schedSpec),
"access_token":__accessToken,
"status":"ENABLED"
};
let f = await fetch(`${api}/act_${accountId}/adrules_library?access_token=${__accessToken}`, {
"headers": headers,
"referrer": "https://business.facebook.com/",
"referrerPolicy": "origin-when-cross-origin",
"body": new URLSearchParams(body).toString(),
"method": "POST",
"mode": "cors",
"credentials": "include"
});
let json = await f.json();
console.log(`${name}: ${JSON.stringify(json)}`);
return json;
}
@dvygolov
Copy link
Author

dvygolov commented Nov 12, 2022

First of all you'll be asked if you want to remove all of your existing autorules. Click Ok or Cancel.
Then you'll be asked to enter maximum CPA that is ok for you.
After that you'll be asked to enter time in 24-hour format (for example, 2). At this time all of the switched off adsets will be switched on.
Here are all the rules that will be created:

  1. Turn Off Adsets without leads if spend is more then X
  2. Turn Off Adsets with CPA > X
  3. Turn ON Adsets with CPA< X
  4. Turn all Adsets On at Y o'clock

UPD: Added 2 more rules for switching adsets with high CPC and CPM. They are optional, you can opt-out if you don't need them.

@dvygolov
Copy link
Author

dvygolov commented Nov 15, 2022

Use this service to create a one-liner from this script.
After you got a one-liner use
javascript: (async () => { one-liner })();
to create a bookmarklet.

@veinsis
Copy link

veinsis commented Nov 28, 2022

What a wonderful script! You're just GENIUS! Now I spend not 8 hours working a day but only 3 for run ads and then after all I do just chill

@dvygolov
Copy link
Author

What a wonderful script! You're just GENIUS! Now I spend not 8 hours working a day but only 3 for run ads and then after all I do just chill

Haha, if that's true - lucky you are!

@veinsis
Copy link

veinsis commented Nov 28, 2022

Yeah I am so happy find your scripts. My work became so easy. You should give me a chance to donate you bro, I didn't found where you collect donations

@dvygolov
Copy link
Author

dvygolov commented Nov 28, 2022

Yeah I am so happy find your scripts. My work became so easy. You should give me a chance to donate you bro, I didn't found where you collect donations

No problem, USDT TRC20: TKeNEVndhPSKXuYmpEwF4fVtWUvfCnWmra and thanks in advance)

@veinsis
Copy link

veinsis commented Nov 28, 2022

Yeah I am so happy find your scripts. My work became so easy. You should give me a chance to donate you bro, I didn't found where you collect donations

No problem, USDT TRC20: TKeNEVndhPSKXuYmpEwF4fVtWUvfCnWmra and thanks in advance)

Donated. Thank you for making my work more easier :)

@veinsis
Copy link

veinsis commented Jan 1, 2024

Sadly it doesn't work anymore :c

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment