roman / pikabuuspeli 3.0

// ==UserScript==
// @name            pikabuuspeli 3.0
// @description     pikabuuspeli
// @namespace       pikabuuspeli
// @version         3
// @include         https://pikabu.ru/story/*
// @connect uspeli.tk
// @connect vk.com
// @connect localhost
// @connect userapi.com
// @connect *
// @author github.com/xPomaHx
// @require      https://code.jquery.com/jquery-3.4.1.min.js
// @require      https://browser.sentry-cdn.com/5.11.0/bundle.min.js

// @updateURL https://openuserjs.org/meta/roman/pikabuuspeli_3.0.meta.js
// @downloadURL https://openuserjs.org/install/roman/pikabuuspeli_3.0.user.js
// @copyright2020, roman (https://openuserjs.org/users/roman)

// @homepage     https://vk.com/brobothelper
// @homepage     https://vk.com/klubnikapikabu

// @license MIT
// @grant GM_xmlhttpRequest
// ==/UserScript==

'use strict';

// const API_URL = 'http://localhost:5000/api/v1/comment';
const API_URL = 'http://uspeli.tk/api/v1/comment';
const http = (method, urlPath, payload) => {
    const url = new URL(urlPath, API_URL);
    const data = JSON.stringify(payload);
    if (method.toLowerCase() === 'get' && typeof payload === 'object') {
        Object.entries(payload).forEach((param) => {
            url.searchParams.append(...param);
        });
    }
    return new Promise(resolve => {
        GM_xmlhttpRequest({
            method: method.toUpperCase(),
            url: url.toString(),
            data,
            headers: {
                'Content-Type': 'application/json;charset=UTF-8'
            },
            onload: function ({ response }) {
                resolve(JSON.parse(response/*.replace('\\"', '"')*/));
            }
        });
    });
};

function throttle(func, wait, options) {
    var context, args, result;
    var timeout = null;
    var previous = 0;
    if (!options) options = {};
    var later = function () {
        previous = options.leading === false ? 0 : Date.now();
        timeout = null;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
    };
    return function () {
        var now = Date.now();
        if (!previous && options.leading === false) previous = now;
        var remaining = wait - (now - previous);
        context = this;
        args = arguments;
        if (remaining <= 0 || remaining > wait) {
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            previous = now;
            result = func.apply(context, args);
            if (!timeout) context = args = null;
        } else if (!timeout && options.trailing !== false) {
            timeout = setTimeout(later, remaining);
        }
        return result;
    };
}

const getComment = async (commentId) => {
    return http('get', 'comment', {
        commentId
    });
};

const addUI = () => {
    if (localStorage.pikabuuspelisaved) {
        var pikabuuspelisaved = JSON.parse(localStorage.pikabuuspelisaved);
    } else {
        localStorage.pikabuuspelisaved = JSON.stringify([]);
    }
    const $comments = $('.comments .comment');
    $comments.each((i, comment) => {
        const $comment = $(comment);
        const commentId = $comment.attr('data-id');
        const message = {
            content: [
                $comment.find('>.comment__body>.comment__content').text().trim().replace(/[ \t]{2,}/gim, ' '),
                ...$comment.find('>.comment__body>.comment__content [data-external-link]').map(function () {
                    return $(this).attr('data-external-link');
                }),
                ...$comment.find('>.comment__body>.comment__content> a')
                    .map(function () { return $(this).attr('href'); })
            ].join('\n'),
            attachments: [
                ...$comment.find('>.comment__body>.comment__content>.comment-image  a')
                    .map(function () { return $(this).attr('href'); })
            ],
            user: $comment.find('>.comment__body>.comment__header>.comment__user>.user').attr('href'),
            childrenIds: $comment.find('>.comment__children>.comment').map((i, el) => {
                return $(el).attr('data-id');
            }).toArray(),

        };
        const $comment__tools = $comment.find('>.comment__body>.comment__header>.comment__tools');
        if (!$comment__tools.find('>.broTools').length) {
            $comment__tools.find('>.broTools').remove();
            const $broTools = $('<div>').addClass('broTools');

            const $saveBtn = $('<button>');
            $saveBtn.html('💾');
            const $restoreBtn = $('<button>');
            $restoreBtn.html('✨');

            $broTools.append($saveBtn);
            $broTools.append($restoreBtn);

            if (pikabuuspelisaved.includes(commentId)) {
                $saveBtn.attr('disabled', true);
            }
            $saveBtn.on('click', () => {
                pikabuuspelisaved.push(commentId);
                localStorage.pikabuuspelisaved = JSON.stringify(pikabuuspelisaved);
                $saveBtn.attr('disabled', true);
                http('post', 'comment', [
                    { commentId, message }
                ]).then(console.dir);
            });
            $restoreBtn.on('click', () => {
                $restoreBtn.attr('disabled', true);
                $comment.find('>.comment__body>.comment__content .bro-restored-comment').remove();
                getComment(commentId).then(comments => {
                    const $broRestoredComments = $('<div>').addClass('bro-restored-comment');
                    if (!comments.length) {
                        $broRestoredComments.html('не найдено').css({ 'background-color': 'rgba(200,20,30,0.3)' });
                    }
                    comments.forEach(comment => {
                        const comentContent = [comment.message.user, comment.message.content, ...comment.message.attachments.map(url => {
                            return url + `\n<img width='100%' src='${url}'/>`;
                        })].join('\n');
                        const $comment = $('<pre>').html(comentContent);
                        $comment.css({ margin: 10, 'background-color': 'rgba(10,20,30,0.2)', 'white-space': 'pre-wrap' });
                        $broRestoredComments.append($comment);
                    });
                    $comment.find('>.comment__body>.comment__content').append($broRestoredComments);
                    $restoreBtn.attr('disabled', false);
                });


                $comment.find('>.comment__body>.comment__content').append()
            });
            $comment__tools.append($broTools);
        }
    });
};

async function init() {
    Sentry.init({ dsn: 'https://7f9ff1a34bf541089eab4c1aac92f01a@sentry.io/1878469' });
    const addUIaddUIthrottled = throttle(addUI, 5000, { 'trailing': true, leading: true });
    addUIaddUIthrottled();
    $('.comments').each(function () {
        new MutationObserver(function () {
            addUIaddUIthrottled();
        }).observe(this, { attributes: true, childList: true, subtree: true });
    });
}

! function (win) {
    if (window != window.top) return;
    win.addEventListener('load', setTimeout.bind(null, init, 999), false);
}(typeof unsafeWindow == 'undefined' ? window : unsafeWindow);