Post 通过控制台在 Facebook 页面上发表评论

Post comments on Facebook page via console

如图所示,Facebook 评论框没有提交按钮,当您写下内容并按下 Enter 按钮时,评论就会发布。

我想通过 JavaScript 在控制台中 运行 提交评论,但我试图触发 Enter 事件,提交 DOM 的事件。无法使其工作。

当前的评论框不是 <form> 内的传统 <textarea>。他们在 div 上使用 contenteditable 属性。为了在这种情况下提交,您需要监听其中一个键盘事件(keydownkeypresskeyup)并查找 Enter 键是键码 13.

在这种情况下,看起来 FB 正在监听 keydown evt,所以当我 运行 这段代码时,我能够伪造提交评论:

function fireEvent(type, element) {
    var evt;

    if(document.createEvent) {
        evt = document.createEvent("HTMLEvents");
        evt.initEvent(type, true, true);
    } else {
        evt = document.createEventObject();
        evt.eventType = type;
    }

    evt.eventName = type;
    evt.keyCode = 13;
    evt.which = 13;

    if(document.createEvent) {
        element.dispatchEvent(evt);
    } else {
        element.fireEvent("on" + evt.eventType, evt);
    }
}

fireEvent('keydown', document.querySelector('[role="combobox"]._54-z span span'));

有几点需要注意。 class ._54-z 是他们刚好在我的页面上使用的 class。你的旅费可能会改变。使用开发工具确保您获取了正确的元素(它应该具有 aria 角色 "combobox")。此外,如果您希望支持旧版浏览器,则必须调整上面的 fireEvent 函数代码。我只在最新的Chrome.

测试了上面的例子

最后,为了使您的事情复杂化,Facebook 正在使用 React 创建当前页面的虚拟 DOM 表示。如果您在组合框中手动输入字符,然后 运行 上面的代码,它将按预期工作。但是您将无法将组合框最里面的 <span> 的 innerHTML 设置为您要执行的操作然后触发 keydown。您可能需要触发组合框上的 change 事件以确保您的消息持续到虚拟 DOM.

这应该让你开始了!希望对您有所帮助!

经过 3 周的试验(使用@Benjamin Solum 的 fireEvent 函数),这是一个可行的解决方案:

  • 此版本 post 仅针对页面上的第一个 post 发表评论(使用 querySelector 方法)
  • 此版本只能在您的个人墙上使用(除非您更改查询选择器)

    function fireEvent(type, element, keyCode) {
        var evt;
    
        if(document.createEvent) {
            evt = document.createEvent("HTMLEvents");
            evt.initEvent(type, true, true);
        } else {
            evt = document.createEventObject();
            evt.eventType = type;
        }
    
        evt.eventName = type;
    
        if (keyCode !== undefined){ 
            evt.keyCode = keyCode;
            evt.which = keyCode;
        }
    
        if(document.createEvent) {
            element.dispatchEvent(evt);
        } else {
            element.fireEvent("on" + evt.eventType, evt);
        }
    }
    
    // clicking the comment link - it reveals the combobox
    document.querySelector(".fbTimelineSection .comment_link").click();
    
    setTimeout(function(){
        var combobox = document.querySelector(".fbTimelineSection [role='combobox']");
        var spanWrapper = document.querySelector(".fbTimelineSection [role='combobox'] span");
    
        // add text to the combobox
        spanWrapper.innerHTML = "<span data-text='true'>Thank you!</span>";
    
        var spanElement = document.querySelector(".fbTimelineSection [role='combobox'] span span");
    
        fireEvent("blur", combobox);
        fireEvent("focus", combobox);
        fireEvent("input", combobox);
        fireEvent("keydown", spanElement, 13); // pushing enter
    },2000);
    
function fireEvent(type, element) {
    var evt;

    if(document.createEvent) {
        evt = document.createEvent("HTMLEvents");
        evt.initEvent(type, true, true);
    } else {
        evt = document.createEventObject();
        evt.eventType = type;
    }

    evt.eventName = type;
    evt.keyCode = 13;
    evt.which = 13;

    if(document.createEvent) {
        element.dispatchEvent(evt);
    } else {
        element.fireEvent("on" + evt.eventType, evt);
    }
}

