corginyan / Instagram post comment usernames collector

// ==UserScript==
// @name     Instagram post comment usernames collector
// @include  https://www.instagram.com/p/*
// @grant    GM.setClipboard
// @license MIT
// @version 0.0.5
// ==/UserScript==

class Tool {
    constructor() {
        this._wait = 0;
        this._dataString = "";

        console.log('Create comments tool')
    }

    download(filename, text) {
        let pom = document.createElement('a');
        pom.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
        pom.setAttribute('download', filename);

        if (document.createEvent) {
            let event = document.createEvent('MouseEvents');
            event.initEvent('click', true, true);
            pom.dispatchEvent(event);
        }
        else {
            pom.click();
        }
    }

    setWait(timeout) {
        this._wait = timeout;
    }

    getCommentSection() {
        let popup = this.getSectionByXpath(document, '/html/body/span/section/main/div/div/article/div[2]/div[1]/ul');

        if (popup) {
            return popup
        }

        let post = this.getSectionByXpath(document, '/html/body/div[2]/div/div[2]/div/article/div[2]/div[1]/ul');

        if (post) {
            return post
        }
    }

    getRandom(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    isCommentSection() {
        return !!this.getCommentSection();
    }

    isWait() {
        if (this._wait > 0) {
            this._wait--;
            return true
        }

        return false
    }

    getCommentButton() {
        let comments = this.getCommentSection();

        let loadMoreButton;

        for (let liItem of comments.children) {

            let liChild = liItem.children[0];

            if (!liChild) {
                return
            }

            if (liChild.nodeName === 'BUTTON') {
                loadMoreButton = liChild;
                break
            }
        }

        return loadMoreButton;
    }

    isCommentButton() {
        return !!this.getCommentButton();

    }

    isHasData() {
        return !!this._dataString;
    }

    pressMoreComments() {
        let button = this.getCommentButton();

        if (!button) {
            return
        }

        console.log('Load more comments');

        button.click();
    }

    getSectionByXpath(root, path) {
        return document.evaluate(path, root, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
    }

    getCommentUsername(root) {
        let element = this.getSectionByXpath(root, 'div/div/div/h3/a');

        if (!element) {
            element = this.getSectionByXpath(root, 'div/div/div/h2/a');
        }

        if (element) {
            return element.href
        }

        return ""

    }

    getCommentData(root) {

        let element = this.getSectionByXpath(root, 'div/div/div/span');

        if (element) {
            return element.textContent
        }

        return ""
    }


    copyData() {

        console.log('Process data copy');

        let withCommentsString = "";
        let usernamesString = "";

        let count = 0;

        let comments = this.getCommentSection();

        let children = comments.children;

        console.log('Posts count: ' + children.length);

        let dataArray = [];

        for (let item of children) {

            let username = this.getCommentUsername(item);

            if (!username) {
                continue
            }

            let comment = this.getCommentData(item);

            dataArray.push([username, comment]);

            count++;
        }

        dataArray.shift();
        count -= 1;

        console.log('Urls count: ' + count);

        let maxLength = 0;

        dataArray.forEach((array) => {
            if (array[0].length > maxLength) {
                maxLength = array[0].length;
            }
        });

        maxLength += 1;

        dataArray.forEach(array => {
            array[0] += ' '.repeat(maxLength - array[0].length)
        });

        dataArray.forEach(array => {
            withCommentsString += array[0] + array[1] + '\n';
            usernamesString += array[0] + '\n'
        });

        withCommentsString = withCommentsString.replace(/\n$/, "");
        usernamesString = usernamesString.replace(/\n$/, "");

        // console.log('Trying copy to clipboard...');

        // GM.setClipboard(withCommentsString);

        this.download("comments.txt", withCommentsString);
        setTimeout(() => this.download("usernames.txt", usernamesString), 100);

        // alert('Collecting data finished');

        this._dataString = dataString
    }

    isPathChanged() {
        if (this._lastPath !== location.pathname) {
            this._lastPath = location.pathname;
            return true
        }
    }

    isPathValid() {
        return /^\/p\//.test(location.pathname);
    }

    clearData() {
        this._dataString = "";
    }

    update() {
        if (!this.isPathValid()) {
            return
        }

        if (this.isPathChanged()) {
            console.log('Url is changed, clear data and collect again...');
            this.clearData()
        }

        if (this.isHasData()) {
            return
        }

        if (this.isWait()) {
            return
        }

        if (!this.isCommentSection()) {
            this.setWait(60);
            return
        }

        // Assuming if no comments button - comments are fully loaded
        // TODO: check
        if (this.isCommentButton()) {
            this.pressMoreComments();
            this.setWait(this.getRandom(60, 80));
        } else {
            this.copyData();
        }

    }
}

let tool = new Tool();

function loop() {
    tool.update();

    window.requestAnimationFrame(loop)
}

window.requestAnimationFrame(loop);