// ==UserScript== // @name facebook set posts to private public or friends // @description Automation tool that sets posts in facebooks activity log to private public or friends for the new 2020 design // @author Yorai Levi // @namespace https://github.com/YoraiLevi // @update https://github.com/YoraiLevi/SetFacebookPostsPrivate/raw/master/GreaseMonkeySetFacebookPrivate.user.js // @supportURL https://github.com/YoraiLevi/SetFacebookPostsPrivate/issues // @include https://www.facebook.com/* // @version 0.22.3 // @grant GM_setValue // @grant GM_getValue // @grant GM.openInTab // @grant window.close // @run-at document-idle // ==/UserScript== //3 user scripts merged into a single file: (function () { 'use strict'; const SCRIPT_NAME = "facebook set posts to private" console.log('Loading', SCRIPT_NAME) let GLOBAL_TIMEOUT = Infinity function delayPromise(delay) { return new Promise(resolve => setTimeout(() => { resolve() }, delay)) } async function get_selector_visible(selector, period, timeout = GLOBAL_TIMEOUT) { let s_time = performance.now() while (performance.now() - s_time < timeout) { let obj = document.querySelector(selector) if (obj && !(window.getComputedStyle(obj).display === "none" || window.getComputedStyle(obj).visibility === "hidden")) // checks if exists in dom? and supposedly? visible return obj await delayPromise(period) } TamperLog("Waiting timed out: " + selector) throw "Waiting timed out: " + selector; } async function get_selector_not_visible(selector, period, timeout = GLOBAL_TIMEOUT) { let s_time = performance.now() while (performance.now() - s_time < timeout) { let obj = document.querySelector(selector) if (!obj || (window.getComputedStyle(obj).display === "none" || window.getComputedStyle(obj).visibility === "hidden")) // checks if exists in dom? and supposedly? visible return obj await delayPromise(period) } TamperLog("Waiting timed out: " + selector) throw "Waiting timed out: " + selector; } const privacy_mode = { "public": "public", "friends" : "friends", // "friends_except" : "friends_except", // "specific_friends" : "specific_friends", "private": "private" } const post_privacy_mode_icons = { "public": "https://static.xx.fbcdn.net/rsrc.php/v3/yJ/r/YSM7OHnZVHv.png", "friends" : "https://static.xx.fbcdn.net/rsrc.php/v3/y5/r/9EJBw2oYDPv.png", "friends_except" : "https://static.xx.fbcdn.net/rsrc.php/v3/yA/r/cV1LK8Ks1o7.png", "specific_friends" : "https://static.xx.fbcdn.net/rsrc.php/v3/yq/r/NmyxV_4nmDz.png", "private": "https://static.xx.fbcdn.net/rsrc.php/v3/yD/r/JSmL99pVrUz.png" } function TamperLog(str){ let log = GM_getValue("log", []) log.push(Date.now()+": "+str) GM_setValue("log",log) } function addInput(parentElement = document.body, cssObj) { cssObj = cssObj || { position: 'relative', 'z-index': 3 } let input = document.createElement('input'), btnStyle = input.style parentElement.appendChild(input) input.type = "number" Object.keys(cssObj).forEach(key => btnStyle[key] = cssObj[key]) return input } function addButton(text, onclick, parentElement = document.body, cssObj) { cssObj = cssObj || { position: 'relative', 'z-index': 3 } let button = document.createElement('button'), btnStyle = button.style parentElement.appendChild(button) button.innerHTML = text button.onclick = onclick Object.keys(cssObj).forEach(key => btnStyle[key] = cssObj[key]) return button } function stopButtonHandle(){ GM_setValue("active",false) } //set privacy action if (document.URL.match("https://www.facebook.com/.+/posts/.+|https://www.facebook.com/photo.+") && GM_getValue("active",false)) { (function () { 'use strict'; console.log("Initializing") window.addEventListener('load', async () => { let parent = document.body let divButtons = document.createElement('div') addButton('Stop',stopButtonHandle,divButtons) let div = document.createElement('div') let cssObj = { position: 'fixed', display: "block", 'z-index': 3, background: "#ffffff", border: "3px solid red" } Object.keys(cssObj).forEach(key => div.style[key] = cssObj[key]) div.appendChild(divButtons) parent.insertBefore(div, parent.childNodes[0]) }) let three_dot_menu_selector = "div > div.nqmvxvec.j83agx80.jnigpg78.cxgpxx05.dflh9lhu.sj5x9vvc.scb9dxdr.odw8uiq3 > div > div" let menu_buttons_selector = "div.cwj9ozl2.ue3kfks5.pw54ja7n.uo3d90p7.l82x9zwi.nwpbqux9.rq0escxv.jgsskzai.ni8dbmo4.stjgntxs > div > div.j83agx80.cbu4d94t.buofh1pr > div.tojvnm2t.a6sixzi8.k5wvi7nf.q3lfd5jv.pk4s997a.bipmatt0.cebpdrjk.qowsmv63.owwhemhu.dp1hu0rb.dhp61c6y.l9j0dhe7.iyyx5f41.a8s20v7p > div" const privacy_choice_selector = { "public": "div.kr520xx4.pedkr2u6.ms05siws.pnx7fd3z.b7h9ocf4.pmk7jnqg.j9ispegn.k4urcfbm > div.cbu4d94t.j83agx80 > div > div > div > div > div > div > div:nth-child(1) > div", "friends" : "div.kr520xx4.pedkr2u6.ms05siws.pnx7fd3z.b7h9ocf4.pmk7jnqg.j9ispegn.k4urcfbm > div.cbu4d94t.j83agx80 > div > div > div > div > div > div > div:nth-child(2) > div", "friends_except" : "div.kr520xx4.pedkr2u6.ms05siws.pnx7fd3z.b7h9ocf4.pmk7jnqg.j9ispegn.k4urcfbm > div.cbu4d94t.j83agx80 > div > div > div > div > div > div > div:nth-child(3) > div", "specific_friends" : "div.kr520xx4.pedkr2u6.ms05siws.pnx7fd3z.b7h9ocf4.pmk7jnqg.j9ispegn.k4urcfbm > div.cbu4d94t.j83agx80 > div > div > div > div > div > div > div:nth-child(4) > div", "private": "div.kr520xx4.pedkr2u6.ms05siws.pnx7fd3z.b7h9ocf4.pmk7jnqg.j9ispegn.k4urcfbm > div.cbu4d94t.j83agx80 > div > div > div > div > div > div > div:nth-child(5) > div" } window.addEventListener('load', async () => { await delayPromise(1000) await setPrivate(GM_getValue("privacy_mode",privacy_mode["private"])) TamperLog("closing page: "+document.URL) close() }) //p async function setPrivate(selected_privacy_mode) { let only_me_choice_selector = privacy_choice_selector[selected_privacy_mode] console.log("Setting post to",selected_privacy_mode) //checking privacy state: const privacy_settings_icon = post_privacy_mode_icons[privacy_mode[selected_privacy_mode]] let audience = document.querySelector("div > span > span > div > div > div> img") if (audience && audience.src === privacy_settings_icon){ TamperLog(document.URL+" Post was detected to be "+privacy_mode[selected_privacy_mode]+"This is the correct privacy mode for this post.") return } //The actual action to set a post to the desired privacy let three_dot_menu = await get_selector_visible(three_dot_menu_selector) three_dot_menu.click() TamperLog(document.URL+" Opened 3 dot menu") let edit_audience_menu_button = null await get_selector_visible(menu_buttons_selector) let buttons = document.querySelectorAll(menu_buttons_selector) const privacy_selector_icons = { "public": "https://static.xx.fbcdn.net/rsrc.php/v3/yS/r/k1K2sJ70emI.png", "friends" : "https://static.xx.fbcdn.net/rsrc.php/v3/yH/r/hHt2U1bRtLs.png", "friends_except" : "https://static.xx.fbcdn.net/rsrc.php/v3/yH/r/hHt2U1bRtLs.png", "specific_friends" : "https://static.xx.fbcdn.net/rsrc.php/v3/yv/r/cD5R2-Z_DDa.png", "private": "https://static.xx.fbcdn.net/rsrc.php/v3/yU/r/cfUGV2EoMCu.png" } TamperLog(document.URL+" Searching for audience settings images") let imgs = Object.values(privacy_selector_icons); for (const b of buttons) { //make cross language with icon detection instead? let img = b.querySelector("*> img") if (img && imgs.indexOf(img.src) !== -1) edit_audience_menu_button = b } if (edit_audience_menu_button) { edit_audience_menu_button.click() } else { TamperLog(document.URL+" Couldn't detect edit_audience_menu_button") return } let only_me_choice = await get_selector_visible(only_me_choice_selector) await delayPromise(1000) //dummy(presses the x to close window) // let closeX = await get_selector_visible("#mount_0_0 > div > div:nth-child(1) > div.rq0escxv.l9j0dhe7.du4w35lb > div:nth-child(5) > div > div > div.rq0escxv.l9j0dhe7.du4w35lb > div > div.iqfcb0g7.tojvnm2t.a6sixzi8.k5wvi7nf.q3lfd5jv.pk4s997a.bipmatt0.cebpdrjk.qowsmv63.owwhemhu.dp1hu0rb.dhp61c6y.l9j0dhe7.iyyx5f41.a8s20v7p > div > div > div > div > div.kr520xx4.pedkr2u6.ms05siws.pnx7fd3z.b7h9ocf4.pmk7jnqg.j9ispegn.k4urcfbm > div.cypi58rs.pmk7jnqg.fcg2cn6m.tkr6xdv7 > div") // closeX.click() TamperLog(document.URL+" Changing Privacy") only_me_choice.click() await get_selector_not_visible(only_me_choice_selector) //wait for privacy to change in page while (audience && audience.src !== post_privacy_mode_icons[selected_privacy_mode]) { console.log("Waiting for audience to update") TamperLog(document.URL+" Waiting for audience to update") await delayPromise(100) } TamperLog(document.URL+" Processing done. Post is correctly set to "+privacy_mode[selected_privacy_mode]) return } })(); } //inject activity manager else if (document.URL.match("https://www.facebook.com/.+/allactivity.+")) { (function () { 'use strict' console.log("Initializing") function get_items() { let activity_selector = "div > div:nth-child(1) > div.rq0escxv.l9j0dhe7.du4w35lb > div.rq0escxv.l9j0dhe7.du4w35lb > div > div > div.j83agx80.cbu4d94t.d6urw2fd.dp1hu0rb.l9j0dhe7.du4w35lb > div.rq0escxv.l9j0dhe7.du4w35lb.j83agx80.pfnyh3mw.jifvfom9.gs1a9yip.owycx6da.btwxx1t3.buofh1pr.dp1hu0rb.ka73uehy > div.rq0escxv.l9j0dhe7.tkr6xdv7.j83agx80.cbu4d94t.pfnyh3mw.d2edcug0.hpfvmrgz.dp1hu0rb.rek2kq2y.o36gj0jk > div > div.q5bimw55.rpm2j7zs.k7i0oixp.gvuykj2m.j83agx80.cbu4d94t.ni8dbmo4.eg9m0zos.l9j0dhe7.du4w35lb.ofs802cu.pohlnb88.dkue75c7.mb9wzai9.d8ncny3e.buofh1pr.g5gj957u.tgvbjcpo.l56l04vs.r57mb794.kh7kg01d.c3g1iek1.k4xni2cv > div.j83agx80.cbu4d94t.buofh1pr > div.aov4n071 > div *> div> a" let activities = document.querySelectorAll(activity_selector) TamperLog("found selector scrolling_container_selector: "+Boolean(activities)+" "+activity_selector) if(!activities){ throw "No activities detected" } return activities } window.addEventListener('load', async () => { console.log("Injecting GUI") let parent = document.body function insertAfter(referenceNode, newNode) { referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); } let divInputs = document.createElement('div') let spanMin = document.createElement('div'); spanMin.innerText = "From:(Blank=First)", divInputs.appendChild(spanMin) let min = addInput(divInputs) min.value = GM_getValue("from", null) + 1 //one based let spanMax = document.createElement('div'); spanMax.innerText = "To:(Blank=No Limit)", divInputs.appendChild(spanMax) let max = addInput(divInputs) function openRangeButtonActionClosure(min_input_element, max_input_element) { return function openRangeButtonAction() { //The gui is 1 indexed and the functions are 0 indexed let from = min_input_element.value ? min_input_element.value - 1 : 0 let to = max_input_element.value ? max_input_element.value - 1 : Infinity console.log("Processing range [from:", from, "to:", to, ")") openRange(from, to) } } async function openButtonAction() { openRange(0, Infinity) } let divButtons = document.createElement('div') addButton('Open Range', openRangeButtonActionClosure(min, max), divButtons) addButton('Open All', openButtonAction, divButtons) function downloadLogs(){ var a = document.createElement("a"); a.href = "data:text,"+JSON.stringify(GM_getValue("log",[]), null, 2); a.download = "FacebookAutomationToolLog.txt"; a.click(); } addButton('Download Logs', downloadLogs, divButtons) addButton('Stop',stopButtonHandle,divButtons) let SelectPrivacyMode = addOption(privacy_mode,divInputs) function setPrivacyMode(){ GM_setValue("privacy_mode",SelectPrivacyMode.value) console.log(GM_getValue("privacy_mode",privacy_mode["private"])) } SelectPrivacyMode.value = GM_getValue("privacy_mode",privacy_mode["private"]) SelectPrivacyMode.onchange = setPrivacyMode let div = document.createElement('div') let cssObj = { position: 'fixed', display: "block", 'z-index': 3, background: "#ffffff", border: "3px solid red" } Object.keys(cssObj).forEach(key => div.style[key] = cssObj[key]) div.appendChild(divButtons) div.appendChild(divInputs) parent.insertBefore(div, parent.childNodes[0]) }) function addOption(options, parentElement = document.body,cssObj){ cssObj = cssObj || { position: 'relative', 'z-index': 3 } let select = document.createElement('select'), btnStyle = select.style parentElement.appendChild(select) for (const [key, value] of Object.entries(options)) { let option = document.createElement('option') option.value = value option.innerText = key select.appendChild(option) } Object.keys(cssObj).forEach(key => btnStyle[key] = cssObj[key]) return select } async function openRange(from, to) { GM_setValue("active",true) //zero indexed [0from,to) async function handleRange(from, to) { let items = Array.from(get_items()) let rangeItems = items.slice(from, to) //reseting from value to valid value per iteration GM_setValue("from", from) const only_me_lock_icon = post_privacy_mode_icons[GM_getValue("privacy_mode",privacy_mode["private"])] for (const item of rangeItems) { if(!GM_getValue("active",false)) return TamperLog("Processing element:"+ item) console.log("Processing element:", item) let audience = item.querySelector("*>img") if (audience && audience.src !== only_me_lock_icon) { await new Promise(async (resolve, reject) => { let tab = await GM.openInTab(item.href, false) tab.onclose = () => { resolve() } }) } GM_setValue("from", GM_getValue("from", 0) + 1) } return items.length } async function scrollRange(from, max_failures = 30) { //zero indexed [0from,to) let failed = 0; let prevscrollTop = null; while (from > get_items().length - 1) { //from is both an index and is inclusive! let scrolling_container_selector = "div > div:nth-child(1) > div.rq0escxv.l9j0dhe7.du4w35lb > div.rq0escxv.l9j0dhe7.du4w35lb > div > div > div.j83agx80.cbu4d94t.d6urw2fd.dp1hu0rb.l9j0dhe7.du4w35lb > div.rq0escxv.l9j0dhe7.du4w35lb.j83agx80.pfnyh3mw.jifvfom9.gs1a9yip.owycx6da.btwxx1t3.buofh1pr.dp1hu0rb.ka73uehy > div.rq0escxv.l9j0dhe7.tkr6xdv7.j83agx80.cbu4d94t.pfnyh3mw.d2edcug0.hpfvmrgz.dp1hu0rb.rek2kq2y.o36gj0jk > div > div.q5bimw55.rpm2j7zs.k7i0oixp.gvuykj2m.j83agx80.cbu4d94t.ni8dbmo4.eg9m0zos.l9j0dhe7.du4w35lb.ofs802cu.pohlnb88.dkue75c7.mb9wzai9.d8ncny3e.buofh1pr.g5gj957u.tgvbjcpo.l56l04vs.r57mb794.kh7kg01d.c3g1iek1.k4xni2cv" let scrolling_container = document.querySelector(scrolling_container_selector) TamperLog("found selector scrolling_container_selector: "+Boolean(scrolling_container)+" "+scrolling_container_selector) //are we at the bottom? if (prevscrollTop === scrolling_container.scrollTop) { failed++; //yes we are and for too long, something is either wrong or we're done. if (failed > max_failures){ TamperLog("Failure Scrolling Down, is that everything?") throw "Failure Scrolling Down, is that everything?" } } prevscrollTop = scrolling_container.scrollTop scrolling_container.scrollTop = scrolling_container.scrollHeight await delayPromise(1000) } } while (from < to) { await scrollRange(from) from = await handleRange(from, to) } GM_setValue("active",false) } }()) } //ignore else if (document.URL.match("https://www.facebook.com/permalink.php.*|https://www.facebook.com/groups/.*")) { TamperLog(document.URL+" This is a post from a group or an unavailable source. cannot change this privacy") close() } })();