Greasy Fork is available in English.

facebook set posts to private

Automation tool that sets posts in facebooks activity log to private for the new 2020 design

Per 27-02-2021. Zie de nieuwste versie.

// ==UserScript==
// @name        facebook set posts to private
// @description Automation tool that sets posts in facebooks activity log to private 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()
    }
})();