fireEvent('keydown', document.

几年后,这个 post 仍然相关,实际上是我发现的唯一一个与此相关的,而我正在尝试通过 JS 代码 post 到 FB 组(类似的任务到原来的问题)。

我终于破解了它 - 经过测试并有效:

  setTimeout(() => {
        document.querySelector('[placeholder^="Write something"]').click();
        setTimeout(() => {
            let postText = "I'm a Facebook post from Javascript!";
            let dataDiv = document.querySelector('[contenteditable] [data-offset-key]');
            let dataKey = dataDiv.attributes["data-offset-key"].value;
            //Better to construct the span structure exactly in the form FB does it
            let spanHTML = `<span data-offset-key="${dataKey}"><span data-text="true">${postText}</span></span>`;
            dataDiv.innerHTML = spanHTML;

            let eventType = "input";
            //This can probably be optimized, no need to fire events for so many elements
            let div = document.querySelectorAll('div[role=presentation]')[1].parentElement.parentElement;
            let collection = div.getElementsByTagName("*");
            [...collection].forEach(elem => {
                let evt = document.createEvent("HTMLEvents");
                evt.initEvent(eventType, true, true); //second "true" is for bubbling - might be important
                elem.dispatchEvent(evt);
            });
            //Clicking the post button
            setTimeout(()=>{
                document.querySelector('.rfloat button[type=submit][value="1"]').click();
            },2000);
        }, 4000);
    }, 7000);

故事是这样的,正如我从 post 之前的评论和深入研究 FB 的代码中了解到的那样。 FB 使用 React,因此对 DOM 的更改不会 "catch on",因为 React 使用虚拟 DOM。如果您在从 JS 更改 DOM 后单击 "Post",则不会 post 编辑文本。这就是为什么您必须按照此处的建议手动触发事件。

但是 - 为正确的元素触发正确的事件是一件棘手的事情,几乎让我无法成功。几个小时后,我发现这段代码有效,可能是因为它针对多个元素,从组 post 的父元素开始,向下钻取到所有子元素并为每个子元素触发事件(this是 [...collection].forEach(elem => { 位)。如所写,这显然可以优化以找到需要触发事件的正确元素。

至于要触发 哪个 事件,正如此处所讨论的,我已经试验了几种,发现 "input" 是其中之一。此外,在我将 initEvent 的第二个参数更改为 true 后代码开始工作 - 即 evt.initEvent(eventType, true, true)。不确定这是否有所作为,但我已经有足够的时间摆弄这个,如果它有效,那对我来说就足够了。顺便说一句,setTimeout当然可以玩。

(未成功)深挖FB的React数据结构

另一个关于我尝试走的不同路径但最终没有结果的说明:使用 React Dev Tools Chrome 扩展,您可以访问组件本身及其所有 propsstates 使用 $r。令人惊讶的是,这在控制台 之外 也有效,因此使用 TamperMonkey 之类的东西到 运行 JS 代码也有效。我实际上找到了 FB 将 post 文本保存在状态中的位置。作为参考,它位于一个名为 ComposerStatusAttachmentMentionsInputContainer 的组件中,该组件负责 post 的编辑器部分,下面是访问它的代码。

$r 实际上提供了对很多 React 内容的访问,比如 setState。从理论上讲,我相信我可以使用它来设置 React 中 post 文本的状态(如果你了解 React,你会同意 setState 将是触发会持续的更改的正确方法) .

然而,经过几个小时后,我发现这很难做到,因为 FB 在 React 之上使用了一个框架 ,称为 Draft.js,它处理所有post秒。这个框架有它自己的方法,类,数据结构等等,如果没有源代码,很难对来自 "outside" 的那些进行操作。

我还尝试手动触发附加到组件的 onchange 函数,但没有成功,因为我没有正确的参数,这些参数是 editorContent 和Draft.Js 中的 selectionContent,需要使用我无法访问的 Draft.js 中的 Modifier 之类的方法仔细构造(你到底是怎么从外部访问静态文件的?来自库的方法纠缠在源代码中??我没有设法)。

无论如何,访问存储文本的状态变量的代码,前提是你有 React 开发工具并且你已经突出显示 ComposerStatusAttachmentMentionsInputContainer:

let blockMap = $r["state"].activeEditorState[""].currentContent.blockMap;
let innerObj = JSON.parse(JSON.stringify(blockMap)); //this is needed to get the next property as it's not static or something

let id = Object.keys(innerObj)[0]; //get the id from the obj property 
console.log(innerObj[id].text); //this is it!

但正如我所写,这几乎没用:-)

因为我无法通过 "normal" facebook 页面 post 发表评论,我记得他们也有移动版本,在 m.facebook 上。 com,在那里,他们仍然有提交按钮,所以根据您的需要,这可能是一个不错的选择

因此,您可以转到移动 facebook post(例如 https://m.facebook.com/${author}/posts/${postId})并执行

// Find the input element that saves the message to be posted
document.querySelector("input[name='comment_text']").value='MESSAGE TO POST';
// find the submit button, enable it and click it
const submitButton = document.querySelector("button[name='submit']");
submitButton.disabled = false;
submitButton.click();

为了解决您的问题,您可以看看这个 link,这里有一个示例如何“使用 JavaScript 在 Facebook post 上自动发表评论”

"步骤如下:

使用 m.facebook.com 转到 Facebook 页面 登录并打开任何 post。 按 Ctrl+Shift+I 在 Chrome 中打开开发者模式 导航到控制台。 现在,运行 下面的脚本。"

var count = 100;
var message = "Hi";
var loop = setInterval(function(){
    var input = document.getElementsByName("comment_text")[0];
    var submit = document.querySelector('button[type="submit"]');
    submit.disabled = false;
    input.value = message;
    submit.click();
    count -= 1;
    if(count == 0)
    {
        clearInterval(loop);
    }
}, 10000);

亲切的问候

参考:source page